Multiple Proxy with Mobile Phone

Last Update Article: 2026-11-08 01:33:26


Saat melakukan Red Teaming hal yang menjadi masalah umum dan lazim adalah terkena blocking connection oleh service WAF terutama untuk mencari entry point lewat Web Application dari Public Application milik perusahaan. Biasanya teknik evasion blocking WAF ini dilakukan setelah adanya valid vulnerability, Fresh Connection adalah cara paling efektif dan cepat untuk lolos Blocking connection namun harus melakukan connection VPN atau membeli Residential Proxy yang tersedia di pasaran namun apakah sampai sekarang tidak ada cara atau opsi yang lain untuk bisa secepatnya lolos dari Blocking Connection ini? setup umum koneksi pentester adalah Wifi dari kantor, Wifi rumah sendiri, koneksi pribadi handphone jadi setidaknya hanya ada maximum 3-5 IP berbeda untuk terkoneksi ke target, dari keresahan ini apakah memang tidak ada cara lain untuk mendapatkan slot fresh IP untuk evasion dari WAF Blocking Connection ini? berikut ini kami sajikan Mobile Prorxy For Red Teaming solusi murah yang efektif.

Mekanisme SIM Card dan Android

Saat handphone kita memiliki sebuah sim card provider terpasang dalam konteks sim card ready dan sudah selesai onboarding atau provisioning seperti verifikasi Wajah / NIK / KK secara ringkas apa-apa saja sih yang terjadi pada proses ini, mari kita bahas komponen utama terlebih dahulu yaitu Sim Card apakah memang sesederhana ada kartu yang memiliki identitas nomor untuk dapat dihubungi? Sim Card sendiri adalah Smart Card yang memili komponen prosesor kecil yang berjalan pada Java Card menurut spesifikasi 3GPP TS 31.102 ada komponen berikut yang mengisi USIM

Komponen Fungsi
IMSI Ini identitas subscriber pada jaringan, singkatnya bisa dibilang identitas SIM
Ki (Key Identifier) anggap saja secret key untuk autentikasi subscribe ke jaringan
ICCID Nomor seri fisik dari SIM
MSISDN Nomor yang dapat dihubungi dari SIM tersebut, biasanya tidak tersimpan lookup akan by IMSI

IMSI International mobile subscriber identity adalah komponen yang dipakai jaringan untuk dapat mengenali device kita yang memiliki Sim Card, nomor telepon / MSISDN biasanya hanya ada pada database operator yang nantinya akan jadi Hash Lookup misal IMSI X adalah MSISDN Y, secara format pada aturan 3GPP TS 23.003 klausul 2 IMSI teridir dari 3 digits MCC (kode negara) + dua sampai tiga digit MNC (kode operator) + sisanya mirip MSISDN.

APN (Access Point Name) adalah nama jaringan tujuan yang ingin dicapai oleh handphone kita, dan menariknya APN biasanya memang benar-benar cuman nama. Setiap operator sudah menanamkan list APN ini pada handphone jauh sejak sebelum SIM tertancap pertama kali unique tiap operator isinya nama-nama milik operator dari berbagai negara yang well known, pada saat SIM tertancap dia akan melakukan lookup dengan IMSI dan kode operatornya mencari mana yang match misalnya untuk provider Telkomsel akan ada APN internet yang dari namanya memang ini untuk kebutuhan internet, akan ada yang lain misalnya ims yang peruntukannya untuk telepon VoLTE dan banyak lainnya. Jadi nama APN inilah yang sebenarnya menentukan kita akan di gateway yang mana, dan dari sinilah juga yang berperan menentukan alamat IP apa yang akan kita terima lewat PDN.

PDN

image.png

PDN (Packet Data Network connection) adalah koneksi yang menjadi satuan alokasi IP pada Handphone kita bukan SIM atau MSISDN kita, setiap kali handphone kita meminta satu PDN ke network, operator lewat mekanisme pada LTE/56 akan memberikan satu alamat IPv4 untuk akses network tersebut. Android akan menampilkan interface biasanya bernama rmnet yang mana penjelasannya adalah 1 PDN == 1 rmnet == 1 koneksi secara default pada android memang akan membuka 1 PDN saja.

