Poin Utama: CVE-2026-32710 adalah kerentanan buffer overflow berbasis heap yang sangat serius pada fungsi JSON_SCHEMA_VALID() di MariaDB Serveryang dapat menyebabkan database mengalami crash. Dalam kondisi yang sangat terkendali, kerentanan ini berpotensi memungkinkan eksekusi kode jarak jauh. Sebagai bagian dari Program Beasiswa Keamanan SiberOPSWAT , para peserta program kami melakukan analisis teknis terhadap CVE-2026-32710.
Versi MariaDB Server 11.4.1–11.4.9, 11.8.1–11.8.5, dan 12.1.2 mengandung kerentanan buffer overflow berbasis heap (CWE-122) dalam proses validasi JSON Schema. Pengguna basis data yang terotentikasi dapat memicu kerentanan ini melalui fungsi JSON_SCHEMA_VALID(), yang mengakibatkan kerusakan memori dan kegagalan server. Pemberitahuan keamanan yang dipublikasikan menjelaskan bahwa eksekusi kode jarak jauh hanya mungkin terjadi di bawah kondisi tata letak memori yang sangat terkendali, yang umumnya dapat dicapai di lingkungan laboratorium.
Berikut ini adalah analisis teknis mengenai CVE-2026-32710 dan bagaimana eksploitasi kerentanan tersebut dapat memengaruhi validasi skema JSON di MariaDB, yang dilakukan oleh Khoa Bui Le Anh dan Minh Pham Nhat, sebagai bagian dari Program Beasiswa Keamanan Siber OPSWAT /

Klasifikasi
NVD mencantumkan skor CVSS v3.1 sebesar 9,9 (Kritikal); catatan tersebut juga mencakup penilaian CNA terpisah sebesar 8,5 (Tinggi). Perbedaan peringkat kompleksitas serangan tersebut mencerminkan penilaian yang berbeda mengenai seberapa praktis eksploitasi tersebut dilakukan.

Tentang MariaDB
MariaDB adalah basis data relasional sumber terbuka yang dikembangkan oleh para pengembang asli MySQL, dan banyak digunakan di lingkungan Linux, cloud, hosting, serta perusahaan. MariaDB menangani JSON dengan menyimpannya sebagai teks (alias untuk tipe data LONGTEXT) dan menyediakan fungsi-fungsi untuk mengolahnya, termasuk JSON_SCHEMA_VALID(), yang memeriksa kesesuaian dokumen JSON dengan skema yang telah ditentukan sebelumnya.
Kerentanan buffer overflow berbasis heap pada fungsi ini, yang terdaftar sebagai CVE-2026-32710, dapat menyebabkan server macet dan, dalam kondisi tertentu, memungkinkan eksekusi kode jarak jauh.
Latar Belakang Teknis
Validasi Skema JSON di MariaDB
Dokumen JSON yang valid secara sintaksis belum tentu sesuai dengan struktur yang diharapkan oleh suatu aplikasi. Fungsi JSON_VALID() dari MariaDB hanya memeriksa apakah suatu nilai mengandung sintaks JSON yang valid. Aplikasi mungkin juga perlu memastikan kepatuhan terhadap properti yang wajib ada, tipe data yang diharapkan, batasan numerik, keunikan array, atau nilai-nilai yang diperbolehkan.
Untuk tujuan tersebut, MariaDB menyediakan fungsi JSON_SCHEMA_VALID(schema, json), yang memeriksa kesesuaian dokumen JSON dengan skema JSON yang ditentukan. Fungsi ini mengembalikan nilai 1 jika dokumen memenuhi skema tersebut, dan 0 jika validasi gagal.

Variabel yang Didefinisikan Pengguna
MariaDB mendukung variabel yang ditentukan pengguna yang memungkinkan nilai-nilai disimpan dan digunakan kembali di berbagai pernyataan SQL dalam sesi basis data yang sama. Variabel-variabel ini diawali dengan simbol @ dan tetap tersedia sepanjang sesi tersebut. Contohnya:

Pernyataan pertama menyimpan nilai "red" ke dalam @color. Pernyataan-pernyataan selanjutnya dalam sesi yang sama dapat membaca nilai tersebut.
Secara internal, MariaDB menyimpan setiap variabel dalam struktur `user_var_entry`. Struktur tersebut berisi metadata untuk variabel tersebut dan sebuah penunjuk ke nilai saat ini. Gambar di bawah ini menunjukkan tata letak lengkapnya. Untuk analisis ini, bidang-bidang yang penting adalah penunjuk nilai dan panjang nilai.

