Tut01: GDB/x86

IOLI-crackme

Did you successfully connect to the CTF server? Let's play with a binary.

We prepared four binaries. The goal is very simple: find a password that each binary accepts. Before tackling this week's challenges, you will learn how to use GDB, how to read x86 assembly, and a hacker's mindset!

We highly recommend to tackle crackme binaries first (at least upto 0x04) before jumping into the bomblab. In bomblab, if you make a mistake (i.e., exploding the bomb), you will get some deduction.

In this tutorial, we walk together to solve two binaries.

crackme0x00

# login to the CTF server
# ** check Canvas for login information! **
[host] $ ssh lab01@<ctf-server-address>

# let's start lab01!
[CTF server] $ cat README
[CTF server] $ cd tut01-crackme

Where to start? There are many ways to start:

  1. reading the whole binary first (e.g., try objdump -M intel -d crackme0x00);

  2. starting with a gdb session (e.g., gdb ./crackme0x00) and setting a breakpoint on a well-known entry (e.g., luckily main() is exposed, try nm crackme0x00);

  3. run ./crackme0x00 first (waiting on the "Password" prompt) and attach it to gdb (e.g., gdb -p $(pgrep crackme0x00));

  4. or just running with gdb then press C-c (i.e., sending a SIGINT signal).

Let's take 4. as an example

$ gdb ./crackme0x00
Reading symbols from ./crackme0x00...(no debugging symbols found)...done.
(gdb) r

[r]un: run a program, please check help run

Starting program: /home/lab01/tut01-crackme/crackme0x00
IOLI Crackme Level 0x00
Password: ^C

press ctrl+C (^C) to send a signal to stop the process

Program received signal SIGINT, Interrupt.
0xf7fd8d09 in __kernel_vsyscall ()
(gdb) bt
#0  0xf7fd5079 in __kernel_vsyscall ()
#1  0xf7ecbdf7 in __GI___libc_read (fd=0, buf=0x804b570, nbytes=1024) at ../sysdeps/unix/sysv/linux/read.c:27
#2  0xf7e58258 in _IO_new_file_underflow (fp=<optimized out>) at fileops.c:531
#3  0xf7e5937b in __GI__IO_default_uflow (fp=0xf7fbd5c0 <_IO_2_1_stdin_>) at genops.c:380
#4  0xf7e3ccb1 in _IO_vfscanf_internal (s=<optimized out>, format=<optimized out>, argptr=<optimized out>, errp=<optimized out>) at vfscanf.c:630
#5  0xf7e47e25 in __scanf (format=0x80487f1 "%s") at scanf.c:33
#6  0x080486d1 in main (argc=1, argv=0xffffd6c4) at crackme0x00.c:14

[bt]: print backtrace (e.g., stack frames). Again, don't forget to check help bt

(gdb) tbreak *0x080486d1
Temporary breakpoint 1 at 0x080492fa

set a (temporary) breakpoint (help b, tb, rb) to the call site (next) of the scanf(), which is potentially the most interesting part.

(gdb) c
Continuing.
aaaaaaaaaaaaaaaaaaaaaaaa

[c]ontinue to run the process, type aaaaaaaaaaaaaa (inject a random input)

Temporary breakpoint 1, 0x080492fa in main ()

ok, it hits the breakpoint, let check the context

[disas]semble: dump the assembly code in the current scope

(gdb) set disassembly-flavor intel
(gdb) disas
   0x080486a3 <+0>: push   ebp
   0x080486a4 <+1>: mov    ebp,esp
   0x080486a6 <+3>: sub    esp,0x10
   0x080486a9 <+6>: push   0x80487f4
   0x080486ae <+11>:    call   0x8048470 <puts@plt>
   0x080486b3 <+16>:    add    esp,0x4
   0x080486b6 <+19>:    push   0x804880c
   0x080486bb <+24>:    call   0x8048430 <printf@plt>
   0x080486c0 <+29>:    add    esp,0x4
   0x080486c3 <+32>:    lea    eax,[ebp-0x10]
   0x080486c6 <+35>:    push   eax
   0x080486c7 <+36>:    push   0x80487f1
   0x080486cc <+41>:    call   0x8048480 <scanf@plt>
=> 0x080486d1 <+46>:    add    esp,0x8
   0x080486d4 <+49>:    push   0x8048817
   0x080486d9 <+54>:    lea    eax,[ebp-0x10]
   0x080486dc <+57>:    push   eax
   0x080486dd <+58>:    call   0x8048420 <strcmp@plt>
   0x080486e2 <+63>:    add    esp,0x8
   0x080486e5 <+66>:    test   eax,eax
   0x080486e7 <+68>:    jne    0x8048705 <main+98>
   0x080486e9 <+70>:    push   0x804881e
   0x080486ee <+75>:    call   0x8048470 <puts@plt>
   0x080486f3 <+80>:    add    esp,0x4
   0x080486f6 <+83>:    push   0x804882d
   0x080486fb <+88>:    call   0x80485f6 <print_key>
   0x08048700 <+93>:    add    esp,0x4
   0x08048703 <+96>:    jmp    0x8048712 <main+111>
   0x08048705 <+98>:    push   0x804883c
   0x0804870a <+103>:   call   0x8048470 <puts@plt>
   0x0804870f <+108>:   add    esp,0x4
   0x08048712 <+111>:   mov    eax,0x0
   0x08048717 <+116>:   leave
   0x08048718 <+117>:   ret