Mungkin agar lebih mudah dibayangkan atau dipahami, PDN itu sebenarnya tidak jauh beda dengan konsep koneksi VPN yang biasa kita pakai sehari-hari. Handphone kita berperan sebagai client, operator sebagai 'server' dan pair IMSI beserta Ki pada SIM sebagai credential untuk autentikasi ke jaringan. Akan ada komponen tambahan yaitu APN kita bisa anggap seperti profile mana yang akan kita tuju, misal kita mau terkoneksi ke profile negara mana di VPN. Saat sudah selesai full terkoneksi dengan VPN, server akan memberikan kita alamat IP dan OS akan menampilkan satu interface untuk jalur itu, tun0 adalah interface VPN saat menekan connect, rmnet otomatis PDN dari Android.

Dengan menganggap PDN sama dengan VPN, maka secara teknis tidak pernah ada aturan yang melarang client membuka lebih dari satu gateway dengan satu credential yang sama. Seperti sedang melakukan telepon akan ada VoLTE yang terbuka berarti ada lebih dari 1 PDN yang dapat terbuka dengan begini harusnya dengan 1 sim kita dapat membuka lebih dari. 1 PDN untuk mendapatkan lebih dari 1 IPv4 secara teori

CGNAT

image-e71d39.png

Jika diperhatikan lebih dalam alamat pada interface rmnet1 adalah 10.69.25.196 ini adalah alamat private, artinya handphone kita ini belum ada di internet, masih di layer di bawah operator yang disebut CGNAT (Carrier-Grade NAT) yang mana nantinya IP Private tersebut akan ditranslasikan menjadi 182.5.241.113 seperti yang terlihat pada tangkapan layar di atas.

image-b5ac09.png

Menariknya saat handphone menjadi hotspot dan tersambung dengan laptop lalu melakukan pengecekan lewat curl ipinfo.io, alamat IPv4 yang keluar adalah sama persis seperti dengan handphone, karena trafic dari laptop akan melewati handphone terlebih dahulu sebelum menjadi public IP seperti pada gambar. Dalam konteks Red Teaming blocking pada WAF itu bisa menjadi pisau bermata dua, karena public CGNAT ini dipakai bersama-sama oleh banyak customer, jadi misalnya ada seorang aktor 'jahat' melakukan tindakan ke sistem dan WAF melakukan block IP itu bisa juga melakukan block ke user yang legitimate ini lah salah satu alasan WAF itu cenderung lebih 'longgar' ke IP seluler dibanding IP dari datacenter, hal inilah yang akan kita manfaatkan.

Android Tidak Punya Fiturnya

image-81783e.png

Setelah dari prolog bahwa satu SIM secara teori dasar bisa punya lebih dari satu PDN, lalu bagaimana cara implementasinya? ternyata di Android tidak menyediakan mekanisme untuk dapat melakukan ini, di pengaturan atau hal apapun tidak ada fiturnya, mungkin dengan akses root kita bisa memaksanya? mungkin tapi secara dasar tidak ada sama sekali. Menu APN memang pasti ada dan isinya bisa kita lakukan CRUD sesuka hati tapi dari banyak APN itu hanya bisa dipilih hanya satu.

image-b3bb1d.png

Jika dibandingkan dengan rmnet1 ada scope global berbeda dengan scope link padahal untuk mendapatkan PDN yang berbeda flagnya harus scope global yang mana datang dari jaringan operator. Jadi kesimpulannya adalah menyalakan interface secara paksa tidak begitu memiliki impact yang lebih, karena PDN tidak ter-create dengan benar. Jika melihat pada gambar fitur APN TSEL-MMS tidak bisa dipilih atau diubah state-nya, padahal MMS jelas bisa berjalan di Handphone tanpa harus melakukan aktivasi pada menu tersebut. Hmm apakah ada mekanisme lain untuk dapat melakukan kontrol ini?

Mekanisme APN

content query --uri content://telephony/carriers --projection name,apn,type,numeric | grep 51010

image-c0aa96.png

Ternyata kontrol tadi terjawab dengan query dengan command di atas, tabel APN pada hasil membawa 1 kolom yang tidak ditampilkan pada UI yaitu kolom type kolom ini lah yang menyatakan APN apa berfungsi untuk apa. Pada kolom tersebut juga memberikan kita informasi, internet dan TSEL-WAP sama-sama memiliki tipe default, artinya keduanya menjadi jalur data utama maka dari itu kita dapat melakukan pilihan pada UI mana yang ingin dipilih. TSEL-MMS bertipe mms saja, dia tidak ikut dalam pilihan UI. Sedangkan IMS yang bertipe ims tidak muncul sama sekali pada UI karena jalur ini murni urusan sistem yang user tidak perlu tau. Penomoran 51010 itu adalah MCC 510 untuk kode negara Indonesia dan 10 untuk Telkomsel ini adalah format IMSI untuk Telkomsel.

