
Seiring waktu, semakin banyak teknologi protektif muncul, karena itu peretas harus mengencangkan ikat pinggang mereka lebih erat. Namun, koin ini memiliki dua sisi: teknologi pertahanan juga menciptakan permukaan serangan tambahan, dan untuk menyiasatinya, Anda hanya perlu menggunakan kerentanan dalam kode mereka.
Mari kita lihat salah satu teknologi ini - ARM TrustZone. Implementasinya mengandung sejumlah besar kode, dan untuk mencari kerentanan di dalamnya, Anda memerlukan semacam cara otomatis. Kami menggunakan metode lama yang terbukti - fuzzing. Tapi pintar!
Kami akan menghapus aplikasi khusus yang muncul dengan diperkenalkannya teknologi TrustZone - trustlet. Untuk menjelaskan secara rinci metode fuzzing yang telah kami pilih, pertama-tama kita beralih ke teori tentang TrustZone, sistem operasi tepercaya, dan interaksi dengan sistem operasi konvensional. Ini tidak lama. Ayo pergi!
ARM TrustZone
Teknologi TrustZone dalam prosesor ARM memungkinkan Anda mentransfer pemrosesan informasi rahasia ke lingkungan aman yang terisolasi. Pemrosesan tersebut dilakukan, misalnya, oleh Keystore, layanan Sidik Jari di OS Android, teknologi perlindungan hak cipta DRM, dll.
Sudah banyak yang ditulis tentang perangkat TrustZone, jadi kami hanya akan mengingat sebentar.

TrustZone membagi "dunia" (dalam hal TrustZone - Dunia) menjadi dua - Dunia Normal dan Dunia Aman - dan menambahkan empat mode eksekusi ke prosesor:
- EL3 - mode monitor - mode di mana sistem mulai, dan yang merupakan mode eksekusi yang paling disukai;
- S-EL2 - mode hypervisor tepercaya;
- S-EL1 - mode sistem operasi tepercaya;
- S-EL0 - mode aplikasi tepercaya (aplikasi tepercaya, TA, trustlets), atau trustlets.
Pada SoC dengan teknologi TrustZone, dua sistem operasi dapat bekerja secara bersamaan. Salah satu yang berfungsi di Dunia Normal disebut Rich OS, dan yang kedua dari Secure World adalah OS TEE (Trusted Execution Environment). Sudah ada lebih dari selusin sistem operasi tepercaya ini. Kami akan fokus pada satu spesifik - Trustonic Kinibi. Ini, khususnya, digunakan pada ponsel Samsung dengan SoC Exynos inklusif hingga Galaxy S9.
Kinibi trustonic
Trustonic diciptakan oleh ARM, Gemalto, dan Giesecke & Devrient (G&D) dan terus mengembangkan sistem operasi Mobicore Giesecke & Devrient (G&D) dengan nama Kinibi.
Sistem operasi Kinibi mendukung standar Lingkungan Eksekusi Tepercaya Platform Global . Diagram strukturalnya ditunjukkan pada gambar.

Seperti yang Anda lihat, implementasi TrustZone mencakup komponen tidak hanya di "dunia yang dilindungi", tetapi juga komponen di "dunia normal". Untuk memahami yang utama, lebih baik untuk melihat skema sistem ini dari sudut pandang pengembang.

