Using `new AsyncFunction` to dynamically execute code pushed from the server.
Summary
The Wechaty library uses new AsyncFunction() to dynamically execute JavaScript code pushed from the server. When the server sends a function payload (event name: 'botie'), the client uses AsyncFunction constructor to create and execute the function without any validation, code signing, or security controls. This allows a malicious or compromised server to cause clients to execute arbitrary JavaScript code, leading to full client compromise.
Impact
A malicious or compromised server can:
- Execute arbitrary JavaScript code in the Wechaty client context
- Access all Wechaty functionality and user data
- Read and send messages, contacts, and group chats
- Access authentication tokens and session data
- Perform actions on behalf of the user
- Potentially escalate to compromise the underlying system
Proof of Concept
- Server pushes a malicious function to the client:
{
"event": "io",
"name": "botie",
"payload": {
"args": ["message"],
"source": "const fn = () => { console.log(document.cookie) }; return fn;"
}
}- Client executes the code using
AsyncFunction:
// src/io.ts:306
const fn = new AsyncFunction(...args, source)
this.onMessage = fn- Arbitrary code executes in the client context
Note: This PoC is based on static analysis and has not been dynamically verified.
Affected Component
src/io.ts:306
case 'botie':
{
const payload = ioEvent.payload
const args = payload.args
const source = payload.source
try {
if (args[0] === 'message' && args.length === 1) {
const fn = new AsyncFunction(...args, source) // <-- RCE
this.onMessage = fn
} else {
log.warn('Io', 'server pushed function is invalid. args: %s', JSON.stringify(args))
}
} catch (e) {
log.warn('Io', 'server pushed function exception: %s', e)
this.options.wechaty.emitError(e)
}
}
breakRoot Cause
- Dynamic code execution: Using
new AsyncFunction()to execute server-provided code - No code validation: No verification of the source code's safety
- No code signing: No cryptographic verification of server-pushed code
- Trust in server: Client implicitly trusts all code pushed from the server
Suggested Fix
Please maintainer evaluate. Suggested mitigations:
Remove server-pushed code execution: This feature appears to be for dynamic bot behavior, but poses severe security risks. Consider removing or deprecating this functionality.
Implement code signing (if functionality is required):
interface SignedCode {
code: string;
signature: string;
timestamp: number;
}
// Verify signature before execution
async function verifyAndExecute(signedCode: SignedCode): Promise<void> {
const isValid = await verifySignature(
signedCode.code,
signedCode.signature,
serverPublicKey
);
if (!isValid) {
throw new Error('Invalid code signature');
}
// Execute verified code
}- Add user confirmation prompt:
// Prompt user before executing server-pushed code
const userConfirmed = await this.promptUser(
'Server wants to install new message handler. Allow?'
);
if (!userConfirmed) {
return;
}- Use sandboxed execution:
// Execute in isolated worker context
const workerCode = `
self.onmessage = function(e) {
// Execute code in isolation
}
`;
const worker = new Worker(workerCode);- Log and audit: Add detailed logging of all server-pushed code execution for security monitoring
Source: wechaty/wechaty