这些测试在未打补丁的系统上运行:
# ls='() { echo NO; }' bash -c 'ls'
NO
* 通配符是必需的,否则 perl 将用 fork/exec 组合替换系统调用
# ls='() { echo NO; }' perl -e 'system("ls *");'
NO
但是ls不是内置的:
# type ls
ls is hashed (/bin/ls)
# type echo
echo is a shell builtin
因此,让我们尝试覆盖内置 echo (“命令”覆盖内置以避免递归):
# echo='() { command echo NO; }' bash -c 'echo YES'
NO
到目前为止,我们展示了:
- 它可以覆盖一个内置的
- 并且不需要调用脚本
- 即使第一个参数不是脚本
- 对“系统”的每次调用都是一个向量,赋予调用者特权
我认为赤裸裸的“系统”和“popen”已经烂透了,我认为这些的大多数用途都不会费心去逃避用户数据中的 shell 元字符。
当我想让 shell 生成管道或使用 shell 内置函数时,我有这个调用,但它仍然需要 fork/exec:
exec("sh", "-c", "\"$0\" \"$1\" | tee -a \"$2\"", ...);
该技术使用 bash 来引用 -c 命令的参数,$0 是第一个这样的参数,或者“--”可以用作填充符:
exec("sh", "-c", "\"$1\" \"$2\" | tee -a \"$3\"", "--", ...);
此测试在已修补的系统上运行:
$ env "BASH_FUNC_ls()=() { echo NO ; }" bash -c 'ls'
NO
清楚地表明这个问题与炮击无关。
虽然脚本或脚本使用工具的作者应该知道验证用户输入,并且应该知道验证/清理环境(因为它包含用户输入),但环境的某些部分可能需要保留,并且不能总是保留很容易看出它们可能是哪个;
XAUTHORITY、DBUS_SESSION_BUS_ADDRESS、XDG_SESSION_DESKTOP、SSH_AUTH_SOCK 是少数,其他重要的环境变量可能尚未发明
它被称为“环境”是有原因的。PATH 可能会被清理,但发出给 system() 的第一个命令可以被取代
sudoedit 手册页写道:
Running shell scripts via sudo can expose the same kernel bugs that make
setuid shell scripts unsafe on some operating systems (if your OS has a
/dev/fd/ directory, setuid shell scripts are generally safe).
但是,除非它们拒绝导入所有 bash 函数,否则对于 setuid bash shell 脚本而言,情况并非如此