Pendahuluan
Pada pertengahan April, kami
menerbitkan berita tentang trojan
Android.InfectionAds.1 , yang mengeksploitasi beberapa kerentanan kritis di Android. Salah satunya, CVE-2017-13156 (juga dikenal sebagai
Janus ), memungkinkan malware menginfeksi file APK tanpa merusak tanda tangan digital. Yang lainnya adalah CVE-2017-13315. Ini memberi trojan hak istimewa yang diperluas, sehingga dapat menginstal dan menghapus aplikasi secara terpisah dari pengguna. Analisis terperinci tentang
Android.InfectionAds.1 tersedia di
perpustakaan virus kami ; sementara kita di sini kita akan menyentuh kerentanan CVE-2017-13315 dan melihat apa fungsinya.
CVE-2017-13315 termasuk dalam kelompok kerentanan yang dijuluki EvilParcel. Mereka ditemukan di berbagai kelas sistem Android. Kesalahan di kelas-kelas ini memungkinkan untuk mengganti informasi selama pertukaran data antara aplikasi dan sistem. Malware yang mengeksploitasi kerentanan EvilParcel dengan demikian diberikan hak istimewa yang lebih tinggi dan menjadi mampu melakukan hal berikut:
- menginstal dan menghapus aplikasi dengan izin apa pun tanpa konfirmasi dari pengguna;
- menginfeksi perangkat lunak yang diinstal pada perangkat dan mengganti dokumen asli yang bersih dengan salinan yang terinfeksi ketika digunakan bersama dengan kerentanan lainnya;
- mengatur ulang PIN layar kunci pada perangkat Android.
Sampai sekarang, kita tahu tentang 7 kerentanan jenis ini:
- CVE-2017-0806 (kesalahan di kelas GateKeeperResponse), diterbitkan pada Oktober 2017;
- CVE-2017-13286 (kesalahan dalam kelas OutputConfiguration, diterbitkan pada April 2018;
- CVE-2017-13287 (kesalahan dalam kelas VerifyCredentialResponse), diterbitkan pada April 2018;
- CVE-2017-13288 (kesalahan dalam kelas PeriodicAdvertizingReport), diterbitkan pada April 2018;
- CVE-2017-13289 (kesalahan dalam kelas ParcelableRttResults), diterbitkan pada bulan April 2018;
- CVE-2017-13311 (kesalahan dalam kelas SparseMappingTable), diterbitkan pada Mei 2018;
- CVE-2017-13315 (kesalahan dalam kelas DcParamObject), diterbitkan pada Mei 2018.
Semuanya menimbulkan ancaman bagi perangkat yang menjalankan Android 5.0 - 8.1 tanpa pemutakhiran Mei 2018 (atau lebih baru) diinstal.
Prasyarat untuk kerentanan EvilParcel
Mari kita lihat bagaimana kerentanan EvilParcel dapat muncul. Pertama-tama, kita perlu melihat beberapa fitur aplikasi Android. Semua program Android berinteraksi satu sama lain, serta dengan sistem operasi, dengan mengirim dan menerima objek Intent. Objek-objek ini dapat berisi jumlah pasangan kunci-nilai yang sewenang-wenang di dalam objek Bundle.
Ketika mentransfer Intent, objek Bundle dikonversi (serial) menjadi array byte yang dibungkus Parcel, dan kemudian secara otomatis deserialized setelah membaca kunci dan nilai-nilai dari Bundel serial.
Di Bundle, kuncinya adalah string, dan nilainya bisa hampir apa saja. Misalnya, itu bisa menjadi tipe primitif, string, atau wadah dengan tipe atau string primitif. Ini juga bisa menjadi objek Parcelable.
Dengan demikian, Bundle dapat berisi objek jenis apa pun yang mengimplementasikan antarmuka Parcelable. Untuk ini, kita perlu mengimplementasikan metode writeToParcel () dan createFromParcel () untuk membuat cerita bersambung dan deserialisasi objek.
Untuk mengilustrasikan poin kami, mari buat bundel serial sederhana. Kami akan menulis kode yang menempatkan tiga pasangan nilai kunci di Bundle dan membuat serialisasi:

Gambar 1. Struktur objek bundel berseri
Perhatikan fitur spesifik dari serialisasi bundel:
- semua pasangan kunci-nilai ditulis secara berurutan;
- tipe nilai ditunjukkan sebelum setiap nilai (13 untuk array byte, 1 untuk integer, 0 untuk string, dll.);
- ukuran panjang data variabel ditunjukkan sebelum data (panjang untuk string, jumlah byte untuk array);
- semua nilai selaras 4-byte.
Semua kunci dan nilai dituliskan dalam Bundle secara berurutan sehingga saat mengakses kunci atau nilai objek Bundle berseri apa pun, yang terakhir ini akan menghilangkan desisialisasi seluruhnya, juga menginisialisasi semua objek Parcelable yang berisi.
Jadi, apa masalahnya? Masalahnya adalah bahwa beberapa kelas sistem yang mengimplementasikan Parcelable mungkin mengandung kesalahan dalam metode createFromParcel () dan writeToParcel (). Di kelas-kelas ini, jumlah byte yang dibaca di createFromParcel () akan berbeda dari jumlah byte yang ditulis dalam writeToParcel (). Jika Anda menempatkan objek kelas ini di dalam Bundel, batas objek di dalam Bundel akan berubah setelah reserialisasi. Ini menciptakan kondisi untuk mengeksploitasi kerentanan EvilParcel.
Mari kita lihat contoh kelas yang mengandung kesalahan ini:
class Demo implements Parcelable { byte[] data; public Demo() { this.data = new byte[0]; } protected Demo(Parcel in) { int length = in.readInt(); data = new byte[length]; if (length > 0) { in.readByteArray(data); } } public static final Creator<Demo> CREATOR = new Creator<Demo>() { @Override public Demo createFromParcel(Parcel in) { return new Demo(in); } }; @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(data.length); parcel.writeByteArray(data); } }
Jika ukuran array data adalah 0, maka saat membuat objek, satu int (4 byte) akan dibaca di createFromParcel () dan dua int (8 byte) akan ditulis dalam writeToParcel (). Int pertama akan ditulis dengan secara eksplisit memanggil writeInt. Int kedua akan ditulis saat memanggil writeByteArray (), karena panjang array selalu ditulis sebelum array dalam Parcel (lihat Gambar 1).
Situasi di mana ukuran array data sama dengan 0 cukup langka. Tetapi bahkan ketika ini terjadi, program tetap beroperasi, jika Anda hanya mengirimkan satu objek serial pada satu waktu (dalam contoh kami, objek Demo). Karena itu, kesalahan semacam itu cenderung tidak diperhatikan.
Sekarang kita akan mencoba menempatkan objek Demo dengan panjang array nol di Bundel:

