pre CTF (2019.8.7-8)

Our team. KaisHackGoN was qualified to the DEFCON CTF 27 Finals. We arrived about a day early by reserving a room for an extra day at planet hollywood hotel. The first day due to the flight we were all in bad condition. We had our meal at Gordon Ramsay burger and went to sleep early.

Day 1 (2019.8.9)

The CTF started with two challenges. One KoTH (ropship) and one A&D (telooogram) challenge. I only looked at the telooogram challenge briefly but other teammates were much better at understanding it so I moved on to the next challenge released, called aoool.

aoool was a web server (nginx-like) written in C++. It was linked to libboost, a regex library. Due to some dependency issues, I set up a 18.04 virtual machine to run it. (it didn’t run on my local machine, and I wasted some time adjusting this) While my vm was being set up, I loaded the binary on IDA and did some basic reversing, revealing that there are 3 methods that the server can handle: UF, UC and GET. It seems that there are also serveral modes in GET, and there is something that looks like a JIT compiler in one of the modes. Knowing this, I tried to execute the binary but it just died without doing anything. WTF?

By using strace, I realized that the binary tries to open a file in the path “/aoool/etc/default” so I set up the directories and files. After doing that though, the binary wouldn’t run. It seemed that there is a certain format that the default file should adhere. I found that there was a function that parses the file and creates some data structures from it. However, there was something that looked like a DFA within the logic and reversing it seemed like a shitty idea. So, I googled the internet and found out some example config files for nginx and fed it to the binary. However, it still didn’t work. After wasting a lot of time complaining about the reversing pain I decided that I would make the binary skip that function by patching it. Now, the binary ran and hanged for input.

After reversing the HTTP header parsing part, I found out how to use the UF protocol. However, I couldn’t use the UC and GET protocol because it relied on the default file, and without the data structures properly initialized the handlers for UC and GET would segfault. Afterwards I wasted a lot more time, asking other teammates about the challenge, repeatedly doing stupid things, repeatedly saying stupid things… and the servers were closed for today. I decided that we should prepare really hard during the night so that we can write an exploit for it and shoot it tomorrow.

And I thought of something that would make the whole thing a lot easier. The reason that we couldn’t test our other features was because we couldn’t find out how the default file looks like. So, why don’t we find a way to show it? After conversing with my teammates, I found out that in previous DEFCON CTF Finals, all teams could have SSH access to their own challenge-hosting servers. This year, we didn’t have anything like that. But what if we could do something like that? I quickly patched the aoool binary so that when the content-length header field was 7777 the binary would pop a shell. Since other teams could not look at our binary’s patch, I reckoned that it would be safe to do something like this (==backdooring our own binary)

Day 2 (2019.8.10)

The next day, I uploaded the malicious patch along with a patch for a vulnerability we couldn’t make use of. The backdoor worked and I leaked the /aoool/var/default file and showed it to the rest of the team. Also, HITCON pwned the challenge, so checked all the log files and uploaded files since they would contain the exploit. That revealed something very interesting: a php-like programming language. I wasn’t sure if the payload was just an attempt or guess but it seemed to make sense, and the length of the payload was too long and well-written to be considered an experiment. Two new challlenges (dooom and mirror-universe) were released at the start of the day, but they did not affect my fixation to aoool, as the new information we obtained via the backdoor was far more interesting. I uploaded the default file to the team slack and after about an hour one of my smart teammates wrote an exploit that could read an arbitrary file excluding the flag, by changing the config so that the base directory is root, instead of the /aoool/var/www/main/html directory (default). Using this exploit, we stole the binaries from other teams, and applied them as patches to our machine and bindiff’ed them for vulnerabilities. According to the best teams, there was probably a buffer overflow in the JIT compiler (one of the teams resized the JIT code buffer from 0x2000 to something much larger), which was something that I did put into consideration but did not consider it to be powerful enough to obtain an arbitrary read/write primtiive. Other patches included skipping some functions and changing offsets of data structures, which was kinda hard to catch just by looking at it. Also, some teams somehow managed to patch the LFI vulnerability so we couldn’t check them out.

