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:
Decoding the base64 sent back, they were PNG files! You get some beautiful pictures like the following sequence:
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!
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 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:
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://
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! 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.
I think TBL would be glad that the world wide web turned out to be this useful.
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:
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.
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
).
However, that didn't work, and nobody wasted the time to figure out why. Instead, returning directly to system appeared possible.
This code branches to 0x05003128
(yes that's a null), however, inspecting that address in GDB:
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.
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:
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: