WASM (rev)

We are given an encryption script which contains the following code:

function strToBytes(str) {
    const bytes = new Uint8Array(str.length);
    for (let i = 0; i < str.length; i++)
      bytes[i] = str.charCodeAt(i);
    return bytes;
  }
  
  const wasmBuffer = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x01, 0x7f, 0x00, 0x02, 0x09, 0x01, 0x00, 0x03, 0x6d, 0x65, 0x6d, 0x02, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0a, 0x01, 0x06, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x00, 0x00, 0x0a, 0x64, 0x01, 0x62, 0x02, 0x04, 0x7f, 0x01, 0x7c, 0x02, 0x40, 0x41, 0x00, 0x21, 0x01, 0x03, 0x40, 0x20, 0x01, 0x20, 0x00, 0x41, 0x01, 0x6b, 0x4e, 0x0d, 0x01, 0x02, 0x40, 0x20, 0x01, 0x21, 0x02, 0x03, 0x40, 0x20, 0x02, 0x20, 0x00, 0x41, 0x01, 0x6b, 0x4e, 0x0d, 0x01, 0x20, 0x02, 0x2d, 0x00, 0x00, 0x21, 0x03, 0x20, 0x02, 0x41, 0x01, 0x6a, 0x2d, 0x00, 0x00, 0x21, 0x04, 0x20, 0x03, 0x20, 0x04, 0x73, 0x21, 0x04, 0x20, 0x02, 0x41, 0x01, 0x6a, 0x20, 0x04, 0x3a, 0x00, 0x00, 0x20, 0x02, 0x41, 0x01, 0x6a, 0x21, 0x02, 0x0c, 0x00, 0x0b, 0x0b, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x0b, 0x0b]).buffer;
  
  const importObject = {
    "": {
      mem: new WebAssembly.Memory({ initial: 1 }),
    }
  };
  
  WebAssembly.instantiate(wasmBuffer, importObject)
  .then(({ instance }) => {
    let memArr = new Uint8Array(importObject[""].mem.buffer);
    let flagArr = strToBytes('THIS_IS_A_FAKE_FLAG');
  
    for (let i = 0; i < flagArr.length; i++)
      memArr[i] = flagArr[i];
    instance.exports.encode(flagArr.length);
    let result = "";
    for (let i = 0; i < flagArr.length; i++) {
      result += ("00" + memArr[i].toString(0x10)).slice(-2);
    }
      
    console.log(result); # 571653080c6e350c6b0f01196d06436075756365
  });

It uses WebAssembly.instantiate to create a WebAssembly instance. Then, each encoded character is appending to result. We then receive the result.

After some googling, I realised that the bytes given as wasmBuffer is actually a WebAssembly payload which exports a function called encode. After using the wasm2wat tool, I get the following web assembly:

(module
  (type $t0 (func (param i32)))
  (import "" "mem" (memory $.mem 0))
  (func $encode (export "encode") (type $t0) (param $p0 i32)
    (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32) (local $l5 f64)
    (block $B0
      (local.set $l1
        (i32.const 0))
      (loop $L1
        (br_if $B0
          (i32.ge_s
            (local.get $l1)
            (i32.sub
              (local.get $p0)
              (i32.const 1)))) 
        (block $B2
          (local.set $l2
            (local.get $l1))
          (loop $L3
            (br_if $B2
              (i32.ge_s
                (local.get $l2)
                (i32.sub
                  (local.get $p0)
                  (i32.const 1))))
            (local.set $l3
              (i32.load8_u
                (local.get $l2)))
            (local.set $l4
              (i32.load8_u
                (i32.add
                  (local.get $l2)
                  (i32.const 1))))
            (local.set $l4
              (i32.xor
                (local.get $l3)
                (local.get $l4)))
            (i32.store8
              (i32.add
                (local.get $l2)
                (i32.const 1))
              (local.get $l4))
            (local.set $l2
              (i32.add
                (local.get $l2)
                (i32.const 1)))
            (br $L3)))
        (local.set $l1
          (i32.add
            (local.get $l1)
            (i32.const 1)))
        (br $L1)))))

I asked chatGPT to convert this into Python and got the following encoding function:

def encode(data):
    for i in range(len(data) - 1):
        for j in range(i, len(data) - 1):
            data[j + 1] = data[j] ^ data[j + 1]
    return data

The encoding function iterates i from from the 0 to n - 1, and for each i it iterates j from i to n - 1. For each j value, the element at index j + 1 is XOR'd with the element at index j. To decode this function we can do the XOR operations in the reverse order.

x = "571653080c6e350c6b0f01196d06436075756365"

ct = [x[i:i+2] for i in range(0, len(x), 2)] # create an array of hex strings
n = len(ct)
for i in range(0, n):
    ct[i] = int(ct[i], 16) # convert hex values to ints
for i in range(n - 2, -1, -1):
    for j in range(n - 2, i - 1, -1):
        ct[j + 1] ^= ct[j] # decode
for i in range(0, n):
    ct[i] = chr(ct[i])
print(ct) # CDDC24{WASM_15_R34LY_C00L!!}

Last updated