crackmes.one (Example 3)

Setup

Data

  • URL: BitFriends’s admin_panel
  • Language: C/C++
  • Platform: Unix/Linux (ELF64)
  • Description: “Welcome to my little crackme! Your goal is to get a shell!
    As usual patching is not allowed. ld_preload, dll injection and rootkits are not allowed too. I hope the crackme is not overrated or underated. Have fun!”

Output

$ ./admin_panel 
Welcome to the admin panel! The program which admins can
interact with on a guest computer to do admin stuff!

status: (admin=false; shell=unavailable)

*> 

Analysis

Tools
Cutter/Radare2

Decompiler (Commented)

undefined8 main(void)
{
    int32_t iVar1;
    int64_t iVar2;
    undefined8 uVar3;
    int64_t in_FS_OFFSET;
    char *src;
    char *dest;
    char *format;
    int64_t canary;
    
    canary = *(int64_t *)(in_FS_OFFSET + 0x28);
    strcpy(&dest, admin, admin);
    puts(
        "Welcome to the admin panel! The program which admins can\ninteract with on a guest computer to do admin stuff!\n"
        );
    while( true ) {
        if (_admin != 0xe9a) {
            puts("status: (admin=false; shell=unavailable)\n");
        }
        if (_admin == 0xe9a) {
            puts("status: (admin=true; shell=available)\n");
        }
        printf(0x20cf); // 0x20cf:"*> "
        iVar2 = fgets(&format, 0x100, _reloc.stdin);
        if (iVar2 == 0) break;
        strtok(&format, 0x20d3); // 0x20d3:"\n"; Removes "\n" from &format
        iVar1 = strcmp(&format, 0x20d5); // 0x20d5:"shell"
        if ((iVar1 == 0) && (_admin == 0xe9a)) {
            system("/bin/bash");
        } else {
            printf("input: ");
            printf(&format);
            puts(0x20d3);
        }
    }
    uVar3 = 0;
    if (canary != *(int64_t *)(in_FS_OFFSET + 0x28)) {
        uVar3 = __stack_chk_fail();
    }
    return uVar3;
}

Conclusion
The value of iVar1 has to be 0 and the value of admin (bss segment addr: 0x407c) has to be 0xe9a. To get iVar1 to 0 we have to enter “shell\n”. To get admin to 0xe9a we have to find a vulnerability to write that value. The app prints out what we have entered so we can possibly use a format string attack.

Attached gdb to running admin_panel process to resolve address.

$ pidof admin_panel
3915
$ sudo gdb ~/Downloads/admin_panel
(gdb) attach 3915
(gdb) p &admin
$1 = ( *) 0x559045ffb07c 

That address (&admin) changes with every new startup.

Let’s find out if that address is currently stored on the stack.

*> %x %x %x %x %x %x %x %x
input: 45ff90ec aff3c780 7 b014d700 7 2e5f26f2 45ffb07c b014d700

Indeed it is at position 7.


Solution

Format string
Let’s overwrite the value (to 0xe9a) to which the 7th parameter is pointing.

1. Let’s print out the 7th parameter

*> %7$x
input: 45ffb07c

2. Let’s write a number to the 7th param

*>     %7$n
input:     
(gdb) x/x &admin
0x559045ffb07c :	0x00000004

The value of 4 has been written to admin, cause there have been 4 characters (here: spaces) entered before %7$n.

3. Let’s write 0xe9a to the 7th param

*> %3738u%7$n

status: (admin=true; shell=available)

E9A hex is 3738 dec. Also valid:

"%3737u %7$n", "%3736u  %7$n", "%3735u   %7$n", ...
(gdb) x/x &admin
0x559045ffb07c :	0x00000e9a

For more informations: Brad Daniels Blog

Output

*> %3738u%7$n

status: (admin=true; shell=available)

*> shell