Saat MariaDB menjalankan perintah SET @name = value;, fungsi Item_func_set_user_var::set_entry() memanggil fungsi get_variable().

Jika @name sudah ada dalam tabel variabel sesi, MariaDB akan menggunakan kembali user_var_entry yang sudah ada. Jika tidak ada, MariaDB akan membuat entri baru dan mengalokasikan struktur user_var_entry di heap. Pada tahap ini, MariaDB telah membuat catatan variabel, tetapi belum menyimpan nilai yang ditetapkan. entry->value bernilai NULL, dan entry->length bernilai nol.
MariaDB kemudian mengevaluasi ekspresi di sebelah kanan tanda = dalam fungsi `Item_func_set_user_var::check()`. Setelah mengevaluasi nilai tersebut, fungsi `Item_func_set_user_var::update()` memanggil fungsi `update_hash()`.

Untuk string, fungsi `update_hash()` menyisihkan satu byte tambahan untuk karakter penutup \0.
Keputusan mengenai penyimpanan bergantung pada panjang string yang baru:
- 7 byte atau kurang: MariaDB menyimpan nilai tersebut di area inline. Jika nilai sebelumnya menggunakan buffer eksternal, buffer tersebut akan dilepaskan.
- 8 byte atau lebih: MariaDB menyimpan nilai tersebut dalam buffer heap terpisah yang ukurannya disesuaikan dengan ukuran data ditambah \0.
- Selama proses pembaruan melalui SET, jika nilai sebelumnya disimpan secara inline, MariaDB akan mengalokasikan buffer eksternal baru. Jika nilai sebelumnya sudah menggunakan buffer eksternal, fungsi `my_realloc()` akan menyesuaikan ukuran alokasi tersebut di tempat atau memindahkannya ke alamat heap baru, tergantung pada ukuran yang diminta.
UDF (Fungsi yang Didefinisikan Pengguna)
Fungsi Pengguna (UDF) MariaDB memungkinkan pengembang untuk memperluas kemampuan SQL dengan fungsi-fungsi yang diimplementasikan dalam pustaka bersama. Fungsi-fungsi ini berguna ketika suatu aplikasi memerlukan perilaku yang tidak disediakan oleh MariaDB melalui fungsi bawaan.
Sebuah UDF didaftarkan menggunakan perintah `CREATE FUNCTION` dan kemudian dapat dipanggil seperti fungsi SQL biasa:

Pustaka bersama harus ditempatkan di direktori plugin MariaDB yang telah dikonfigurasi. Mekanisme ekstensi ini akan menjadi penting nantinya, karena rantai eksploitasi akan mengalihkan nilai `plugin_dir` dan kemudian memuat objek bersama yang dikendalikan oleh penyerang.
Alokasi dan Penggunaan Kembali Heap
Di lingkungan Linux yang digunakan untuk analisis ini, MariaDB menggunakan fungsi `malloc()` dari glibc untuk alokasi heap. Jalur `json_get_normalized_string()` yang rentan menginisialisasi buffer sementara dengan kapasitas 128 byte. Setelah glibc menambahkan metadata alokator dan penyelarasan, alokasi tersebut masuk ke dalam kelas ukuran chunk heap 0x90. Oleh karena itu, analisis heap berfokus pada chunk 0x90, karena penggunaan ulang chunk dengan ukuran tersebut dapat menempatkan buffer yang rentan di samping objek user_var_entry yang dipilih.
Ketika glibc mengalokasikan blok lain dalam kelas ukuran ini, glibc terlebih dahulu memeriksa cache lokal benang (tcache). Tcache menyimpan blok-blok yang baru saja dibebaskan dalam daftar bertaut tunggal dan menggunakannya kembali sesuai urutan last-in, first-out. Pada gambar, EEE, FFF, dan ZZZ mewakili blok-blok kosong berukuran 0x90. Jika ZZZ berada di ujung daftar, alokasi berturut-turut akan menggunakan kembali ZZZ, kemudian FFF, dan selanjutnya EEE.

