Some Assembly Required 2 picoCTF 2021 Solution

Published: April 2, 2026

Description

What is in this web assembly? Find the flag hidden in the WASM module loaded by the challenge page.

Remote

Open the challenge URL in your browser with DevTools open.

bash
# Open DevTools with F12 and navigate to the Network tab before loading the page
  1. Step 1Download the WASM file from the Network tab
    In DevTools > Network, reload the page and look for a request with Content-Type application/wasm or a .wasm extension. Right-click and save the file.
    Learn more

    Unlike Some Assembly Required 1, the flag in this challenge is not stored as a plaintext string. Instead, it is XOR-encoded in the WASM data section. A runtime function decodes it on the fly during the validation check. You need to identify both the encoded data and the XOR key.

  2. Step 2Decompile the WASM to WAT
    Use wasm2wat to convert the binary to readable text format. Search for data segments and the XOR logic in the validation function.
    bash
    wasm2wat xSAR2.wasm -o xSAR2.wat
    bash
    grep -n 'i32.xor\|data\|pico' xSAR2.wat
    Learn more

    In the WAT output, look for a data segment near the bottom of the file - this is the encoded flag. Then find the validation function (often named something like $check or indexed as func 2). Trace through the logic to find where the XOR operation occurs and what value is used as the key.

    WAT XOR pattern: A XOR loop in WAT looks like a series of i32.load8_u, i32.const <key>, i32.xor, and i32.store8 instructions. The key is the i32.const immediately preceding i32.xor (or i64.xor). If the key is not an inline constant, trace backwards from the XOR instruction - the value being XORed comes from somewhere (a local, another load, a function argument), and walking the data flow reveals the source.

    Data segment offset. A WAT data segment looks like (data (i32.const 1024) "...") - the encoded bytes start at memory offset 1024 in this example. Inside the segment, byte literals are written as \41\42\43 for the bytes [0x41, 0x42, 0x43]. Match the offset to where the validation function reads from to confirm you are looking at the right segment.

  3. Step 3Decode the XOR-encoded flag
    Extract the encoded bytes from the data segment and XOR each byte with the discovered key to recover the plaintext flag.
    python
    python3 - <<'EOF'
    # Paste the encoded bytes from the WAT data segment here
    encoded = bytes([0x??, 0x??, ...])  # Replace with actual bytes
    key = 0x??  # Replace with XOR key from WAT
    flag = bytes(b ^ key for b in encoded)
    print(flag.decode())
    EOF
    Learn more

    XOR encoding is trivially reversible: if encoded = plaintext XOR key, then plaintext = encoded XOR key. Applying the same key twice returns the original value, making XOR its own inverse. This makes XOR useless as an encryption scheme when the key is static and embedded in the binary - the "obfuscation" only protects against a raw strings search, not actual analysis.

Flag

picoCTF{...}

The flag was XOR-encoded in the WASM data segment - decompiling to WAT reveals both the encoded bytes and the XOR key, making decoding trivial.

Want more picoCTF 2021 writeups?

Useful tools for Web Exploitation

Related reading

What to try next