
Dalam kerangka kerja artikel ini, kami akan mempertimbangkan secara cukup rinci proses penulisan eksploit untuk kerentanan di Microsoft Edge, dengan keluar selanjutnya dari kotak pasir. Jika Anda tertarik untuk mengetahui seperti apa proses ini, selamat datang di bawah kucing!
Pendahuluan
Pada Pwn2Own 2019
terbaru di Montreal, dalam kategori browser, sebuah eksploitasi untuk meretas Microsoft Edge
didemonstrasikan . Dua kerentanan digunakan untuk ini: double free
di renderer dan kerentanan logis untuk keluar dari kotak pasir. Kedua kerentanan ini baru saja ditutup dan diberikan CVE
sesuai: CVE-2019-0940
dan CVE-2019-0938
. Anda dapat membaca lebih lanjut tentang kerentanan di blog: Pwn2Own 2019: Eksploitasi Microsoft Edge Renderer (CVE-2019-0940). Bagian 1 dan Pwn2Own 2019: Microsoft Eedge Sandbox Escape (CVE-2019-0938). Bagian 2
Sebagai bagian dari artikel kami, kami ingin menunjukkan proses penulisan eksploit seperti itu dan berapa banyak waktu dan sumber daya yang diperlukan untuk ini menggunakan contoh Microsoft Edge
pada Windows 10
menggunakan CVE-2017-0240
dan CVE-2016-3309
. Salah satu perbedaannya adalah bahwa jika exploit yang didemonstrasikan di Pwn2Own
menggunakan kerentanan logis untuk keluar dari sandbox, maka dalam skenario kami, kerentanan di kernel Windows 10
akan digunakan untuk keluar dari sandbox. Seperti yang ditunjukkan oleh tambalan dari Microsoft
, ada lebih banyak kerentanan di kernel daripada kerentanan dalam implementasi sandbox. Akibatnya, rantai kerentanan seperti itu jauh lebih mungkin ditemui, dan akan berguna untuk mengetahui karyawan IS di perusahaan.
Sumber data
Artikel ini akan membahas proses penulisan eksploitasi 1 hari untuk browser Microsoft Edge
. CVE-2017-0240
akan dioperasikan. Tahap pertama operasi akan didasarkan pada bahan dari sumber [1], kita akan mendapatkan arbitrary address read/write
primitif, dan juga berkenalan dengan berbagai teknik yang mungkin berguna dalam mengeksploitasi kerentanan tersebut. Selanjutnya, kami akan memperkenalkan Anda ke alat pwn.js
, yang akan membantu Anda mendapatkan panggilan ke fungsi sewenang-wenang berdasarkan membaca dan menulis sewenang-wenang, dan juga akan mempertimbangkan berbagai mitigations
dan cara untuk memotongnya. Pada tahap terakhir, kerentanan kernel Windows CVE-2016-3309
akan dieksploitasi untuk meningkatkan hak istimewa, memotong batasan AppContainer
dan mendapatkan kontrol penuh atas mesin yang diserang.
Pengoperasian akan dilakukan di dudukan dengan Microsoft Windows 10 Pro 1703 (10.0.15063)
dan browser Microsoft Edge (40.15063.0.0)
.
Langkah 1. Mendapatkan arbitrary address read/write
primitif
Deskripsi Kerentanan dan Perolehan OOB
Kerentanan tipe use-after-free
hadir dalam metode copyFromChannel dari objek Audio Buffer .
AudioBuffer adalah antarmuka aset audio pendek yang terletak di memori dan dibuat dari file audio menggunakan metode AudioContext.decodeAudioData (), atau dari sumber data menggunakan metode AudioContext.createBuffer (). Data audio yang ditempatkan di AudioBuffer dapat diputar di AudioBufferSourceNode.
Presentasi The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10
memberikan analisis terperinci tentang kerentanan dan tambalan. Ketika metode copyFromChannel
, isi saluran buffer audio disalin ke buffer destination
ditentukan oleh argumen pertama. Metode ini juga menerima nomor saluran ( channelNumber
) dan offset dalam buffer audio ( startInChannel
), mulai dari mana penyalinan diperlukan. Sebelum secara langsung menyalin data ke destination
dalam fungsi CDOMAudioBuffer::Var_copyFromChannel
, buffer destination
-cache (alamat dan ukuran buffer disimpan dalam variabel fungsi lokal di stack) dan nilai channelNumber
dan startInChannel
objek startInChannel
ke tipe Int
, untuk mana valueOf
metode objek yang dikonversi dipanggil. Kerentanannya adalah buffer yang di-cache dapat dibebaskan pada saat konversi tipe dalam metode yang ditimpa objek valueOf
. Untuk verifikasi, kami menggunakan kode berikut:
Kode ini menggunakan teknologi Web Workers
untuk membebaskan buffer. Setelah membuat Worker
kosong, kami dapat mengiriminya pesan menggunakan metode postMessage
. Argumen transfer
opsional kedua dari metode ini menerima larik objek yang dapat Transferable
( ArrayBuffer
, MessagePost
atau ImageBitmap
), hak atas objek akan ditransfer ke Worker
dan objek tidak akan lagi tersedia dalam konteks saat ini, sehingga dapat dihapus. Setelah ini, panggilan untuk sleep
terjadi - fungsi yang untuk sementara menghentikan eksekusi suatu program (ini diterapkan secara independen). Ini diperlukan agar sistem pengumpulan sampah ( GC
, Garbage Collector
) berhasil membebaskan buffer, hak-hak yang ditransfer.
Pekerja Web menyediakan cara sederhana untuk menjalankan skrip di utas latar belakang. Utusan pekerja dapat melakukan tugas tanpa mengganggu antarmuka pengguna. Selain itu, mereka dapat melakukan I / O menggunakan XMLHttpRequest (meskipun atribut responseXML dan kanal akan selalu nol). Pekerja yang ada dapat mengirim pesan JavaScript ke kode pembuat melalui pengendali acara yang ditentukan oleh kode ini (dan sebaliknya).
Dengan menjalankan kode ini di Edge di bawah debugger, Anda bisa mendapatkan crash berikut.

