cve-2021-3156的poc流程分析
2021-02-22 17:35:41 Author: xz.aliyun.com(查看原文) 阅读量:200 收藏

这个洞应该都不陌生,这篇文章就挨着poc流程分析一遍。
前置知识
1、linux的动态链接库,这个东西在web这里基本就在php的bypass_disable_function里面出现,以前php的bypass就是通过设置环境变量LD_PRELOAD来加载恶意的动态链接库执行我们想要执行的恶意代码,可以通过动态链接库重写函数也可以通过__attribute__这个特殊的函数直接执行恶意代码,在程序员的自我修养里面也提到过。
2、suid文件,这个东西大概就是使用文件者具有文件所有者的权限。
到这里就应该会想,既然LD_PRELOAD这个环境变量存放的动态链接库是预加载的,那suid文件运行是设置这个环境变量不就行了,但是事实是glibc有验证,这里就可以看得如果是setuid(也就是suid文件)就不会加载这个环境变量的动态链接库了,因为这是非常不安全的。

poc的流程分析
首先从github上面下载poc源码

https://github.com/blasty/CVE-2021-3156

然后调试第二个重写service_user的那个poc

https://www.qualys.com/2021/01/26/cve-2021-3156/baron-samedit-heap-based-overflow-sudo.txt

现在就来调试(后面的的调试都是在root的权限下,不然无法跟进子进程)这个poc吧,首先下载一个sudo(和本机的版本相同,我的是ubuntu的sudo 1.8.21)的源码,然后编译安装,这个可以参考

https://bbs.pediy.com/thread-265669.htm

编译安装完成后把生成的sudo复制到/usr/bin/sudd,并且设置为root的suid权限(chown root:root /usr/bin/sudd;chmod u+s /usr/bin/sudd)

然后修改一下poc的2个地方,然后make


先看一下漏洞的地方,在set_cmnd的这个函数,不过开始不能执行下断点,得在sudd这个elf文件加载后才行

先在hax.c的execve这里下好断点,然后运行

然后在__libc_start_main打好断点,因为要进入子进程了

然后在310行下好断点运行,到现在sudd这个elf文件已经加载进来了,但是set_cmnd并没有加载了,因为这个是在sudoers.so里面的,得等到他加载了这个动态链接库才行


然后单步(si)运行直到进入了main函数

再次在211下好断点,此时已经加载好了sudoers.so



现在就可以在set_cmnd打好断点了,继续运行了

现在就可以开始看他写入修改了什么

先看看to变量,发现有本来就只可以写入0x70这么多的数据,后面是一个unsortedbin的chunk,当然这个poc来打开启调试的这个sudo(sudd)是打不通的,因为堆布局不同,这里的目的就是看看他可以修改后面的哪些数据


看看av,可以知道他存放了4个地址,然后挨着看一下


看看第一个A后面还有什么,发现了很多005c(\\x00),这个就是\\x00,当指向这里时from就会先向后移一位(就指到了\x00),然后to写入\x00,from又会加一这样就到了005c(\\x00)后面的那个字符了,这样*from就不为空,就会继续循环,循环后会在最后那里写入空格,这也是为什么poc在创建恶意动态链接库时有个空格


好了现在我们通过开启debug的sudo看了流程,大概就是可以堆溢出向后写入数据,现在我们可以退出去调试没有开启debug的sudo了
现在修改hax.c,然后make

然后继续调试,在__libc_start_main断点之前都一样,现在没有一些符号字符串就比较难下断点了,不过通过刚才的流程可以知道,真正起作用的部分是在加载sudoers.so之后的操作,所以我们在_dl_open下好断点


一直继续运行,直到sudoers.so出现,然后finish执行完当前函数返回,然后就加载了sudoers.so



现在我们也不知道具体断点该下在哪里,不过漏洞发布的地方写了是修改的systemd这个字符串,并且将library设置为了空

经过刚刚有完整符号表的那个调试可以知道,影响修改的地方是在堆中(堆(heap)的范围通过vmmap指令看到),所以我们通过find命令来查找systemd

然后watch这2个地址后,继续运行,成功断在了想要停止的地方


然后观察寄存器,R13应该就是to的指针了,让我们在看看这部分堆块的内容


现在看看汇编,然后在计算一下偏移(也就是通过vmmap查看sudoers.so的基址,然后和这里这个地址相减)


好了,现在重新开始,目的是为了看看没有溢出前后面的原始数据,重复步骤,直到sudoers.so加载后,vmmap得到基址,加上偏移,然后下好断点,继续运行直到停在断点



停在断点后查看寄存器和堆块的初始值,可以发现没有开启debug的sudo后面不是unsortedbin


然后我们把断点下在nss_load_library继续运行,就可以发现指针指向的service_user这个结构体的地方已经被我们修改了


我用bp的一个字符串比较对比了一下修改前后的区别,这个地址是一个service_user结构体,我们看看这个结构体的定义,可以发现在ubuntu18下面修改的应该是compat的名字,对于的图中的0x55859fd2a3d0地址的值


然后看看nss_load_library函数,发现如果ni->library->lib_handle如果为空就可以触发__libc_dlopen,这个就是加载动态链接库的,配合开始的动态链接库知识就知道,只要加载了我们的恶意链接库就成功了

由于ni->library此时是空,所以会初始化ni->library,初始化的代码,发现他会先到database->library里面找,如果存在就直接返回,不存在就会初始化设置(*currentp)->lib_handle = NULL这样就满足后面的那个ni->library->lib_handle==NULL了,所以我们修改的名字只要不出现在database->library里面就可以了

然后继续运行就会看到shlib_name就为libnss_X/P0P_SH3LLZ_ .so.2,这就和poc生成的动态链接库名字一样了

退出调试运行就直接成功提权了


文章来源: http://xz.aliyun.com/t/9192
如有侵权请联系:admin#unsafe.sh