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 hint:--keep-going
.
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. dosbox
+ 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: display_library: win32,options="gui_debug"
.
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 0x7c35
.
HV20{h1dd3n-1n-pl41n-516h7}
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, int
on 0x7C66
and 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.