Wawasan Ahli
22 August 2025

Bahaya Komunikasi Client-Side Android yang Tidak Aman (Part 1)

Bahaya Komunikasi Client-Side Android yang Tidak Aman (Part 1)

\Halo semuanya! Dalam postingan part 1 ini, kami akan membahas secara detail bagaimana komunikasi client-side yang tidak aman antar komponen aplikasi Android dapat digunakan untuk mencuri informasi yang tersimpan di local storage sandbox aplikasi. 

Pernah dengar kasus distribusi masif malware berupa APK undangan online/kiriman paket melalui WhatsApp yang menyebabkan akun mobile banking korban diretas? Nah, serangan ini jauh lebih canggih, karena penyerang hanya perlu membuat korban mengakses APK berbahaya atau melakukan aksi tertentu di aplikasi target untuk bisa mencuri informasi.

Untuk memahami lebih jauh bagaimana kerentanan ini bisa muncul dan dieksploitasi oleh pihak berbahaya, kami akan menggunakan sebuah aplikasi Android production-build sebagai studi kasus. Dalam studi ini, kami akan mengeksploitasi dua kerentanan yang teridentifikasi untuk menunjukkan bagaimana implementasi komunikasi client-side yang tidak aman dapat dimanfaatkan oleh pelaku jahat yang mendistribusikan aplikasi berbahaya kepada korban.

Kerentanan: Direct Return of Intent Result pada Exported Component

Static Analysis

Langkah pertama adalah memperoleh source code hasil decompile. Kami menggunakan decompiler open-source Android APK to Java, yaitu JADX (https://github.com/skylot/jadx) untuk memperoleh informasi ini. Setelah melakukan decompiling pada aplikasi, kami memulai static analysis dari file manifest aplikasi dan menemukan sebuah activity yang dikonfigurasi untuk diekspor sehingga menarik perhatian kami.

p1.JPG

Investigasi lebih lanjut pada activity tersebut mengungkap adanya onComplete() callback yang langsung mengembalikan hasil intent yang diterima dari komponen pemanggil melalui metode setResult(), tanpa melakukan validasi atau sanitasi tambahan terhadap data intent yang diterima.

p2.JPG

Kami kemudian menganalisis lebih jauh bagaimana activity ini dapat dipanggil dengan menelusuri metode init()dari activity tersebut, dan menemukan bahwa activity menerima data string key_scenario yang berisi nilai dari scenario yang tersedia untuk diproses oleh aplikasi, agar tidak terjadi exception oleh aplikasi target.

p3.JPG

Analisis dan penelusuran source code lebih lanjut pun dilakukan, dan kami menemukan beberapa kemungkinan nilai string scenario yang dapat diproses oleh activity tersebut. Kami akan mendemonstrasikan serangan dengan salah satu scenario yang tersedia, yang ditandai dengan kotak merah pada gambar berikut.

p4.JPG

 

p5.JPG

Memverifikasi Kerentanan

Mari kita rangkum kembali apa yang sudah kita analisis. Kita menemukan adanya exported activity yang menjalankan metode setResult() dengan langsung menggunakan data intent yang diterima dari komponen pemanggil, tanpa proses sanitasi terlebih dahulu, di dalam callback onComplete().

Agar aplikasi tidak mengalami terminated akibat exception, kita perlu menentukan sebuah key_scenario yang bisa diproses oleh target activity yang ingin kita instantiate. Nah!, dengan informasi ini, kita bisa melanjutkan untuk mengembangkan aplikasi Android berbahaya dengan tujuan membuat aplikasi target mengalami misbehave melalui penyalahgunaan implementasi komunikasi sisi klien (client-side communication).

Dalam aplikasi yang kita buat, kita mengirimkan explicit intent yang berisi nama package dan nama class dari target activity beserta data key_scenario yang sesuai agar dapat diproses oleh target activity. Setelah itu, kita mendaftarkan activity result callback untuk mendengarkan setiap hasil yang dikembalikan oleh target activity, lalu mencatat log ketika callback berhasil dipanggil sebagai bagian dari debugging.

Aplikasi tersebut kemudian di-build dan di-deploy ke perangkat Android yang sudah terpasang aplikasi target. Code snippet dari aplikasi berbahaya yang kita buat ditunjukkan pada gambar di bawah ini.

p6.JPG

Sebelum melanjutkan untuk mengeksploitasi kerentanan ini, pertama-tama kita harus mempelajari cara mengeksekusi potongan kode yang rentan, yaitu dengan menjalankan callback onComplete(). Kami menggunakan runtime mobile exploration toolkit bernama objection (https://github.com/sensepost/objection) untuk memverifikasi apakah callback function yang dimaksud berhasil dipicu dengan cara hooking pada metode yang relevan.

Setelah target activity berhasil diinisialisasi, kami pun mengetahui cara memicu potongan kode yang rentan, yang ditandai dengan terpanggilnya callback onComplete(). Hal ini terlihat dari output objection, serta activity result callback kami yang berhasil menerima incoming intent result dari target activity, ditandai melalui keluaran (log output) aplikasi.

p7.JPG

 

p8.JPG

Eksploitasi

Semua tampak berjalan sesuai harapan. Kini, kita masuk ke tahap terakhir, yaitu fase exploitation! Kami menganalisis lebih lanjut berkas manifest aplikasi target dan menemukan sebuah protected (not exported) file provider yang memungkinkan akses sementara ke file milik aplikasi dengan menetapkan nilai android:grantUriPermissions sebagai true.

p9.JPG

Investigasi dilanjutkan ke direktori yang diizinkan untuk akses sementara dengan menelusuri berkas provider_paths.xml. Dari sana terlihat bahwa file provider memberikan akses sementara ke file yang berada di dalam direktori files/ yang bersifat sandbox.

p11.JPG

Kami kemudian membuat daftar semua file dalam penyimpanan internal aplikasi target (files/) dan menemukan sebuah file yang diduga menyimpan riwayat OTP dari akun yang sedang login.

p12.JPG

Bagus! Sekarang kami telah menemukan file di dalam sandbox yang bisa dicuri dari aplikasi berbahaya yang kami buat. Selanjutnya, kode aplikasi kami dikembangkan dengan menentukan URI content:// dari file target dan menambahkan flag Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION agar aplikasi berbahaya bisa memperoleh akses sementara ke file internal aplikasi target.

Kami juga menambahkan kode tambahan pada activity result callback untuk mengambil informasi file yang dicuri, membaca isinya, lalu menyalin semuanya ke log aplikasi kami. Potongan kode final dari aplikasi berbahaya ditunjukkan pada gambar di bawah.

Aplikasi berbahaya tersebut kemudian dibangun ulang (re-built) dan dipasang kembali pada perangkat. Setelah potongan kode rentan berhasil dipicu, kami sukses melakukan pencurian file yang berada di dalam direktori sandbox aplikasi target. Hal ini ditandai dengan informasi file serta kontennya yang berhasil ditampilkan di log aplikasi kami.

p13.JPG

Aplikasi berbahaya tersebut kemudian dibangun ulang (re-built) dan dipasang kembali ke perangkat. Setelah potongan kode rentan berhasil dipicu, kami berhasil melakukan pencurian file yang berada di dalam direktori sandbox aplikasi target melalui aplikasi berbahaya kami. Hal ini ditandai dengan informasi file serta isi kontennya yang berhasil ditampilkan pada log aplikasi kami.

p14.JPG

Mitigasi

Mungkin Anda bertanya, bagaimana serangan ini bisa terjadi? Ingat intent yang berisi data file internal yang kita kirim sebelumnya? Data intent tersebut langsung dikembalikan ke aplikasi kita oleh instance aplikasi target. Karena intent itu berasal dari konteks aplikasi target, maka akses sementara ke file internal melalui file provider pun diberikan. Menarik, bukan?

Oleh karena itu, mitigasi dapat dilakukan dengan menyaring (sanitizing) data intent yang diterima dari komponen pemanggil dengan cara menghapus flag izin URI dari intent. Dengan begitu, akses ke content provider yang dilindungi tidak akan diberikan kepada aplikasi berbahaya.

Beberapa cara yang bisa dilakukan antara lain:

1. Menghapus flag izin URI dengan metode removeFlags()

Intent intent = getIntent();

intent.removeFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

intent.removeFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

setResult(-1, intent);

finish();

 

2. Mengatur flag intent menjadi 0

Intent intent = getIntent();

intent.setFlags(0);

setResult(-1, intent);

finish();

 

3. Mengkloning data intent yang diterima dengan cloneFilter()

setResult(-1, getIntent().cloneFilter());

finish();

 

4. Menghapus semua izin terhadap content provider dengan revokeUriPermission()

Intent intent = getIntent();

ClipData clipData = intent.getClipData();

 

// if data is set via ClipData object

if (clipData != null && clipData.getItemAt(0).getUri() != null) {

revokeUriPermission(clipData.getItemAt(0).getUri(),   Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

}

 

// if data is set via setData() method

if (intent.getData() != null) {

revokeUriPermission(intent.getData(), Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

}

 

setResult(-1, intent);

finish();

Selain itu, validasi pada intent yang diterima juga dapat dilakukan untuk mencegah upaya pemberian izin URI yang mencurigakan:

Intent intent = getIntent();

int flags = intent.getFlags();

if ((flags & Intent.FLAG_GRANT_READ_URI_PERMISSION == 0) && (flags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION == 0)) {

setResult(-1, intent);

finish();

}

Sebagai tambahan, dengan menerapkan prinsip least privilege, komponen aplikasi Android sebaiknya tidak dikonfigurasi sebagai exported (kecuali launcher activity). Hal ini penting bila seluruh komunikasi sisi klien dilakukan hanya di dalam konteks aplikasi dan tidak ditujukan untuk berinteraksi dengan aplikasi pihak ketiga.

<activity>

...

android:exported="false"

...

<intent-filter>

...

</intent-filter>

</activity>

 

Kerentanan pada komunikasi client-side Android yang tidak aman jelas menunjukkan betapa seriusnya ancaman terhadap data internal aplikasi. Kasus ini menjadi pengingat penting bahwa pengembang aplikasi perlu lebih cermat agar tidak menimbulkan celah berbahaya.

Untuk pemahaman yang lebih mendalam, termasuk studi lanjutan mengenai skenario serangan lainnya dan strategi pencegahan yang lebih komprehensif, jangan lewatkan pembahasan pada Part 2 di artikel selanjutnya.