Lokakarya Looney Tunables Peningkatan hak istimewa lokal (CVE-2023-4911) (untuk tujuan pendidikan saja)
Dalam komputasi, linker dinamis adalah bagian dari sistem operasi yang memuat dan menghubungkan pustaka bersama yang diperlukan oleh file yang dapat dieksekusi saat dijalankan, dengan menyalin konten pustaka dari penyimpanan persisten ke RAM, mengisi tabel lompat, dan merelokasi pointer.
Misalnya, kami memiliki program yang menggunakan perpustakaan openssl untuk menghitung hash md5:
$ head md5_hash.c
#include
#include
#include
ld.so mem-parsing biner dan mencoba menemukan perpustakaan yang terkait dengan
$ ldd md5_hash
linux-vdso.so.1 (0x00007fffa530b000)
libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f19cda00000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f19cd81e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f19ce032000)
Seperti yang bisa kita lihat, ia menemukan perpustakaan kripto yang diperlukan di /lib/x86_64-linux-gnu/libcrypto.so.3 Selama startup program, ia memasukkan kode perpustakaan ini ke dalam RAM proses dan menghubungkan semua referensi ke perpustakaan ini.
Ketika sebuah program dimulai, pemuat ini pertama-tama memeriksa program tersebut untuk menentukan perpustakaan bersama yang diperlukannya. Ia kemudian mencari perpustakaan ini, memuatnya ke dalam memori, dan menghubungkannya dengan file yang dapat dieksekusi pada waktu proses. Dalam prosesnya, pemuat dinamis menyelesaikan referensi simbol, seperti referensi fungsi dan variabel, memastikan bahwa semuanya sudah diatur untuk eksekusi program. Mengingat perannya, pemuat dinamis sangat sensitif terhadap keamanan, karena kodenya berjalan dengan hak istimewa yang lebih tinggi ketika pengguna lokal meluncurkan program set-user-ID atau set-group-ID.
Tunables adalah fitur di Perpustakaan GNU C yang memungkinkan pembuat aplikasi dan pengelola distribusi mengubah perilaku perpustakaan runtime agar sesuai dengan beban kerja mereka. Ini diimplementasikan sebagai satu set saklar yang dapat dimodifikasi dengan cara yang berbeda. Metode default saat ini untuk melakukan hal ini adalah melalui variabel lingkungan GLIBC_TUNABLES dengan menyetelnya ke string pasangan nama=nilai yang dipisahkan titik dua. Misalnya, contoh berikut mengaktifkan pemeriksaan malloc dan menetapkan ambang trim malloc menjadi 128 byte:
GLIBC_TUNABLES=glibc.malloc.trim_threshold=128:glibc.malloc.check=3
export GLIBC_TUNABLES
Meneruskan --list-tunables ke pemuat dinamis untuk mencetak semua merdu dengan nilai minimum dan maksimum:
$ /lib64/ld-linux-x86-64.so.2 --list-tunables
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.elision.skip_lock_after_retries: 3 (min: 0, max: 2147483647)
glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.perturb: 0 (min: 0, max: 255)
glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
glibc.pthread.rseq: 1 (min: 0, max: 1)
glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
glibc.mem.tagging: 0 (min: 0, max: 255)
Pada awal eksekusinya, ld.so memanggil __tunables_init() untuk menjelajahi lingkungan (pada baris 279), mencari variabel GLIBC_TUNABLES (pada baris 282); untuk setiap GLIBC_TUNABLES yang ditemukan, ia membuat salinan variabel ini (pada baris 284), memanggil parse_tunables() untuk memproses dan membersihkan salinan ini (pada baris 286), dan terakhir mengganti GLIBC_TUNABLES asli dengan salinan yang telah dibersihkan ini (pada baris 288 ):
// (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c)
269 void
270 __tunables_init ( char * * envp )
271 {
272 char * envname = NULL ;
273 char * envval = NULL ;
274 size_t len = 0 ;
275 char * * prev_envp = envp ;
...
279 while (( envp = get_next_env ( envp , & envname , & len , & envval ,
280 & prev_envp )) != NULL )
281 {
282 if ( tunable_is_name ( "GLIBC_TUNABLES" , envname )) // searching for GLIBC_TUNABLES variables
283 {
284 char * new_env = tunables_strdup ( envname );
285 if ( new_env != NULL )
286 parse_tunables ( new_env + len + 1 , envval ); //
287 /* Put in the updated envval. */
288 * prev_envp = new_env ;
289 continue ;
290 }
Argumen pertama parse_tunables() (tunestr) menunjuk ke salinan GLIBC_TUNABLES yang akan segera dibersihkan, sedangkan argumen kedua (valstring) menunjuk ke variabel lingkungan GLIBC_TUNABLES asli (dalam tumpukan). Untuk membersihkan salinan GLIBC_TUNABLES (yang harus dalam bentuk "tunable1= aaa:tunable2=bbb"
), parse_tunables() menghapus semua merdu berbahaya (sXID_ERASE merdu) dari tunestr, namun tetap mempertahankan SXID_IGNORE dan NONE merdu (pada baris 221- 235):