CVE-2019-18634
复现下sudo的一个cve
搭建环境
ubuntu 16.04 vmware 虚拟机
下载sudo-1.8.25
源码并编译
1 | wget https://www.sudo.ws/dist/sudo-1.8.25.tar.gz |
编译好的程序会在build
目录下的bin
目录里
漏洞分析
要复现漏洞的话,要先开启sudo的pwfeedback
选项,这个选项就是sudo要求输入密码的时候,输进去的密码会有*
显示,算是一种反馈吧:
1 | ruan@ubuntu ~/cve/cve-2019-18634/build/bin ./sudo ls |
开启的方法是在/etc/sudoers
文件中添加一行Defaults pwfeedback
poc
该poc 适用 sudo 1.8.25p1
以下的版本 :
1 | perl -e 'print(("A" x 100 . "\x{00}") x 50)' | sudo -S id |
程序奔溃:
1 | ✘ ruan@ubuntu ~/cve/cve-2019-18634/build/bin perl -e 'print(("A" x 100 . "\x{00}") x 50)' | ./sudo -S id |
调试
参考安全客文章里的调试代码:
1 | import sys,os |
./poc
的话就是perl -e 'print(("A" x 100 . "\x{00}") x 50)' > ./poc
生成的
我们在第一个pause()的时候用gdb attach上去,sudo gdb attach pid
,然后跑起来,gdb会断在程序奔溃处:
出问题的是tgetpass.c
文件里的getln
函数
漏洞代码分析
getln函数源码:
1 | static char * |
出问题的就是在:
1 | if (feedback) { //我们开启了pwfeedback选项,所以会进入这里 |
sudo_term_kill
的值可以调试得知:
1 | pwndbg> p sudo_term_kill |
因为我们是通过管道传进来的字符串,但是管道是单向的,所以write(fd, "\b \b", 3)
会返回-1,于是直接break
出while (cp > buf)
的循环,又对left
进行了赋值,但是此时的cp
并没有回到buf
的头部,所以造成了溢出
在查看下buf是哪里的变量,从奔溃的栈布局可以知道是tgetpass
函数调用了getln
,源码里对应:
1 | if (timeout > 0) |
在向上看看,能找到对buf的定义:
1 | char * |
1 |
buf是static变量,说明它是存放在数据区的:
1 | pwndbg> p buf |
用ida打开我们编译的sudo程序,查看下buf变量下面都有些啥全局的变量:
这个好像用不同版本的gcc编译会有一些差别
可以看到的是,buf底下有user_details
结构体,还有一个tgetpass_flags
,先来看下这个tgetpass_flags
tgetpass
函数里有一段:
1 | /* If using a helper program to get the password, run it instead. */ |
如果设置了TGP_ASKPASS
的flag,那程序就会fork出一个子进程去执行那个helper program
而sudo_askpass
函数里就用到了user_details
这个结构:
1 | child = sudo_debug_fork(); |
子进程的权限通过user_details.uid
和user_details.gid
来设置,这两个值我们是可以通过这个漏洞改写掉的,也就是说我们可以通过这里用root权限来运行程序
思路
sudo有3次机会给我们输入密码
设置环境变量
SUDO_ASKPASS
指定外部程序第一次用溢出把
user_details
覆盖掉,顺便把tgetpass_flags
也给覆盖为TGP_ASKPASS
,启用askpass
功能第二次输入密码的时候就会去执行外部的程序
踩坑
一开始我是从stdin给sudo输入数据,第一次溢出是可以的,第二次就不知道为什么不行,一只卡在read那里,无论输入什么都没有反应,而且从stdin输入数据的时候,sudo_term_kill
一直为\x00
,这样是没办法一次写入多个\x00
只能写一个\x00
字节进去。
后来还是选择从伪终端里输入数据,这样的话:
1 | pwndbg> p/x sudo_term_kill |
我们就可以一次写入多个\x00
了
还有一个坑就是,我指定了SUDO_ASKPASS
为shell脚本,但是执行的时候老是说找不到文件,后来换成程序就可以了。。。
最终:
1 | ruan@ubuntu ~/cve/cve-2019-18634/build/bin python debug.py DEBUG |
test程序是一个反弹shell的程序
exp:
1 | import sys,os |