ConnectivityManager berperan sebagai perantara antara kebutuhan UI Aplikasi dengan tabel APN yang ada di internal yang sudah bisa dilakukan query. UI Aplikasi cukup melakukan Request lewat NetworkCapabilities misalnya NET_CAPABILITY_MMS tanpa harus menyebut nama APN apa yang ingin dipakai, ConnectivityManager yang akan melakukan pencocokan sendiri ke kolom type yang ada, misal butuh kapabilitas MMS dia akan cari yang type mms system langsung buka sendiri PDN baru dan akan ada interface rmnet yang lainnya. Selain mms ada type yang lainnya seperti SUPL untuk urusan lokasi, DUN untuk tethering, IMS untuk telepon dan masih banyak lainnya dalam konstanta pada prefix NET_CAPABILITY_*. Setelah sekian banyak flag dan type ini berapa ya yang benar-benar bisa melakukan generate PDN baru dengan mungkin alamat IP yang berbeda?

Proof of Concept

image-dcfceb.png

Pada handphone Pixel 6A untuk percobaan ini modem pada handphone harusnya ada 30 interface rmnet. Sumber 30 berasal dari device tree pada /proc/device-tree/cpif/iodevs/io_device_5/, yaitu DTB yang dimuat bootloader dari partisi dtbo yang yang signed dan sifatnya harusnya read only dan jika ingin merubah harusnya butuh modifikasi pada Verified Boot sederhananya ini bawaan dari pabriknya. Properti iod,ch_count tertulis 0x1e, yaitu 30 desimal. Data lain ada pada: /sys/class/net juga isinya adalah 30, lalu ada Satu keterangan lagi, drv=samsung,exynos-cp, menyebut driver modemnya; ini akan terpakai sebentar lagi. Sebenarnya 30 itu adalah slot awalnya yang memang disediakan, exact yang bisa diisi itu bisa di-check dari

image-68e3aa.png

dumpsys connectivity | grep "Provider Id (6)" | grep -o "Capabilities: [^ ]*" | tr '&' '\n'

dumpsys carrier_config | grep -i capability_priorit

Ada 18 capability yang dipakai dan sudah jadi konfigurasi bawaan dari operator. Semua ini sebenarnya di-broadcast sendiri oleh sistem lewat network offering yang didaftarkan provider seluler ke ConnectivityService.

Kalau dilihat satu-satu, namanya juga sebenarnya nggak asing-asing amat:

  • MMS -> buat pesan bergambar
  • SUPL -> membantu kebutuhan lokasi
  • DUN -> tethering
  • FOTA -> update sistem
  • IMS dan MMTEL -> kebutuhan telepon
  • XCAP -> pengaturan layanan telepon
  • CBS -> darurat
  • RCS
  • EIMS
  • MCX
  • IA
  • BIP
  • VSIM
  • ENTERPRISE
  • INTERNET -> koneksi internet yang kita pakai sehari-hari
  • PRIORITIZE_LATENCY -> kebutuhan yang mengutamakan latency
  • PRIORITIZE_BANDWIDTH -> kebutuhan yang mengutamakan bandwidth

Yang cukup menarik, dari 18 capability tersebut, 14 di antaranya bahkan sudah punya nilai prioritas di konfigurasi bawaan Telkomsel.

Nilainya juga lumayan menarik:

  • EIMS -> 90, paling tinggi karena berkaitan dengan panggilan darurat
  • SUPL -> 80
  • MMS dan XCAP -> 70
  • CBS, MCX, dan FOTA -> 50
  • INTERNET -> malah cuma 20

Nah, di sini mulai muncul pertanyaan yang menarik.

Kenapa harus ada priority kalau pada akhirnya cuma satu PDN yang boleh aktif?