Jika tidak ada chunk yang kompatibel, glibc akan mencari di bin-bin lain yang dapat digunakan kembali. Tergantung pada status alokator, bin-bin tersebut dapat mencakup fastbins, small bins, atau unsorted bin. Jika tidak ada chunk kosong yang sesuai, glibc akan mengambil memori dari chunk teratas, yaitu ruang kosong yang belum terpakai di bagian akhir arena heap saat ini.
Fungsi, variabel, aturan | Tujuan | Rincian penting |
JSON_VALID() | Hanya memeriksa sintaks JSON | Tidak memverifikasi struktur, tipe, atau kolom yang wajib diisi |
JSON_SCHEMA_VALID(skema, json) | Memeriksa kesesuaian dokumen JSON dengan Skema JSON | Memastikan kepatuhan terhadap properti yang diwajibkan, tipe data, batas numerik, keunikan array, dan nilai yang diperbolehkan. Mengembalikan 1 jika valid, 0 jika tidak |
Variabel yang ditentukan pengguna | Penyimpanan bernilai dengan cakupan sesi, yang diawali dengan @ | Tetap berlaku di seluruh pernyataan dalam sesi yang sama (misalnya, @color = 'red') |
entri_variabel_pengguna | Struktur heap internal yang menyimpan setiap variabel | Kolom utama: penunjuk nilai dan panjang. Entri baru dimulai dengan nilai = NULL, panjang = 0 |
SET @name = nilai | Alur tugas | set_entry() → get_variable() (menggunakan kembali atau membuat entri) → check() (mengevaluasi sisi kanan) → update() → update_hash() (menyimpan nilai) |
Aturan penyimpanan string | Di mana nilai tersebut disimpan, berdasarkan panjangnya (+1 byte untuk \0) | ≤ 7 byte: disimpan secara inline, buffer eksternal yang sudah tidak terpakai akan dibebaskan. ≥ 8 byte: disimpan dalam buffer heap terpisah. |
Memperbarui string yang tersimpan | Perilaku memori saat penugasan ulang | Nilai sebelumnya inline → mengalokasikan buffer eksternal baru. Nilai sebelumnya eksternal → my_realloc() mengubah ukurannya di tempat atau memindahkannya ke alamat heap baru |
Fungsi yang Didefinisikan Pengguna (UDF) | Fungsi SQL khusus yang diimplementasikan dalam pustaka bersama | Telah didaftarkan dengan CREATE FUNCTION, dipanggil seperti fungsi bawaan |
Direktori plugin | Di mana pustaka bersama UDF harus disimpan | Yang perlu diperhatikan nanti: rantai eksploitasi tersebut mengalihkan `plugin_dir` dan memuat objek bersama yang dikendalikan penyerang |
Mengapa Hal Ini Penting bagi Eksploit Tersebut
Variabel pengguna memberi penyerang cara untuk mengendalikan alokasi heap (ukuran, waktu, dan perilaku realokasi), sedangkan UDF membuka jalan untuk mengubah kerusakan memori menjadi eksekusi kode dengan memuat pustaka berbahaya.
Alur Eksekusi Internal
Alur evaluasi JSON_SCHEMA_VALID()
Fungsi JSON_SCHEMA_VALID() bekerja dalam dua tahap.
MariaDB menyiapkan skema sekali untuk setiap pernyataan: MariaDB mengurai argumen pertama dan membuat validator untuk setiap kata kunci skema.
Karena, dalam representasi saat ini, skema harus bersifat konstan, maka validator-validator tersebut digunakan kembali. Argumen kedua, yaitu dokumen, dibaca setiap kali fungsi dijalankan, sehingga dapat berupa nilai kolom atau ekspresi yang berubah-ubah, dan setiap baris divalidasi secara terpisah.
Fungsi ini mengembalikan nilai 1 jika dokumen memenuhi skema dan 0 jika tidak. Sebagai contoh, kueri berikut ini mengembalikan nilai 1 karena "red" merupakan salah satu nilai yang diizinkan:

Persiapan Skema
Langkah 1) Membuat validator skema
MariaDB dimulai pada fungsi `fix_length_and_dec()`, yang mempersiapkan argumen skema konstan sebelum mengevaluasi dokumen. Setelah menginisialisasi parser JSON, fungsi ini memanggil `create_object_and_handle_keyword()` untuk setiap kata kunci skema. Setiap kata kunci yang dikenali akan membuat validator dan menambahkannya ke `keyword_list` untuk validasi dokumen selanjutnya.

