错误
CVE-2014-7169 是 bash 解析器中的一个错误。Bash 的解析器使用一个变量eol_ungetc_lookahead
来消除跨行的字符。该变量没有从reset_parser
函数中正确重置,例如在某些语法错误时调用。使用该错误,可以将一个字符注入到下一个 bash 输入行的开头。
因此,测试代码使用(a)=
or强制语法错误,function a a
添加重定向字符以添加到下一行>
,并添加行继续\
,这导致测试代码的任一版本:
() { (a)=>\
() { function a a>\
执行 bash 时,它会处理来自环境的变量,发现该变量X
是导出函数,并对其求值以导入函数。但是评估失败并出现解析错误,将>
字符留在eol_ungetc_lookahead
变量中。然后,在解析命令参数时echo date
,它会在>
字符前面加上>echo date
,然后运行date
重定向到名为echo
.
它与上一个错误的关系
上面的 bug 显然与原来的 shellshock bug 有很大的不同。其实有几个问题:
- Bash 完全评估一个看起来像导出函数的变量(以四个字符开头
() {
)。CVE-2014-6271。
- 在某些情况下,可以将字符注入到 ungetc 变量中,该变量将被添加到下一个输入行。CVE-2014-7169。
- Bash 允许将每个环境变量视为导出函数,只要它以四个字符开头
() {
。CVE-2014-6271、CVE-2014-7169、所有其他在 bash 解析器中触发错误的 CVE。
- here-doc 重定向的堆栈有限,并且没有检查溢出。CVE-2014-7186,导致内存损坏,并且可能被用于任意代码执行。
- 嵌套控制结构 (
select
/ for
/ while
) 的堆栈有限,带有溢出检查。该堆栈仍然损坏。CVE-2014-7187。
修复
- 第一个补丁将 bash 限制为在每个看起来像导出函数的变量中评估单个函数定义。
- 第二个补丁正确重置
eol_ungetc_lookahead
为reset_parser
。
- 第三个补丁改变了函数的导出方式:现在它们以名为
BASH_FUNC_functionname%%
.
攻击面
这里最大的问题是每个环境变量都可以用作攻击的载体。通常,攻击者无法控制任意环境变量,否则已经存在其他已知攻击(想想LD_PRELOAD
, PATH
, IFS
, ...)。
sudo
不受影响,因为它从环境中剥离了导出的 bash 函数,正如Gilles 在 security.SE中提到的那样。
ssh
被影响。典型的 sshd 安装只允许按照AcceptEnv in 中的配置导出一组有限的环境变量sshd_config
,例如:LANG
和LC_*
. 即使采用这种激进的白名单方法,在 shellshock 中,任何变量都可能成为攻击媒介。
不仅每个环境变量都是潜在的攻击媒介,而且还暴露了超过 6000 行的解析器。
重新吸取的教训
system
, popen
, 和其他有潜在危险。您不仅应该注意它们的参数:即使参数在编译时固定,环境也是一个潜在的攻击向量。最好使用fork()/execve()
干净的环境(但至少将环境限制为列入白名单的变量,最好对其值进行健全性检查)。请记住,一个质量好的系统做它应该做的事情,而一个安全的系统做它应该做的事情,仅此而已。调用一个成熟的 shell 会使做任何事情变得更加困难。
复杂性是安全的敌人。这些天来,您可以很容易地找到推荐更简单 shell 的人。大多数 shell 完全不支持导出函数,因此不受 shellshock 的影响。相反,bash 多年来获得了许多安全功能(您需要调用它-p
以避免它在启动时放弃特权,它会清理 IFS,...),所以不要假设我提倡切换 shell,这是更多的一般建议。
David Wheeler 古老的“Linux 和 UNIX 安全编程 HOWTO”章节中关于环境变量的一些节选仍然值得重读。
§5.2.3 ¶1:
对于安全的 setuid/setgid 程序,应仔细提取需要作为输入(如果有)的环境变量的简短列表。然后应该擦除整个环境,然后将一小组必要的环境变量重置为安全值。如果您调用从属程序,确实没有更好的方法。没有列出“所有危险值”的实用方法。
§5.2.3 ¶6:
如果您确实需要用户提供的值,请先检查这些值(以确保这些值与合法值的模式匹配并且它们在某个合理的最大长度内)。