An in-depth analysis of reverse engineering client-side encryption in a web application.
Target Background
The target is a web application accessible through both web browsers and mobile applications on Android and iOS platforms.
The application utilizes a centralized CDN (Content Delivery Network) system indicating multiple applications sharing the same resources and architecture.
Through subdomain enumeration it was discovered that the target had more than 15 applications sharing the same technology stack.
Overview
Reconnaissance identified an interesting implementation where all REST API communications were encrypted.
The request and response bodies were encoded before transmission. However the developers hardcoded encryption keys and IVs directly inside client-side JavaScript.
Client-side encryption becomes ineffective once secrets are embedded inside distributed JavaScript bundles.
Recon
- Technology Stack: ReactJS
- CDN: Amazon CDN
- REST API backend
- Different API domain from application domain
- JavaScript source analysis
- Encrypted request/response analysis
Files Discovered
- booking.js
- desktop.js
- home.js
- search.js
- signin.js
- signup.js
- main.js
Initial keyword searches focused on:
- encrypt
- enc
- key
- iv
- initialization vector
Further analysis of main.js exposed the encryption logic.
91622: function (e, t) {
'use strict';
t.Z = {
apiDomain: 'https://api.abc.com',
bookingApiDomain: 'https://bookings.abc.com',
cdnPath: 'https://cdn.abc.com',
dataHasKey: '###################=',
dataIVKey: 'jm8lgqa3j1d0ajus',
encryptionKeys: '[...]'
}
}
The application exposed both the encryption key and initialization vector directly within the frontend bundle.
Encryption Function Discovery
Additional CDN-hosted JavaScript files were analyzed.
A file named app.js exposed both encryption and
decryption routines used by the API layer.
abc.factory('EncryptDecrypt', [
'AppSettings',
function () {
return {
encryptKEY:
'UiQ1TmNyeXBUITBuJDVDUmV0KEBRJH0=',
encrypt: function (t) {
var e = this.encryptKEY,
n = 'jm8lgqa3j1d0ajus',
r = JSON.stringify(t);
return CryptoJS.AES.encrypt(
r,
CryptoJS.enc.Utf8.parse(e),
{
iv: CryptoJS.enc.Utf8.parse(n),
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
}
).toString()
}
}
}
])
Encryption Parameters
- AES CBC mode
- Static IV
- Hardcoded encryption key
- Client-side decryption support
- Predictable payload structure
Payload Structure
Encrypted Payload + "---" + Base64(IV)
After decrypting the request body the API payload became fully visible and modifiable.
{
"first_name":"test",
"email":"test@example.com",
"password":"Pass@123",
"source":"LoginActivity"
}
CAPTCHA Workflow
The application used Google reCAPTCHA during registration.
The CAPTCHA validation process consisted of:
- Initial CAPTCHA challenge
- Verification request
- Token generation
- Encrypted API request submission
Bypass Analysis
Although CAPTCHA tokens were short-lived, the token generation endpoint lacked proper restrictions.
HTTP/1.1 200 OK
["uvresp",null,null,null,2]
Attack Workflow
Get CAPTCHA code
↓
Verify CAPTCHA
↓
Obtain final CAPTCHA token
↓
Encrypt crafted JSON body
↓
Send automated request
Impact
By chaining multiple weaknesses together it became possible to:
- Automate account registrations
- Generate large-scale fake accounts
- Abuse protected API endpoints
- Perform spam and brute-force attacks
- Increase infrastructure and DB resource usage
- Bypass intended anti-automation controls
Root Cause
- Client-side secret exposure
- Weak API trust model
- Improper CAPTCHA validation design
- Lack of anti-automation controls
- Predictable encryption workflow
Conclusion
Client-side encryption should never be treated as a standalone security control when encryption secrets are distributed directly to untrusted clients.
Combined with weak CAPTCHA workflows and unrestricted token generation, the application exposed a fully automatable attack surface.
Thanks for reading.