MariaDB menggunakan validator khusus untuk kata kunci seperti `enum`, `const`, dan `uniqueItems`. Sebagai contoh, kata kunci `enum` membuat validator `Json_schema_enum` yang menyimpan nilai-nilai yang diizinkan untuk perbandingan di kemudian hari.
Langkah 2) Menyiapkan nilai-nilai untuk validasi dokumen
Fungsi `create_object_and_handle_keyword()` memproses setiap kata kunci beserta nilai JSON yang terkait dengannya. Fungsi `create_object()` memilih validator berdasarkan nama kata kunci, lalu memanggil metode `handle_keyword()` dari validator tersebut.

Fungsi `json_read_value()` melakukan pemrosesan JSON secara umum serta mencatat tipe dan isi nilai tersebut. Metode `handle_keyword()` dari setiap validator kemudian menyimpan nilai yang telah diproses dalam format yang diperlukan untuk perbandingan selanjutnya.
Validasi Dokumen
Langkah 1) Memulai validasi dokumen
Pemrosesan dokumen dalam val_bool() baru dimulai setelah fix_length_and_dec() berhasil mengurai skema dan membuat validatornya. Jika schema_parsed bernilai false, val_bool() akan segera mengembalikan hasil tanpa membaca dokumen tersebut.

Jika `schema_parsed` bernilai `true`, fungsi `val_bool()` akan membaca argumen kedua, yaitu dokumen JSON yang sedang diperiksa. Fungsi ini menginisialisasi parser JSON, memanggil fungsi `json_read_value()` untuk membaca nilai tingkat atas dokumen tersebut, dan meneruskan nilai tersebut ke setiap validator dalam `keyword_list`.
Langkah 2) Memparsing string JSON
Fungsi `json_read_value()` terlebih dahulu mengabaikan spasi kosong di awal menggunakan `get_first_nonspace()`, lalu memeriksa karakter pertama yang bukan spasi kosong untuk menentukan tipe nilai JSON dan memilih parser yang sesuai.
Misalnya, saat memproses string JSON "database_admin", karakter pertama adalah tanda kutip. Berdasarkan karakter tersebut, entri JST_READ_VALUE dalam json_actions memilih fungsi read_strn() untuk memproses masukan tersebut sebagai string JSON.

Fungsi read_strn() menginisialisasi status parser dengan menetapkan value_type ke JSON_VALUE_STRING dan mencatat posisi awal isi string. Fungsi ini kemudian memanggil skip_str_constant() untuk memvalidasi sintaks string.

Fungsi `skip_str_constant()` memproses string karakter demi karakter melalui `json_next_char()`. Proses ini berlanjut hingga menemukan tanda kutip penutup atau mendeteksi karakter yang tidak valid atau urutan escape. Ketika menemukan tanda garis miring terbalik (backslash), fungsi ini menetapkan `value_escaped` dan memanggil `json_handle_esc()` untuk memvalidasi urutan escape tersebut.

Pemeriksaan ini menerapkan perilaku penguraian string JSON MariaDB berdasarkan RFC 8259, Bagian 7 [1]. Tabel berikut merangkum contoh string JSON yang valid dan tidak valid.
Kondisi | Contoh | Hasil |
Karakter kontrol yang tidak di-escape (U+0000–U+001F) | Karakter baris baru atau tab yang sebenarnya di dalam sebuah string | Dilarang |
Tanda kutip yang tidak di-escape di dalam string JSON | "akun 'admin'" | Dilarang |
Tanda kutip yang terlewat | "admin\"akun" | Diperbolehkan |
Urutan pelarian yang sah | "admin\nakun" | Diperbolehkan |
Karakter yang dapat dicetak tanpa kode escape | "database_admin" | Diperbolehkan |
Kode pelarian Unicode yang disusun dengan benar | "\u0041" | Diperbolehkan |
Kode pelarian Unicode yang tidak valid | "\u12G4" | Dilarang |
Tanda kutip penutup hilang | "database_admin" | Dilarang |
Jika proses penguraian berhasil, fungsi `read_strn()` menghitung `value_len` sebagai panjang dalam byte dari isi string pada input JSON asli, tanpa termasuk tanda kutip di sekelilingnya. Sebagai contoh, "database_admin" menghasilkan `value_len` sebesar 14 byte, sedangkan "\u0041" menghasilkan `value_len` sebesar 6 byte, meskipun urutan escape tersebut mewakili satu karakter.
Jika berhasil, fungsi `json_read_value()` mengembalikan nilai 0 dan menyimpan tipe, posisi, batas, serta panjang nilai yang telah diparsing dalam status parser untuk pemrosesan selanjutnya. Nilai kembalian yang tidak nol menandakan kegagalan parsing.
Langkah 3) Menerapkan validator skema
Setelah mengurai dokumen JSON, fungsi val_bool() memanggil validator-validator yang tersimpan dalam keyword_list. Setiap validator mengimplementasikan metode validate() untuk memastikan kepatuhan terhadap batasan yang terkait dengan kata kunci skema masing-masing. Beberapa validator melakukan pemeriksaan langsung, sementara yang lain menormalisasi nilai-nilai JSON sebelum melakukan perbandingan.
Fungsi bantu normalisasi digunakan oleh beberapa validator, termasuk enum, const, dan uniqueItems. Analisis ini berfokus pada Json_schema_enum::validate(), yang menyediakan jalur langsung ke fungsi normalisasi yang rentan.
Untuk kata kunci `enum`, fungsi `Json_schema_enum::validate()` menangani nilai literal JSON `true`, `false`, dan `null` menggunakan bendera yang disiapkan selama pemrosesan skema. Tipe nilai JSON lainnya, termasuk string, bilangan, larik, dan objek, diteruskan ke fungsi `json_get_normalized_string()` sebelum dibandingkan dengan nilai enum yang tersimpan.

Validator tersebut menggunakan konvensi pengembalian nilai Boolean internal:
- Nilai `false` menunjukkan bahwa nilai tersebut memenuhi syarat validator.
- nilai "true" menandakan kegagalan validasi atau kesalahan internal.
Dalam kondisi validasi normal, validasi yang berhasil akan menghasilkan nilai 1 pada fungsi JSON_SCHEMA_VALID(), sedangkan ketidaksesuaian skema akan menghasilkan nilai 0.
Yang penting, normalisasi dilakukan sebelum pemeriksaan keanggotaan enum. Oleh karena itu, bahkan string JSON yang tidak sesuai dengan nilai enum yang diizinkan pun dapat mencapai fungsi normalisasi yang rentan tersebut.
Analisis Kerentanan
Penyebab Utama
Kerentanan ini berasal dari fungsi `json_get_normalized_string()`, di mana sebuah string JSON disalin ke buffer heap sementara tanpa memverifikasi apakah buffer tujuan memiliki kapasitas yang cukup.
Fungsi ini menginisialisasi sebuah DYNAMIC_STRING dengan alokasi default sebesar 128 byte. Namun, saat memproses string JSON, fungsi ini menyalin je->value_len byte secara langsung ke dalam buffer yang telah dialokasikan tanpa terlebih dahulu menyesuaikan ukurannya.
Jika panjang input melebihi kapasitas yang tersedia, proses penyalinan akan menulis data melampaui batas buffer yang dialokasikan, sehingga menyebabkan buffer overflow berbasis heap.
Kode yang Rentan: Normalisasi String
Selama proses validasi enum, fungsi `Json_schema_enum::validate()` memanggil fungsi `json_get_normalized_string()` untuk menormalkan nilai yang diajukan sebelum membandingkannya dengan nilai-nilai enum yang diizinkan.
Normalisasi memberikan representasi yang konsisten terhadap nilai-nilai JSON, sehingga nilai-nilai yang setara dapat dibandingkan meskipun representasi teks aslinya berbeda.
Contohnya antara lain:
- Angka: 0 -> 0,0E0
- Array: [1, 0] -> [1,0E0, 0,0E0]
- Object: {"b":2,"a":1} -> {"a":1.0E0,"b":2.0E0}
Fungsi ini menerima nilai JSON yang telah diparsing melalui `je`, menulis hasil yang telah dinormalisasi ke `res`, dan menggunakan `error` untuk menunjukkan apakah normalisasi berhasil. Secara internal, fungsi ini menginisialisasi `a_res`, sebuah `DYNAMIC_STRING` sementara yang didukung oleh alokasi memori heap.

Bilangan, larik, dan objek diproses melalui fungsi `json_normalize()`, yang dapat memperluas buffer tujuan sesuai kebutuhan.
Namun, string JSON mengikuti jalur eksekusi yang berbeda. Fungsi tersebut menyalin je->value_len byte secara langsung ke a_res.str menggunakan strncpy(), tanpa terlebih dahulu memverifikasi atau memperluas kapasitas buffer tujuan.
Penugasan berikutnya, `a_res.length = je->value_len`, hanya memperbarui panjang string yang tercatat. Penugasan ini tidak mengubah ukuran alokasi yang mendasarinya maupun mencegah penulisan di luar batas yang terjadi sebelumnya.
Akibatnya, ketika je->value_len melebihi kapasitas buffer yang dialokasikan, strncpy() akan menulis data di luar batas alokasi heap, yang berpotensi merusak memori heap di sekitarnya.
Eksploitasi: dari Heap Buffer Overflow hingga Eksekusi Kode Jarak Jauh
Bagian ini menunjukkan bagaimana CVE-2026-32710 dapat digabungkan dengan dua masalah MariaDB lainnya, yaitu MDEV-40470 dan MDEV-39581, untuk melakukan eksekusi kode jarak jauh (RCE) dalam lingkungan laboratorium yang terkendali.
Eksploit ini memanfaatkan kelebihan kapasitas buffer heap untuk mengubah konfigurasi internal `plugin_dir` MariaDB, sehingga memungkinkan Fungsi yang Didefinisikan Pengguna (UDF) berbahaya dimuat dari direktori yang dapat ditulis. Eksploitasi yang berhasil memerlukan hak akses basis data tertentu, kondisi sistem berkas tertentu, serta kendali yang tepat atas tata letak heap. Oleh karena itu, RCE tidak dijamin akan terjadi hanya karena adanya CVE-2026-32710 saja.
Langkah 1: Menetapkan Hak Akses Database yang Diperlukan
MariaDB membatasi operasi berkas melalui dua mekanisme keamanan:
- Hak akses basis data
Operasi seperti LOAD_FILE() dan SELECT ... INTO DUMPFILE memerlukan hak akses FILE. - Izin sistem berkas
MariaDB melakukan operasi berkas menggunakan akun sistem operasi yang menjalankan mariadbd, yang umumnya tidak dapat menulis ke direktori plugin default milik root.
Selain itu, `secure_file_priv` dapat membatasi operasi berkas hanya pada direktori yang telah dikonfigurasi. Secara default, variabel ini kosong, sehingga tidak ada pembatasan direktori tambahan yang berlaku.

Untuk mengatasi pembatasan hak akses basis data, eksploit tersebut terlebih dahulu memanfaatkan celah keamanan MDEV-40470 untuk mengubah konfigurasi otentikasi akun root@localhost. Dalam kondisi yang diperlukan untuk kerentanan tambahan ini, penyerang dapat melakukan otentikasi sebagai akun MariaDB yang memiliki hak istimewa dan memperoleh izin basis data yang diperlukan untuk operasi berkas selanjutnya.
Eskalasi hak istimewa ini terjadi di dalam MariaDB dan tidak memberikan akses root pada sistem operasi.

Hal ini memberikan hak akses basis data yang diperlukan untuk operasi berkas selanjutnya, namun tidak memberikan akses root pada sistem operasi. Pembatasan sistem berkas pada direktori plugin default akan diatasi nanti melalui serangan overflow buffer heap.
Langkah 2: Menentukan Alamat Memori dan Mempersiapkan Heap
Address Space Layout Randomization (ASLR) mencegah penggunaan alamat memori tetap. Dalam lingkungan laboratorium, eksploit tersebut memanfaatkan hak akses basis data yang telah diperoleh untuk membaca berkas eksekusi MariaDB dan /proc/self/maps, sehingga memungkinkan eksploit tersebut menentukan alamat runtime dari opt_plugin_dir dan opt_plugin_dir_ptr meskipun ada ASLR.

Selanjutnya, proses heap grooming dilakukan dengan membuat 100 variabel yang didefinisikan pengguna yang berisi nilai berukuran 127 byte. Alokasi-alokasi ini menggunakan kelas ukuran 0x90 yang sama dengan buffer sementara yang dialokasikan oleh fungsi json_get_normalized_string().

