This challenge had 0 solves, probably because it was for a smaller CTF and had a difficult logical jump. I got about halfway through this challenge before getting stuck. I ended up asking the organizers for the intended solve after the competition ended, at which point I quickly solved it.

We’re given a WASM binary and a webserver asking for a username and password. This previous writeup was immensely helpful.

To solve:

  1. Reverse the binary to learn that there’s a username N Gonzalez.
  2. Send the username and any password (HTTP Basic Auth, so b64(user:pass)). Notice there is a new Progress HTTP header from the server than sends back an int (starting at 1).
  3. Brute force the password using the Progress header – when it goes up, you have the correct character of the flag.

Reversing the binary

  1. I used wabt to compile the wasm to C.
  2. I compiled the C to an x86 object file (since it was looking for imports and I didn’t care about them.
  3. I threw the object file in Ghidra and started copying out chunks of relevant decompiled C into a text editor.

I tried two approaches to generating C, both from wabt.

wasm-decompile appeared to generate javascript, which was not what I wanted.

~/wabt/build/wasm-decompile ../given/alien_tech.wasm > from-wasm-decompile.js

The approach I ended up using was wasm2c, which created an x86 object file that I could then look at in ghidra.

~/wabt/build/wasm2c ../alien_tech.wasm -o source_from_wasm2c.c
gcc -c  -o bin_from_wasm2c source_from_wasm2c.c -I wasm2c

This was a lot easier to read than any of the intermediate stages, which enabled me to start reversing. I spent most of the weekend reading / marking up the C. Eventually, I learned:

  • The program will quit if it doesn’t have at least 2 args.
  • Function 8 xor’s a region of memory to give you N Gonzalez. This is clearly a username, which suggests you should feed it to the HTTP Basic Auth.
  • Function 9 accepts two parameters from the webapp and does…. something with them.

I realized it was far more than was reasonable to reverse. At this point, I should’ve tested the username and noticed the Progress header, but I tried learning more about the binary by running it.

Down the wabt hole we go (solving dynamically)

I set up the required HTML and JS to run WebAssembly. However, I got the arg passing wrong, so I kept crashing my tab because argv held illegal addresses, which was super frustrating. Eventually they gave us an emscripten.js that had been generated by the emscripten compiler, which enabled me to figure out how to pass args correctly. This still required a lot of googling around for how emscripten does args.

// This is how the server is calling into the binary.
arguments_.push(btoa('foo')); // arg1 i think
arguments_.push(btoa('bar')); // arg2 i think

I’m not confident about this, but I think arg passing is fairly analogous to calling a C program on the command line. So, the server basically calls ./binary arg1 arg2 (but keep in mind this is in WASM, so it’s not exactly the same as invoking something on a command line).

At this point, I was more or less stuck. I could see that the WASM would return a -2 or -1 in some cases, and I could see the code paths that would do this, but the functions were really difficult to reverse (it took me a few hours to figure out one of them was strlen), so the CTF ended. I asked the organizers what the solve was, and they told me about the Progress header.

Debugging in Chrome Web Tools is okay, but not great because I couldn’t find a good way to access memory (ideally console-style like gdb).

Frankly, it probably would’ve been easier to figure out compiling it to a working x86 binary and then just GDB’d that until things made sense.

Addendum: Calling WASM functions directly from the JS console

That being said, you can get the same, albeit janky, call-whatever-you-want-in-wasm behavior that you might want from GDB. It’s totally possible with a little poking around. See 3-can-you-export-anything-you-want in git repo.

  • Add an export for the numerical function in the wat, then compile to wasm
  • Add a…. trampoline? for the function you want to expose
// in generated emscripten js
// put this with the other similar-looking functions
var nine = Module["nine"] = function() {
  assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
  assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
  return Module["asm"]["nine"].apply(null, arguments)
  • Do the following in callMain:
  var p1 = allocateUTF8OnStack(arg1);
  var p2 = allocateUTF8OnStack(arg2);

  var ret = nine(p1, p2);
  exit(ret, /* implicit = */ true);
  // see console for the return val

… writing C in JS is definitely one of the weirder things that I’ve done.

Next time

Figure out imports and GDB it. youtube

A few different ways of loading, that demonstrate how to re-use the module (say, you want to run it multiple times).