writeup

JustCause

WebAssembly parser vulnerability in WebKit's JavaScriptCore caused by removing WASM section order validation.

Name: JustCause
Author: N/A
Flag format: midnight{...}
Objective: Abuse malformed out-of-order WASM sections to leak memory, bypass ASLR, and execute commands to read the flag.

JustCause

Challenge Overview

Name: JustCause

Author: N/A

Description: WebAssembly parser vulnerability in WebKit's JavaScriptCore caused by removing WASM section order validation.

Flag format: midnight{...}

Objective: Abuse malformed out-of-order WASM sections to leak memory, bypass ASLR, and execute commands to read the flag.

Files Provided

  • p.patch
  • JavaScriptCore/WebKit challenge files

Solution Plan

1. Analyze the patch and identify that validateOrder(m_previousKnownSection, section) was removed from WasmStreamingParser.cpp.

2. Build malformed WASM modules with out-of-order sections to confuse parser state and leak libc-related pointers through custom sections.

3. Calculate libc base from the leak, prepare a second malformed WASM module to trigger the RCE path, and retry until ASLR/heap layout aligns.

4. Execute a flag-reading command such as /readflag, cat /flag, or cat flag.

Code (Exploit Script)

class JustCauseExploit:
    def __init__(self, host, port):
        self.host = host
        self.port = port
        # Encode malformed WASM payloads.

    def send_script(self, io):
        # Send: "script size: <size>\nscript: \n<payload>\n"
        pass

    def recv_output(self, io):
        # Receive output from the remote JavaScriptCore service.
        pass

    def check_flag(self, output):
        return b"midnight{" in output

    def exploit(self):
        # Retry loop:
        # 1. leakLibc()
        # 2. calculate system/binsh
        # 3. send RCE WASM
        # 4. run flag-reading command
        pass
// Leak phase idea:
// globalI32(4) + customSec('x', 192) + globalV128()
// customSections(module, 'x') returns confused memory containing useful pointers.

// RCE phase idea:
// globalI32(8) + memSec() + dataPassive(24, 24)
// + globalV128(system, binsh) + badSec()
// badSec() triggers parser error and affects cleanup/heap state.
// Address calculation idea:
const jscOff = [0x1d153fb, 0x161f8fb, 0x1fbbfb, 0x16494fb];
libc_base = (leaked_addr - jsc_offset) + 0x1c90000;
system = libc_base + 0x470a4;
binsh = libc_base + 0x152ce0;

Flag

midnight{Exploiting_JavaScript_engines_can_have_many_Shapes}

Notes

The patch removed WASM section order validation from WasmStreamingParser.cpp. Normal WASM section order is enforced for known sections, while custom sections can appear anywhere. Without the validation, malformed sequences such as Global -> Custom -> Memory become possible and can confuse parser state.

The exploit used two main phases. The first phase crafted malformed WASM sections to make customSections(module, 'x') expose memory containing libc-related pointers. The second phase used the calculated libc base to prepare an RCE-oriented WASM module containing system and /bin/sh addresses.

Because the exploit depends on heap and JIT layout, it is probabilistic. The final approach repeatedly leaked libc, calculated system and /bin/sh, sent the RCE WASM, and attempted commands such as /readflag || cat /flag || cat flag || id until the layout aligned and the flag was printed.