Alokasi-alokasi ini menggunakan kelas ukuran 0x90 yang sama dan menggunakan potongan-potongan yang dapat digunakan kembali dengan ukuran tersebut. Setelah potongan-potongan yang dapat digunakan kembali tersebut habis, alokasi selanjutnya diambil dari potongan teratas, sehingga tata letak heap awal menjadi lebih dapat diprediksi.

Namun, proses heap grooming saja tidak mengungkapkan alamat-alamat objek yang telah dialokasikan. Untuk memperoleh alamat-alamat tersebut, MDEV-39581—sebuah kerentanan pengungkapan informasi dalam fungsi COLUMN_LIST()—digunakan untuk membocorkan memori heap.
Proses ini dimulai dengan menyimpan nilai kolom dinamis yang tidak valid ke dalam @src, dilanjutkan dengan pembuatan variabel yang didefinisikan pengguna berukuran 127 byte.



Memanggil fungsi COLUMN_LIST() pada nilai yang tidak valid akan mengungkap sekitar 65 KB memori heap yang berisi struktur user_var_entry beserta buffer nilai yang terkait dengannya.

Memori yang bocor dianalisis untuk mengidentifikasi struktur `user_var_entry` yang valid dengan memeriksa nama, panjang nilai, penyelarasan penunjuk, dan bidang struktur di sekitarnya. Eksploit tersebut kemudian memilih tiga entri yang berurutan, yang disebut sebagai `entry_a`, `entry_b`, dan `entry_c`, dengan tata letak memori yang diperlukan. Alamat-alamat tersebut juga harus memenuhi batasan penguraian string JSON, karena dua byte alamat disematkan dalam muatan overflow berikutnya.

Langkah 3: Memicu Kelebihan Kapasitas Buffer Heap
Setelah mengidentifikasi tiga struktur `user_var_entry` yang berurutan dan sesuai (entry_a, entry_b, dan entry_c), buffer nilai dari entry_a dilepaskan sementara entri variabelnya tetap dialokasikan.


Buffer sementara yang dialokasikan oleh json_get_normalized_string() kemudian menggunakan kembali chunk yang telah dibebaskan tersebut. String JSON yang dirancang khusus yang berisi 192 byte pengisi dan dua byte alamat yang dipilih memicu kelebihan kapasitas buffer heap. Kelebihan kapasitas tersebut hanya menimpa dua byte terendah dari entry_c.value.ptr, sementara byte-byte lainnya dari penunjuk asli tetap terjaga. Penimpaan penunjuk yang parsial ini mengalihkan entry_c.value.ptr ke bidang yang ditargetkan di dalam entry_a, sehingga memungkinkan manipulasi memori selanjutnya.

Memperbarui `entry_c` melalui penunjuk yang rusak akan mengubah `entry_a.value.ptr` menjadi alamat `opt_plugin_dir`, sehingga memungkinkan penulisan memori yang diperlukan untuk mengubah direktori plugin.

Langkah 4: Mengalihkan Direktori Plugin dan Melakukan RCE
Panjang nilai entry_a dipertahankan pada 9 byte untuk menghemat ruang penyimpanan eksternal, alih-alih beralih ke penyimpanan inline. Dengan demikian, saat entry_a diperbarui, opt_plugin_dir akan ditimpa dengan /tmp/.

Setelah direktori plugin dialihkan ke lokasi yang dapat ditulis, sebuah pustaka bersama berbahaya (raptor.so) ditulis ke /tmp/ dan didaftarkan menggunakan perintah CREATE FUNCTION.
Pustaka ini mengimplementasikan fungsi `sys_exec()`, yang memanggil fungsi `system()` untuk menjalankan perintah sistem operasi dengan hak akses akun layanan MariaDB.