Gambar 2. Hasil menambahkan objek Demo panjang-nol ke Bundel
Kami cerita bersambung objek:

Gambar 3. Objek Bundel setelah serialisasi
Sekarang mari kita coba deserialize:

Gambar 4. Objek Bundel setelah deserialisasi
Apa yang kita dapatkan Mari kita lihat fragmen Parcel:

Gambar 5. Struktur parsel setelah deserialisasi bundel
Dalam Gambar 4 dan 5, kita melihat bahwa alih-alih dua int, satu int dibaca dalam metode createFromParcel selama deserialization. Oleh karena itu, semua nilai selanjutnya dari Bundel dibaca secara tidak benar. Nilai 0x0 pada 0x60 dibaca sebagai panjang kunci berikutnya. Nilai 0x1 pada 0x64 dibaca sebagai kunci. Nilai 0x31 pada 0x68 dibaca sebagai tipe nilai. Parcel tidak memiliki nilai dengan tipe 0x31, oleh karena itu readFromParcel () dengan susah payah melaporkan pengecualian.
Bagaimana ini bisa digunakan dalam kehidupan nyata dan menjadi kerentanan? Ayo lihat! Kesalahan di atas dalam kelas sistem Parcelable memungkinkan pembuatan Bundel yang mungkin berbeda selama deserialisasi yang pertama dan berulang. Untuk mendemonstrasikan ini, kami akan memodifikasi contoh sebelumnya:
Parcel data = Parcel.obtain(); data.writeInt(3);
Kode ini menciptakan bundel berseri yang berisi kelas rentan. Sekarang mari kita lihat apa yang kita dapatkan setelah mengeksekusi kode ini:

Gambar 6. Membuat Bundel dengan kelas rentan
Setelah deserialisasi pertama, Bundel ini akan berisi kunci-kunci berikut:

Gambar 7. Setelah deserialisasi bundel dengan kelas rentan
Sekarang kita akan membuat serial bundel lagi, lalu deserialisasi lagi, dan lihat daftar kunci:

Gambar 8. Hasil reserializing dan deserializing Bundle dengan kelas rentan
Apa yang kita lihat Bundel sekarang berisi kunci Tersembunyi (dengan nilai string "Hai di sana!"), Yang sebelumnya tidak ada di sana. Mari kita lihat fragmen Parcel Bundle ini untuk melihat mengapa ini terjadi:

Gambar 9. Struktur paket objek Bundle dengan kelas rentan setelah dua siklus serialisasi dan deserialisasi
Di sinilah kita bisa melihat seluruh titik kerentanan EvilParcel. Kami dapat secara khusus membuat Bundel yang akan berisi kelas rentan. Mengubah batas-batas kelas ini akan memungkinkan penempatan objek apa pun di Bundel ini; misalnya, sebuah Intent, yang hanya akan muncul di Bundel setelah deserialisasi kedua. Ini membantu menyembunyikan Intent dari mekanisme keamanan OS.
Memanfaatkan EvilParcel
Android.InfectionAds.1 mengeksploitasi CVE-2017-13315 untuk menginstal dan menghapus perangkat lunak secara mandiri dari pemilik perangkat. Tapi bagaimana caranya?
Pada 2013,
kesalahan 7699048 , juga dikenal sebagai Launch AnyWhere, ditemukan. Itu memungkinkan aplikasi pihak ketiga untuk memulai aktivitas sewenang-wenang atas nama pengguna sistem yang lebih istimewa. Lihat diagram di bawah ini untuk mekanisme tindakan:

Gambar 10. Pengoperasian error 7699048
Aplikasi yang mengeksploitasi dapat menggunakan kerentanan ini untuk mengimplementasikan layanan Authenticator Akun, yang dirancang untuk menambahkan akun baru ke sistem operasi. Kesalahan 7699048 membantu kegiatan peluncuran mengeksploitasi untuk menginstal, menghapus, mengganti aplikasi, serta mengatur ulang PIN atau Pattern Lock dan menyebabkan lebih banyak masalah.
Google Inc. telah menghilangkan pelanggaran ini dengan melarang peluncuran aktivitas sewenang-wenang dari AccountManager. Sekarang, AccountManager hanya memungkinkan peluncuran aktivitas yang berasal dari aplikasi yang sama. Untuk tujuan ini, ia memeriksa dan mencocokkan tanda tangan digital dari perangkat lunak yang memulai aktivitas dengan tanda tangan aplikasi di mana aktivitas tersebut berada. Ini terlihat seperti ini:
if (result != null && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { int authenticatorUid = Binder.getCallingUid(); long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); int targetUid = resolveInfo.activityInfo.applicationInfo.uid; if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid, targetUid)) { throw new SecurityException( "Activity to be started with KEY_INTENT must " + "share Authenticator's signatures"); } } finally { Binder.restoreCallingIdentity(bid); } }
Tampaknya masalah telah diselesaikan, tetapi tidak semudah itu. Ternyata kerentanan terkenal, EvilParcel CVE-2017-13315, memberikan solusi! Seperti yang sudah kita ketahui, setelah memperbaiki Launch AnyWhere, sistem memverifikasi tanda tangan digital aplikasi. Jika berhasil diverifikasi, Bundle ditransfer ke IAccountManagerResponse.onResult (). Pada saat yang sama, onResult () dipanggil melalui mekanisme IPC, sehingga Bundle diserialisasi lagi. Saat menerapkan onResult (), berikut ini terjadi:
private class Response extends IAccountManagerResponse.Stub { public void onResult(Bundle bundle) { Intent intent = bundle.getParcelable(KEY_INTENT); if (intent != null && mActivity != null) {
Kemudian, Bundel mengekstrak kunci maksud, dan aktivitas diluncurkan tanpa verifikasi apa pun.
Dengan demikian, untuk meluncurkan aktivitas sewenang-wenang dengan hak istimewa sistem, Anda hanya perlu membuat Bundel dengan bidang Intent yang disembunyikan pada deserialisasi pertama dan muncul selama deserialisasi berulang.
Seperti yang sudah kita ketahui, kerentanan EvilParcel sebenarnya dapat melakukan tugas ini.
Saat ini, semua kerentanan yang diketahui dari tipe ini telah diperbaiki dalam kelas Parcelable yang rentan. Namun, kelas rentan baru dapat muncul di masa depan. Implementasi bundel dan mekanisme untuk menambah akun baru masih sama seperti sebelumnya. Mereka masih memungkinkan kita untuk membuat exploit yang tepat ini ketika mendeteksi kelas Parcelable rentan lama atau baru. Selain itu, kelas-kelas ini masih diimplementasikan secara manual, dan programmer harus memastikan bahwa panjang objek Parcelable serial tetap sama, yang merupakan faktor manusia dengan semua yang disiratkannya. Namun, kami berharap akan ada kesalahan sesedikit mungkin, dan kerentanan EvilParcel tidak akan menjadi ancaman bagi pengguna Android.
Anda dapat memeriksa kerentanan perangkat seluler EvilParcel Anda menggunakan
Ruang Keamanan Dr.Web kami untuk Android. Auditor Keamanan bawaan akan melaporkan masalah yang terdeteksi dan merekomendasikan cara untuk menghilangkannya.