Can you unlock the secret boot sequence hidden within our flag bootloader to recover the flag?
A reversing challenge on the 247CTF.com website.
Warning: The challenge contains a real life bootable file, if you boot your computer with it, shit can really hit the fan. Always only emulate.
Check what are we dealing with.
$ file flag.com
flag.com: DOS/MBR boot sector
Find out how to run and debug it.
$ qemu-system-i386 -drive format=raw,file=flag.com
Play around, try to input random stuff. In this part we can check for the maximum length of the input. Try inputting a lot of characters and we can see that we get “No memory message” if we try 18+. So the maximum password length is 18 characters.
Disassemble the file.
$ ndisasm flag.com
Disassemble with offset.
$ ndisasm -o 7c00h flag.com > flag-offset.asm
Value 0x7c00h
for offset is chosen, as that is the boot entry point.
al
- characterbh
- display page (alpha modes)bl
- foreground color (graphic modes)si
- source index register, It is used as source index for string operations.bx
- 16bit data register, base register[si]
- memory content of si addressThis is one of the many checks. It takes the character we input al
and checks if al XOR 0x6
is equal to 0x53
. If it is true, the nature of assembly sets the ZF
flag to 0
. jnz
or jump if not zero, checks the ZF
flag, if it is not zero it jumps to specified location.
00007C7E 3004 xor [si],al
00007C80 46 inc si
00007C81 3004 xor [si],al
00007C83 43 inc bx
00007C84 46 inc si
00007C85 B053 mov al,0x53
00007C87 3406 xor al,0x6
00007C89 3807 cmp [bx],al
00007C8B 0F85ED00 jnz near 0x7d7c
There are 16 checks in the code, so we need 16 breakpoints and after every break we need to unset the ZF
flag.
We can set wich flags are set with gdb by using this command:
(gdb) info registers eflags
or
(gdb) print $eflags
So we could calculate all the values, but on the other hand, we can simply set the Z
flag to 0
after every arbitrary character we input.
We want to create breakpoints at every check, so we can do the magic before jnz
happens.
We can set the Z
flag with this command:
(gdb) set $eflags |= (1 << 6)
ZF
is the 6th bit of the $eflags
.
More info about setting, toggling and clearing the flag bits can be found here.
Run the file so that we can attach gdb to it.
$ qemu-system-x86_64 -s -S -m 512 -fda flag.com
$ gdb
GNU gdb (GDB) 9.2
(gdb) target remote localhost:1234
(gdb) break *0x7c00
Breakpoint 1 at 0x7c00
(gdb) c
Continuing.
Breakpoint 1, 0x0000000000007c00 in ?? ()
(gdb) break *0x7c7a
Breakpoint 2 at 0x7c7a
(gdb) break *0x7c8b
Breakpoint 3 at 0x7c8b
(gdb) break *0x7c9c
Breakpoint 4 at 0x7c9c
(gdb) break *0x7cad
Breakpoint 5 at 0x7cad
(gdb) break *0x7cbe
Breakpoint 6 at 0x7cbe
(gdb) break *0x7ccf
Breakpoint 7 at 0x7ccf
(gdb) break *0x7ce0
Breakpoint 8 at 0x7ce0
(gdb) break *0x7cf1
Breakpoint 9 at 0x7cf1
(gdb) break *0x7d02
Breakpoint 10 at 0x7d02
(gdb) break *0x7d11
Breakpoint 11 at 0x7d11
(gdb) break *0x7d20
Breakpoint 12 at 0x7d20
(gdb) break *0x7d2f
Breakpoint 13 at 0x7d2f
(gdb) break *0x7d3e
Breakpoint 14 at 0x7d3e
(gdb) break *0x7d4d
Breakpoint 15 at 0x7d4d
(gdb) break *0x7d5c
Breakpoint 16 at 0x7d5c
(gdb) break *0x7d6b
Breakpoint 17 at 0x7d6b
(gdb) c
Continuing.
Breakpoint 2, 0x0000000000007c7a in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 3, 0x0000000000007c8b in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 4, 0x0000000000007c9c in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 5, 0x0000000000007cad in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 6, 0x0000000000007cbe in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 7, 0x0000000000007ccf in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 8, 0x0000000000007ce0 in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 9, 0x0000000000007cf1 in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 10, 0x0000000000007d02 in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 11, 0x0000000000007d11 in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 12, 0x0000000000007d20 in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 13, 0x0000000000007d2f in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 14, 0x0000000000007d3e in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 15, 0x0000000000007d4d in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 16, 0x0000000000007d5c in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
Breakpoint 17, 0x0000000000007d6b in ?? ()
(gdb) set $eflags |= (1 << 6)
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x000000000000ff53 in ?? ()
(gdb) x/s 0x7DAA
0x7daa: "247CTF{xxx}\n\r"
The last command explained:
(gdb) x/s 0x7DAA
x ... examine
s ... display format = string
0x7DAA ... memory location
Additional info about memory examination in gdb can be found here.