Tags: wasm webassembly web js
Rating:
# Summary
This is a web assembly challenge. You have to basically reverse some web assembly. What I did was debug the assembly letter by letter and get the final output. The input string is supposed to be the flag.
## Reversing JS
You'll see that in `index.html`, you're running some function when you submit the flag: `Module.ccall('checkFlag', 'number', ['string'], [flag_attempt]);` and checks to see if that returns `1` or not.
The `checkFlag` function will compare your input to the correct flag
The `checkFlag` function is _not_ in the `flagco.js` file though.
However, if you go through the `flagco.js` file, you'll see that there is a `ccall` function. If you search for this in Google, you might find this page: https://emscripten.org/docs/api_reference/preamble.js.html#ccall. This implies that the JS is running some C in the backend somewhere, or at least something very low level (you can also get a hint of this because the JS deals with heaps and stacks. These are all things that JS and other higher level languages usually stuff under the bed and they usually don't expose them to the developer).
One of the things that's helpful is that you can look up some keywords plus "ctf" in Google to see if there are any challenges like it. If you search up `"ccall" javascript ctf`, you might come across this: https://medium.com/tenable-techblog/coding-a-webassembly-ctf-challenge-5560576e9cb7
I noticed that this is web assembly. I've had absolutely zero experience with web assembly and it honestly sounds scary but here we go~
I noticed that in the challenge site, under dev tools -> Debugger, there is indeed a `flagco.wasm` file. I can't see into it but I can download it.
## Setup WASM Debugging
Get the wasm and source files.
You can do this by going to the website and opening up Firefox dev tools, and under Debugger, download every resource: the `index.html`, `flagco.js`, and `flagco.wasm`
Then put this in a directory in your local workstation. In that directory, run a Python webserver (or equivalent- it needs to run a webserver is all): `python3 -m http.server`
You should now have your own instance of the website available for you to play around in.
## Reversing WASM
We're going to use the Firefox debugger a lot.
All the functions in the WASM are very esoteric. They have no names- only numbers. They aren't very helpful. However, you can figure out which function `checkFlag` is by inputting some random flag, clicking the "Pause" button in the Firefox Developer Tools -> Debugger (top right area of the console), then clicking the "Flag Me Up" submission button. Step through until you hit the `ccall` function in `flagco.js` (L640).
You can then step through the `ccall` and you'll end up in `flagco.wasm` address `0x44E`- in the middle of a function called `func12`. Keep stepping through and you'll get to address `0x484`: `local.get $var8` and the next instruction is `i32.ne`. One can infer that this means "32 bit integers not-equals comparison".
In regular assembly, you're playing around with registers when you do arithmetic commands and it generates flags which in turn influence conditional statements.
Check the "Scopes" section on the right of the debugger and you'll see all the variables and what they contain. Look at `var7` and `var8`. One of these will be 26 and the other will be the length of your input string. I did this a couple times and inferred that this simply means that your input string must be 26 characters long.
Let's do that. Input something like `abcdefghijklmnopqrstuvwxyz` and start the debugging sequence again (you can also set up breakpoints so you don't have to step through things for so long)
You'll find that after the `i32.ne` in `0x486`, you'll have `var9` set to `0`. This lets us get past a later part in the code in `0x49A`. If `var9` were not zero, it would hit `br_if $label0` which exits the function and skips most of the code.
-----
Next, you'll hit the main part of the code. You'll find that there's a lot of setting variables then some `call $func162` in `0x4C1`. Go into that function and you'll find more web assembly to look at.
In `func162`, there's a loop in `0x17C3`. You can get up to here and you'll find that if you step through it, `var4` and `var5` change.
`var4` starts with `77` which is decimal for the ASCII `M`.
`var5` is the decimal form of your first character in your input flag.
I used https://onlineasciitools.com/convert-decimal-to-ascii to note the decimals one by one.
You can see where this is going. You can follow through the web assembly of `func162` and get each character one by one. Obviously, the first few letters will be `MetaCTF{`, the last letter will be `}`. That leaves 17 actual flag characters. If you get the wrong character, the function immediately breaks out of the loop and exits.
I won't put the final flag in this write-up.
## Things That Didn't Work For Me
I tried using `wabt` (https://github.com/WebAssembly/wabt) - the Web Assembly Binary Toolkit- to reverse the wasm. I decompiled the wasm, converted the wasm to wat, converted it to C, even compiled it back to an ELF binary which I loaded into Ghidra. None of these attempts were very fruitful. I did find a `checkFlag` function somewhere but static analysis of the function in C was not trivial...
I thought since there was some sort of C backend or low level backend, I could do some OS injection in the `ccall` function. Something like `Module.ccall('checkFlag', 'number', ['string'], ['asdf; ls`]);`. That didn't work.
I tried looking at the `strings` output for the WASM but couldn't find the flag itself. That would've been a lot easier.
### Others
Turns out, I don't need to download the WASM and site or anything. I had Firefox extensions that were blocking the WASM URIs so while it would still work, I could see the WASM in the Debugger. Disable your extensions/ad or script blockers to see the WASM. (Ref: https://stackoverflow.com/questions/45879671/only-on-firefox-loading-failed-for-the-script-with-source/46469460)