Hardcoded RSA Key in Native Library Enables Remote Wallet Compromise
The Risk
A crypto wallet app shipped with a secret key buried inside it that anyone could extract in seconds. This key allowed an attacker to send fake push notifications to any user of the app, using only their public wallet address. When the user tapped the notification (which looked identical to a real one), the attacker could access their wallet and initiate transactions. Every user of the app worldwide was vulnerable, and all wallet addresses are publicly available.
The Vulnerability
The RSA private key was embedded at a fixed offset in the native library. Extraction required a single command:
strings lib/arm64-v8a/libsecure.so | grep "^MIIE" This returned the complete base64-encoded PKCS#8 RSA-2048 private key. No obfuscation, no runtime decryption, no device binding. The key was loaded by a JNI function and used directly to sign JWTs.
How the Push Gateway Worked
The app's push notification flow:
- App generates an RS256 JWT signed with the hardcoded RSA key
- JWT claims include
iss(app identifier),sub(wallet address), andexp(expiry) - App sends the JWT to the push gateway API with a target wallet address and notification payload
- Gateway validates the RSA signature and delivers the notification via FCM
Critical flaw: the server validated that the JWT was signed with the correct key, but did not verify that the sub claim matched the target address. A JWT signed with any arbitrary sub value could send notifications to any wallet address.
Three-Way Differential Proof
To confirm the key was actually used for authentication (not just a leftover), three requests were sent to the push gateway:
| Request | Response |
|---|---|
| No Authorization header | HTTP 401 |
| Bearer token with random invalid JWT | "Invalid token" |
| Bearer token with forged JWT using extracted key | "success", notification delivered |
The Attack
- Extract key from the APK's native library (5 seconds)
- Forge JWT with RS256 using the extracted key
- Send push notification to any wallet address with an attacker-controlled URL in the payload
- Victim receives notification that looks identical to a real app notification (title and body fully controlled)
- Victim taps, enters their PIN, and the app opens an internal DApp WebView with the attacker's URL
- Attacker's page calls bridge methods to request the victim's wallet address and prompt transaction signing
WebView Bridge Exposure
The DApp WebView injected a JavaScript bridge that exposed full wallet signing capabilities. Once the attacker's page loaded inside the WebView, it could call:
| Bridge Method | Impact |
|---|---|
requestAccounts | Returns victim's wallet address (confirmed) |
signTransaction | Signs arbitrary fund transfer transactions |
signPersonalMessage | Signs arbitrary messages (confirmed: sign dialog shown) |
signTypedDataV4 | Signs EIP-712 permits for ERC-20 token theft |
The wallet address exfiltration and sign request dialog were confirmed on two physical devices (Samsung and Pixel, Android 12 and 13).
Silent Device Enumeration
An additional finding: sending push notifications with empty title and body produced no visible notification on the device, but the gateway returned whether the target address had any registered devices. This enabled silent reconnaissance to determine which wallet addresses had the app installed, and how many devices each address used. No rate limiting was observed.
A scan of the top 10 wallets on the blockchain found 5 with registered devices, including one high-value wallet with 6 registered devices.
The Impact
This is a textbook example of why mobile binary analysis is essential. The vulnerability was invisible from any external perspective:
- The push gateway API was not documented or publicly accessible in any obvious way
- The RSA key was in a compiled native library, not in Java/Kotlin source or resource files
- The JWT authentication appeared secure from the server side (RS256 signature validation)
- The DApp WebView was a legitimate feature used for decentralized app interactions
The entire chain, from key extraction to wallet compromise, only became visible through reverse engineering the APK and tracing the code paths through decompiled JNI calls.
Remediation
- Remove the hardcoded key from the native library immediately and rotate the server-side public key
- Use per-device key pairs generated via Android Keystore on first launch
- Validate JWT
submatches the target address on the server side - Add URL allowlisting in the DApp WebView to only load trusted domains
- Stop forwarding
data.linkfrom push payloads to the WebView, or require server-side signing of links - Add rate limiting to the push gateway to prevent mass targeting and silent enumeration