#2827·wechaty

Using `new AsyncFunction` to dynamically execute code pushed from the server.

Author: JLGitHub66Created Apr 20, 2026Updated Apr 20, 2026

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

  1. Server pushes a malicious function to the client:
json
{
  "event": "io",
  "name": "botie",
  "payload": {
    "args": ["message"],
    "source": "const fn = () => { console.log(document.cookie) }; return fn;"
  }
}
  1. Client executes the code using AsyncFunction:
typescript
// src/io.ts:306
const fn = new AsyncFunction(...args, source)
this.onMessage = fn
  1. 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

typescript
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)
    }
  }
  break

Root Cause

  1. Dynamic code execution: Using new AsyncFunction() to execute server-provided code
  2. No code validation: No verification of the source code's safety
  3. No code signing: No cryptographic verification of server-pushed code
  4. Trust in server: Client implicitly trusts all code pushed from the server

Suggested Fix

Please maintainer evaluate. Suggested mitigations:

  1. 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.

  2. Implement code signing (if functionality is required):

typescript
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
}
  1. Add user confirmation prompt:
typescript
// Prompt user before executing server-pushed code
const userConfirmed = await this.promptUser(
  'Server wants to install new message handler. Allow?'
);
if (!userConfirmed) {
  return;
}
  1. Use sandboxed execution:
typescript
// Execute in isolated worker context
const workerCode = `
  self.onmessage = function(e) {
    // Execute code in isolation
  }
`;
const worker = new Worker(workerCode);
  1. Log and audit: Add detailed logging of all server-pushed code execution for security monitoring