首页
社区
课程
招聘
7
[KCTF2023 年度赛-Pwn] 第四题 AI控制空间站(VecPass) WriteUp
发表于: 2023-7-3 19:53 8196

[KCTF2023 年度赛-Pwn] 第四题 AI控制空间站(VecPass) WriteUp

2023-7-3 19:53
8196

题目概述

本题是一道LLVM PASS PWN的题目,重点考察了C++ STL中本身存在的一个漏洞,可导致double free的产生。结合glibc堆以及LLVMopt中的相关特性可完成漏洞的利用,最终在不同的内核环境中寻找一条共同的劫持链,可拿到稳定的远程shell

附件中的漏洞模块VecPass.so文件并没有去符号表,本着想让各位师傅集中精力在漏洞的发掘和利用上的意图,希望各位师傅能够玩得开心。

LLVM PASS PWN的基础以及调试方法等内容可参考笔者之前写的文章:https://bbs.kanxue.com/thread-274259.htm

逆向分析

在本题所给的附件中,optLLVM的优化器和分析器,其版本是Ubuntu LLVM version 14.0.0libc对应的版本是Ubuntu GLIBC 2.35-0ubuntu3.1。而漏洞存在于自定义的LLVM PASS模块VecPass.so中,故我们需要对其进行逆向分析。

寻找PASS注册名

