Challenge - Santa's Special GIFt
Today, you got a strange GIFt from Santa:
You are unsure what it is for. You do happen to have some wood lying around, but the tool seems to be made for metal. You notice how it has a rather strange size. You could use it for your fingernails, perhaps? If you keep looking, you might see some other uses…
First thing was to check the image for the usual steganography and hidden data. With such a small image I did not have much hope.
binwalk got me nothing and also visual inspection of the pixels was a miss. But in my hex editor, appened to the image, i saw the ROT13 ciphtertext
uvag:--xrrc-tbvat which reads
Not exactly sure what to think I went further through the few remaining bytes in my hex editor when my eyes fixated on the last two:
55 AA. I have seen this sequence! The list of file magics i consulted didn’t have it, in neither endianess. So I wanted to go down the road of network protocols because thats where I suspected to have seen it. Luckily Daniel's list was better. Turns our its the magic of a master boot record. Indeed the file is exactly 512 long and thus fits perfectly into a sector.
I quickly dug out Bochs, as I remembered using it when I experimented with bootloaders a few years back. Daniel disassembled the file with an online disassembler but it failed halfway trough.
ndisasm got us the full source. Since we are both not particularly experienced with assember and the code is mixed with bogus instruction that resulted from the GIF data, we decided to better run it first.
Bochs did a great job and we got our first result: half a QR code. So we probably had to patch it to get the full QR. Good that bochs comes with a debugger. I set a breakpoint to 0x7c00 (
lbreak 0x7c00) since this is where the BIOS loads the MBR to and executes it. Then I single-stepped through the code, hoping to see something on the screen, but got nothing. Time to look for better tools. And indeed, i found out that bochs has a kind of hidden graphical debugger as well, making the job easier. You just have to set an option in your bochsrc.bxrc config:
Once in the graphical debugger I realized that I could just set breakpoints to the BIOS interrupts since they will be used to print on the screen. It also takes me right to the location of the most real code, and not some GIF junk. So I searched for all int 0x10 instructions in the disassembly, calculated the memory adresses and set a breakpoint for each. Then I hopped (
cont) through the code interrupt by interrupt.
The breakpoints lbreak 0x7C35 lbreak 0x7C44 lbreak 0x7C4D lbreak 0x7C66 lbreak 0x7C6A lbreak 0x7C71 lbreak 0x7C90 lbreak 0x7C9A
Turns out this was the key as I saw something on the screen. No QR code, but a secret flag! Normally it would have been shown only very very briefly. This was the code around the interrupt at
Continuing further then printed the half of the QR. Now we had to figure out how to make the program printing the whole.
A bit later we had it,
0x7C6A set up the line,
0x7C71 prints the spaces to center the QR in the middle of the screen and
int 0x10 on
0x7C90 is used to print the QR itself.
jnz 0x57 on
0x7c94 ends the horizontal iteration and
cmp si,0xe0 on
0x7c5b is the premature halting condition that prevents the full QR from showing.
Listing of the relevant code
(Please excude the wrong syntax highlighting. The highlighter doesn't work with the address and opcode listing.)
< ... > 00000057 85FF test di,di ; begin QR loop 00000059 751E jnz 0x79 0000005B 81FEE000 cmp si,0xe0 ; premature halting condition 0000005F 7502 jnz 0x63 00000061 FA cli 00000062 F4 hlt ; premature halt 00000063 B80D0E mov ax,0xe0d 00000066 CD10 int 0x10 ; line feed, color, etc? 00000068 B00A mov al,0xa 0000006A CD10 int 0x10 ; line feed, color, etc? 0000006C B91B00 mov cx,0x1b ; init loop counter 0000006F B020 mov al,0x20 ; begin loop, load space 00000071 CD10 int 0x10 ; print spaces to center QR 00000073 49 dec cx 00000074 75F9 jnz 0x6f ; end loop 00000076 BF1900 mov di,0x19 00000079 89F1 mov cx,si 0000007B 21D1 and cx,dx 0000007D 01C9 add cx,cx 0000007F 89F3 mov bx,si 00000081 C1EB02 shr bx,byte 0x2 00000084 8BAF9E7C mov bp,[bx+0x7c9e] 00000088 D3ED shr bp,cl 0000008A 21D5 and bp,dx 0000008C 8A86F07C mov al,[bp+0x7cf0] 00000090 CD10 int 0x10 ; print QR 00000092 4F dec di 00000093 4E dec si 00000094 75C1 jnz 0x57 ; end QR loop / done with the line? 00000096 B401 mov ah,0x1 00000098 B53F mov ch,0x3f 0000009A CD10 int 0x10 0000009C FA cli 0000009D F4 hlt ; the actual halt < ... >
Only thing left to do now, was to either patch the code (for example
cmp 0xe0 to
cmp 0xff) or change
esi temporarily to never hit the condition. Both ways worked fine, and we got the QR code that contained the flag. While this write-up is quite long and detailed, finding both flags took us only one and a half hour.