Crack the heart [Reverse]

Crack the heart

Convince your crush to go out with you. Note: the flag is what you need to say to convince him/her to go out with you.


$ file crackme
crackme: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

$ ./crackme
Why should I go out with you?
becuz im leet haxx0r
that was pretty cringe

Okay, this binary is not falling for my advances.

Cursory observation when attempting to reverse this binary: very annoying flow control. During a CTF we're obviously pressed for time, so we looked for a way to cut some corners.

Near the entrypoint we encounter:

.data:00000000004021E5 mov     eax, 65h
.data:00000000004021EA syscall                 ; LINUX - sys_ptrace
.data:00000000004021EC mov     rbx, 0FFFFFFFFFFFFFFFFh
.data:00000000004021F3 cmp     rax, rbx
.data:00000000004021F6 jnz     short GOOD
.data:00000000004021F8 jmp     short BAIL
.data:00000000004021FA GOOD:
.data:00000000004021FA pop     rcx

Sweet, anti-debugging tricks from the early 2000's. 90 90 at 4021f8 (0x21f8 in the ELF file) it is.

Somewhere in the disassembly we find:

.data:000000000040220C mov     qword ptr [rcx], offset loc_40214D
.data:0000000000402213 mov     qword ptr [rcx+18h], offset BAIL
.data:000000000040221B test    rbp, rbp
.data:000000000040221E jz      short loc_402232
.data:0000000000402220 mov     qword ptr [rcx+8], offset str_cringe <-- the "bad boy" string
.data:0000000000402228 mov     qword ptr [rcx+10h], 17h

So apparently rbp is used as some kind of good/bad flag. What a creative use of the frame pointer.

Cruising through the disassembly some more, we find:

.data:0000000000402137 mov     al, [r10+r11]
.data:000000000040213B sub     al, bl
.data:000000000040213D jz      short loc_402144
.data:000000000040213F mov     ebp, 1

So this rbp flag is set based on a subtraction result here. Time for some GDB scripting:

set pagination off

br *0x40213b

    if $al == $bl
        printf "[GOOD] "
        printf "SUB %02x - %02x = %02x\n", $al&0xff, $bl&0xff, ($al - $bl)&0xff
        printf "[BAD ] "
        printf "SUB %02x - %02x = %02x\n", $al&0xff, $bl&0xff, ($al - $bl)&0xff


Lets take this technology for a spin, making a rough assumption here the flag starts with utflag{.. like the other challenges:

$ gdb -q -x x.gdb ./crackme.patched | egrep "GOOD|BAD"
[GOOD] SUB 04 - 04 = 00
[GOOD] SUB 91 - 91 = 00
[GOOD] SUB 05 - 05 = 00
[GOOD] SUB 02 - 02 = 00
[GOOD] SUB 06 - 06 = 00
[GOOD] SUB cb - cb = 00
[GOOD] SUB 64 - 64 = 00
[BAD ] SUB 61 - 7a = e7

Sweet, we literally get a hit per character.. and thus should be able to recover the flag using a bit of bruteforce without paying any attention to the details(tm).


// cset is a file with the bf charset, 1 char per line

$pwd = $argv[1];

while(strlen($pwd) < 82) {
    $found = false;
    foreach($cset as $c) {
        $p = $pwd . $c;
        $o = `./ '$p'`;
        if (strstr($o, "GOOD")) {
            $found = true;
            $pwd .= $c;
    if (!$found) {
        echo "CHAR NOT FOUND\n";
    echo "PWD: '" . $pwd . "'\n";

echo "$1" | 
    gdb -q -x x.gdb ./crackme.patched | 
    egrep "GOOD|BAD" | head -n ${#1} | tail -n1
$ php crack.php 'utflag{'
PWD: 'utflag{w'
PWD: 'utflag{wh'
PWD: 'utflag{wha'
PWD: 'utflag{what'
PWD: 'utflag{what_'

Well, this isn't a pretty/optimal solution, but it does work. Grab a cup of coffee and you will eventually learn the flag is:

utflag{what_1f....i_mapp3d_mY_m3m0ry_n3xt_to_y0urs....ahahaha, jkjk....unless ;)?}