_cxx_global_var_init_10中,可以找到这个自定义LLVM PASS模块的PASS注册名为winmt,以及一个彩蛋(笑

因此,需要使用opt -load ./VecPass.so -winmt ./exp.ll -enable-new-pm=0 -f命令加载模块并启动LLVM的优化分析(注意新版需要加-enable-new-pm=0,至于-f选项也可以不加,不影响后续利用)。

定位runOnFunction函数

定位到vtable虚表,即可找到重写的runOnFunction函数:

runOnFunction函数分析

runOnFunction函数中,先关闭了标准报错,然后将numnm两个变量清零。接着,获取了当前处理的函数名,并将其全部转为大写字母,只有结果为KCTF才能进行后续操作。简单来说,就是只处理函数名的大写为KCTF的所有函数

满足了上述要求后,设置标记变量sign0,并调用run函数进一步对当前函数中的指令进行解析操作。只有当run函数的返回值为1的时候,才会跳出while循环,代表对该函数的处理结束。

run函数的预处理

这里我对几个局部变量重命名了,index代表vector中的编号,del_times代表删除的次数,total_times代表之后的createdelete总操作次数,初始的时候index-1del_times0total_times0。然后会实例化15ChunkInfo类的对象,并依次push_back放入C++ STL Vector中。

再往下看,可以看到仅当命令的操作符编号为56号,即Call操作符的时候(从/usr/include/llvm-xx/llvm/IR/Instruction.def中可查到),才会跳出循环进一步解析处理。

接着就是对于Call操作符调用的不同函数及参数做不同的解析处理了。

恢复ChunkInfo类

结合下面的create等操作,很容易恢复出ChunkInfo的成员变量如下:

其中,size是堆块大小,buf指向申请分配的堆块区域。

ChunkInfo类的构造函数ChunkInfo::ChunkInfo()如下,初始化清零操作:

ChunkInfo类的析构函数ChunkInfo::~ChunkInfo如下,对堆块进行释放:

这里的析构函数虽然很常规,但是很重要,是漏洞出现的关键,后文会具体分析。

create操作

若调用的是create函数,则需要有两个参数(这里getNumOperands的值需要为3是因为算上了被调用者)。然后,当前total_times(创建和删除总操作次数)不得超过15。此外,还需要indexdel_times的和小于14,即仍存在的和已删除的vector中元素的数量和,确保vector不溢出。

先通过llvm::isa判断参数是否为常数,再用getArgOperand(xxx, 0)getZExtValue获取create函数的第一个参数值,即堆块可用区域的大小,范围是[0x10, 0x100)

然后,根据第一个参数的堆块大小值申请相应的堆块,存放入vector中的下一个未使用对象的buf指针中。

create函数的第二个参数可以是0/1/2,若为0则不进行任何操作,若为1则将申请的堆块(buf指针指向的地址)中的前八个字节给全局变量num,若为2则相反地将num的值赋给堆块的前八个字节。

remake操作

remake操作受标记变量sign控制,每个KCTF函数只能调用一次remake函数可以有三个参数,其中第一个参数代表着vector中需要重置的对象编号,后面两个参数和create操作相同。

delete操作

delete函数仅有一个参数,代表着需要删除的vector中的对象编号。此外,当前total_times(创建和删除总操作次数)不得超过15次才能执行delete操作

循环vector的迭代器,找到对应编号的vector元素,先将此对象中的sizebuf变量清空,再用erase直接对这个vector元素进行删除处理

最后,index编号减一,del_times删除次数加一。

加减运算操作

加减运算addsub操作都只有一个参数,代表全局变量num所需加减的值。

位运算操作

位运算操作有and/or/xor三种,在做位运算操作前都需要num只取后四个字节。根据传入的单一参数值,位运算分为两种:将全局变量numnm进行相应位运算操作和与传入的参数值进行位运算操作。

swap操作

swap操作无参数,但受全局变量used控制,整个程序执行过程中只能调用一次

swap操作会将全局变量numnm的值相互交换。

end操作

end操作无参数,会返回1,使得跳出runOnFunction中的while循环,表示该KCTF函数处理完成。

此外,若是其他未定义操作,则返回0,并将instIter往后移动一位,再次重新调用run函数,接着当前指令之后继续解析处理。

C++ STL 中的一个漏洞

根据上述逆向分析的过程,本题乍一看没有明显的漏洞,但是注意其中使用了C++ STLvector容器。在delete操作中,在对某个对象删除的时候会将对应的vector元素用erase直接删除掉。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-9-11 13:28 被kanxue编辑 ,原因:
收藏
免费 7
支持
分享
赞赏记录
参与人
雪币
留言
时间
bwner
为你点赞~
2023-10-14 17:07
一半人生
为你点赞~
2023-9-13 08:46
ZIKH26
为你点赞~
2023-9-12 11:57
whereslow
为你点赞~
2023-9-11 20:39
mangovo
为你点赞~
2023-9-11 14:53
mb_mgodlfyn
为你点赞~
2023-9-11 12:10
winmt
为你点赞~
2023-7-3 20:15
最新回复 (10)
雪    币: 53082
活跃值: (21135)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2

题目收到,方便时先加我下微信:×****
在比赛前评委,会评定题目

最后于 2023-9-11 12:00 被kanxue编辑 ,原因:
2023-7-3 21:10
0
雪    币: 53082
活跃值: (21135)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
第四题 AI控制空间站
2023-8-27 10:29
0
雪    币: 24044
活跃值: (12558)
能力值: ( LV15,RANK:1744 )
在线值:
发帖
回帖
粉丝
4

点赞作者,从解题人的角度考虑的非常全面,包括各种有意无意的坑和解法的稳定性。

题目其实还有两处漏洞,即index为-1时,delete和remake的索引与index做的是无符号比较,所以可以任意取值。对于delete,这相当于能在堆区任意偏移处写12个0,对于remake则是一个堆区任意偏移的四字节常量任意写。

最早没有意识到run函数可以被多次执行,面对15次的限制以为无法通过正常的方式填到vector的最后一项,刚好发现了这两处无符号比较的越界,以为是预期(特别的,create的index用的是有符号比较),没想到是非预期。
所以,可以用remake越界直接填写vector的最后一项,然后delete vector靠前的一项将最后一项的堆块释放,再用delete的越界写0改掉释放堆块的key(显然,这一步对堆块布局的稳定性要求相当高…),破坏tcache double free的检测。index为-1时用delete越界改写后index变为-2,但此后都不能再调用create(否则覆盖了vector堆块前面的头在析构时会出错),所以不能通过正常的方式调用delete触发第二次free。
后来才发现run函数可以调用多次,所以实际触发double free由最后的vector析构完成(发现这里时更坚定的认为这是预期。。。)。
好不容易把one_gadget写入got表,才发现所有的got表项都不满足……上面writeup的是最接近的,有考虑能不能再搞一次任意地址分配改掉堆上内容……由于remake的次数限制不太好搞,再加上不想碰堆风水,以及又发现在两台不同机器上堆地址不一样,还有one_gadget的限制搞不定,放弃……

最后于 2023-9-11 23:36 被mb_mgodlfyn编辑 ,原因:
2023-9-11 12:33
0
雪    币: 163
活跃值: (809)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
5
唉,我也是一直在试index为-1时,delete和remake导致的堆区访问漏洞,开始没把vector占满,erase时没有触发free操作,以为不是个堆题。。。
2023-9-11 13:18
0
雪    币: 76
活跃值: (8699)
能力值: ( LV13,RANK:438 )
在线值:
发帖
回帖
粉丝
6
mb_mgodlfyn 点赞作者,从解题人的角度考虑的非常全面,包括各种有意无意的坑和解法的稳定性。 题目其实还有两处漏洞,即index为-1时,delete和remake的索引与index做的是无符号比较,所以可以任 ...
当时出题的时候的确没有考虑到初始index是-1,无符号比较会出问题。师傅厉害,很细节,学习了。
2023-9-11 13:22
0
雪    币: 76
活跃值: (8699)
能力值: ( LV13,RANK:438 )
在线值:
发帖
回帖
粉丝
7
phyman 唉,我也是一直在试index为-1时,delete和remake导致的堆区访问漏洞,开始没把vector占满,erase时没有触发free操作,以为不是个堆题。。。[em_5]
当时也是故意vector大小设置的比较大,想着一般人不会直接填满试出来这个洞hhh,其实如果知道vector的erase存在这个问题,就简单了。
2023-9-11 13:26
0
雪    币: 1221
活跃值: (1172)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
llvm STL glibc Kernel全会,winmt我真的哭死
2023-9-14 12:59
0
雪    币: 2267
活跃值: (1553)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
9
llvm STL glibc Kernel全会,winmt我真的哭死
2023-9-26 09:07
0
雪    币: 4720
活跃值: (31346)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
感谢分享
2023-9-26 09:24
1
雪    币: 2492
活跃值: (5614)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
11
winmt太强了
2023-10-14 17:07
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册