Pada level rendah, di dunia yang dilindungi, selain microkernel, driver dan pekerjaan manajer runtime. Dan di dunia normal, driver khusus berfungsi, yang memastikan transisi prosesor ke dunia yang dilindungi atas permintaan aplikasi. Di tingkat userspace, aplikasi dan komponen beroperasi yang menyediakan API untuk menghubungkan aplikasi antara dunia normal dan aman. Ada juga daemon khusus di dunia normal yang menyediakan peluncuran awal beberapa trustlet, dan melaluinya semua permintaan untuk trustlets dari aplikasi klien harus dilalui.
Ada dua set API di Kinibi: Global Platform API (ditunjukkan dengan warna hijau) dan Legacy API (merah). Kedua set menyediakan kira-kira set fungsi yang sama, hanya yang pertama dibangun sesuai dengan standar Platform Global, dan yang kedua, tampaknya, sebelum standar, dan oleh karena itu disebut Legacy. Terlepas dari kenyataan bahwa, jika dilihat dari namanya, Anda harus menjauh dari penggunaannya, hanya Legacy API yang digunakan dalam trustlets Samsung.
Interaksi antar dunia
Untuk memanfaatkan peluang yang disediakan oleh teknologi TrustZone, aplikasi di dunia normal, yang disebut aplikasi klien, berkomunikasi dengan aplikasi tepercaya - trustlet. Trustlets menerapkan berbagai fungsi: otentikasi, manajemen kunci, bekerja dengan komponen perangkat keras yang mengimplementasikan fungsi keamanan, dll.
Permintaan ke trustlet dikirimkan menggunakan memori bersama khusus. Dunia normal dan dunia yang dilindungi, menurut teknologi TrustZone, diisolasi satu sama lain di tingkat atas (EL0 dan S-EL0) dari memori, dan untuk menciptakan wilayah memori bersama di antara mereka, yang disebut Memori Bersama Dunia (WSM), API yang disediakan oleh yang dilindungi dunia.
Skema umum interaksi antara aplikasi klien dan trustlet terlihat seperti ini:

- Aplikasi klien mengakses daemon dengan UID trustlet yang dengannya ia ingin membuat sesi;
- Daemon menggunakan driver untuk menghubungi sistem operasi tepercaya dengan permintaan untuk mengunduh trustlet;
- Sistem operasi tepercaya memuat trustlet ke dalam ruang alamat dunia yang dilindungi;
- Aplikasi klien lagi membuat buffer WSM melalui permintaan ke daemon dan menulis data ke dalamnya untuk permintaan ke trust;
- Aplikasi klien memberi tahu dunia yang dilindungi tentang kesiapan permintaan;
- Di dunia yang aman, permintaan dikirim ke trustlet yang diinginkan untuk diproses, dan trustlet menulis hasil kerjanya ke buffer WSM;
- Permintaan dan siklus respons dapat diulang;
- Aplikasi klien mengakhiri sesi dengan kepercayaan.
Kode pseudo dari sesi interaksi untuk aplikasi klien dan untuk trustlet terlihat cukup sederhana. Untuk aplikasi klien:
void main() { uint8_t* tciBuffer; uint32_t tciLength; uint8_t* mem; uint32_t mem_size; mcOpenDevice(MC_DEVICE_ID_DEFAULT); mcMallocWsm(MC_DEVICE_ID_DEFAULT, 0, tciLength, &tciBuffer, 0); session.deviceId = MC_DEVICE_ID_DEFAULT; mcOpenSession(&session, &uuid, tciBuffer, tciLength); mcMap(&session, mem, mem_size, &mapInfo); mcNotify(&session); mcWaitNotification(&session, -1); mcUnmap(&session, mem1, &mapInfo1); mcCloseSession(&session); mcFreeWsm(MC_DEVICE_ID_DEFAULT, tciBuffer); mcCloseDevice(MC_DEVICE_ID_DEFAULT); }
Untuk trustlet:
void tlMain(uint8_t *tciData, uint32_t tciLen) { // Check TCI size if (sizeof(tci_t) > tciLen) { // TCI too small -> end Trusted Application tlApiExit(EXIT_ERROR); } // Trusted Application main loop for (;;) { // Wait for a notification to arrive tlApiWaitNotification(INFINITE_TIMEOUT); // Process command // Notify the TLC tlApiNotify(); } }
mcNotify
/ tlApiNotify
dan mcWaitNotification
/ tlApiWaitNotification
- ini adalah fungsi notifikasi yang permintaan / responsnya siap terima di dunia lain, dan fungsi menunggu pemrosesan permintaan. Juga, aplikasi klien memiliki kemampuan untuk menggunakan fungsi mcMap. Ini memungkinkan Anda untuk membuat buffer WSM lain, jika diperlukan. Secara total, menggunakan fungsi ini, Anda hanya dapat membuat empat buffer seperti itu.
Dengan aplikasi klien, jelas - untuk ponsel Samsung ini adalah aplikasi Android biasa. Tapi apa itu trustlets?
Kinibi Trustlets
Trustlets terletak di sistem file biasa perangkat dan file yang berisi kode yang dapat dieksekusi. Ini bukan format ELF atau APK yang biasa untuk Android. Trastlets dalam sistem operasi Kinibi memiliki MobiCore Load Format (MCLF) mereka sendiri. Hal ini dijelaskan dalam komponen tingkat- sumber pengguna - open source yang telah diposting Trustonic di Github. Struktur file trustlet dapat digambarkan secara skematis dalam gambar seperti itu (trustlet ada di sebelah kiri).

Fitur-fitur berikut dapat dibedakan untuk trustlets:
- dieksekusi di ruang alamat yang terisolasi, yaitu, satu trustlet tidak melihat yang lain;
- tidak memiliki akses ke memori dunia normal, dengan pengecualian buffer WSM, ke memori sistem operasi TEE dan ke memori fisik;
- terletak di memori di bagian dengan hak berbeda untuk membaca, menulis, dan mengeksekusi;
- Buffer WSM berada dalam memori yang tidak dapat dieksekusi;
- Boot tanpa ASLR
- mereka menggunakan API yang disediakan oleh mclib, perpustakaan yang mengimplementasikan Global Platform API dan Legacy API untuk dunia yang dilindungi;
- dapat mengakses driver yang dilindungi menggunakan fungsi
tlApi_callDriver
.
Seperti yang Anda lihat, trustlets memiliki kemampuan yang sangat terbatas. Selain itu, mereka menggunakan beberapa mekanisme pertahanan, seperti berbagai atribut memori, dan juga sebagian besar trustlets menggunakan canary stack untuk melindungi terhadap menimpa stack. Tetapi Kinibi tidak memiliki ASLR, meskipun direncanakan dalam versi baru.
Meskipun semua pembatasan, trustlets adalah target yang sangat menarik bagi penyerang karena alasan berikut:
- Ini adalah jendela di TrustZone dari tingkat userspace di Android;
- mereka dapat berfungsi sebagai titik awal untuk meningkatkan hak istimewa ke inti sistem operasi TEE;
- trustlets memiliki akses ke informasi yang dilindungi, di mana bahkan kernel Android tidak memiliki akses.
Sebagai perangkat uji, kami menggunakan Samsung Galaxy S8. Jika Anda mencari trastlets di dalamnya, ternyata jumlahnya cukup banyak.

Artinya, ada banyak kode. Menggunakan analisis statis kode biner untuk mencari kerentanan sepertinya ide yang buruk. Melibatkan analisis dinamis tidak akan bekerja, jika hanya karena trastlets memiliki format sendiri, berbeda dari apa yang dapat dijalankan pada sistem operasi tradisional. Akan menyenangkan untuk menggunakan metode fuzzing yang terbukti dengan umpan balik, dan entah bagaimana menangkap crash trustlets ketika mereka terjadi. Mari kita coba selesaikan masalah yang menarik ini.
Bagaimana ini fuzz?
Bagi mereka yang belum bertemu dengan alat AFL yang luar biasa dan banyak tambahannya, kami sarankan untuk membaca artikel yang bagus ini . Dan semua orang mungkin tahu bahwa AFL dapat menghapus file ELF. Selain itu, bahkan file biner dikompilasi pada awalnya tanpa instrumentasi AFL. Ini dicapai melalui mode qemu. AFL menggunakan build khusus emulator qemu, di mana fungsionalitas instrumentasi biner dari instruksi cabang ditambahkan ke mode pengguna qemu. Ini memungkinkan dia untuk fuzz dengan kontrol cakupan kode bahkan untuk file biner. Dan bonus untuk ini adalah kemampuan untuk menghapus file yang dapat dieksekusi tidak hanya dari arsitektur asli, tetapi dari semua arsitektur yang didukung qemu. Tetapi untuk menggunakan mode ini dalam tugas kita, kita perlu mengubah trustlets ke format ELF.
Mari kita lihat lebih dekat file trustlet. Berkat format terbuka, ada loader untuk IDA Pro untuk mereka. Jika Anda membuka trustlet apa pun, kecuali, sebenarnya, kodenya, Anda dapat melihat bahwa itu menggunakan fungsi pustaka mclib. Sangat menarik bahwa semua panggilan ke fungsi tersebut melewati satu fungsi di alamat yang terekam di header trastlet. Sebagai contoh, ini adalah bagaimana fungsi tlApiLogvPrintf terlihat dalam kode trustlet, yang, jelas, berkaitan dengan output dari string.

Dapat dilihat bahwa ia meneruskan semua parameter lebih jauh dari fungsi lain. Ini adalah fungsi penjadwalan mclib, yang alamatnya ditulis di header MCLF di bidang yang disebut tlApiLibEntry
. Artinya, fungsi perpustakaan yang dipanggil dengan cara ini adalah satu-satunya dependensi untuk trustlet, trastlets tidak memiliki tautan lain di luar. Ini berarti bahwa jika kita menerapkan stubs untuk fungsi API, kita dapat mengeksekusi kode trustlet di lingkungan Linux biasa, tentu saja, terlebih dahulu mengubahnya menjadi file ELF. Dan itu berarti kita dapat men-debug dan menghapusnya.
Untuk mengubah trustlet menjadi file ELF, Anda dapat mengambil file yang sudah jadi, misalnya, mengkompilasi aplikasi kosong dengan fungsi utama, dan menambahkan bagian trustlet bersama dengan header-nya. Mudah! Juga penting untuk mentransfer kontrol ke kode trustlet. Tidak ada masalah dengan ini, header trastlet berisi alamat titik masuknya. Kami mendefinisikan alamat ini di fungsi main
kami sebagai alamat fungsi dan menyebutnya. Setelah berpikir dan bereksperimen, kita dapat menguraikan rencana berikut untuk menyelesaikan masalah kita:
- menerapkan transfer eksekusi ke titik masuknya trustlet;
- menerapkan fungsi pustaka atau bertopik pada mereka;
- mengimplementasikan fungsi pengiriman dan menuliskan alamatnya di header tralet;
- mengatur bagian trustlet ke alamat yang diinginkan.
Karena kita perlu mengubah banyak trustlet menjadi elf sekaligus, kita perlu memikirkan mengotomatisasi tugas-tugas ini. Untuk setiap trustlet, parameter berikut harus ditentukan secara otomatis: titik masuk, alamat bagian trustlet dan ukuran buffer input WSM. Tambahkan ini ke rencana.
- Tetapkan titik masuk, alamat bagian, dan ukuran buffer WSM.
Kumpulkan peri itu
1) titik masuk
Item pertama dalam paket mudah diimplementasikan dengan kode berikut. Itu dapat ditambahkan ke fungsi main
untuk file ELF sumber kami.
typedef void (*tlMain_t)(const void* tciBuffer, const uint32_t tciBufferLen); tlMain_t tlMain = sym_tlMain; tlMain(tciBuffer, tciBufferLen);
Kami mengkompilasi kode kami ke file objek.
$(CC) $(INCLUDE) -g -c tlrun.c
sym_tlMain
harus ditambahkan ke file objek. Ini dapat dilakukan dengan menggunakan keberatan.
arm-linux-gnueabi-objcopy --add-symbol sym_tlMain=$(TLMAIN) tlrun.o tlrun.o.1
Akibatnya, kami mendapatkan tlrun.o.1
- sumber yang dikompilasi dengan fungsi main
yang mentransfer kontrol ke kode trustlet.
2) Fungsi perpustakaan
Untuk mengimplementasikan fungsi pustaka, pertama-tama kita perlu daftar semua fungsi ini. Sekali waktu ada kebocoran dari Qualcomm dengan banyak bahan untuk perangkat seluler berdasarkan prosesor mereka. Di antara bahan-bahan ini juga beberapa gambar, file header dan gambar debug dari beberapa komponen untuk sistem operasi mobicore. Dari sana kami mengambil prototipe fungsi perpustakaan dengan angka-angka mereka, diteruskan sebagai parameter ke fungsi pengiriman. Untuk fungsi dengan tujuan yang diketahui seperti tlApiMalloc
atau tlApiLogvPrintf
kami membuat implementasi yang sesuai menggunakan fungsi serupa dari libc. Dan fungsinya tidak begitu jelas, misalnya, kami mengganti tlApiSecSPICmd
dengan bertopik sederhana yang menampilkan nama mereka dan mengembalikan status OK. Seluruh API mengkompilasi ke tllib.o
$(CC) $(INCLUDE) -g -c tllib.c
3) Fungsi pengiriman
Mirip dengan alamat titik masuk, tambahkan simbol, alamatnya sama untuk semua trustlet:
arm-linux-gnueabi-objcopy --add-symbol sym_tlApiLibEntry=0x108c tlrun.o tlrun.o.1
Implementasi fungsi penjadwalan adalah sepele. Hanya perlu mempertimbangkan bahwa alamatnya harus ditulis di header. Karena kita tidak tahu sebelumnya alamat fungsi pengiriman mana yang akan ditemukan setelah menautkan dan memulai, kita harus menulis alamatnya di header trustlet yang sudah di runtime. Misalnya, ketika memulai file sebelum fungsi main
mulai dijalankan.
void (*sym_tlApiLibEntry)(int num) __attribute__((weak)); void tlApiLibEntry(int num) __attribute__((noplt)); __attribute__((constructor)) void init() { sym_tlApiLibEntry = tlApiLibEntry; }
4) Bagian
Tambahkan bagian ke file objek, kami juga menggunakan objcopy
.
arm-linux-gnueabi-objcopy --add-section .tlbin_text=.text.bin \ --set-section-flags .tlbin_text=code,contents,alloc,load \ --add-section .tlbin_data=.data.bin \ --set-section-flags .tlbin_data=contents,alloc,load \ --add-section .tlbin_bss=.bss.bin \ --set-section-flags .tlbin_bss=contents,alloc,load \ tlrun.o.1 tlrun.o.2
Di sini .tlbin_text
adalah nama bagian dari trustlet, dan .text.bin
adalah nama file dengan dump bagian ini. Anda dapat membuang file menggunakan IDA yang sama.
Sebagai hasil dari konversi ini, trust biner akan ditambahkan ke file ELF sumber.
5) Otomasi
Untuk seluruh majelis, kami memutuskan untuk menggunakan satu Makefile besar yang umum untuk semua trustlet dan satu kecil, terhubung ke sana untuk setiap trustlet individu dengan parameternya. Untuk setiap trustlet, Anda perlu menentukan titik masuk, alamat bagian, dan ukuran buffer WSM. Dua parameter pertama mudah diperoleh dengan skrip sederhana untuk IDA, dan menentukan ukuran buffer terkadang tidak begitu mudah untuk diotomatisasi. Anda dapat mengotomatiskan tugas ini juga, atau Anda dapat menghabiskan 10 menit untuk menentukannya untuk semua trustlet dengan menganalisis kode mereka secara manual. Parameter ini dapat ditetapkan sebagai variabel di Makefile kecil Anda.
TLMAIN := 0x98F5D TLTEXT := 1000 TLDATA := c0000 TLBSS := c10e0 TLTCI_LEN := 4096
Dan dalam Makefile besar, gunakan parameter ini dengan cara ini:
$(CC) $(INCLUDE) -g -DTCILEN=$(TLTCI_LEN) -c tlrun.c # ... $(CC) -g tlrun.o.2 tllib.o --section-start=.tlbin_text=$(TLTEXT),--section-start=.tlbin_data=$(TLDATA),--section-start=.tlbin_bss=$(TLBSS) -o tlrun
Jadi, kami mengubah trustlet menjadi file ELF dengan lokasi yang benar dari bagian trustlet dalam memori dan alamat yang benar di header. Secara teori, itu bahkan dapat dieksekusi dengan benar dan fuzzed lebih lanjut. Baiklah, mari kita periksa!
Fuzzing
Karena AFL menggunakan qemu untuk menjalankan kode arsitektur non-asli, untuk permulaan akan lebih baik untuk memeriksa apakah elf kami berjalan di bawah emulator sama sekali. Dan kemudian masalah mulai segera.
Masalah nomor 1: toolchainUntuk mengkompilasi kode dan membuat file, kami menggunakan toolchain arm-linux-gnueabihf. "hf" pada akhirnya berarti kompiler menggunakan dukungan perangkat keras Hard Float dalam prosesor ARM. Ketika saya mencoba menjalankan file kami di bawah emulator qemu, itu langsung macet, mengeluarkan "Segmentasi kesalahan". Menimbang bahwa dalam kode kami tidak ada pekerjaan dengan angka floating-point di mana pun, alasan kecelakaan ini benar-benar tidak dapat dipahami. Setelah berpikir sebentar, kami memutuskan untuk mencoba menggunakan toolchain tanpa Hard float arm-linux-gnueabi. Dan kita beruntung! File berfungsi, dan output darinya mulai muncul di konsol.