Kalau memang cuma ada satu jalur yang bisa digunakan dalam satu waktu, sebenarnya nggak terlalu masuk akal untuk punya mekanisme prioritas seperti ini. Buat apa menentukan mana yang lebih penting kalau semuanya harus antre di jalur yang sama? Artinya, desain ini memang terlihat sudah memperhitungkan kondisi di mana beberapa kebutuhan jaringan bisa hidup secara bersamaan. Dan ini juga penting: kemampuan membuka beberapa PDN dari satu SIM bukanlah melakukan Exsploitasi Kerentanan Karena memang Mekanismenya memang sudah ada dari awal. Operator sudah mendefinisikan capability tersebut, sudah memasukkannya ke konfigurasi network offering, dan handphone kita menerima konfigurasi itu secara normal, Jadi sebenarnya kita selama ini bukan "nggak bisa". Lebih tepatnya, belum pernah ada yang meminta capability tersebut secara langsung.

Kalau requestNetwork() memang cuma bisa dipanggil dari aplikasi, sekilas kita jadi berpikir harus bikin APK dulu. Tapi ternyata Android punya jalur yang cukup menarik dan jarang dipakai: ActivityThread.systemMain(), yang bisa memberikan Context milik proses system. Dari Context ini kita bisa mengambil ConnectivityManager seperti biasa, lalu menggunakan API networking yang sama tanpa harus membuat aplikasi Android konvensional.

Jadi sebenarnya kita cuma perlu mendapatkan system Context → ConnectivityManager -> requestNetwork(), dan dari situlah kita bisa mulai mengajukan network request dengan capability yang memang sudah disediakan oleh sistem. Menariknya, capability tersebut bukan sesuatu yang kita tambahkan sendiri, melainkan memang sudah ada di konfigurasi jaringan yang sebelumnya di-broadcast oleh operator.

image-c171da.png

package pdn;

import android.app.ActivityThread;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.os.Handler;
import android.os.Looper;

import java.lang.reflect.Method;

/**
 *   CLASSPATH=/data/local/tmp/pdnreq.dex app_process /system/bin --nice-name=pdnreq pdn.PdnReq --sub 2 mms supl fota cbs xcap
 */
public final class PdnReq {

    private static int capId(String name) throws Exception {
        return NetworkCapabilities.class
                .getField("NET_CAPABILITY_" + name.toUpperCase())
                .getInt(null);
    }

    private static void out(String s) {
        System.out.println(s);
        System.out.flush();
    }

    public static void main(String[] args) throws Exception {
        if (args.length == 0) {
            out("usage: pdn.PdnReq [--sub N] cap[:enterpriseId] ...");
            out("  cap: mms supl dun fota ims cbs ia rcs xcap mcx enterprise vsim bip");
            return;
        }

        int subId = -1;
        int i = 0;
        if ("--sub".equals(args[0])) {
            subId = Integer.parseInt(args[1]);
            i = 2;
        }

        Looper.prepareMainLooper();
        Context ctx = ActivityThread.systemMain().getSystemContext();
        ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class);
        Handler h = new Handler(Looper.getMainLooper());
        out("ctx=" + ctx.getOpPackageName() + " uid=" + android.os.Process.myUid());

        for (; i < args.length; i++) {
            final String tag = args[i];
            String name = tag;
            int entId = -1;
            int colon = tag.indexOf(':');
            if (colon > 0) {
                name = tag.substring(0, colon);
                entId = Integer.parseInt(tag.substring(colon + 1));
            }

            NetworkRequest.Builder b;
            try {
                b = new NetworkRequest.Builder()
                        .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                        .addCapability(capId(name));
            } catch (Exception e) {
                out("[" + tag + "] unknown capability: " + e);
                continue;
            }

            if (subId >= 0) {
                b.setNetworkSpecifier(new TelephonyNetworkSpecifier.Builder()
                        .setSubscriptionId(subId).build());
            }
            if (entId > 0) {
                try {
                    Method m = NetworkRequest.Builder.class
                            .getMethod("addEnterpriseId", int.class);
                    m.invoke(b, entId);
                } catch (Throwable t) {
                    out("[" + tag + "] addEnterpriseId not available: " + t);
                }
            }

            try {
                cm.requestNetwork(b.build(), new ConnectivityManager.NetworkCallback() {
                    @Override public void onAvailable(Network n) {
                        out("[" + tag + "] AVAILABLE " + n);
                    }
                    @Override public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
                        out("[" + tag + "] IFACE=" + lp.getInterfaceName()
                                + " addr=" + lp.getLinkAddresses());
                    }
                    @Override public void onUnavailable() {
                        out("[" + tag + "] UNAVAILABLE / TIMEOUT");
                    }
                    @Override public void onLost(Network n) {
                        out("[" + tag + "] LOST " + n);
                    }
                }, h);
                out("[" + tag + "] request terkirim");
            } catch (Throwable t) {
                out("[" + tag + "] GAGAL: " + t);
            }
        }

        out("Waiting callback...");
        Looper.loop();
    }
}

