Skip to content

Overview

Two Attackers, One Broken Trust Model

Consider two realistic scenarios.

Scenario A: An external attacker downloads the APK from a public store or pulls it from a device they briefly had access to. No credentials, no source code. Just the binary — and on Android, the binary is enough. Within an hour of static analysis they have the backend URL, the SSL pinning fingerprint, and a clear map of the authentication logic. Within two hours, they are reading other users' data.

Scenario B: A legitimate user decides to see what they can access beyond their own account. They log in normally, intercept their own traffic, and start probing the API with their valid token. The server never checks whether the resource they are requesting belongs to them. One parameter change is enough.

Both scenarios succeed for the same reason: the application treats the client as a trust boundary. Protections are implemented client-side where the attacker has full control. The backend trusts the client to enforce rules it never validates itself.

This lab demonstrates both attack paths end to end, chains the individual findings, and explains why fixing the client-side protections without fixing the backend only solves half the problem.


The Application: What It Appears to Be vs. What It Is

The target is a Flutter-based Todo app backed by a Node.js REST API. It appears to follow standard mobile security practices — SSL pinning, root detection, JWT authentication.

What the app appears to do What it actually does
Enforce SSL pinning Pins at the Dart layer — bypassable at the Flutter engine (BoringSSL) level
Detect rooted devices Three safe_device checks — each hookable via Frida native offsets
Protect sensitive configuration Hardcodes backend URL, pinning fingerprint, and admin token in libapp.so
Control access via JWT Issues tokens the server never fully validates for authorization
Restrict admin functions Exposes role as a writable field — any user can self-promote

The gap between what the app appears to enforce and what it actually enforces is the entire attack surface.


How the Findings Connect

Path 1 — External Attacker (No Credentials)

Download APK
Static analysis → backend URL, pinning fingerprint, x-admin-token, protection call chain
Frida root bypass → hook safe_device functions via ARM64 offsets from blutter
Binary patch libflutter.so → NOP the BoringSSL verification branch
Full HTTPS visibility via Burp Suite
Register account → obtain valid JWT
IDOR + privilege escalation → read any user's data, self-promote to admin

Path 2 — Malicious Legitimate User (Valid Credentials)

Log in normally → obtain valid JWT
Intercept own traffic → observe API structure
IDOR → substitute other users' resource IDs
PUT /users/{id} with "role": "admin" → privilege escalation
Admin access confirmed in mobile app

Path 2 requires no reverse engineering, no bypass tooling, no binary analysis. A motivated user with a proxy and a valid account reaches the same outcome as a sophisticated external attacker. That asymmetry is the most important finding in this lab.


Scope

  • Android only — Flutter RE methodology transfers to iOS but tooling and bypass techniques differ
  • No advanced obfuscation — R8 aggressive mode, NDK-level protections, and OLLVM are out of scope
  • Manual only — every attack is performed manually; no scanner output