Deserialisasi Tidak Aman

Kategori OWASP: MASVS-CODE: Kualitas Kode

Ringkasan

Saat menyimpan atau mentransfer data objek Java dalam jumlah besar, sering kali lebih efisien untuk melakukan serialisasi data terlebih dahulu. Data tersebut kemudian akan melalui proses deserialisasi oleh aplikasi, aktivitas, atau penyedia penerima yang akhirnya menangani data. Dalam keadaan normal, data diserialisasi, lalu di-deserialisasi tanpa intervensi pengguna. Namun, hubungan kepercayaan antara proses deserialisasi dan objek yang dimaksud dapat disalahgunakan oleh pelaku berbahaya yang dapat, misalnya, mencegat dan mengubah objek yang diserialisasi. Hal ini akan memungkinkan pelaku berbahaya melakukan serangan seperti denial of service (DoS), eskalasi hak istimewa, dan eksekusi kode jarak jauh (RCE).

Meskipun class Serializable adalah metode umum untuk mengelola serialisasi, Android memiliki class-nya sendiri untuk menangani serialisasi yang disebut Parcel. Dengan menggunakan class Parcel, data objek dapat diserialisasi menjadi data byte stream dan dikemas ke dalam Parcel menggunakan antarmuka Parcelable. Hal ini memungkinkan Parcel diangkut atau disimpan secara lebih efisien.

Namun, pertimbangan yang cermat harus diberikan saat menggunakan class Parcel, karena dimaksudkan sebagai mekanisme transpor IPC yang efisien, tetapi tidak boleh digunakan untuk menyimpan objek serialisasi dalam penyimpanan persisten lokal karena dapat menyebabkan masalah kompatibilitas data atau kehilangan. Saat data perlu dibaca, antarmuka Parcelable dapat digunakan untuk mendeserialisasi Parcel dan mengubahnya kembali menjadi data objek.

Ada tiga vektor utama untuk mengeksploitasi deserialisasi di Android:

  • Memanfaatkan asumsi salah developer bahwa melakukan deserialisasi objek yang dimulai dari jenis class kustom adalah hal yang aman. Pada kenyataannya, objek apa pun yang bersumber dari class apa pun berpotensi diganti dengan konten berbahaya yang, dalam skenario terburuk, dapat mengganggu loader class aplikasi yang sama atau aplikasi lain. Interferensi ini berupa memasukkan nilai berbahaya yang, menurut tujuan class, dapat menyebabkan, misalnya, pemindahan data yang tidak sah atau pengambilalihan akun.
  • Mengeksploitasi metode deserialisasi yang dianggap tidak aman secara desain (misalnya CVE-2023-35669, kelemahan eskalasi hak istimewa lokal yang memungkinkan injeksi kode JavaScript arbitrer melalui vektor deserialisasi deep-link)
  • Mengeksploitasi kelemahan dalam logika aplikasi (misalnya CVE-2023-20963, kelemahan eskalasi hak istimewa lokal yang memungkinkan aplikasi mendownload dan mengeksekusi kode dalam lingkungan dengan hak istimewa melalui kelemahan dalam logika paket WorkSource Android).

Dampak

Aplikasi apa pun yang melakukan deserialisasi data serial yang tidak tepercaya atau berbahaya dapat rentan terhadap eksekusi kode jarak jauh atau serangan denial of service.

Risiko: Deserialisasi input yang tidak tepercaya

Penyerang dapat memanfaatkan kurangnya verifikasi paket dalam logika aplikasi untuk memasukkan objek arbitrer yang, setelah di-deserialisasi, dapat memaksa aplikasi untuk mengeksekusi kode berbahaya yang dapat menyebabkan denial of service (DoS), eskalasi akses, dan eksekusi kode jarak jauh (RCE).

Jenis serangan ini mungkin tidak terlihat jelas. Misalnya, aplikasi mungkin berisi intent yang hanya mengharapkan satu parameter yang, setelah divalidasi, akan dideserialisasi. Jika penyerang mengirim parameter tambahan berbahaya kedua yang tidak terduga bersama dengan parameter yang diharapkan, hal ini akan menyebabkan semua objek data yang dimasukkan dideserialisasi karena intent memperlakukan tambahan sebagai Bundle. Pengguna berbahaya dapat memanfaatkan perilaku ini untuk memasukkan data objek yang, setelah dideserialisasi, dapat menyebabkan RCE, kompromi data, atau kehilangan.

Mitigasi

Sebagai praktik terbaik, asumsikan bahwa semua data serial tidak tepercaya dan berpotensi berbahaya. Untuk memastikan integritas data serial, lakukan pemeriksaan verifikasi pada data untuk memastikan class dan format yang benar yang diharapkan oleh aplikasi.

Solusi yang dapat dilakukan adalah menerapkan pola look-ahead untuk library java.io.ObjectInputStream. Dengan mengubah kode yang bertanggung jawab untuk deserialisasi, Anda dapat memastikan bahwa hanya kumpulan class yang ditentukan secara eksplisit yang dideserialisasi dalam intent.

Mulai Android 13 (API level 33), beberapa metode telah diperbarui dalam class Intent yang dianggap sebagai alternatif yang lebih aman daripada metode lama dan yang kini tidak digunakan lagi untuk menangani paket. Metode baru yang lebih aman, seperti getParcelableExtra(java.lang.String, java.lang.Class) dan getParcelableArrayListExtra(java.lang.String, java.lang.Class), melakukan pemeriksaan jenis data untuk menemukan kelemahan ketidakcocokan yang dapat menyebabkan aplikasi mengalami error dan berpotensi dieksploitasi untuk melakukan serangan eskalasi akses, seperti CVE-2021-0928.

Contoh berikut menunjukkan cara menerapkan versi class Parcel yang aman:

Misalkan class UserParcelable menerapkan Parcelable dan membuat instance data pengguna yang kemudian ditulis ke Parcel. Metode readParcelable yang lebih aman jenis berikut kemudian dapat digunakan untuk membaca paket yang diserialisasi:

Kotlin

val parcel = Parcel.obtain()
val userParcelable = parcel.readParcelable(UserParcelable::class.java.classLoader)

Java

Parcel parcel = Parcel.obtain();
UserParcelable userParcelable = parcel.readParcelable(UserParcelable.class, UserParcelable.CREATOR);

Perhatikan dalam contoh Java di atas penggunaan UserParcelable.CREATOR dalam metode. Parameter yang diperlukan ini memberi tahu metode readParcelable jenis yang diharapkan dan lebih berperforma tinggi daripada versi metode readParcelable yang sekarang tidak digunakan lagi.

Risiko Khusus

Bagian ini mengumpulkan risiko yang memerlukan strategi mitigasi non-standar atau dimitigasi di tingkat SDK tertentu dan dilakukan untuk menyelesaikan.

Risiko: Deserialisasi Objek yang Tidak Diinginkan

Mengimplementasikan antarmuka Serializable dalam class akan otomatis menyebabkan semua subjenis class tertentu mengimplementasikan antarmuka. Dalam skenario ini, beberapa objek dapat mewarisi antarmuka yang disebutkan di atas, yang berarti objek tertentu yang tidak dimaksudkan untuk dideserialisasi akan tetap diproses. Hal ini dapat secara tidak sengaja meningkatkan permukaan serangan.

Mitigasi

Jika class mewarisi antarmuka Serializable, sesuai dengan panduan OWASP, metode readObject harus diimplementasikan sebagai berikut untuk menghindari sekumpulan objek di class dapat dideserialisasi:

Kotlin

@Throws(IOException::class)
private final fun readObject(in: ObjectInputStream) {
    throw IOException("Cannot be deserialized")
}

Java

private final void readObject(ObjectInputStream in) throws java.io.IOException {
    throw new java.io.IOException("Cannot be deserialized");
}

Referensi