Did you successfully connect to the CTF server? Let's start playing with some binaries.
For this tutorial, we've prepared four "crackme" binaries. Your goal is very simple: find a password that each binary accepts. Before tackling this week's challenges, you'll learn how to use GDB, how to read x86 assembly, and how to have the mindset of a hacker!
We highly recommend tackling the crackme binaries first (at least up to 0x03) before jumping into the bomblab. In the bomblab, if you make a mistake ("exploding the bomb"), some points will be deducted from your score.
In this tutorial, we'll solve two of the crackme binaries together.
# log into 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 should we start? There are many options:
Reading the whole binary first (e.g., objdump -M intel -d crackme0x00)
objdump -M intel -d crackme0x00
Starting it in a GDB session (e.g., gdb ./crackme0x00) and setting a breakpoint on some known function (for example, main() is luckily exposed in this binary -- try nm crackme0x00 to see) before running it (r)
gdb ./crackme0x00
main()
nm crackme0x00
r
Running ./crackme0x00 first (waiting on the "Password" prompt) and then attaching it to GDB (e.g., gdb -p $(pgrep crackme0x00))
./crackme0x00
gdb -p $(pgrep crackme0x00)
Or just running with GDB (gdb ./crackme0x00), starting the binary (r), and then pressing Ctrl-C (i.e., sending a SIGINT signal) to return to GDB while on the "Password" prompt
Ctrl-C
Let's take 4. as an example.
$ gdb ./crackme0x00 Reading symbols from ./crackme0x00...(no debugging symbols found)...done. (gdb) r
[r]un is the command to run a program; try help run for more.
[r]un
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, which will bring you back to the GDB prompt.
Ctrl+C
^C
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 (i.e., stack frames). Again, don't forget to check help bt.
[bt]
help bt
(gdb) tbreak *0x080486d1 Temporary breakpoint 1 at 0x080486d1
The backtrace shows that main() called __scanf() with return address 0x080486d1 (your addresses may be different). That seems like a good place to set a temporary breakpoint, since we'd like to investigate what main() does with the value it gets from that call. We use [tbreak] to set a temporary breakpoint (help b, help tb, help rb) there.
__scanf()
0x080486d1
[tbreak]
help b
help tb
help rb
(gdb) c Continuing. aaaaaaaaaaaaaa
[c]ontinue to run the process. Type aaaaaaaaaaaaaa or some other random input, so the call to scanf() will end.
[c]ontinue
aaaaaaaaaaaaaa
scanf()
Temporary breakpoint 1, 0x080486d1 in main ()
OK, it hit the breakpoint. Let's check the context.
[disas]semble: dump the assembly code in the current scope.
[disas]semble
(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.
This is the full assembly code for main(). The "=>" shows the instruction the binary is currently paused on.
=>
Please try to read and understand the code.
0x080486c3 <+32>: lea eax,[ebp-0x10] 0x080486c6 <+35>: push eax 0x080486c7 <+36>: push 0x80487f1 0x080486cc <+41>: call 0x8048480 <scanf@plt>
This is the call to scanf(). The second value pushed is the address of the format string; let's check what it is:
(gdb) x/s 0x80487f1 0x80487f1: "%s"
So, in C, that call would look like this ("buf" being some buffer on the stack):
buf
scanf("%s", buf);
Your input can be found in the buffer:
(gdb) x/s $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.
e[x]amine
help x
0x080486d4 <+49>: push 0x8048817 0x080486d9 <+54>: lea eax,[ebp-0x10] 0x080486dc <+57>: push eax 0x080486dd <+58>: call 0x8048420 <strcmp@plt>
In the same way as before (try examining the argument string), you can find that this is equivalent to:
strcmp(buf, "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>
That corresponds to the following (prove to yourself that it does):
if (!strcmp(buf, "250381")) { printf("Password OK :)\n") ... } else { printf("Invalid Password!\n"); }
[Task] Try the password we found! Does it work? You can submit the flag to the submission site to get 20 points for the tutorial!
Let's go more quickly with this binary. Please take similar steps as for crackme0x00, and reach 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)?
0x8048593
=> 0x080484b4 <+46>: add esp,0x8 0x080484b7 <+49>: mov eax,DWORD PTR [ebp-0x4] 0x080484ba <+52>: cmp eax,0xc8e
This is comparing our input with 0xc8e (hex? integer?), which means that's probably the password.
0xc8e
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 four crackme binaries, and when you think you're ready, please start the bomblab!
The bomblab challenges are all in a single "bomb" binary, which you can find under the home directory of user lab01 on the CTF server.
bomb
lab01
[host] ssh lab01@<ctf-server-address> [CTF server] $ pwd /home/lab01 [CTF server] $ ls -alh | grep bomb -rwsr-x--- 1 bomb110-raspberry lab01 22K Jan 14 2021 bomb
Execute the bomb binary and provide your API key to get started:
[CTF server] $ ./bomb Enter your api-key: <your-api-key> ,--.!, ____ _ _ _ __/ -*- | __ ) ___ _ __ ___ | |__ | | __ _| |__ ,d08b. '|` | _ \ / _ \| '_ ` _ \| '_ \| |/ _` | '_ \ 0088MM | |_) | (_) | | | | | | |_) | | (_| | |_) | `9MMP' |____/ \___/|_| |_| |_|_.__/|_|\__,_|_.__/ cs6265 Welcome to my fiendish little bomb. You have N? phases with which to blow yourself up. See you alive! (hint: seriously, security question?) >
[Task] Defuse the bomb by providing the right answer to each phase. Be careful when handling the bomb; if you enter a wrong answer, the bomb will explode, and you'll lose points for that phase. Submit the flags from each phase to the submission site to earn points.