Bash 软件中声名狼藉的 bug,CVE-ID 为 CVE-2014-6271 (译者注:CVE 即 Common Vulnerabilities and Exposures,这个系统为公开的信息安全漏洞提供参考信息),现在有了新的名字“ShellShock”(译者注:在英语中 ShellShock 是指弹震症,一种精神疾病,参考这里)。精心伪造的数据通过网络传到一台服务器上,直接或间接触发一个bash 脚本,就可以远程执行恶意代码。最初的bug 已经修复了,但引发了人们对Bash 的解析程序可能产生0day 漏洞的关切,随后又挖掘出了第二个漏洞 CVE-2014-7169 ,这个漏洞也在前天得到了修复。但这种漏洞的根源到底是什么?同一类型的漏洞已经全部被消灭了吗?FreeBSD 和 NetBSD 已经默认关闭了自动导入函数的功能,以应对未来可能出现的漏洞。
这个问题的发生是因为 Bash 的一个功能(不是bug 吗?),它允许在Bash 的shell 中使用环境变量来定义函数。函数的作用是把经常调用的代码封装起来,然后在其他地方复用,所有的shell 脚本语言都有这个功能。Bash 中函数的定义是这样的(大多数其他shell 也是):
function hello { echo "Hello" } hello # 调用这个函数
但是,Bash 还有一种使用环境变量来定义函数的方法,这是它独有的。如果环境变量的值以字符“(){”开头,这个变量就会被当作是一个导入函数的定义,这种定义只有在 shell 启动的时候才生效。
$ export HELLO="() { echo 'Hello'; }" $ HELLO -bash: HELLO: command not found $ bash $ HELLO Hello
因为它只会在 shell 启动的时候生效,所以大多数用来演示这个漏洞的示例代码都只有一行:
env HELLO="() { echo 'Hello'; }" bash -c HELLO
这行代码的作用跟上面的例子是一样的。(env 命令代表“先设置下面的环境变量,再运行下面的程序”,并且执行完成后当前的环境变量不受影响。实际上,直接写成 HELLO="() { echo ‘Hello’; }" bash -c HELLO 也可以。)这一行 bash 命令指定了 -c 选项是为了启动 bash 时就执行 HELLO 函数,这里必须新起一个 bash,因为只有 bash 启动的时候才会去解析函数的定义。
解析代码中最早发现的缺陷发生在 bash 解析完函数定义,执行函数的时候,所以:
env CVE_2014_6271="() { echo 'Hello';}; echo 'Goodbye'" bash -c CVE_2014_6271
在修复后的系统中,上面命令的结果应该只打印“Hello”,而有漏洞的系统还会把“Goodbye”打出来。问题出在自动导入函数的解析器越过了函数定义的结尾,接着执行后面的代码——并且由于每一个新的 bash 启动时都会触发这个漏洞,相当于任意代码都能被执行了。
(译者注:读者需要明白,执行 CVE_2014_6271 这个函数并不会导致打印 Goodbye,导入 CVE_2014_6271 这个函数才会导致打印 Goodbye。把两者放在一起有一定的误导性。)
这个问题因为两个原因被放大了:首先,Bash 是一个被广泛集成的软件,所有的系统都在运行,从 Rapsberry Pis 到手机,再到数据中心的服务器以及大型机。由于自动导入函数的功能至少从 Bash 3.0 开始就存在了,所以这个 bug 有可能在大多数系统中存在近 20 年了。其次,当我们在 Apache 服务器中使用 mod_cgi(不包括 mod_php 或 mod_python)运行脚本的时候,数据是通过环境变量来传递的,这可以算是互联网领域最古老的一些技术了。其他一些客户端也会受到影响——比如 Linux 的 DHCP 客户端——它大量运用 Bash 脚本来使修改生效,这也使黑客能通过在 DHCP 数据包中加入恶意数据来达到攻击的目的。
鉴于 Bash 是大多数 Linux 系统(以及 OSX)上默认的 shell,这个漏洞就意味着,把有害数据编入环境变量,传到服务器端,触发服务器运行脚本,就完成了攻击。举个例子,HTTP 协议的头 User-Agent 通常是通过环境变量 HTTP_USER_AGENT 来传递的,这意味使用以下命令就可以利用这个漏洞了:
curl -A "() {:;}; echo 'Game Over'}" http://example.com/some-cgi/script.cgi
(译者注:这条命令的意思是以"() {:;}; echo ‘Game Over’}"为 user agent 去下载那个脚本。)
对于不传递 user agent 的服务器来说,常常还有其他受攻击的可能——比如 Cookie,或者请求本身。
注意,这个 bug 不仅仅影响 CGI 脚本和 Apache——如果其他程序也收到并传递了有害的环境变量(比如 ssh 服务会接收 TERM 或 DISPLAY 环境变量),然后这些进程再运行一个 Bash 脚本(或通过 system() 调用来执行),同样的漏洞也会被利用。和 HTTP 不一样,ssh 一般不允许匿名请求——触发这个漏洞之前必须要登录——但是代码托管服务商们却允许匿名登录(哪怕只登录到一个权限受限的 shell),所以为了防止入侵, GitHub 更新了他们的企业级产品,Bitbucket 也更新了他们的服务器。
CVE-2014-6271 中的 bug 修复后,问题马上就解决了,大多数厂商都及时提供了修复后的 Bash 版本。面向互联网的服务器没有理由不马上修复它,因为这个漏洞会使主机完全落入别人的控制(以 Apache 所使用的用户身份)中。
但是,大家的目光已经聚焦在这个领域,新的 bug 被发现了。同时使用 Bash 的 shell 重定向功能和函数自动导入功能, CVE-2014-7169 出现了。这回导致的结果是可以随意读写远程机器上的文件,使用的手段和上次一样,只不过这次是利用了 shell 的重定向符号 < 或 >。
env CVE_2014_7169='() { (a)=>\' bash -c "echo date"; cat echo
这次解析器先停在 = 号上(由于 (a)= 不是一个有效的 Bash 表达式),但至关重要的是把 < 号留在了解析管道中。接下来的转义符\会使解析器在重定向命令之间插入一些空格(但无关紧要),最终导致了后面的命令变成了一条重定向命令:
>echo data
这是一条有效的 Bash 命令,语义上它等价于下面这种更常见的形式:
date >echo
注意,另外一个重定向符号 < 在这里也是有效的,它可以把输入重定向到文件。
所以这个 bug 也被修复了。有人担心,修复 Bash 的自动导入函数功能中的边界情况所引起的 bug,会演变成一场打地鼠游戏:一个bug 刚刚被消灭,另外一个马上又会冒出来。甚至在这些bug 被发现之前,就有人担心使用Bash 的自动导入函数功能会导致系统中那些不需要使用绝对路径来访问的标准程序被覆盖:
$ env ls="() { echo 'Game over'; }" bash -c ls Game over $ env ls="() { echo 'Game over'; }" bash -c /bin/ls Applications Desktop Documents Downloads ...
(在最近几个 patch 发布之前,我们可以通过 env /bin/ls="() { echo ‘Game over’; }" bash -c /bin/ls 来覆盖绝对路径,但在最新的集成所有补丁的版本中,这个问题已经被修复了。)
但是很多可执行程序在运行外部程序的时候并没有指定它的路径,这意味着精心设计好一个函数变量,就可以让程序做它不能做的事情:
$ touch /tmp/a /tmp/b $ env test='() { echo vulnerable >&2; }' /usr/bin/bzdiff /tmp/a /tmp/b vulnerable bzip2: Can't open input file a.bz2: No such file or directory.
(译者注:上面的例子的意思是,bzdiff 命令里面会调用 test 命令,但又没有通过绝对路径来调用它,所以 test 命令被我们自己伪造的 test 函数覆盖了。)
当然,有能力设置任何环境变量,可以使攻击者控制一切东西——修改 IFS 环境变量(过去被利用过的一个漏洞),甚至修改PATH 环境变量,会影响新启动的shell 脚本的行为,无论如何这些手段都会导致问题。但至少前面提到的SSH 和CGI 攻击中,涉及的环境变量要么是有限几个(TERM、DISPLAY 等),要么是以某个前缀(HTTP_USER_AGENT、HTTP_REFERRER)开头的。所以除了前缀名所代表的程序外,其他的程序受影响有限。
有些人预计Bash 自动导入函数的功能还存在安全漏洞,担心未来还会有bug 曝出。NetBSD 在Bash 中默认关闭了自动导入函数的功能, FreeBSD 也这么做了,转而以新增选项( --import-functions)的方式提供这个功能。虽然这种做法破坏了后向兼容,但这个时候小心为上,而且可能过不了多久,这个选项就会出现在上游代码(upstream)中了。OSX 的用户也有对应的补丁可以下载。
InfoQ 会关注事态发展,报道事态变化。
9 月 29 日更新:上面提到的缺陷已经在最新一组补丁中得到修复。除了苹果以外,所有厂商都提供了修复后的版本供下载。
查看英文原文: ShellShocked - Behind the Bug
评论