Langkah 5: Memulihkan Integritas Heap
Kelebihan kapasitas buffer heap juga merusak metadata heap yang berdekatan serta bidang-bidang di dalam `entry_c`, yang berpotensi menyebabkan MariaDB mengalami crash selama proses pembersihan sesi.
Untuk mencegah hal ini, eksploit tersebut menggunakan fungsi UDF perbaikan untuk memulihkan metadata heap dan bidang struktur yang rusak dengan menggunakan byte asli yang diperoleh dari pengungkapan memori sebelumnya.
Selain itu, karena entry_a.value.ptr telah dialihkan ke opt_plugin_dir, eksploit tersebut mengembalikan penunjuk tersebut ke penyimpanan inline-nya dan mengatur ulang entry_a.value.length menjadi 0. Struktur entry_b tetap tidak berubah.

Perbaikan ini memungkinkan MariaDB melepaskan sumber daya sesi tanpa mencoba membebaskan memori yang tidak valid.
Bukti Konsep
Rangkaian serangan secara keseluruhan berhasil direproduksi di lingkungan laboratorium MariaDB yang terisolasi dengan menggunakan skrip PoC.
Rekan-rekan kami memulai dengan akun SQL terotentikasi yang memiliki hak akses terbatas dan mendemonstrasikan rantai serangan secara lengkap, termasuk kelebihan kapasitas buffer heap pada fungsi JSON_SCHEMA_VALID(), penimpaan direktori plugin, pemuatan UDF berbahaya, serta eksekusi reverse-shell.

Fungsi callback reverse-shell tersebut membuktikan bahwa perintah sistem operasi dapat dieksekusi dengan hak akses akun layanan MariaDB, yang menunjukkan bagaimana kelebihan kapasitas buffer heap dapat menyebabkan RCE jika digabungkan dengan kerentanan tambahan dan kondisi lingkungan yang diperlukan.
Mitigasi
Langkah mitigasi utama untuk CVE-2026-32710 adalah memperbarui MariaDB Server ke versi yang telah diperbaiki:
- MariaDB 11.4: Lakukan pembaruan ke versi 11.4.10 atau yang lebih baru.
- MariaDB 11.8: Lakukan pembaruan ke versi 11.8.6 atau yang lebih baru.
- MariaDB 12.2: Lakukan pembaruan ke versi 12.2.2 atau yang lebih baru.
Organisasi juga sebaiknya menerapkan langkah-langkah keamanan berikut ini untuk mengurangi risiko penyalahgunaan:
- Batasi hak akses basis data: Terapkan prinsip hak akses minimal, terutama untuk hak akses FILE dan pembuatan UDF.
- Operasi berkas yang aman: Konfigurasikan `secure_file_priv` untuk membatasi akses berkas dan mencegah perubahan yang tidak sah pada direktori plugin MariaDB.
- Batasi paparan basis data: Batasi akses ke basis data hanya untuk pengguna dan sistem yang tepercaya, serta pantau aktivitas SQL yang mencurigakan.
Selain melakukan penambalan, organisasi dapat memanfaatkan solusi MetaDefender™ dariOPSWAT untuk meningkatkan visibilitas kerentanan dan proses perbaikannya:
- MetaDefender™ Core dengan analisis SBOM: Mengidentifikasi komponen MariaDB yang rentan dan versi yang terpengaruh melalui analisis Software Bill of Materials (SBOM) , sehingga membantu organisasi mengevaluasi risiko paparan dan memprioritaskan pembaruan.
- MetaDefender ™ Tri dengan Daftar Pantauan: Memantau MariaDB dan produk terkait lainnya secara proaktif untuk mendeteksi CVE yang baru diungkapkan atau diperbarui. Dengan menggabungkan informasi kerentanan dan Skor Risiko (Heat Score) dariOPSWAT , Tri membantu tim keamanan memprioritaskan ancaman dan mengambil tindakan perbaikan secara tepat waktu.

Secara bersama-sama, visibilitas komponen berbasis SBOM dan pemantauan CVE yang proaktif membantu organisasi mengidentifikasi perangkat lunak yang rentan lebih dini serta mengurangi paparan terhadap ancaman yang baru muncul.
Daftar Pustaka
- RFC 8259, Bagian 7
- Perbaikan MariaDB dengan commit 93ef1236002
- Gambaran Umum Otentikasi yang Dapat Dipasang pada MariaDB
- Dokumentasi Secure_file_priv
- Catatan CVE-2026-32710 — Program CVE
- CVE-2026-32710 — NVD
- Dokumentasi MariaDB JSON_SCHEMA_VALID()
- Dokumentasi Dukungan MDEV-40470
- Dokumentasi Dukungan MDEV-39581
