ByteBlind Security
Back to blog
IDORFridaBug Bounty

Forging API Signatures to Find 15 IDORs in an E-Wallet App

Richard · April 30, 2020

A lot of apps assume that signing a request is enough to stop parameter tampering — if the client computes a signature over the payload and the server checks it, changing a parameter should invalidate the signature. That assumption held right up until the signing function itself turned out to be reachable.

The setup

This particular e-wallet app signed sensitive requests with an MD5-based signature computed client-side over the request parameters. Endpoints like deleteBankAccount took a target ID plus a signature, and the server trusted any request where the signature matched. Change the ID without recomputing the signature, and the request gets rejected outright.

So the actual target wasn’t the endpoints — it was the function computing the signature.

Burp Suite request and response for the deleteBankAccount endpoint, showing the id and signature parameters A stock deleteBankAccount request — change id without recomputing signature and the server rejects it outright.

Cracking the signing function

Decompiling the APK with apktool and reading through it in jadx-gui surfaced the method responsible: a straightforward MD5Encode function that concatenated a handful of request parameters and hashed them. Nothing obfuscated, nothing dynamically loaded — just a plain static method.

Decompiled Java source in jadx-gui showing the MD5 class and its byteArrayToHexString/MD5Encode methods The whole signing routine, sitting in plain, unobfuscated Java.

That meant the signature could be recomputed offline: extract the relevant Java, compile it locally, and calculate valid signatures for arbitrary parameter values without touching the app at all. For deleteBankAccount, that was enough on its own — swap in another user’s bank account ID, compute a matching signature locally, and the server accepted it as a legitimate request from that account’s owner.

IntelliJ IDEA running a local copy of the signing method to bulk-generate valid signatures for a range of IDs The extracted signing method recompiled locally, generating valid signatures for a whole range of IDs at once — no device, no Frida, no app required.

The recompiled MD5Util.encrypt method, an exact copy of the app’s own client-side signing logic A direct copy of the app’s own signing logic — since nothing server-side ever validated it, reimplementing it offline was enough.

Scaling it up with Frida

Some endpoints combined more parameters, or used values that were awkward to obtain outside a live session (session tokens, rotating IDs). Rather than reimplementing the signing logic offline for every case, hooking the method at runtime made this trivial:

Java.perform(() => {
  const Signer = Java.use('com.razer.pay.security.Signer');
  Signer.MD5Encode.implementation = function (input) {
    const sig = this.MD5Encode(input);
    console.log(`[+] input=${input} signature=${sig}`);
    return sig;
  };
});

With the hook in place on a rooted test device, any payload — legitimate or tampered — could be run through the app’s own signing function and get back a valid signature for it. From there, forging requests for other users’ data was just a matter of building the payload and letting the app sign it.

Frida console output hooking MD5Encode on a Google Pixel, printing a recomputed signature for each intercepted call Every call to MD5Encode on the live device, hooked and echoed back with its signature — legitimate traffic or forged, the app signs whatever it’s handed.

What that unlocked

Chained across the affected endpoints, this pattern led to roughly 15 separate IDORs: deleting other users’ linked bank accounts, joining chat groups without invitation, reading private transaction history, and viewing (and in some flows, participating in) other users’ fund transfers.

Burp Suite response confirming the researcher’s account was successfully added to another user’s existing chat group One of the chained IDORs: forging a signature for addGroupUser was enough to join a chat group with zero invitation.

The lesson

Signing a request is only as strong as how hard the signing function is to reach. If the signature is computed entirely client-side with no server secret involved, it isn’t really authentication — it’s just an extra step an attacker has to automate. The interesting work here wasn’t finding the IDOR endpoints; it was recognizing that the thing meant to stop tampering was itself sitting right there in the APK, waiting to be called.