Akibatnya, panggilan ke copyFromChannel
mencoba menyalin konten buffer audio ke area memori yang tidak terisi. Untuk mengeksploitasi kerentanan, perlu untuk mencapai alokasi objek apa pun di area memori ini. Dalam hal ini, segmen array sempurna.
Array dalam Chakra
(mesin JS
yang digunakan dalam browser Edge
) disusun sebagai berikut: objek array memiliki ukuran tetap, pointer ke objek array (atau nilai, dalam kasus IntArray
) disimpan di area memori yang terpisah - segmen, pointer ke mana terdapat di objek. array Header segmen berisi berbagai informasi, termasuk ukuran segmen, yang sesuai dengan ukuran array. Ukuran array juga ada pada objek array itu sendiri. Secara skematis, tampilannya seperti ini:

Dengan demikian, jika kita berhasil memilih segmen array di ruang yang sebelumnya dibebaskan, maka kita dapat menimpa header segmen array dengan isi buffer audio. Untuk melakukan ini, kami memodifikasi kode di atas dengan menambahkan baris berikut setelah sleep(1000);
:
... arr = new Array(128); for(var i = 0; i < arr.length; i++) { arr[i] = new Array(0x3ff0); for(var j = 0; j < arr[i].length; j++) arr[i][j] = 0x30303030; } ...
Ukuran array dipilih sehingga ukuran segmen array menempati seluruh segmen heap (potongan memori minimum yang tidak dapat dibagi, ukurannya adalah 0x10000 byte). Jalankan kode ini, tentukan fungsi memcpy
sebagai breakpoint (akan ada banyak panggilan memcpy
, jadi masuk akal untuk berhenti dulu di edgehtml!WebCore::AudioBufferData::copyBufferData
), di mana crash terjadi. Kami mendapatkan hasil sebagai berikut:

Hebat! Sekarang kita dapat menimpa header segmen array dengan nilai kita sendiri. Nilai yang paling menarik dalam hal ini adalah ukuran array, offset yang dapat kita lihat pada screenshot di atas. Ubah isi buffer audio sebagai berikut:
... var t = audioBuf.getChannelData(0); var ta2 = new Uint32Array(t.buffer); ta2[0] = 0; ta2[1] = 0; ta2[2] = 0xffe0; ta2[3] = 0; ta2[4] = 0; ta2[5] = 0; ta2[6] = 0xfba6; ta2[7] = 0; ta2[8] = 0; ta2[9] = 0x7fffffff - 2; ta2[10] = 0x7fffffff; ta2[11] = 0; ta2[12] = 0; ta2[13] = 0; ta2[14] = 0x40404040; ta2[15] = 0x50505050; ...
Perhatikan nilai-nilai ta2[14]
dan ta2[15]
- mereka sudah merujuk bukan ke header segmen, tetapi ke nilai array itu sendiri. Dengan ini, kita dapat menentukan array yang kita butuhkan dalam array global sebagai berikut:
... for(var i = 0; i < arr.length; i++) { if(arr[i][0] == 0x40404040 && arr[i][1] == 0x50505050) { alert('Target array idx: ' + i); target_idx = i; target_arr = arr[i]; break; } }
Jika sebagai hasilnya ditemukan array, dua elemen pertama yang diubah dengan cara tertentu, maka semuanya baik-baik saja. Sekarang kami memiliki array yang ukuran segmennya lebih besar dari yang sebenarnya. Array yang tersisa dapat dibebaskan.
Di sini perlu diingat bahwa ukuran array ada di dua entitas: di objek array, di mana ia tetap tidak berubah, dan di segmen array, di mana kami meningkatkannya. Ternyata ukuran dalam objek array diabaikan jika kode dieksekusi dalam mode JIT
dan telah dioptimalkan. Ini mudah dicapai, misalnya, sebagai berikut:
function arr_get(idx) { return target_arr[idx]; } function arr_set(idx, val) { target_arr[idx] = val; } for(var i = 0; i < 0x3ff0; i++) { arr_set(i, arr_get(i)); }
Setelah itu, menggunakan fungsi arr_get
dan arr_set
Anda dapat melampaui batas array ( OOB
, out-of-bound
).
Menggunakan OOB
untuk membuat primitif membaca dan menulis ke alamat yang sewenang-wenang
Di bagian ini, kami mempertimbangkan teknik yang memungkinkan Anda membaca dan menulis ke alamat arbitrer menggunakan OOB
. Metode yang digunakan untuk mendapatkan ini akan mirip dengan yang digunakan dalam sumber [1], tetapi juga akan ada perubahan signifikan.
Dalam versi Edge
digunakan Edge
blok memori untuk heap dialokasikan secara berurutan, yang karenanya, ketika mengalokasikan sejumlah besar objek, cepat atau lambat mereka akan muncul setelah segmen array, di mana kita dapat pergi.
Pertama, ini memberi kita kemampuan untuk membaca pointer ke tabel virtual metode objek ( vftable
), sehingga kita dapat memotong pengacakan ruang proses alamat ( ASLR
). Tetapi akses ke objek mana yang akan membantu kita mencapai membaca dan menulis secara sewenang-wenang? Beberapa objek DataView
sangat bagus untuk ini.
DataView menyediakan antarmuka tingkat rendah untuk membaca dan menulis beberapa jenis angka dalam biner ArrayBuffer, terlepas dari urutan byte platform.
Struktur internal DataView
berisi pointer ke buffer. Untuk membaca dan menulis ke alamat yang berubah-ubah, misalnya, kita dapat membangun rantai dua DataView
( dv1
dan dv2
) sebagai berikut: sebagai buffer dv1
tentukan alamat dv2
dengan mengakses array. Sekarang menggunakan dv1
kita dapat mengubah alamat buffer dv2
, yang dengannya pembacaan dan penulisan sewenang-wenang dicapai. Secara skematis, ini dapat direpresentasikan sebagai berikut:

Untuk menggunakan metode ini, Anda perlu mempelajari cara menentukan alamat objek dalam memori. Untuk melakukan ini, ada teknik berikut: Anda perlu membuat Array
baru, gunakan OOB
untuk menyimpan vftable
dan typeId
(dua bidang 64-bit pertama dari struktur) dan menetapkan objek yang alamatnya menarik ke elemen pertama array. Kemudian, Anda harus mengembalikan nilai vftable
dan typeId
sebelumnya disimpan. Sekarang kata ganda junior dan senior dari alamat objek dapat diperoleh dengan merujuk pada elemen pertama dan kedua dari array. Faktanya adalah, secara default, array baru adalah IntArray
, dan nilai-nilai 4-byte array disimpan di segmennya sebagaimana adanya. Saat menetapkan objek ke array, array dikonversi ke ObjectArray
, dan segmennya digunakan untuk menyimpan alamat objek. Konversi mengubah vftable
dan typeId
. Dengan demikian, jika kita mengembalikan nilai asli vftable
dan typeId
, melalui elemen array ini kita dapat mengakses segmen secara langsung. Proses yang dijelaskan secara skematis dapat direpresentasikan sebagai berikut:

Fungsi untuk mendapatkan alamat akan terlihat seperti ini:
function addressOf(obj) { var hdr_backup = new Array(4);
Pertanyaan terbuka tetap berupa penciptaan objek yang diperlukan dan pencarian mereka menggunakan OOB
. Seperti disebutkan sebelumnya, ketika mengalokasikan sejumlah besar objek, cepat atau lambat mereka akan mulai menonjol setelah segmen array, di luar itu kita dapat pergi. Untuk menemukan objek yang diperlukan, Anda hanya perlu menelusuri indeks di luar array untuk mencari objek yang diperlukan. Karena semua objek dari tipe yang sama terletak di satu segmen tumpukan, Anda dapat mengoptimalkan pencarian dan pergi melalui segmen tumpukan dengan peningkatan 0x10000
, dan periksa hanya beberapa nilai pertama dari awal setiap segmen tumpukan. Untuk mengidentifikasi objek, Anda dapat menetapkan nilai unik untuk beberapa parameter (misalnya, untuk DataView
dapat berupa byteOffset
) atau, menggunakan konstanta yang sudah diketahui dalam struktur objek (misalnya, dalam versi Edge
digunakan dalam IntArray
, nilai 0x10005
selalu ditemukan pada 0x18
).
Dengan menggabungkan semua teknik di atas, Anda dapat membaca dan menulis ke alamat yang berubah-ubah. Di bawah ini adalah tangkapan layar untuk membaca objek memori DataView
.

Langkah 2. Melakukan fungsi API sewenang-wenang
Pada tahap ini, kami dapat membaca dan menulis ke alamat sewenang-wenang di dalam proses tampilan konten Edge
. Pertimbangkan teknologi utama yang harus mengganggu operasi lebih lanjut dari aplikasi dan solusi mereka. Kami sudah menulis serangkaian artikel pendek app specific security mitigation
( bagian 1, pengantar , bagian 2, Internet Explorer dan Edge , bagian 3, Google Chrome ), tetapi perlu diingat bahwa pengembang tidak tinggal diam dan menambahkan alat baru ke produk mereka perlindungan.
Address Space Randomization ( ASLR
)
ASLR (English address space layout randomization) adalah teknologi yang digunakan dalam sistem operasi yang secara acak mengubah lokasi struktur data penting dalam ruang alamat proses, yaitu: gambar file yang dapat dieksekusi, perpustakaan yang dimuat, tumpukan dan tumpukan.
Di atas, kami belajar membaca alamat tabel kelas virtual, menggunakannya, kami dapat dengan mudah menghitung alamat dasar modul Chakra.dll
, sehingga ASLR
tidak menimbulkan masalah untuk operasi lebih lanjut.
Perlindungan eksekusi data ( DEP
, NX
)
Pencegahan Eksekusi Data (DEP) adalah fitur keamanan yang dibangun di Linux, Mac OS X, Android, dan Windows yang mencegah aplikasi untuk mengeksekusi kode dari area memori yang ditandai sebagai "hanya data". Ini akan mencegah beberapa serangan, yang, misalnya, menyimpan kode di area tersebut menggunakan buffer overflows.
Salah satu cara untuk melindungi ini adalah dengan memanggil VirtualAlloc
menggunakan rantai ROP
. Tetapi dalam kasus Edge
metode ini tidak akan berfungsi karena ACG
(lihat di bawah).
Control Flow Guard ( CFG
)
CFG
adalah mekanisme perlindungan yang bertujuan mempersulit proses eksploitasi kerentanan biner dalam aplikasi mode pengguna dan kernel. Pekerjaan mekanisme ini terdiri dalam memvalidasi panggilan tidak langsung, yang mencegah penyerang mencegat utas eksekusi (misalnya, dengan menimpa tabel fungsi virtual)
Teknologi ini hanya mengontrol panggilan tidak langsung, misalnya, panggilan metode dari tabel virtual fungsi objek. Alamat pengirim pada tumpukan tidak dikontrol, dan ini dapat digunakan untuk membangun rantai ROP
. Penggunaan rantai ROP/JOP/COP
dapat terhalang oleh teknologi baru Intel
: Control-flow Enforcement Technology
( CET
). Teknologi ini terdiri dari dua bagian:
Shadow Stack
(shadow stack) - digunakan untuk mengontrol alamat pengirim dan melindungi rantai ROP
;Indirect Branch Tracking
adalah metode perlindungan terhadap rantai JOP/COP
. Ini adalah instruksi ENDBRANCH
baru, yang menandai semua alamat transisi yang valid untuk instruksi call
dan jmp
.
Penjaga Kode Sewenang-wenang ( ACG
)
ACG
adalah teknologi yang mencegah pembuatan kode dinamis (dilarang mengalokasikan area memori VirtaulAlloc
menggunakan VirtaulAlloc
) dan modifikasinya (tidak mungkin untuk VirtaulAlloc
kembali area memori yang ada sebagai yang dapat dieksekusi)
Perlindungan ini, seperti CFG
, tidak mencegah penggunaan rantai ROP
.
Isolasi AppContainer
AppContainer adalah teknologi Microsoft yang memungkinkan Anda mengisolasi suatu proses dengan menjalankannya di lingkungan berpasir. Teknologi ini membatasi akses suatu proses ke kredensial, perangkat, sistem file, jaringan, proses dan jendela lain dan bertujuan meminimalkan kemampuan malware yang memiliki kemampuan untuk mengeksekusi kode arbitrer dalam suatu proses.
Perlindungan ini sangat menyulitkan proses operasi. Karena itu, kami tidak dapat memanggil file pihak ketiga yang dapat dieksekusi atau mengakses informasi pengguna yang sensitif dalam memori atau pada disk. Namun, perlindungan ini dapat diatasi dengan menggunakan kerentanan dalam penerapan kotak pasir AppContainer atau dengan meningkatkan hak istimewa melalui pemanfaatan kerentanan di kernel OS.
Perlu dicatat bahwa Microsoft
memiliki program hadiah terpisah untuk teknik menghindari teknologi security mitigation
. Program menunjukkan bahwa menggunakan kembali kode yang dapat dieksekusi (membangun rantai ROP
adalah variasi dari teknik ini) tidak termasuk dalam program ini, karena adalah masalah arsitektur.
Menggunakan pwn.js
Dari analisis semua teknologi keamanan, dapat disimpulkan bahwa untuk dapat mengeksekusi kode arbitrer, Anda perlu mem-bypass kotak pasir AppContainer
. Pada artikel ini, kami menjelaskan metode yang menggunakan kerentanan di kernel Windows
. Dalam hal ini, kami hanya dapat menggunakan kode JS
dan rantai ROP
. Menulis sebuah exploit untuk kernel hanya menggunakan rantai ROP
bisa sangat sulit. Untuk menyederhanakan tugas ini, Anda dapat menemukan satu set gadget yang dengannya kami dapat memanggil metode WinAPI
diperlukan. Untungnya, ini sudah diterapkan di perpustakaan pwn.js
Dengan menggunakannya, setelah hanya menjelaskan fungsi read
dan write
untuk membaca dan menulis sewenang-wenang, Anda bisa mendapatkan API
mudah untuk menemukan fungsi WinAPI
diperlukan dan memanggilnya. pwn.js
juga menyediakan alat yang nyaman untuk bekerja dengan nilai 64-bit dan pointer dan alat untuk bekerja dengan struktur.
Pertimbangkan contoh sederhana. Pada langkah sebelumnya, kami mendapat rantai dua DataView
terkait. Untuk menyiapkan exploit, Anda harus membuat kelas berikut:
var Exploit = (function() { var ChakraExploit = pwnjs.ChakraExploit; var Integer = pwnjs.Integer; function Exploit() { ChakraExploit.call(this); ...
, MessageBoxA
:
function run() { with (new Exploit()) {
:

3.
WinAPI
. . CVE-2016-3309
. [7] [8], pwn.js
[2] , GDI
-. [9], [10] [11]. . , . , AppContainer
, pwn.js
. cmd.exe
SYSTEM
.
GDI β Windows , , .
, . JS
- 64- kernel_read_64
kernel_write_64
, . Windows. BITMAP
, . pwn.js
. BITMAP
, , :
var BITMAP = new StructType([ ['poolHeader', new ArrayType(Uint32, 4)],
Tid
KTHREAD
, , , EmpCheckErrataList
, . , :
... var nt_EmpCheckErrataList_ptr = worker_bitmap_obj.Tid.add(0x2a8); var nt_EmpCheckErrataList = kernel_read_64(nt_EmpCheckErrataList_ptr); var ntoskrnl_base_address = nt_EmpCheckErrataList.sub( g_config.nt_empCheckErrataList_offset); ...
, AppContainer
. AppContainer
IsPackagedProcess
( Process Environment Block
, PEB
), . Access Token
, AppContainer
. Access Token
, . Access Token
, . EPROCESS
ActiveProcessLinks
, . PEB
EPROCESS
. PsInitialSystemProcess
, , ActiveProcessLinks
.
Edge
: , Edge
. SYSTEM
. , , winlogon.exe
.
pwn.js
:
:

YouTube , Microsoft Edge.
Ringkasan
:
- ,
Edge
Windows
, 13 , CVE-2017-0240
, . CVE-2016-3309
. JS
- 666
JS
- :
cmd.exe
SYSTEM
,
, , . , , . .
- Liu Jin β The Advanced Exploitation of 64-bit Edge Browser Use-After-Free Vulnerability on Windows 10
- Andrew Wesie, Brian Pak β 1-Day Browser & Kernel
Exploitation - Natalie Silvanovich β The ECMA and the Chakra. Hunting bugs in the Microsoft Edge Script Engine
- Natalie Silvanovich β Your Chakra Is Not Aligned. Hunting bugs in the Microsoft Edge Script Engine
- phoenhex team β cve-2018-8629-chakra.js
- Quarkslab β Exploiting MS16-145: MS Edge TypedArray.sort Use-After-Free (CVE-2016-7288)
- Exploiting MS16-098 RGNOBJ Integer Overflow on Windows 8.1 x64 bit by abusing GDI objects
- Siberas β Kernel Exploitation Case Study β "Wild" Pool Overflow on Win10 x64 RS2 (CVE-2016-3309 Reloaded)
- Saif El-Sherei β Demystifying Windows Kernel Exploitation by Abusing GDI Objects
- Diego Juarez β Abusing GDI for ring0 exploit primitives
- Nicolas A. Economou β Abusing GDI for ring0 exploit
primitives: Evolution - pwn.js