在Ubuntu系统中,sudo提权漏洞的一个常见且易被忽视的路径是环境变量继承问题,特别是通过env_keep配置不当导致的。管理员或用户通过sudo执行命令时,默认会清理大部分环境变量以确保安全,但/etc/sudoers文件中的env_keep选项允许特定环境变量被保留并传递给特权进程。攻击者可以利用被保留的、未受严格控制的环境变量(如LD_PRELOAD、LD_LIBRARY_PATH、PS1、甚至PATH)来劫持sudo执行的命令,加载恶意共享库,或执行非预期路径下的程序,从而实现权限提升。
理解sudo的环境清理机制与env_keep的作用
默认情况下,sudo会启动一个干净、最小化的执行环境,这被称为“安全策略”。它会重置环境变量,仅保留少数几个被认为安全的变量(如TERM、PATH在某些配置下)。然而,为了满足某些应用程序的特殊需求(例如需要特定的库路径或配置),sudo允许通过env_keep指令在/etc/sudoers文件中显式保留一批环境变量。例如,一行配置可能如下:Defaults env_keep += "LD_LIBRARY_PATH MY_CUSTOM_VAR"。问题在于,过度宽松或错误的保留策略会将危险变量引入特权环境。
危险的env_keep变量与经典提权手法
最典型的危险变量是LD_PRELOAD和LD_LIBRARY_PATH。它们控制着动态链接器加载共享库的行为。如果攻击者能够控制这些变量的值(例如,在用户shell中将其设置为指向恶意编写的共享库),并且当用户通过sudo执行一个动态链接的程序时,这些变量被env_keep保留了下来,那么攻击者的恶意库就会被以root权限加载和执行。
另一个常见目标是PATH变量。如果sudoers中配置了env_keep += "PATH",并且用户本地的PATH目录(如用户家目录)优先级高于系统目录(如/usr/bin),那么当用户执行sudo some_command时,系统可能会优先运行用户目录下名为some_command的恶意脚本,而非系统二进制文件。此外,像PS1(Shell提示符)这样的变量也可能被利用,因为它可能在sudo环境下触发某些解析或执行行为。
实战演示:利用LD_PRELOAD进行sudo提权
假设一个配置错误的sudoers文件保留了LD_PRELOAD,并且用户被允许以root身份运行某个不需要密码的命令(例如sudo /usr/bin/vi)。攻击者可以按以下步骤操作:
// 1. 创建一个简单的恶意共享库
// evil.c
#include#include#include__attribute__((constructor)) void init() {
setuid(0);
setgid(0);
system("/bin/bash -p");
}
// 2. 编译为共享库
gcc -fPIC -shared -o evil.so evil.c -nostartfiles
// 3. 设置LD_PRELOAD环境变量
export LD_PRELOAD=/tmp/evil.so
// 4. 利用sudo执行一个允许的命令(如vi)
sudo /usr/bin/vi当vi以root权限启动时,动态链接器会加载LD_PRELOAD指定的evil.so,其中的构造函数(init)会立即执行,弹出一个具有root权限的shell。
如何检测和审计不安全的env_keep配置
作为系统管理员,首要任务是审计/etc/sudoers文件及其包含的目录(/etc/sudoers.d/*)。使用命令sudo visudo -c检查语法后,重点查看所有以“Defaults env_keep”开头的行。特别警惕其中是否包含以下高危变量:
LD_PRELOAD
LD_LIBRARY_PATH
LD_AUDIT
LD_DEBUG
PS1, PS2, PS3, PS4
PATH(除非有严格理由且配合secure_path使用)
SHELL
PYTHONPATH, PERLLIB等解释器路径变量
同时,检查每个用户或用户组的sudo权限规则,确认是否有不必要的环境变量被保留。可以使用自动化脚本或安全基线检查工具(如Lynis)进行扫描。
加固策略:清理与最小化env_keep配置
最安全的做法是遵循最小权限原则,只保留绝对必要的环境变量。
1. 使用secure_path替代保留PATH:
不要使用env_keep += "PATH"。相反,在/etc/sudoers中使用Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"来定义一个受信任的、安全的PATH路径,完全忽略用户环境的PATH。
2. 移除高危变量:
编辑/etc/sudoers(务必使用visudo),删除或注释掉包含LD_*系列变量、PS*系列变量的env_keep行。除非运行特定应用程序(如某些科学计算或商业软件)有强制要求,否则这些变量都不应被保留。
3. 使用白名单而非黑名单思维:
考虑将默认的env_keep策略重置为空,然后仅添加必需的变量。例如:
Defaults env_reset Defaults env_keep = "LANG LANGUAGE LC_* COLORTERM"
这里只保留了本地化相关的变量,这对于大多数系统来说已经足够。
4. 对特定命令禁用环境重置(谨慎使用):
在某些极少数情况下,程序需要完整环境。可以为单个命令配置SETENV标签,但这会带来风险。语法示例:username ALL=(ALL) SETENV: /path/to/command。使用此选项时必须极其小心,并确保命令本身是可信的。
深度防御:结合其他sudo安全最佳实践
仅仅清理env_keep还不够,需要结合多层防御。
1. 启用sudo日志:
确保syslog或journald记录所有sudo命令。在/etc/sudoers中添加Defaults logfile=/var/log/sudo.log和Defaults log_input, log_output可以记录详细的输入输出,便于事后审计和攻击溯源。
2. 限制命令范围:
避免赋予用户广泛的ALL权限。尽量将sudo权限限定到具体的、必要的命令路径上,并使用绝对路径。例如:user1 ALL=(root) /usr/bin/systemctl restart nginx。
3. 使用sudo时间戳类型:
默认的timestamp_type是tty,这要求在一个真实的终端会话中。可以将其改为global或ppid来调整超时行为,但需注意这不会直接阻止env_keep攻击。
4. 定期更新与漏洞扫描:
保持Ubuntu系统和sudo包处于最新状态,以获取安全补丁。同时,定期使用如chkrootkit、rkhunter等工具检查系统是否已被植入恶意共享库或后门。
总结:将env_keep管理纳入安全基线
Ubuntu系统中sudo的env_keep配置是一个关键的安全杠杆。默认配置相对安全,但一旦被修改,就可能引入严重的提权路径。作为管理员,你应该:
(1) 定期审计env_keep列表,清除所有非必要的高危变量;
(2) 强制使用secure_path;
(3) 结合详细的日志记录和最小命令权限原则。通过主动管理和持续监控环境变量的继承,可以极大地压缩攻击者利用sudo进行横向移动和权限提升的攻击面,筑牢服务器安全的一道重要防线。
