June 16, 2026
Analysis CVE-2026–48907 — Joomla JCE
Sekitar beberapa hari lalu, terdapat kerentanan pada extension JCE(Joomla Content Editor) yang digunakan CMS joomla untuk menggantikan…
xpl0dec
6 min read
Sekitar beberapa hari lalu, terdapat kerentanan pada extension JCE(Joomla Content Editor) yang digunakan CMS joomla untuk menggantikan editor bawaan. JCE berfungsi untuk mempermudah user ketika menulis content, artikel, mengatur table, upload file dan semacamnya.
NVD Awaiting Enrichment This CVE record has been marked for NVD enrichment efforts. A vulnerability in the JCE editor…
CVE-2026–48907 ini merupakan kerentanan yang terjadi pada fungsi import dan dapat di exploit tanpa authentikasi / unaunthenticated maka dari itu score CVSS pada CVE ini 9.8 atau critical dengan versi yang terdampak yaitu ≤= 2.9.99.5. Disini saya mencoba melakukan analisis dan research untuk menemukan root cause pada source code, jadi kita akan banyak belajar bagaimana routing dan penulisan code pada CMS joomla itu sendiri.
Cara kerja component pada joomla ?
Component pada joomla merupakan salah satu jenis extension, yang dikembangkan dengan menggunakan pola arsitektur MVC(Model View Controller) dan mungkin bagi banyak programmer hal ini cukup umum karena MVC sendiri digunakan agar code lebih terstruktur yang membagi code melalui 3 komponen dan hal ini bertujuan untuk memisahkan business logic dan user interface. Component memiliki 2 bagian yaitu bagian sites dan administrator, bisa dikatakan juga sites bertugas di sisi frontend sementara administrator bertugas untuk menangani data dari sisi backend.
Jadi untuk akses component pada joomla kita bisa menggunakan 2 pendekatan melalui parameter GET seperti berikut :
- http://localhost/index.php?option=com_<component_name>
- http://localhost/index.php?option=com_jce
Dan jika pada administrator / backend tinggal kita tambahkan path:
- http://localhost/administrator/index.php?option=com_<component_name>
- http://localhost/administrator/index.php?option=com_jce
Kemudian, yaitu directory structure. Ketika selesai instalasi suatu extension maka component akan disimpan pada directory /components/com<component_name>/_ yang pada banyak kasus akan memanggil atau melakukan include pada file component backend yang berada di directory /administrator/components/com<component_name>/_
Bisa dilihat, jika jce.php pada frontend akan melakukan require jce.php di directory administrator melalui JPATH_ADMINISTRATOR yang merupakan core/loader file.
JCE.php akan memanggil includes/base.php
Menggunakan static method JLoader::register yang memungkinkan untuk memanggil class pada file tanpa melalui include, karena hampir semua file pada joomla itu berisi deklarasi dari class.
Jika kita melihat directory strukturnya, maka akan menemukan directory dengan nama models, views dan controller dan hal ini seperti yang sudah dibahas diatas jika joomla menerapkan design MVC.
Bagaimana memanggil method pada joomla ?
Jadi cara unik joomla untuk melakukan routing pada controller yaitu dengan menggunakan parameter
- option : parameter untuk nama component/extension
- task : menentukan nama class dan action atau method dengan contoh system.execute biasanya akan diterjemahkan menjadi controller(system) dan method(execute)
- view : untuk menentukan tampilan yang ada di folder view
Sebagai contoh, pada extension JCE ada file controller dengan nama cpanel.php dengan method feed()
Jadi untuk memanggil method feed() pada saat runtime kita bisa langsung melakukan request melalui parameter GET seperti berikut :
http://localhost/index.php?option=com_jce&task=cpanel.feedhttp://localhost/index.php?option=com_jce&task=cpanel.feed
karena method tersebut hanya melakukan echo json yang diambil dari modelnya, maka response juga akan menampilkan json. Untuk pemanggilannya cukup mudah bukan ?
Sinks dan vulnerability terjadi
Setelah saya lakukan identifikasi analysis, vulnerability terjadi pada method import di controller profile
Jadi memang benar, tidak ada validasi authentikasi yang melakukan cek apakah user sudah login atau belum untuk memanggil method ini. Jadi attacker tanpa mempunyai kredensial yang valid bisa sangat mudah melakukan exploitation.
Pada method tersebut hanya terdapat validasi token menggunakan function checkToken()
Session::checkToken() or jexit(JText::_('JINVALID_TOKEN'));Session::checkToken() or jexit(JText::_('JINVALID_TOKEN'));https://docs.joomla.org/How_to_add_CSRF_anti-spoofing_to_forms
Fungsi ini bertugas hanya untuk melakukan validasi CSRF yang tidak cukup kuat untuk mencegah serangan, karena token bisa didapatkan melalui home page / halaman utama
kemudian, code tersebut memanggil model dengan fungsi import()
$model = $this->getModel();
$result = $model->import();$model = $this->getModel();
$result = $model->import();Disini getModel() tidak mempunyai parameter, yang jika mengacu pada dokumentasi maka akan memanggil model sesuai dengan nama controllernya dan pada kasus kita ada lah profiles.php, tapi ternyata pada akhir baris ada fungsi getModel yang mendefinisikan parameter default
Parameter pertama jika tidak di definisikan, secara default akan di set ke Profile yang artinya akan memanggil models dengan nama profile tanpa s
pada model profile memang terdapat fungsi import yang digunakan untuk melakukan upload
Disini lagi lagi tidak terdapat validasi dan filter untuk melakukan cek extension, jadi sangat mungkin untuk melakukan upload script berbahaya dengan extension PHP.
File hasil upload akan disimpan pada variable $destination, yang dalam hal ini diambil dari config global tmp_path yang jika kita melihat file configuration.php akan menjadi jelas
Disini file akan diupload pada document_root/tmp yang artinya bisa diakses langsung melalui URL seperti http://localhost/tmp/namafile.txt
Proof Of Concept
jadi mari kita coba membuat PoC dari analisis diatas, yang pertama kita memerlukan token, yang mengacu pada dokumentasi berikut
CSRF Protection | Joomla! Programmers Documentation Cross-Site request forgery is an attack type where an HTML form on an external, attacker-controlled site is used to…
Yang intinya dikatakan jika kita melakukan generate token maka form akan otomatis menambahkan input type hidden dengan nama random yang mempunyai value 1, dan kemudian dikatakan juga untuk melakukan validasi kita bisa menggunakan Session::checkToken()
Hal ini sangat relevan dengan apa yang kita butuhkan, jadi ketika kita view source pada halaman login joomla maka terdapat input hidden dengan nama random dan value 1
Hal ini bisa kita gunakan untuk menyusun PoC agar dapat dilakukan exploitation. Yang kedua, kita juga harus menyertakan cookies atau session yanggterkait pada token tersebut, karena CSRF itu merupakan serangan yang terjadi ketika attacker membuat form palsu pada pengguna yang sudah login untuk melakukan action tertentu, maka dari itu 1 CSRF token hanya bisa digunakan untuk 1 session atau gampangnya 1 token 1 session.
jadi cara mudahnya, kita bisa inspect element dan masuk ke tab network kemudian ambil cookies dari headernya
Kemudian kita hit endpointnya menggunakan curl dengan request data yang sesuai
curl -b "0218c0a9c77ce64951823f5fe04c92f3=b3a7efe75e9735771c7250c5f9222f38" -F "93ccbcd2d1c5654b023a771b8b69d050=1" -F "profile_file=@info.php" "http://localhost:9091/index.php?option=com_jce&task=profiles.import" -vcurl -b "0218c0a9c77ce64951823f5fe04c92f3=b3a7efe75e9735771c7250c5f9222f38" -F "93ccbcd2d1c5654b023a771b8b69d050=1" -F "profile_file=@info.php" "http://localhost:9091/index.php?option=com_jce&task=profiles.import" -v- -b : merupakan parameter dengan isi cookie
- -F : disini token CSRF dan file kita inputkan yang pada curl file harus disertai dengan @(contoh : @file.php)
Kita melakukan upload info.php yang berisi phpinfo dengan cookie dan csrf token yang sebelumnya kita dapatkan dan terlihat response menunjukkan success melakukan import
yang pada akhirnya, kita berhasil melakukan exploit dan upload file php
PoC kita berhasil dengan mulus, dan kerentanan ini bisa terbilang sangat berbahaya karena attacker bisa melakukan upload file apapun pada server dan berpotensi bisa melakukan takeover dan merusak server.
Mitigasi & Patching
Sebenarnya mitigasinya sangat mudah, karena bisa langsung update component dari panel administrator, karena JCE juga sudah merilis versi yang lebih baru dan memperbaiki kerentanan ini.
Tapi disini, saya akan menunjukkan bagaimana patching dilakukan langsung dari source code dengan sangat mudah, jadi kita hanya perlu melakukan validasi pada method import di controller profiles, yaitu jika user dengan status guest yang berarti belum login maka langsung lakukan throw exception dan response dengan status code 403
Dan kemudian kita coba ulangi exploit
Hasilnya akan permission denied, karena kita belum melakukan authentication / login.
Kesimpulan
Sebenarnya inti dari kerentanan tersebut ada di controller profiles pada method import, dan kita sudah mempelajari root cause bagaimana kerentanan ini terjadi dan sebenarnya masih ada attack vector lain yang memanfaatkan XML parser pada method processImport() untuk melakukan upload dari controller browser, tapi berhubung akan panjang jadi tidak saya bahas dan apapun itu kita sudah mempelajari dan belajar terkait CMS joomla dan melihat bagaimana CMS ini bekerja dibalik layar. Mungkin itu saja, sekian.
"Kapasitas untuk belajar adalah sebuah anugerah; kemampuan untuk belajar adalah sebuah keterampilan; kemauan untuk belajar adalah sebuah pilihan." — Brian Herbert