Masalah pertamanya sederhana: requestNetwork() itu API buat aplikasi, sementara yang kita punya cuma _shell_ in case rooted. Harusnya butuh bikin APK dulu, install dan running, repot, kan? Ternyata nggak perlu, karena Android menyimpan satu jalan yang jarang dipakai lewat ActivityThread.systemMain() yang mengembalikan Context milik paket sistem, dan begitu Context-nya reachable, ConnectivityManager bisa diambil persis seperti aplikasi biasa mengambilnya. Buktinya kelihatan di baris pertama keluaran program: ctx=android uid=0 paketnya android, uid-nya 0, jadi script yang kita buat ini memang berjalan sebagai sistem. Sisanya tinggal pemakaian API yang normal saja. Nama capability sengaja diterjemahkan lewat contanta NET_CAPABILITY_ , tujuannya biar list yang supported otomatis ikut versi Android di handphonenya. Dan yang penting di NetworkRequest, nama APN-nya tidak pernah disebut. Script-nya cuma menyebut butuhnya apa, sisanya Android yang akan mencocokkan sendiri ke kolom type. Singkatnya script di atas untuk force Android build PDN buat capability yang selama ini nggak pernah ada yang minta. Mekanismenya sama persis dipakai aplikasi MMS bawaan tiap kali kirim pesan, bedanya ini kita paksa untuk keperluan kita.

Ada dua syarat yang harus dipenuhi. Pertama, prosesnya wajib jalan sebagai uid 0. Root di sini untuk bypass permission saja. Kapabilitas seperti XCAP, CBS, MCX, dan BIP sifatnya restricted dan butuh specific permission CONNECTIVITY_USE_RESTRICTED_NETWORKS yang signature-level, artinya cuma aplikasi yang system sign yang bisa jalaninnya. Jalan sebagai uid 0 untuk bypass requirements karena ActivityManagerService.checkComponentPermission memberi PERMISSION_GRANTED tanpa syarat untuk ROOT_UID dan SYSTEM_UID. Kedua, prosesnya harus tetap hidup. Begitu NetworkRequest-nya dilepas, entah karena dimatikan, di-kill, atau selesai sendiri, PDN-nya ikut mati saat itu juga karena framework menganggap udah nggak ada lagi yang butuh jalur itu. Jadi Looper.loop() di akhir program itulah yang menahan PDN-nya tetap aktif. Mirip seperti kayak koneksi VPN yang hilang kalau aplikasinya ditutup atau interface-nya force kill. Ini juga sebabnya pdnreq dijalankan dengan nohup ... &, supaya prosesnya tetap hidup setelah sesi adb-nya ditutup.

image-38a17d.png

15: rmnet1: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc mq state UNKNOWN group default qlen 1000
    link/[519]
    inet 10.149.33.184/32 scope global rmnet1
       valid_lft forever preferred_lft forever
16: rmnet2: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc mq state UNKNOWN group default qlen 1000
    link/[519]
    inet 10.138.16.42/32 scope global rmnet2
       valid_lft forever preferred_lft forever

Setelah menjalankan program di atas, ada 2 rmnet interface yang terbuka dan keduanya memiliki ip yang berbeda 10.149.33.184/32 dan 10.138.16.42/32 dengan begini kita sudah punya 2 interface untuk menjadi proxy, bisa menggunakan rmnet1 atau rmnet2 tapi apakah hanya 2 saja? harusnya tidak selama kita bisa tau banyak kapabilitas, kita akan bisa spawn sebanyak mungkin saya sudah menyiapkan repository-nya agar bisa langsung pakai,

image-cccc2f.png

Seperti terlihat pada pada terminal dengan 2 proxy yang berbeda port setelah deployment, mendapatkan 2 IP Public yang berbeda dari 1 SIM Provider saja, dengan begini jika 1 proxy ter-block dapat langsung mengganti ke proxy lainnya tanpa menunggu downtime beberapa detik untuk melakukan AirPlane mode.


Zoomed Image