CREATIVE CHAOS   ▋ blog

The Flag Bootloader (rev)

PUBLISHED ON 07/09/2020 — EDITED ON 11/12/2023 — 247CTF, INFOSEC

Intro

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.

Basics

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.

Disassembly

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 - character
  • bh - 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 address

This 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.

Using gdb

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.

Debugging

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.

See Also