forbiddenBits 2013 Write-Up: Old

March 21, 2013

This challenge’s target was a binary with just 512 byte total size, named “bin.bin” and could be identified as “x86 boot sector”.
So our first thought was: maybe it’s bootable – and it is! After booting up a VM with this file as disk (for VirtualBox: mount on loop, then VBoxManage createrawvmdk) we were confronted with a password prompt.
Some testing showed: The input is verified after exactly 7 characters of input – return doesn’t trigger prior checking. Wrong input results in “No :(, try again.” being printed.
The next step was to identify the verifying mechanism.
According to Wikipedia the file in fact is a full “classical generic MBR” (512 Bytes total length, 0x55 0xAA at the end). After being loaded up in a hex editor, some strings could be identified: the password prompt text, the “wrong password” error message and the success message: “Nice, 16bits world is nice :D. Validate using that password :)”. It turned out, that the MBR partition table is completely empty, all code – and strings! – are embedded in the bootstrap code area. With the success message (“16bits world”) in mind, the next step becomes obvious: decompiling the binary as 16bit x86 machine code.
This objdump call revealed the readable 16bit assembly:

objdump -D -M intel -b binary -mi386 -Maddr16,data16 bin.bin

(skip -M intel if you prefer AT&T syntax)
Some analysis showed the following: besides normal output subroutines, there are two of more interest: one that kind of hashes/transforms the input password and one that compares the result with a stored value.

Commented version of the transforming subroutine:

00007C97  31ED          xor bp,bp           ;bp=0
00007C99  BEBF7C        mov si,0x7cbf       ;source: addr. of read pw
00007C9C  BFCF7C        mov di,0x7ccf       ;target: addr. of xor val
00007C9F  EB0D          jmp short 0x7cae    ;jump into loop
00007CA1  8B02          mov ax,[bp+si]      ;ax=cur. char in SI
00007CA3  8B1B          mov bx,[bp+di]      ;bx=cur. char in DI
00007CA5  31D8          xor ax,bx
00007CA7  89E9          mov cx,bp
00007CA9  D2C8          ror al,cl           ;rotate al, cl times
00007CAB  8802          mov [bp+si],al      ;save value into SI
00007CAD  45            inc bp              ;bp++
00007CAE  83FD07        cmp bp,byte +0x7    ;cmp bp with 7
00007CB1  72EE          jc 0x7ca1           ;bp < 7, jump back up
00007CB3  C3            ret

The used secret key at 0x7ccf is (hex) BE 21 03 15 1F 2C 6A. The other soubroutine then byte-wise compares the result with (hex) D8 A6 CD 4E 04 0A 3C.

The assembly code is roughly equivalent to this python code:

def RoRByte(val, n):
    rorval = val
    for roI in range(n):
        rorval = ((rorval & 0x1) << 7) + (rorval >> 1)
    return rorval

def check_passwd(passwd):
    secret_key = "\xBE\x21\x03\x15\x1F\x2C\x6A"
    secret_hash = "\xD8\xA6\xCD\x4E\x04\x0A\x3C"
    for i in range(7):
        if chr(RoRByte(ord(passwd[i])^ord(secret_key[i]), i)) != secret_hash[i]:
            return False
    return True

Our next step was to just test possible values for every position and compare with the secret hash. Finally “fl4g_me” came up, worth 50 points.

One Response to “forbiddenBits 2013 Write-Up: Old”

  1. There is certainly a great deal to learn about
    this topic. I love all the points you made.

Leave a Reply