Introduction
Some of Intrinsec’s consultants participated, as independent individuals, to the Nuit du Hack CTF Quals – 2016. We are satisfied with our ranking (#52 over 447 teams who solved at least one challenge) and we were one of the few teams to solve the Matriochka step 4 challenge (500 points) so here is our writeup.
This could also be useful to malware analysts who want to reverse-engineer an infected MBR (like in a recent ransomware).
Identification
The previous step, Matriochka step 3, gives us a binary file (download), weighing 5’120 bytes.
The file command recognizes a « DOS/MBR boot sector ». A fdisk -l on it identifies four 1 Tb partitions with the same start and end offsets (we ignored this fact). Running strings returns an interesting one: « What’s the magic word ? ».
VM setup
Set up a minimal VM (1x vCPU and a few megabytes of RAM and hard-disk space) to execute this MBR code, using VMWare Workstation. The VM is booted on a GParted CD ISO to access the raw disk and copy the MBR on it:
Then disconnect the ISO and reboot the VM to find the expected prompt:
Try to type something and see it fail:
Interestingly, the « badboy » message was not found by strings so it must be decoded during the execution.
Debugging setup
VMWare VMs can be remotely debugged, using the GDB protocol, even during the initial BIOS and MBR steps. You have to add the following lines to the .vmx file (source):
debugStub.listen.guest32 = "TRUE" # enables debugging
debugStub.listen.guest32.remote = "TRUE" # Allows debugging from a different computer / VM instead of localhost. The IP for remote debugging will be that of the host.
debugStub.hideBreakpoints = "TRUE" # Enables the use of hardware breakpoints instead of software (INT3) breakpoints
monitor.debugOnStartGuest32 = "TRUE" # Breaks into debug stub on first instruction (warning: in BIOS!) # This will halt the VM at the very first instruction at 0xFFFF0, you could set the next breakpoint to break *0x7c00 to break when the bootloader is loaded by the BIOS
When you start the VM, it hangs on a black screen, it is normal. Then you can start your prefered GDB-compatible debugger, I chose IDA. In the Debugger menu, choose Attach -> Remote GDB debugger. Input the listening IP and the default TCP port 8832, click OK then select attach to the process started on target, and type F9 to continue execution.
Analysis
The VM should now show you the prompt, type something, but don’t hit ENTER, then suspend the execution in IDA. Inspect the memory in the Hex view to find familiar strings:
Our input is stored at 0x1003 so let’s place a hardware RW breakpoint. I didn’t find an easier way than right-clicking in the IDA view, selecting Synchronize with->Hex view to be able to right-click on the data and select Add breakpoint:
Do not forget to synchronize back the IDA view with EIP. Hit F9 to resume execution, and hit ENTER in the VM to submit this password. The VM pauses because the breakpoint is triggered.
Follow the execution step-by-step or hit F9 until it triggers again to see interesting comparisons:
It looks like the beginning of a string « Goo », so edit the password in the Hex view to make it match (or set EIP at 0x14C0 to skip the checks), then continue following the execution thanks to the breakpoint.
If you go too far and miss something, disable the breakpoint, restart from the beginning, type the password, suspend the execution to enable the breakpoint, resume with F9, hit ENTER.
We progressively find the remaining chars of the string:
So we have « Good_Game_! » which really looks like a flag, let’s try:
I submitted this flag and unfortunately it did not work. An organizer kindly answered us on IRC: it was a nice side effect but not the expected flag (very frustrating!) so I continued the analysis relying on the breakpoints to see if something else happened to the password after this.
It found the following routine which modifies the password in-place with a promising XOR:
I replaced the password with null bytes, to get the key after execution, we can even see that it is repeated:
We extend the length of the breakpoint to 0x18 to catch every operations on this whole area. And we find more interesting comparisons:
Caution: it begins with EBX=0x1004, the second letter of the password, and it adds to EBX at first 2, then 1, then 6. It means that the comparison is not done sequentially char by char. The next routines work the same way:
So we have everything, including the expected string after the XOR routine (length = 11), in the right order. We XOR lines 1 and 2 in the following table and obtain the expected input:
Expected result | 0x28 | 0x37 | 0x77 | 0x5b | 0x31 | 0x90 | 0xd4 | 0x68 | 0xdf | 0x2c | 0xb9 |
XOR key | 0x6C | 0x53 | 0x05 | 0x6a | 0x5c | 0xfc | 0xfb | 0x0e | 0xad | 0x4a | 0xb9 |
Expected input | D | d | r | 1 | m | l | / | f | r | f | 0x00 |
It looks less like a flag than the previous one, but it works, and it is accepted by the submission webapp!
Things that did not work
It is also interesting to know what I tried but that did not lead to anything interesting:
- I began with a quick static analysis of the file but it was inconclusive so I switched to a debugging approach. You can read Hexpresso’s XeR’s writeup for a detailed reverse of the first steps of the boot.
- I tried to convert the initial file from a raw disk image to a VMDK, with VBoxManage from Oracle Virtualbox, but it was not bootable.
- The first emulation was done with qemu and the execution worked. IDA recognized a 16 bits architecture, which looked valid in this MBR context. But the debugging behaviour was strange: step-by-step debugging was skipping instructions and the disassembly was always changing, because they were in fact 32 bits instructions. Perhaps it could have been fixed with an IDA setting.
- A second emulation was tried, using bochs, but it required to have the exact hard-disk geometry settings to work so I finally switched to VMWare which worked fine without tuning.
Greetings
Congratulations to the top 10 teams who will compete in the private CTF.
I thank the Nuit du Hack organizers for this interesting challenge, and also my teammates (especially Adrien and Arthur) who helped me finish this, just half an hour before the end of the competition!
— Clément Notin