Architecture¶
System Overview¶
| Component | Detail |
|---|---|
| Mobile Client | Flutter Android APK (AVD Emulator) |
| Backend API | Node.js + Express, hosted on Railway |
| Communication | HTTPS with SSL pinning |
| Authentication | JWT-based |

Application Flow¶
User submits credentials (mobile app)
↓
Backend validates and issues JWT ← JWT contains role claim — never re-validated server-side
↓
JWT stored in SharedPreferences ← plaintext, readable on rooted device
↓
Each request sent with Authorization header
↓
Backend validates token and returns response ← token presence checked, resource ownership not checked
Deployment & Testing Setup¶
| Component | Configuration |
|---|---|
| Backend | Railway — public HTTPS endpoint |
| Mobile App | Installed on Android Emulator (AVD), rooted via Magisk |
| Proxy | Burp Suite — traffic redirected via iptables |
| Prerequisite | SSL pinning bypass required before interception is possible |

Trust Boundary¶
The backend is the only component that can be trusted — it is the only part of the system the attacker does not fully control. The mobile client is entirely attacker-controlled: the binary can be decompiled, runtime behavior can be modified, and network traffic can be intercepted and replayed.
In this application, the backend does not behave accordingly. It validates that a JWT is
present and correctly signed, but does not check whether the requesting user is authorized to
access the specific resource they are requesting. It accepts a role field on the update
endpoint without verifying whether the user has the authority to change it. It uses a static
shared secret for admin operations that is hardcoded in the client binary.
Every significant finding in this lab is a consequence of the backend treating the client as a trusted participant in authorization decisions it should own entirely.