Jadi kamu bisa fuzz. Kami meluncurkan AFL dan di sini ...
Masalah nomor 2: instrumentasi
Untuk beberapa alasan, AFL tidak melihat instrumentasi. Pada awalnya itu benar-benar tidak jelas apa masalahnya. qemu dibangun dengan benar, opsi -Q (mode qemu) diatur. Mengutuk, saya harus masuk ke kode sumber untuk patch AFL untuk qemu. Ternyata di tambalan AFL, saat mengunduh file ELF, qemu mencari bagian kode dan menetapkan batas-batas alamat di mana ia akan menghasilkan instrumentasi. Masalahnya adalah jika ada beberapa bagian kode, untuk beberapa alasan hanya yang pertama yang akan diinstrumentasi. Ini adalah bug atau fitur, tetapi kami memiliki dua bagian kode, dan titik masuk - utama - adalah yang kedua. Jelas, dia tidak melihat instrumentasi saat startup, karena tidak ada di bagian kedua! Lebih jauh dari sumber, Anda dapat melihat bahwa ketika variabel lingkungan AFL_INST_LIBS dihidupkan, batas-batas instrumentasi menjadi tak terbatas. Nyalakan dan mulai.

Pekerjaan yang membingungkan!
Idenya dikonfirmasi! Kami meluncurkan fuzzing dengan umpan balik pada file format biner kustom. Seperti yang Anda lihat, dia bahkan menemukan semacam kecelakaan. Jadi, kami mendapatkan cara yang andal untuk menghilangkan binari seperti itu, menangkap kesalahan dalam kode mereka dan juga menjalankannya di Linux biasa dan dengan mudah melakukan debug dengan alat yang ada. Kelas!
Selama beberapa hari, kami melakukan fuzzing semua trustlet. Akibatnya, kami memiliki banyak input data yang menghasilkan crash, dan tugas menganalisis semua crash ini.
Menganalisis Kecelakaan
Total selama 23 trusts AFL ditemukan 477 kasus uji menghasilkan crash. Sejumlah besar yang saya benar-benar tidak ingin memproses secara manual. Di antara serangkaian kasus uji ini, ada yang hampir identik yang menghasilkan kerusakan di tempat yang sama. Untuk menghapus redundansi kasus uji, Anda dapat menggunakan alat afl-cmin. Setelah melewati semua trastlets, 225 kasus tetap harus dianalisis. Ngomong-ngomong, banyak! Untuk memudahkan tugas kami, kami memutuskan untuk menggunakan alat analisis dinamis yang akan membantu mengidentifikasi kesalahan perangkat lunak dengan lebih akurat dan semua propertinya. Ini akan membantu mengevaluasi kegunaan bug dan kompleksitas operasinya.
Jadi, untuk menggunakan beberapa jenis alat analisis dinamis, kita perlu setidaknya menjalankan trustlet yang dikonversi pada sistem ARM asli, dan tidak di bawah virtualisasi qemu. Linux atau Android mungkin cocok untuk ini.
Masalah 3: bagianKami memutuskan untuk mengambil sistem 32-bit dengan Linux, karena Trustlet 32-bit, dan Linux lebih nyaman dan memiliki alat analisis yang lebih dinamis daripada Android. Dan di sini ternyata ketika diluncurkan, elf kami segera mengeluarkan kesalahan Segmentasi.
Ternyata masalahnya adalah keanehan binari kita. Saat membuat mereka, Anda harus menempatkan bagian trustlet di alamat yang diinginkan, di mana alamat bagian kode trustlet selalu 0x1000. Ini adalah bagian pertama dalam file, dan di depannya masih header ELF pada 0x0. Dan di Linux, dua halaman pertama ruang alamat, hingga alamat 0x2000, dicadangkan untuk tugas utilitas, jadi ketika loader mencoba memproyeksikan bagian di sana, kesalahan terjadi.
Ternyata, ada jalan keluar dari situasi ini. Pada kernel 64-bit, reservasi seperti itu dari halaman pertama dalam memori tidak terjadi, dan pengaturan bagian ini menjadi mungkin. Karena file kami 32-bit, lebih mudah untuk membuat lingkungan 32-bit pada sistem 64-bit. Paket debootstrap
sangat bagus untuk tujuan ini.
Masalah nomor 4: tidak ada alatSekarang karena trustlet kami yang didesain ulang bekerja pada sistem ARM asli, kita perlu mencoba alat analisis dinamis. Di antara metode analisis dinamis dari file biner adalah debugging dan dynamic binary instrumentation (DBI). Gdb bagus untuk yang pertama. Dan untuk yang kedua, tidak ada banyak pilihan: di bawah ARM, pada dasarnya hanya ada tiga kerangka kerja DBI yang stabil - DynamoRIO, Valgrind, dan Frida. Yang pertama memiliki banyak alat yang bagus untuk melacak dan menangkap kesalahan, tetapi pemuat file ELF, yang diimplementasikan di dalamnya, tidak dapat mengatasi pemuatan file kami. Valgrind adalah kerangka kerja yang cukup kuat, dan memiliki alat callgrind yang cocok bagi kita untuk melacak dan memcheck untuk memantau operasi memori. Ternyata mereka menghasilkan hasil yang sangat tidak nyaman untuk parsing, jadi mereka tidak cocok untuk digunakan dalam mode otomatis pada banyak file. Dan kami tidak punya waktu untuk mencoba Frida. Jika ada yang punya pengalaman menggunakan Linux di ARM, tuliskan kesan Anda di komentar.
Seperti yang Anda lihat, kami hanya bisa puas dengan debugger. Tetapi menggunakan skrip untuk gdb bahkan ini sudah sangat menyederhanakan pekerjaan kami.
Masalah # 5: fungsi perpustakaanMasalah lain yang jelas dari awal adalah fungsi perpustakaan yang digunakan trastlet. Kami menggantinya dengan bertopik, dengan pengecualian fungsi yang dapat diganti dengan yang serupa dari libc. Jelas, jika dalam logika trastlet beberapa kode memproses hasil dari salah satu fungsi tulisan rintisan ini, sangat mungkin bahwa itu akan macet karena mengharapkan data yang sama sekali berbeda, dan ini tidak selalu berarti kesalahan dalam kode.
Ada beberapa fungsi yang tidak mudah untuk mensimulasikan perilaku fungsi sebenarnya:
- tlApiSecSPICmd;
- tlApi_callDriver;
- tlApiWrapObjectExt;
- tlApiUnWrapObjectExt;
- tlApiCipherDoFinal;
- tlApiSignatureSign;
- ...
Agar tidak membuang waktu mempelajari kasus meragukan seperti itu, kami memutuskan untuk tidak mempertimbangkan kasus uji yang menggunakan fungsi ini.
Hasil yang membingungkan
Dalam mode otomatis, menggunakan skrip, kami mengumpulkan informasi berikut di semua trustlet:
- UID Traidlet
- pengidentifikasi kecelakaan;
- jenis kesalahan (jenis sinyal saat crash);
- Alamat tempat kesalahan terjadi
- Fungsi API yang digunakan oleh trastlet.
Ternyata, sangat nyaman untuk memasukkan semua informasi ini ke dalam basis data, dan kemudian memilih kasus yang paling menarik untuk dianalisis oleh kueri SQL dan menambahkan informasi sesuai dengan analisis.

Misalnya, dengan kueri ini, Anda dapat menampilkan semua kasus pengujian tempat kesalahan kesalahan Segmentasi terjadi:
select * from main where type = "SIGSEGV";
Dan memfilter kasus uji yang menggunakan fungsi tlApiSecSPICmd
, yang telah kami implementasikan sebagai rintisan:
select * from main where api not like "tlApiSecSPICmd";
Dengan demikian, kesalahan dari berbagai jenis ditemukan di semua trustlets. Beberapa dari mereka tidak mengarah pada kerentanan, tetapi ada beberapa yang rentan dan dapat digunakan oleh penyerang. Pertimbangkan kerentanan paling menarik yang ditemukan.
SVE-2019-14126

Kerentanan itu ditemukan dalam trustlet keymaster dalam kode untuk memproses konten buffer TCI sambil mem-parsing struktur ASN.1 yang disandikan sesuai dengan aturan DER. Dua bidang dalam struktur ini digunakan sebagai dimensi: satu saat mengalokasikan memori dinamis, dan yang lain saat menyalinnya. Jelas, jika ukuran kedua lebih besar dari yang pertama, terjadi tumpukan tumpukan. Kerentanan seperti itu biasanya mengarah pada kemungkinan eksekusi kode oleh penyerang, jadi kami mencoba untuk membuat exploit penuh untuk kerentanan ini. Ketika menilai kemungkinan eksploitasi, seseorang juga harus memperhitungkan semua batasan kepercayaan yang disebutkan di atas.
Memiliki tumpukan overflow dan berdasarkan pada pembatasan ini, orang dapat membayangkan strategi operasi berikut:
- temukan beberapa penunjuk fungsi di tempat yang dapat diakses untuk menulis ulang, misalnya, di bagian .bss;
- Menggunakan overflow yang ditemukan, buat blok memori tumpukan di tempat ini;
- memulai alokasi memori di lokasi tertentu dan menimpa penunjuk fungsi;
- memulai penunjuk fungsi panggilan ditimpa.
Untuk melakukan ini, tentu saja, Anda perlu memahami secara rinci bagaimana tumpukan bekerja di sistem operasi Kinibi. Untuk melakukan ini, kami harus merekayasa balik fungsi mengalokasikan dan membebaskan memori mclib, tetapi sekarang Anda dapat melihat deskripsi yang baik tentang tumpukan di laporan ini dari konferensi ZeroCon pada bulan April.
— .bss. , .bss . , , , , .

, .bss, .
, . , , , , .bss, . code-reuse.
ROP. , ROP, .bss. , , . , , . , , , .
ROP, JOP. JOP — Jump Oriented Programming. JOP .
JOP , ROPGadget. , JOP, :
ROPgadget --binary tlrun --thumb --range 0x1000-0xbeb44 | grep -E "; b.+ r[0-9]+$"
! .

. ROP . , ROP- weird machine , . JOP , . ARM, , , — LDMIA (Load Memory Increment Address).

, , , , . , . JOP!
LDMIA . - capstone, ROPGadget, LDMLO.

! . , , . stack cookie , .
*(int*)&mem1[offset] = SUPER_GADGET; // r2 *(int*)&mem1[offset + 4] = 0; // r3 *(int*)&mem1[offset + 8] = 0; // r4 *(int*)&mem1[offset + 12] = SUPER_GADGET; // r5 *(int*)&mem1[offset + 16] = 0x9560b; // r7 offset += 0x14; *(int*)&mem1[offset] = 0; // r2 *(int*)&mem1[offset + 4] = 0; // r3 *(int*)&mem1[offset + 8] = 0; // r4 *(int*)&mem1[offset + 12] = 0; // r5 *(int*)&mem1[offset + 16] = 0x96829; // r7 offset += 0x14; *(int*)&mem1[offset] = SUPER_GADGET; // r2 *(int*)&mem1[offset + 4] = 0; // r3 *(int*)&mem1[offset + 8] = 0x3d5f4; // r4 *(int*)&mem1[offset + 12] = mapInfo3.sVirtualAddr; // r5 *(int*)&mem1[offset + 16] = 0x218c7; // r7
Hello, world .
strcpy(mem3 + 0x100, "Hello world from TEE!\n"); *(int*)&mem1[offset] = 0x7d081b1; // r2 *(int*)&mem1[offset + 4] = 0; // r3 *(int*)&mem1[offset + 8] = mapInfo3.sVirtualAddr + 0x100; // r4 *(int*)&mem1[offset + 12] = 0; // r5 *(int*)&mem1[offset + 16] = 0x9545b; // r7

"Hello, world!" , , , keymaster, , . , . , Gal Beniamini TEE Qualcomm , , offline- Android. TEE OS EL-3, .
Kesimpulan
ARM TrustZone , . Secure World Android, . , , Samsung bug bounty TrustZone, .

AFL qemu, "" . . , . !
Tautan yang bermanfaat