End of assembler dump.

please try reading (and understating the code)

   0x080486c3 <+32>:    lea    eax,[ebp-0x10]
   0x080486c6 <+35>:    push   eax
   0x080486c7 <+36>:    push   0x80487f1
   0x080486cc <+41>:    call   0x8048480 <scanf@plt>
 -> scanf("%s", buf)

by the way that's the size of buf?

(gdb) x/1s 0x80487f1
0x80487f1:  "%s"

this is your input

(gdb) x/1s $ebp-0x10
0xffffcb30:     'a' <repeats 24 times>

please learn about the e[x]amine command (help x), which is one of the most versatile commands in gdb

   0x080486d4 <+49>:    push   0x8048817
   0x080486d9 <+54>:    lea    eax,[ebp-0x10]
   0x080486dc <+57>:    push   eax
   0x080486dd <+58>:    call   0x8048420 <strcmp@plt>
 -> strcmp(buf, "250381")
(gdb) x/1s 0x8048817
0x8048817:  "250381"
   0x080486e5 <+66>:    test   eax,eax
   0x080486e7 <+68>:    jne    0x8048705 <main+98>
   0x080486e9 <+70>:    push   0x804881e
   0x080486ee <+75>:    call   0x8048470 <puts@plt>
   0x080486f3 <+80>:    add    esp,0x4
   0x080486f6 <+83>:    push   0x804882d
   0x080486fb <+88>:    call   0x80485f6 <print_key>
   0x08048700 <+93>:    add    esp,0x4
   0x08048703 <+96>:    jmp    0x8048712 <main+111>
   0x08048705 <+98>:    push   0x804883c
   0x0804870a <+103>:   call   0x8048470 <puts@plt>

->
    if (!strcmp(buf, "250381")) {
        printf("Password OK :)\n")
        ...
    } else {
        printf("Invalid Password!\n");
    }
(gdb) x/1s 0x804883c
0x804a0a4:      "Invalid Password!\n"
(gdb) x/1s 0x804881e
0x804a086:      "Password OK :)\n"

[Task] Try the password we found? Does it work? You can submit the flag to the submission site (see above) to get +20 points!

crackme0x01

Let's go fast on this binary. Please take similar steps from crackme0x00 and reach to this place.

(gdb) disas
   0x08048486 <+0>: push   ebp
   0x08048487 <+1>: mov    ebp,esp
   0x08048489 <+3>: sub    esp,0x4
   0x0804848c <+6>: push   0x8048570
   0x08048491 <+11>:    call   0x8048330 <puts@plt>
   0x08048496 <+16>:    add    esp,0x4
   0x08048499 <+19>:    push   0x8048588
   0x0804849e <+24>:    call   0x8048320 <printf@plt>
   0x080484a3 <+29>:    add    esp,0x4
   0x080484a6 <+32>:    lea    eax,[ebp-0x4]
   0x080484a9 <+35>:    push   eax
   0x080484aa <+36>:    push   0x8048593
   0x080484af <+41>:    call   0x8048340 <scanf@plt>

what's scanf() doing (i.e., what's the value of 0x8048593)?

=> 0x080484b4 <+46>:    add    esp,0x8
   0x080484b7 <+49>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080484ba <+52>:    cmp    eax,0xc8e

it means that our input with 0xc8e(hex? integer?) is password.

   0x080484b4 <+46>:    add    esp,0x8
   0x080484b7 <+49>:    mov    eax,DWORD PTR [ebp-0x4]
   0x080484ba <+52>:    cmp    eax,0xc8e
   0x080484bf <+57>:    jne    0x80484d0 <main+74>
   0x080484c1 <+59>:    push   0x8048596
   0x080484c6 <+64>:    call   0x8048330 <puts@plt>
   0x080484cb <+69>:    add    esp,0x4
   0x080484ce <+72>:    jmp    0x80484dd <main+87>
   0x080484d0 <+74>:    push   0x80485a5
   0x080484d5 <+79>:    call   0x8048330 <puts@plt>
   0x080484da <+84>:    add    esp,0x4
   0x080484dd <+87>:    mov    eax,0x0
   0x080484e2 <+92>:    leave
   0x080484e3 <+93>:    ret

[Task] Try the password we found? Does it work? Great. Please explore all crackme binaries and if you think you are ready, please start bomblab!

Reference