BisaTopup — Membongkar skema request signing aplikasi top-up lewat HTTP Toolkit dan Frida, lalu merekonstruksinya jadi SDK TypeScript.
Repo: https://github.com/ReverserID/BisaTopup
Target: com.amanahcorp.bisatopup v6.15.50
Platform: Android
Tools: HTTP Toolkit, Frida, jadx
Requirement: Perangkat root (Magisk/KernelSU) + frida-server on-device
Tujuan: Riset & edukasi — memahami skema signing dan menyusun ulang klien resmi untuk pengujian akun milik sendiri
Latar Belakang
BisaTopup adalah aplikasi top-up/PPOB. Semua request ke api-mobile.bisatopup.co.id ditandatangani: tiap panggilan membawa header X-Signature, X-Secret, dan X-Timestamp. Tanpa tahu cara ketiganya dihitung, request hasil replay langsung ditolak gateway. Tujuan riset ini: menemukan algoritma signing, memverifikasinya byte-for-byte dari traffic asli, lalu menuangkannya ke SDK TypeScript yang bisa dipakai untuk pengujian.
Kenapa traffic saja tidak cukup
Menangkap traffic memperlihatkan header signing, tapi tidak memperlihatkan cara menghitungnya. X-Signature hanyalah hex sha256 — dari string apa? Urutan field, pemisah, penanganan body dan query semuanya tersembunyi. Itu sebabnya alur ini dua tahap: HTTP Toolkit untuk melihat bentuk request, Frida untuk membongkar cara nilainya dibangun.
| Tahap | Alat | Yang didapat |
| 1. Lihat traffic | HTTP Toolkit | Daftar endpoint, header, bentuk body/query |
| 2. Bongkar algoritma | Frida + jadx | Base string sebelum di-hash, sumber device id |
Tahap 1 — Intercept dengan HTTP Toolkit
HTTP Toolkit dijalankan sebagai HTTPS proxy. Masalah pertama: aplikasi memakai certificate pinning, jadi trafficnya tidak terbaca (koneksi putus / handshake gagal) walau CA HTTP Toolkit sudah dipasang.
Solusinya bypass SSL pinning dengan Frida. frida-server berjalan on-device, lalu script bypass pinning umum (unpin OkHttp/TrustManager) diinject ke proses aplikasi. Setelah pinning mati, seluruh request muncul terbaca di HTTP Toolkit.
Dari sini terlihat pola tiap request ke host API:
| Header | Nilai |
Authorization | Bearer <token> (Bearer null sebelum login) |
X-Signature | sha256(baseString) hex |
X-Secret | sha256(bodyConcat) hex |
X-Timestamp | yyyy-MM-dd'T'HH:mm:ssZ zona Asia/Jakarta (+0700) |
X-Device-Id | id stabil per-install |
X-Version-Name | 6.15.50 |
X-Version | 61550 |
X-Platform | Android |
Juga terlihat tiga query param yang selalu di-append ke setiap URL: device_id, version_name, version. Dan pada endpoint transaksi ada X-Idempotency-Key. Traffic memberi peta endpoint (register, auth, user, profile, product, payment, transaksi, dst.), tapi rumus X-Signature masih gelap.
Tahap 2 — Bongkar algoritma dengan Frida
Dekompilasi APK dengan jadx menampakkan interceptor OkHttp bernama pu5 yang menyisipkan header signing, dan helper fl9.p() yang memproduksi device id. Smali interceptor menyusun sebuah base string lalu meng-hash-nya, tapi membaca konstruksi string dari smali gampang salah pada urutan dan pemisah.
Cara paling akurat: hook pu5.intercept saat runtime dengan Frida dan cetak base string persis sebelum masuk sha256. Dengan membandingkan input hook dan X-Signature yang keluar di HTTP Toolkit untuk request yang sama (mis. cek-register), layout base string terkonfirmasi tanpa menebak:
bodyConcat = (urlDecode(body).trim() === "" ? "" : urlDecode(body).trim() + "&") + query
baseString = `${scheme}://${host}${path}:${bodyConcat}:${token ?? "null"}:${timestamp}`
X-Signature = sha256(baseString)
X-Secret = sha256(bodyConcat)
Detail penting yang hanya kelihatan dari hook, bukan dari traffic:
- Body di-URL-decode dulu (
URLDecoder.decode(_, "UTF-8")) sebelum di-hash, lalu di-trim.
query yang dipakai adalah query SETELAH device_id, version_name, version di-append (append, bukan replace), dan urutannya mengikuti HttpUrl.Builder.addQueryParameter.
- Token yang belum ada ditulis sebagai literal string
null, bukan kosong.
Z pada format timestamp adalah gaya RFC 822 (+0700), bukan ISO 8601 (+07:00).
Verifikasi
Rumus di atas diuji ulang terhadap request cek-register yang tertangkap: hitung ulang X-Signature dan X-Secret dari body, query, token, dan timestamp yang sama, lalu bandingkan dengan nilai asli. Cocok byte-for-byte. Implementasinya ada di sdk/src/signing.ts.
Hasil — SDK TypeScript
Hasil akhir riset dituangkan jadi SDK TypeScript yang menandatangani tiap request identik dengan aplikasi. Contoh alur registrasi:
import { BisaTopupClient, RegisterApi, AuthApi, UserApi, ProfileApi } from "bisatopup-sdk";
const client = new BisaTopupClient({ deviceId: process.env.BISATOPUP_DEVICE_ID });
const register = new RegisterApi(client);
const auth = new AuthApi(client);
const user = new UserApi(client);
const profile = new ProfileApi(client);
await register.cekRegister("0851xxxxxxxx");
await register.sendVerification("0851xxxxxxxx", "WA");
await register.codeVerification("0851xxxxxxxx", otp);
const reg = await register.registerData({ phoneNumber: "0851xxxxxxxx", name: "Nama", password: "****", code: otp });
await user.installAttribution();
await auth.authPin("0851xxxxxxxx", "******");
const me = await profile.index();
Endpoint yang sudah dipetakan mencakup register, auth, user, profile, product, home, payment, transaksi, config, dan point. Daftar lengkap beserta method HTTP ada di README repo.
Keterbatasan
- Aplikasi juga mengirim
X-Recaptcha-Token dan X-Integrity-Token (Play Integrity). Server yang ditangkap saat riset menerima request tanpa keduanya, tapi gateway bisa mulai menegakkannya kapan saja. Play Integrity khususnya tidak bisa di-replay di luar perangkat asli.
- SSL pinning bypass butuh perangkat root dan bersifat best-effort terhadap deteksi Frida tingkat lanjut.
- Rekonstruksi ini berlaku untuk v6.15.50; versi lain bisa mengubah skema signing.
Etika
Riset & edukasi. SDK ditujukan untuk pengujian akun milik sendiri — bukan untuk registrasi massal, scraping, atau melewati kontrol fraud. Gunakan sesuai hukum yang berlaku.
Source lengkap (SDK + catatan): https://github.com/ReverserID/BisaTopup
Masukan, koreksi, dan diskusi soal skema signing atau alur hooknya dipersilakan di thread ini.