本题是一道LLVM PASS PWN
的题目,重点考察了C++ STL
中本身存在的一个漏洞,可导致double free
的产生。结合glibc
堆以及LLVM
的opt
中的相关特性可完成漏洞的利用,最终在不同的内核环境中寻找一条共同的劫持链,可拿到稳定的远程shell
。
附件中的漏洞模块VecPass.so
文件并没有去符号表,本着想让各位师傅集中精力在漏洞的发掘和利用上的意图,希望各位师傅能够玩得开心。
LLVM PASS PWN
的基础以及调试方法等内容可参考笔者之前写的文章:https://bbs.kanxue.com/thread-274259.htm
在本题所给的附件中,opt
是LLVM
的优化器和分析器,其版本是Ubuntu LLVM version 14.0.0
,libc
对应的版本是Ubuntu GLIBC 2.35-0ubuntu3.1
。而漏洞存在于自定义的LLVM PASS
模块VecPass.so
中,故我们需要对其进行逆向分析。
在_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
选项也可以不加,不影响后续利用)。
定位到vtable
虚表,即可找到重写的runOnFunction
函数:

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

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

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

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

接着就是对于Call
操作符调用的不同函数及参数做不同的解析处理了。
结合下面的create
等操作,很容易恢复出ChunkInfo
的成员变量如下:

其中,size
是堆块大小,buf
指向申请分配的堆块区域。
ChunkInfo
类的构造函数ChunkInfo::ChunkInfo()
如下,初始化清零操作:

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

这里的析构函数虽然很常规,但是很重要,是漏洞出现的关键,后文会具体分析。
若调用的是create
函数,则需要有两个参数(这里getNumOperands
的值需要为3
是因为算上了被调用者)。然后,当前total_times
(创建和删除总操作次数)不得超过15
次。此外,还需要index
和del_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
操作受标记变量sign
控制,每个KCTF
函数只能调用一次。remake
函数可以有三个参数,其中第一个参数代表着vector
中需要重置的对象编号,后面两个参数和create
操作相同。

delete
函数仅有一个参数,代表着需要删除的vector
中的对象编号。此外,当前total_times
(创建和删除总操作次数)不得超过15
次才能执行delete
操作。
循环vector
的迭代器,找到对应编号的vector
元素,先将此对象中的size
和buf
变量清空,再用erase
直接对这个vector
元素进行删除处理。

最后,index
编号减一,del_times
删除次数加一。
加减运算add
和sub
操作都只有一个参数,代表全局变量num
所需加减的值。

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

swap
操作无参数,但受全局变量used
控制,整个程序执行过程中只能调用一次。
swap
操作会将全局变量num
和nm
的值相互交换。

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

此外,若是其他未定义操作,则返回0
,并将instIter
往后移动一位,再次重新调用run
函数,接着当前指令之后继续解析处理。
根据上述逆向分析的过程,本题乍一看没有明显的漏洞,但是注意其中使用了C++ STL
的vector
容器。在delete
操作中,在对某个对象删除的时候会将对应的vector
元素用erase
直接删除掉。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2023-9-11 13:28
被kanxue编辑
,原因: