September 30, 2026
$10,000 Bounty: How I Chained Weak APK Encryption and a JWT Cookie for Full Admin Access
Bounty: $10,000

By Ahmed Ghadban
3 min read
Severity: Critical
Vulnerabilities: Hardcoded Encrypted Secrets, Weak Cryptography (XOR), Insecure Authentication Implementation
When hunting for high-impact bugs, stopping at the first sign of access usually leaves money on the table. What started as a medium-severity API access vulnerability in an Android application turned into a full-scale admin dashboard takeover, yielding a $10,000 bounty.
Here is the breakdown of how reversing an APK, brute-forcing XOR encryption, and a simple HTTP cookie manipulation led to full control over the target application.
Phase 1: Reversing the APK & Discovering Secrets
The target was a mobile application with a complex backend. The first step in mobile methodology is static analysis. After downloading the APK, I unpacked and decompiled it to inspect the source.
Digging through the .smali files, I was looking for the usual suspects: hardcoded API keys, Firebase credentials, or hidden endpoints. Deep inside the application logic, I found something more interesting: an encrypted client_id and client_secret.
The developers had attempted to secure these credentials using XOR encryption before compiling the app. While obfuscation is a hurdle, XOR is notoriously weak if the key space is small or if the ciphertext can be easily manipulated.
Phase 2: Brute-Forcing the XOR Encryption
Instead of spending hours manually reverse-engineering the exact key generation routine from the Smali code, I opted for a brute-force approach. I extracted the XOR'd ciphertext and wrote a quick Python script to iterate through the possible key space.
After exactly 65,000 iterations, the script hit the jackpot, outputting the decrypted, plaintext client_id and client_secret.
Python
# Conceptual brute-force snippet
ciphertext_id = [...]
ciphertext_secret = [...]
for key in range(65536):
decrypted_id = ''.join([chr(b ^ key) for b in ciphertext_id])
if "expected_prefix" in decrypted_id: # or regex matching standard format
print(f"[+] Key found: {key}")
print(f"[+] Client ID: {decrypted_id}")
# Apply key to secret
break# Conceptual brute-force snippet
ciphertext_id = [...]
ciphertext_secret = [...]
for key in range(65536):
decrypted_id = ''.join([chr(b ^ key) for b in ciphertext_id])
if "expected_prefix" in decrypted_id: # or regex matching standard format
print(f"[+] Key found: {key}")
print(f"[+] Client ID: {decrypted_id}")
# Apply key to secret
breakPhase 3: Generating the Admin Token
Armed with the plaintext credentials, I pivoted to dynamic analysis. Monitoring the application traffic, I identified the main API endpoint at api.prod.target.com.
I sent a request to the authentication endpoint using the decrypted credentials. The server responded beautifully with an admin-level JWT:
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4i...[REDACTED]",
"token_type": "Bearer",
"expires_in": 3600
}HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4i...[REDACTED]",
"token_type": "Bearer",
"expires_in": 3600
}I intercepted my subsequent API requests and injected the token into the headers: Authorization: Bearer <JWT>
It worked. I had access to a few administrative API endpoints. However, the features exposed on this specific API subdomain were relatively minor. Reporting this as-is would have likely been triaged as a Medium severity issue. I needed to dig deeper.
Phase 4: The $10,000 Pivot (Escalating to the Dashboard)
I expanded my reconnaissance footprint to look for web interfaces associated with the production environment. Shortly after, I discovered the main administrative interface hosted at admin.prod.target.com.
I navigated to the dashboard and attempted to bypass the login portal using the admin JWT I had generated from the API. I intercepted the dashboard requests and injected the standard authorization headers:
Authorization: Bearer <JWT>โ 401 UnauthorizedAuthorization: Basic <JWT>โ 401 Unauthorized
At this point, it seemed like the API token was scoped strictly to api.prod.target.com and invalid for the dashboard.
Before giving up, I reviewed the exact JSON response I received when generating the token: {"access_token": "<JWT>"}. Web applications frequently rely on cookies for session management rather than authorization headers. I wondered if the dashboard's authentication middleware was looking for a specific cookie parameter instead of a header.
I stripped the Authorization header, added a Cookie header, and set the parameter name to match the JSON key from the API response:
HTTP
GET /admin/dashboard HTTP/1.1
Host: admin.prod.target.com
Cookie: access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4i...[REDACTED]GET /admin/dashboard HTTP/1.1
Host: admin.prod.target.com
Cookie: access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW4i...[REDACTED]HTTP/1.1 200 OK
The response size changed drastically. The dashboard loaded completely. By using the API-generated token as an access_token cookie, I bypassed the dashboard authentication entirely.
Impact & Takeaways
The cookie manipulation granted me full Super Admin privileges over admin.prod.target.com. I had complete control over the entire application ecosystem, user data, and configurations.
Key Developer Mistakes:
- Security by Obscurity: Relying on basic XOR encryption to hide highly privileged client secrets inside an APK. Hardcoded secrets will always be extracted.
- Cross-Domain Token Reusability: The JWT generated for the mobile API was valid and accepted by the web administrative dashboard, lacking proper audience (
aud) or scope restrictions. - Inconsistent Authentication Middleware: The API expected a
Bearerheader, while the dashboard silently accepted the exact same token via anaccess_tokencookie, allowing an attacker to easily pivot between infrastructure components.