When the game status of aoool turned to RED (bad), one of our teammates found something suspicious. The amount of flags being caught and the number of teams being attacked suddenly increased rapidly. We discovered that one (or maybe two) team installed a symbolic link linked to the flag as a backdoor and was constantly obtaining the flag so that they could bypass all patches. Before we could do anything to stop the aoool tyranny (I suggested we re-place the backdoor and delete all the symlinks) the challenge was over.

One thing that I learned was that in A&D style CTFs we should try to use all means to leak whatever information necessary and creative solutions to problems are very important. Also, I felt the huge difference in skill between me and top CTF players that I couldn’t really feel in 48-hr jeopardy CTFs. The amount of time given on the challenge is much shorter compared to a 48hr jeopardy CTF, because each challenge does not run for more than a day. However, the very skilled teams finished that hard task while I was just starting to understand what the binary is supposed to do. I decided that I should practice a lot harder to follow up.

After wrapping up aoool we encountered two more challenges: bitflip conjecture and babi. Our team was failing in attack scores so we decided to excel in KoH challenges. When bitflip conjecture was released, we all started to look at it. It was a shellcode writing challenge where scores were given for how much the shellcode was bitflip resistant. To be more specific, one of the 1600 bits of shellcode is reversed, and the shellcode must function properly even after this fault is injected. For every position of the bitflip, the shellcode is tested and for more passing test cases the higher score we get. There were a few strategies suggested that put us to a really high score. However, I decided to put the diligent, hard work of writing bulletproof shellcode to the rest of my brilliant teammates and thought of other possibilites. The authors provided a test binary that is used to run the shellcode. It used seccomp to only allow 3 system calls: exit, exit_group and write. However, I thought, are there any ways to bypass this, execute arbirtary code and cheat? I first decided to test if the remote binary is the same as ours. I put a sys_read shellcode and ran it on remote. For some reason, the wrapper complained: your shellcode runs for more than 1 second, nope!. This was very interesting to me. What if this was some hidden stage or a twist? (speaking of return-to-shellql?) Then I found some more interesting points. The wrapper does not handle stderr properly, and we can dump data in arbitrary addresses by using write(2, address, length) shellcode. I decided to dump the entire remote binary and find a way to bypass it, but the servers closed before I could write a script for it and I gave up.

During the night, my teammates devised a way to reach 999 points. That means, that except for 1 position the shellcode is bitflip resistant. I thought this was super cool.

I started to look at babi, which was a rust binary that implemented a php serialiizer and unserializer. Even with shallow reversing I could easily find the bug. When unserializing serials with the ref variable, a double-free was triggered. I could quickly find this out thanks to my libc-2.29. A 18.04 environment could’ve made this hard to find this quickly. Looking at the allocator’s behavior, I concluded that the remote server is 18.04 and uses libc 2.27, because libc-2.23 and libc-2.29 will scream the moment a double free occurs. A teammate found a leak that could only be triggered on a browser. Using two of them would give us an arbitrary read write primitive, but I was very tired and slept instead. (i am not sure if i could exploit this even if I was awake, though)

Day 3 (2019.8.11)

I woke up at 4am in the morning and worked on babi a bit more. I reverse engineered the remianing parts to seek for extra vulnerabilities that would be much more easier to control than a double free, but couldn’t find any. (actually, double free is a very powerful vulnerability, but I thought all teams would’ve patched this and working hard to write an exploit would be pointless. Also one of my teammates devised many patches for the exploit. Instead of fixing the routine, we changed the size of malloc, so that heap exploitation would fail due to changed offsets. Also he suggested to change the malloc size to a largebin size, so that a dobule free would instantly trigger malloc_printerr and cause the program to die. I thought this was a good idea. After the patches were made, I tried to look for better vulnerabilities such as a type confusion vulnerability) Then I went down to the competition zone. HITCON pwned babi once more, which I thought was really awesome. I uploaded patches but the last idea of using largebin sizes were rejected by the SLA. So I just uploaded another version which worked, luckily.

A few hours later Tea Deliverers got 1000 points on bitflip conjecture, which means they either found an unintended solution or wrote a completely bulletproof shellcode. A competing spirit can make the hardest tasks solvable.

One more challenge (web) was released. (jtaste) One of my teammates found the vulnerability and used it to attack other teams, but only a few teams yielded flags, which was kinda sad. Also the competition was coming to an end, which was a relief because I was really tired and sleepy.