Is Export Chats and Contacts safe?
WAexport is high risk. Opening WhatsApp Web sends your license key to a remote AWS Lambda server, which returns an encrypted payload. Decrypted with AES-CBC, it sets this session's behavior, and the server can change that anytime without an update.…
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Encrypted Remote Config Fetched on Every WhatsApp Navigation
Opening WhatsApp Web sends your license key to a remote AWS Lambda server, which returns an encrypted payload.
Decrypted with AES-CBC, it sets this session's behavior, and the server can change that anytime without an update.
You open WhatsApp Web in your browser.
The extension contacts a remote AWS Lambda server, submits your license key, gets an encrypted config payload, decrypts it, and applies it to control this session's behavior.
This happens on every navigation to web.whatsapp.com, before any export interaction.
| Content-Type | application/json |
{
"licenseKey": "waexport--"
}The server's response is encrypted with AES-CBC. The extension decrypts it locally using the SHA-256 hash of the license key as the AES key, and the IV from the response.
{ "mods": { ... } } (JSON object controlling export behavior — exact content server-defined and changeable at any time)Remote config fetch and decrypt (cs.js)
async function getEncryptedData(licenseKey) {
const response = await fetch(
'https://2oo3bvwn63uezlmkhjyb3e4zma0iljdk.lambda-url.eu-north-1.on.aws/user/get-license-data',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey })
}
);
if (response.ok) return await response.json();
throw new Error('Failed to fetch encrypted data');
}
async function decryptData(encryptedData, ivHex, licenseKey) {
const iv = hexToBuffer(ivHex);
// AES key = SHA-256 of the license key
const keyMaterial = await window.crypto.subtle.digest(
'SHA-256', new TextEncoder().encode(licenseKey)
);
const ciphertext = base64ToArrayBuffer(encryptedData);
const aesKey = await window.crypto.subtle.importKey(
'raw', keyMaterial, { name: 'AES-CBC' }, false, ['decrypt']
);
const plaintext = await window.crypto.subtle.decrypt(
{ name: 'AES-CBC', iv }, aesKey, ciphertext
);
return new TextDecoder().decode(plaintext);
}
// Called on every content script load:
async function processDecryption() {
return new Promise((resolve) => {
chrome?.storage?.local.get(['key'], async (stored) => {
const key = stored.key || 'waexport--';
try {
const { encryptedData, iv } = await getEncryptedData(key);
const plaintext = await decryptData(encryptedData, iv, key);
mods = JSON.parse(plaintext); // global — controls export behavior
resolve();
} catch (e) {
resolve(); // silently swallows errors
}
});
});
}
processDecryption(); // invoked at module loadDecrypts the encrypted configuration payload returned by the extension's AWS Lambda endpoint, so you can see exactly what behavioral instructions are delivered to the extension on each WhatsApp Web session.
#!/usr/bin/env node
// wa-export-config-decoder.js
// Fetches and decrypts the remote config the extension receives on every WhatsApp Web load.
//
// Usage: node wa-export-config-decoder.js [licenseKey]
// licenseKey defaults to 'waexport--' (the extension default)
//
// Requires: Node.js 18+ (built-in fetch + WebCrypto)
const LAMBDA_URL = 'https://2oo3bvwn63uezlmkhjyb3e4zma0iljdk.lambda-url.eu-north-1.on.aws/user/get-license-data';
function hexToBuffer(hex) {
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return bytes.buffer;
}
function base64ToArrayBuffer(b64) {
const binary = Buffer.from(b64, 'base64');
return binary.buffer.slice(binary.byteOffset, binary.byteOffset + binary.byteLength);
}
async function main() {
const licenseKey = process.argv[2] || 'waexport--';
console.log(`[*] Fetching config for licenseKey: ${licenseKey}`);
const res = await fetch(LAMBDA_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey })
});
if (!res.ok) {
console.error(`[!] Server returned ${res.status}`);
process.exit(1);
}
const { encryptedData, iv } = await res.json();
console.log('[*] Encrypted response received');
console.log(' iv:', iv);
console.log(' encryptedData (first 40 chars):', encryptedData.slice(0, 40) + '...');
// Reproduce the extension's decryption exactly:
// AES key = SHA-256 of the license key
const keyBytes = new TextEncoder().encode(licenseKey);
const keyHash = await crypto.subtle.digest('SHA-256', keyBytes);
const aesKey = await crypto.subtle.importKey('raw', keyHash, { name: 'AES-CBC' }, false, ['decrypt']);
const cipherBuf = base64ToArrayBuffer(encryptedData);
const ivBuf = hexToBuffer(iv);
const plainBuf = await crypto.subtle.decrypt({ name: 'AES-CBC', iv: ivBuf }, aesKey, cipherBuf);
const plaintext = new TextDecoder().decode(plainBuf);
console.log('[+] Decrypted config:');
try {
console.log(JSON.stringify(JSON.parse(plaintext), null, 2));
} catch {
console.log(plaintext);
}
}
main().catch(e => { console.error('[!]', e.message); process.exit(1); });- 1node wa-export-config-decoder.js <licenseKey>
- 2oo3bvwn63uezlmkhjyb3e4zma0iljdk.lambda-url.eu-north-1.on.aws
AWS Lambda function URL (eu-north-1) operated by the extension developer. Receives license keys and delivers encrypted behavioral configuration on every WhatsApp Web navigation.
Extension Pulls Encrypted Remote Config from AWS Lambda at Load
The content script sends your license key to a remote AWS Lambda server on WhatsApp Web, gets an encrypted payload back.
Decrypted with AES-CBC, it becomes the `mods` config controlling exports, so the server can change behavior anytime.
You open WhatsApp Web in your browser.
The extension contacts a remote AWS Lambda server, submits your license key, gets an encrypted config payload, decrypts it, and applies it to control this session's behavior.
This happens on every navigation to web.whatsapp.com, before any export interaction.
| Content-Type | application/json |
{
"licenseKey": "waexport--"
}The server's response is encrypted with AES-CBC. The extension decrypts it locally using the SHA-256 hash of the license key as the AES key, and the IV from the response.
{ "mods": { ... } } (JSON object controlling export behavior — exact content server-defined and changeable at any time)Remote config fetch and decrypt (cs.js)
async function getEncryptedData(licenseKey) {
const response = await fetch(
'https://2oo3bvwn63uezlmkhjyb3e4zma0iljdk.lambda-url.eu-north-1.on.aws/user/get-license-data',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey })
}
);
if (response.ok) return await response.json();
throw new Error('Failed to fetch encrypted data');
}
async function decryptData(encryptedData, ivHex, licenseKey) {
const iv = hexToBuffer(ivHex);
// AES key = SHA-256 of the license key
const keyMaterial = await window.crypto.subtle.digest(
'SHA-256', new TextEncoder().encode(licenseKey)
);
const ciphertext = base64ToArrayBuffer(encryptedData);
const aesKey = await window.crypto.subtle.importKey(
'raw', keyMaterial, { name: 'AES-CBC' }, false, ['decrypt']
);
const plaintext = await window.crypto.subtle.decrypt(
{ name: 'AES-CBC', iv }, aesKey, ciphertext
);
return new TextDecoder().decode(plaintext);
}
// Called on every content script load:
async function processDecryption() {
return new Promise((resolve) => {
chrome?.storage?.local.get(['key'], async (stored) => {
const key = stored.key || 'waexport--';
try {
const { encryptedData, iv } = await getEncryptedData(key);
const plaintext = await decryptData(encryptedData, iv, key);
mods = JSON.parse(plaintext); // global — controls export behavior
resolve();
} catch (e) {
resolve(); // silently swallows errors
}
});
});
}
processDecryption(); // invoked at module loadDecrypts the encrypted configuration payload returned by the extension's AWS Lambda endpoint, so you can see exactly what behavioral instructions are delivered to the extension on each WhatsApp Web session.
#!/usr/bin/env node
// wa-export-config-decoder.js
// Fetches and decrypts the remote config the extension receives on every WhatsApp Web load.
//
// Usage: node wa-export-config-decoder.js [licenseKey]
// licenseKey defaults to 'waexport--' (the extension default)
//
// Requires: Node.js 18+ (built-in fetch + WebCrypto)
const LAMBDA_URL = 'https://2oo3bvwn63uezlmkhjyb3e4zma0iljdk.lambda-url.eu-north-1.on.aws/user/get-license-data';
function hexToBuffer(hex) {
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return bytes.buffer;
}
function base64ToArrayBuffer(b64) {
const binary = Buffer.from(b64, 'base64');
return binary.buffer.slice(binary.byteOffset, binary.byteOffset + binary.byteLength);
}
async function main() {
const licenseKey = process.argv[2] || 'waexport--';
console.log(`[*] Fetching config for licenseKey: ${licenseKey}`);
const res = await fetch(LAMBDA_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ licenseKey })
});
if (!res.ok) {
console.error(`[!] Server returned ${res.status}`);
process.exit(1);
}
const { encryptedData, iv } = await res.json();
console.log('[*] Encrypted response received');
console.log(' iv:', iv);
console.log(' encryptedData (first 40 chars):', encryptedData.slice(0, 40) + '...');
// Reproduce the extension's decryption exactly:
// AES key = SHA-256 of the license key
const keyBytes = new TextEncoder().encode(licenseKey);
const keyHash = await crypto.subtle.digest('SHA-256', keyBytes);
const aesKey = await crypto.subtle.importKey('raw', keyHash, { name: 'AES-CBC' }, false, ['decrypt']);
const cipherBuf = base64ToArrayBuffer(encryptedData);
const ivBuf = hexToBuffer(iv);
const plainBuf = await crypto.subtle.decrypt({ name: 'AES-CBC', iv: ivBuf }, aesKey, cipherBuf);
const plaintext = new TextDecoder().decode(plainBuf);
console.log('[+] Decrypted config:');
try {
console.log(JSON.stringify(JSON.parse(plaintext), null, 2));
} catch {
console.log(plaintext);
}
}
main().catch(e => { console.error('[!]', e.message); process.exit(1); });- 1node wa-export-config-decoder.js <licenseKey>
- 2oo3bvwn63uezlmkhjyb3e4zma0iljdk.lambda-url.eu-north-1.on.aws
AWS Lambda function URL (eu-north-1) operated by the extension developer. Receives license keys and delivers encrypted behavioral configuration on every WhatsApp Web navigation.