Shellshock:它实际上是如何工作的?

到目前为止,您可能已经看到了这种神奇的咒语或变体,作为 CVE-2014-6271 漏洞的快速测试,被称为“Shellshock”,因为在这个心脏出血后的世界中,显然所有的安全漏洞都会有可爱的过度戏剧化的名字。

env x='() { :;}; echo OOPS' bash -c :

这将在易受攻击的系统上打印“OOPS”,但如果出现以下情况则静默退出 bash 已修补。

您可能听说过它与环境变量有关。 但是,为什么环境变量中的代码会被执行? 嗯,它不应该是——但是,因为我很想把它称为有点太聪明的功能,所以有一些缺陷的空间。 Bash 是您看到的终端提示符,但它也是一种脚本语言,并且具有定义函数的能力。 你这样做:

$ yayfedora() { echo "Fedora is awesome."; }

然后你有一个新的命令。 请记住,这里的“回声”实际上还没有运行; 它只是保存为我们运行新命令时会发生的情况。 这将在一分钟内很重要!

$ yayfedora 
Fedora is awesome.

有用! 但是,比方说,出于某种原因,我需要执行一个新的实例 bash,作为一个子进程,并想在它下面运行我很棒的新命令。 该声明 bash -c somecommand 正是这样做的:在新的 shell 中运行给定的命令:

$ bash -c yayfedora
bash: yayfedora: command not found

哦。 伤心。 孩子没有继承函数定义。 但是,它确实继承了环境——从 shell 导出的键值对的集合。 (这是一个完整的概念;如果您对此不熟悉,请暂时相信我。)而且,事实证明, bash 也可以导出函数。 所以:

$ export -f yayfedora
$ bash -c yayfedora
Fedora is awesome.

这一切都很好——除了实现这一点的机制有点狡猾。 基本上,由于在环境变量中执行函数没有 Linux/Unix 魔法,因此导出函数实际上只是创建了一个包含函数定义的常规环境变量。 然后,当第二个 shell 读取“传入”环境并遇到一个内容看起来像函数的变量时,它会评估它。

理论上,这是完全安全的,因为请记住,定义一个函数并不会真正执行它。 除了——这就是我们在这里的原因——代码中有一个错误,当到达函数定义的末尾时,评估没有停止。 它只是继续前进。

如果使用 export -f 合法地生成存储在环境变量中的函数,则永远不会发生这种情况。 但是,为什么要合法? 攻击者可以随便编造任何旧的环境变量,如果它看起来像一个函数,就新建一个 bash 贝壳会认为是!

所以,在我们的第一个 example:

env x='() { :;}; echo OOPS' bash -c :

“env”命令运行具有给定变量集的命令。 在这种情况下,我们将“x”设置为看起来像函数的东西。 该函数只是一个“:”,实际上是一个简单的命令,定义为什么都不做。 但是,在表示函数定义结束的分号之后,有一个 echo 命令。 那不应该在那里,但没有什么能阻止我们这样做。

然后,在这个新环境下运行的命令是一个新的 bash shell,再次使用“什么都不做:”命令,之后它将完全无害地退出。

但是——哎呀! 当新的 shell 启动并读取环境时,它会到达“x”变量,因为它看起来像一个函数,所以它会评估它。 函数定义被无害地加载——然后我们的恶意负载也会被触发。 因此,如果您在易受攻击的系统上运行上述程序,您将得到“OOPS”打印回给您。 或者,攻击者可以做的不仅仅是打印东西。

我们的更新做了几件事。

首先,它修复了解释不会在函数定义结束时停止的缺陷(包括更多的完整性检查)。

其次,它总是以字符串 BASH_FUNC_ 为保存导出函数的神奇环境变量添加前缀,并以 () 为后缀。 这意味着您不能随便设置任何环境变量——就像那些作为 CGI 接口的一部分传递给 Web 服务器的环境变量! — 看起来像一个函数并通过任何攻击 bash 稍后出现的子shell。

那里还有很多其他的小清理——安全人员 Fedora,在 Red Hat 和世界各地,这几天肯定很忙。 感谢大家的辛勤工作,感谢大家 Fedora的 QA 和发布工程团队非常棒,他们迅速采取行动,确保这些更新能够快速、安全地送达您手中。

Shell 基于“Shell”——Guillaume Kurkdjian 的 CC-BY 3.0——https://thenounproject.com/term/shell/40512/