Saya telah membaca tentang eksploitasi lama terhadap GDI + di Windows XP dan Windows Server 2003 yang disebut JPEG kematian untuk proyek yang saya kerjakan.
Eksploitasi dijelaskan dengan baik di tautan berikut: http://www.infosecwriters.com/text_resources/pdf/JPEG.pdf
Pada dasarnya, file JPEG berisi bagian yang disebut COM yang berisi kolom komentar (mungkin kosong), dan nilai dua byte yang berisi ukuran COM. Jika tidak ada komentar, ukurannya 2. Pembaca (GDI +) membaca ukuran, mengurangi dua, dan mengalokasikan buffer dengan ukuran yang sesuai untuk menyalin komentar di heap. Serangan itu melibatkan penempatan nilai 0
di lapangan. GDI + mengurangi 2
, yang mengarah ke nilai -2 (0xFFFe)
yang akan dikonversi ke bilangan bulat unsigned 0XFFFFFFFE
oleh memcpy
.
Kode sampel:
unsigned int size;
size = len - 2;
char *comment = (char *)malloc(size + 1);
memcpy(comment, src, size);
Perhatikan bahwa malloc(0)
di baris ketiga harus mengembalikan pointer ke memori yang tidak terisi di heap. Bagaimana menulis 0XFFFFFFFE
byte ( 4GB
!!!!) mungkin tidak merusak program? Apakah ini menulis di luar area heap dan ke dalam ruang program dan OS lain? Lalu apa yang terjadi?
Seperti yang saya pahami memcpy
, itu hanya menyalin n
karakter dari tujuan ke sumber. Dalam kasus ini, sumber harus ada di tumpukan, tujuan di heap, dan n
adalah 4GB
.
malloc
ukuran ed hanya 2 byte bukan0xFFFFFFFE
. Ukuran yang sangat besar ini hanya digunakan untuk ukuran salinan, bukan untuk ukuran alokasi.Jawaban:
Kerentanan ini jelas merupakan luapan tumpukan .
Mungkin bisa, tetapi pada beberapa kesempatan Anda punya waktu untuk mengeksploitasi sebelum crash terjadi (terkadang, Anda bisa mengembalikan program ke eksekusi normalnya dan menghindari crash).
Saat memcpy () dimulai, salinan akan menimpa beberapa blok heap lain atau beberapa bagian dari struktur manajemen heap (mis. Daftar bebas, daftar sibuk, dll.).
Pada titik tertentu salinan akan menemukan halaman yang tidak dialokasikan dan memicu AV (Access Violation) saat menulis. GDI + kemudian akan mencoba mengalokasikan blok baru di heap (lihat ntdll! RtlAllocateHeap ) ... tetapi struktur heap sekarang semuanya kacau.
Pada titik itu, dengan menyusun gambar JPEG Anda secara hati-hati, Anda dapat menimpa struktur manajemen heap dengan data yang terkontrol. Ketika sistem mencoba mengalokasikan blok baru, sistem mungkin akan memutuskan tautan blok (gratis) dari daftar gratis.
Blok dikelola dengan (terutama) flink (Tautan maju; blok berikutnya dalam daftar) dan berkedip (Tautan mundur; blok sebelumnya dalam daftar) penunjuk. Jika Anda mengontrol kedipan dan kedip, Anda mungkin memiliki kemungkinan WRITE4 (tulis kondisi Apa / Di mana) di mana Anda mengontrol apa yang dapat Anda tulis dan di mana Anda dapat menulis.
Pada titik itu Anda dapat menimpa pointer fungsi (pointer SEH [Structured Exception Handlers] adalah target pilihan pada waktu itu di tahun 2004) dan mendapatkan eksekusi kode.
Lihat entri blog Korupsi Tumpukan: Studi Kasus .
Catatan: meskipun saya menulis tentang eksploitasi menggunakan freelist, penyerang mungkin memilih jalur lain menggunakan metadata heap lain ("metadata heap" adalah struktur yang digunakan oleh sistem untuk mengelola heap; flink dan blink adalah bagian dari metadata heap), tetapi eksploitasi unlink mungkin adalah yang "termudah". Pencarian google untuk "eksploitasi heap" akan menghasilkan banyak penelitian tentang ini.
Tidak pernah. OS modern didasarkan pada konsep ruang alamat virtual sehingga setiap proses memiliki ruang alamat virtualnya sendiri yang memungkinkan pengalamatan hingga 4 gigabyte memori pada sistem 32-bit (dalam praktiknya Anda hanya mendapatkan setengahnya di lahan pengguna, sisanya untuk kernel).
Singkatnya, sebuah proses tidak dapat mengakses memori dari proses lain (kecuali jika ia meminta kernel untuk itu melalui beberapa layanan / API, tetapi kernel akan memeriksa apakah pemanggil berhak melakukannya).
Saya memutuskan untuk menguji kerentanan ini akhir minggu ini, jadi kami bisa mendapatkan ide bagus tentang apa yang sedang terjadi daripada spekulasi murni. Kerentanannya sekarang sudah 10 tahun, jadi saya pikir tidak masalah untuk menulis tentang itu, meskipun saya belum menjelaskan bagian eksploitasi dalam jawaban ini.
Perencanaan
Tugas tersulit adalah menemukan Windows XP dengan hanya SP1, seperti pada tahun 2004 :)
Kemudian, saya mengunduh gambar JPEG yang hanya terdiri dari satu piksel, seperti yang ditunjukkan di bawah ini (dipotong agar singkat):
Gambar JPEG terdiri dari penanda biner (yang menghasilkan segmen). Pada gambar di atas,
FF D8
adalah marker SOI (Start Of Image), sedangkanFF E0
, misalnya, adalah marker aplikasi.Parameter pertama dalam segmen penanda (kecuali beberapa penanda seperti SOI) adalah parameter panjang dua byte yang mengkodekan jumlah byte dalam segmen penanda, termasuk parameter panjang dan tidak termasuk penanda dua byte.
Saya hanya menambahkan penanda COM (0x
FFFE
) tepat setelah SOI, karena penanda tidak memiliki urutan yang ketat.Panjang segmen COM diatur
00 00
untuk memicu kerentanan. Saya juga menyuntikkan byte 0xFFFC tepat setelah penanda COM dengan pola berulang, angka 4 byte dalam hex, yang akan berguna saat "mengeksploitasi" kerentanan.Debugging
Mengklik ganda gambar tersebut akan segera memicu bug di shell Windows (alias "explorer.exe"), di suatu tempat di
gdiplus.dll
, dalam fungsi bernamaGpJpegDecoder::read_jpeg_marker()
.Fungsi ini dipanggil untuk setiap penanda dalam gambar, cukup: membaca ukuran segmen penanda, mengalokasikan buffer yang panjangnya adalah ukuran segmen dan menyalin konten segmen ke buffer yang baru dialokasikan ini.
Di sini awal fungsinya:
eax
register menunjuk ke ukuran segmen danedi
merupakan jumlah byte yang tersisa pada gambar.Kode kemudian melanjutkan untuk membaca ukuran segmen, dimulai dengan byte paling signifikan (panjangnya adalah nilai 16-bit):
Dan byte paling signifikan:
Setelah ini selesai, ukuran segmen digunakan untuk mengalokasikan buffer, mengikuti perhitungan ini:
alokasi_ukuran = ukuran_segmentasi + 2
Ini dilakukan dengan kode di bawah ini:
Dalam kasus kami, karena ukuran segmen adalah 0, ukuran yang dialokasikan untuk buffer adalah 2 byte .
Kerentanan tepat setelah alokasi:
Kode tersebut hanya mengurangi ukuran_segmen (panjang segmen adalah nilai 2 byte) dari seluruh ukuran segmen (0 dalam kasus kami) dan berakhir dengan kekurangan bilangan bulat: 0-2 = 0xFFFFFFFE
Kode kemudian memeriksa apakah ada byte yang tersisa untuk diurai dalam gambar (yang benar), dan kemudian melompat ke salinan:
Potongan di atas menunjukkan bahwa ukuran salinan adalah potongan 0xFFFFFFFE 32-bit. Buffer sumber dikontrol (konten gambar) dan tujuannya adalah buffer di heap.
Tulis kondisi
Salinan akan memicu pengecualian pelanggaran akses (AV) ketika mencapai akhir halaman memori (ini bisa dari penunjuk sumber atau penunjuk tujuan). Saat AV dipicu, heap sudah berada dalam status rentan karena salinan telah menimpa semua blok heap berikut hingga halaman yang tidak dipetakan ditemukan.
Apa yang membuat bug ini dapat dieksploitasi adalah bahwa 3 SEH (Structured Exception Handler; ini adalah percobaan / kecuali pada level rendah) menangkap pengecualian pada bagian kode ini. Lebih tepatnya, SEH pertama akan melepas tumpukan sehingga kembali mengurai marker JPEG lain, sehingga sepenuhnya melewatkan marker yang memicu pengecualian.
Tanpa SEH kode hanya akan membuat crash seluruh program. Jadi kode melewatkan segmen COM dan mem-parsing segmen lain. Jadi kita kembali ke
GpJpegDecoder::read_jpeg_marker()
segmen baru dan ketika kode mengalokasikan buffer baru:Sistem akan memutuskan tautan blok dari daftar gratis. Kebetulan struktur metadata ditimpa oleh konten gambar; jadi kami mengontrol pemutusan tautan dengan metadata terkontrol. Kode di bawah ini di suatu tempat di sistem (ntdll) di manajer heap:
Sekarang kita bisa menulis apa yang kita mau, dimana kita mau ...
sumber
Karena saya tidak tahu kode dari GDI, yang di bawah ini hanyalah spekulasi.
Nah, satu hal yang muncul dalam pikiran adalah salah satu perilaku yang saya perhatikan pada beberapa OS (saya tidak tahu apakah Windows XP memiliki ini) adalah ketika mengalokasikan dengan yang baru /
malloc
, Anda benar-benar dapat mengalokasikan lebih dari RAM Anda, asalkan Anda tidak menulis ke memori itu.Ini sebenarnya adalah perilaku kernel linux.
Dari www.kernel.org:
Untuk masuk ke memori penduduk, kesalahan halaman harus dipicu.
Pada dasarnya Anda perlu membuat memori kotor sebelum benar-benar dialokasikan pada sistem:
Terkadang itu tidak benar-benar membuat alokasi nyata dalam RAM (program Anda masih tidak akan menggunakan 4 GB). Saya tahu saya telah melihat perilaku ini di Linux, tetapi saya tidak dapat menirunya sekarang pada instalasi Windows 7 saya.
Mulai dari perilaku ini skenario berikut ini mungkin terjadi.
Untuk membuat memori itu ada di RAM, Anda harus membuatnya kotor (pada dasarnya memset atau menulis ke dalamnya):
Namun kerentanan mengeksploitasi buffer overflow, bukan kegagalan alokasi.
Dengan kata lain, jika saya memiliki ini:
Ini akan menyebabkan penulisan setelah buffer, karena tidak ada yang namanya segmen memori berkelanjutan 4 GB.
Anda tidak memasukkan apapun ke dalam p untuk membuat seluruh 4 GB memori kotor, dan saya tidak tahu apakah
memcpy
membuat memori kotor sekaligus, atau hanya halaman demi halaman (saya pikir itu halaman demi halaman).Akhirnya itu akan berakhir menimpa bingkai tumpukan (Stack Buffer Overflow).
Kerentanan lain yang lebih mungkin adalah jika gambar disimpan dalam memori sebagai array byte (baca seluruh file ke dalam buffer), dan ukuran komentar digunakan hanya untuk melewati informasi non-vital.
Sebagai contoh
Seperti yang Anda sebutkan, jika GDI tidak mengalokasikan ukuran tersebut, program tidak akan pernah macet.
sumber
malloc(-1U)
pasti akan gagal, kembaliNULL
danmemcpy()
akan hancur.