本文作者博客
1 | https: / / www.cnblogs.com / hac425 / p / 14336484.html
|
调试方式
首先从github下载代码
1 | https: / / github.com / sudo - project / sudo / archive / SUDO_1_9_5p1.tar.gz
|
编译
1 2 3 4 5 6 7 | tar xf sudo - SUDO_1_9_5p1.tar.gz
cd sudo - SUDO_1_9_5p1 /
mkdir build
cd build /
.. / configure - - enable - env - debug
make - j
sudo make install
|
调试(请以root方式执行gdb)
1 | gdb - - args sudoedit - s '\' `perl -e ' print "A" x 65536 '`
|
gdb加载执行后进程会crash,这时候就可以对有漏洞的源码位置下断点,因为漏洞代码貌似是动态加载的,直接下断点下不到,crash之后就可以下了
断点命令
1 2 | b .. / .. / .. / plugins / sudoers / sudoers.c: 964
b .. / .. / .. / plugins / sudoers / sudoers.c: 978
|
漏洞成因
调试用poc
1 | sudoedit - s '\' 112233445566
|
漏洞位于 set_cmnd 函数中,关键代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | / * Alloc and build up user_args. * /
for (size = 0 , av = NewArgv + 1 ; * av; av + + )
size + = strlen( * av) + 1 ;
if (size = = 0 || (user_args = malloc(size)) = = NULL) {
if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
for (to = user_args, av = NewArgv + 1 ; ( from = * av); av + + ) {
while ( * from ) {
if ( from [ 0 ] = = '\\' && !isspace((unsigned char) from [ 1 ])) / / 关键逻辑!!!
from + + ;
* to + + = * from + + ;
}
* to + + = ' ' ;
}
* - - to = '\0' ;
}
|
进入该函数时NewArgv的结构如下
1 2 3 | NewArgv[ 0 ]: sudoedit
NewArgv[ 1 ]: \
NewArgv[ 2 ]: 112233445566
|
首先会计算 NewArgv 1-2
两个参数的长度 2 + 13 = 15
.
因此user_args
分配的内存大小为 15
字节。
然后会把 NewArgv 1-2
的数据拷贝到user_args
里面。
拷贝过程中如果 from[0]
为 \
,且 from[1]
不是空格就会from++
。
1 2 | if ( from [ 0 ] = = '\\' && !isspace((unsigned char) from [ 1 ])) / / 关键逻辑!!!
from + + ;
|
所以在处理NewArgv[1]
时,from[0]
就是 \
,from[1]
为 \x00
,会通过这个判断让 from++
,然后后面会再次from++.
之后from就指向了NewArgv[1]
字符串\x00后面一个字符的位置,我们看看调试时NewArgv[1]
后面是什么

可以看到NewArgv[1]
(0x5c 0x00
)后面紧跟着的是NewArgv[2]
( 0x31 0x31 ...
),所以此时 from 执行的就是 NewArgv[2]
的开头
从而会再次进入while循环把NewArgv[2]
拷贝到user_args
然后处理NewArgv[2]
会再次把NewArgv[2]
拷贝到user_args
因此最终结果就是 NewArgv[2]
被拷贝了两次,实际的写入数据长度为26字节

这个漏洞是一个堆溢出,不过写的数据需要是非\x00,如果user_args
分配的内存比较小(比如15字节)的话,其后面是unsorted bin
,如果分配的比较大的话(使用原始的poc)其后面跟的是top chunk,感觉都不是很好利用。
感觉需要花一些时间捋一捋代码的逻辑,看看有没有什么其他的想法。
- 修改NewArgv[1]和NewArgv[2]的长度可以控制
user_args
堆块后面的空闲堆块的大小
此外到达漏洞代码的poc构造过程也很精彩,这部分可以去 发布漏洞的博客 查看
便于调试的脚本
写了些gdb插件的代码,可以用来查看堆布局里面的已释放块和未释放块的信息
1 | https: / / github.com / hac425xxx / gdb - heap - trace
|

图中带 FREE_CHUNK
是处于释放状态的块
其他的是还没有释放的,展开chunk里面是这个块分配时的调用栈
对于没有释放块的调用栈可以通过让 gef
加载 heaptrace.py
的日志来实现
使用这个插件便于我们查看发送堆溢出时前后的堆块布局信息,以便找到合适溢出的堆对象。
参考链接
1 | https: / / blog.qualys.com / vulnerabilities - research / 2021 / 01 / 26 / cve - 2021 - 3156 - heap - based - buffer - overflow - in - sudo - baron - samedit
|
[看雪官方培训] Unicorn Trace还原Ollvm算法!《安卓高级研修班》2021年6月班开始招生!!
最后于 2021-1-31 17:27
被暗香沉浮编辑
,原因: