检查shellshock的命令说明


Answers:


45

此答案是Matthew Miller 在Fedora杂志上发表原始文章的衍生版本,该文章获得了Creative Commons Attribution-Share Alike 4.0许可。

让我解释:

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

这将在易受攻击的系统上打印“ OOPS”,但如果修补了bash,则将以静默方式退出。

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

这将在易受攻击的系统上打印“ OOPS”,但“this is a test”如果修补了bash ,则会进行打印。

您可能已经听说过,它与环境变量有关。但是,为什么环境变量中的代码被执行?好吧,这不应该是,但是,由于我很想为自己的功能称它为太聪明了,所以存在一些缺陷的余地。Bash是您在终端提示符下看到的内容,但它也是一种脚本语言,并且具有定义功能的能力。您可以这样操作:

$ Ubuntu()  { echo "Ubuntu is awesome."; }

然后您有一个新命令。请记住,echo这里实际上尚未运行;它只是保存为当我们运行新命令时将发生的情况。一分钟之内,这很重要!

$ Ubuntu
 Ubuntu is awesome.

有用!但是,可以说,由于某种原因,我们需要执行bash的新实例作为子进程,并希望在该实例下运行令人敬畏的新命令。该语句bash -c somecommand正是这样做的:在新的shell中运行给定的命令:

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

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

$ export -f Ubuntu
$ bash -c Ubuntu
  Ubuntu is awesome.

一切都很好,除了实现这一点的机制有点儿狡猾。基本上,由于在环境变量中执行功能没有Linux / Unix魔术,因此export函数实际上只是创建一个包含函数定义的常规环境变量。然后,当第二个Shell读取“传入”环境并遇到内容类似于函数的变量时,它将对其进行求值。

从理论上讲,这是绝对安全的,因为记住,定义一个函数实际上并不会执行它。除了-这就是我们在这里的原因-代码中存在一个错误,当到达函数定义的结尾时,评估没有停止。它一直在继续。

如果使用合法创建存储在环境变量中的函数,那将永远不会发生export -f。但是,为什么要合法呢?攻击者只能组成任何旧的环境变量,并且如果它看起来像一个函数,则新的bash shell会认为它是!

因此,在我们的第一个示例中:

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

env命令运行具有给定变量集的命令。在这种情况下,我们将设置x为类似于函数的内容。该函数只是单个:,实际上是一个简单的命令,定义为不执行任何操作。但是,在semi-colon发出信号表示函数定义结束之后,出现了一条echo命令。那不应该在那里,但是没有什么可以阻止我们这样做。

然后,在该新环境中运行的命令是一个新的bash shell,再次使用“ echo this is a test”或“不执行任何操作:”命令,此后它将完全无害地退出。

但是-哎呀!当新的shell启动并读取环境时,它进入x变量,并且由于它看起来像一个函数,因此对其进行求值。函数定义被无害加载-然后我们的恶意负载也被触发。因此,如果您在易受攻击的系统上运行上述命令,则会被打“OOPS”回。或者,攻击者可能会做得比仅仅打印事情还要糟糕得多。


1
Muchas gracias很好地解释了为什么这样做。
Doug R.

2
请注意,这env不是必需的。使用不带以下命令的命令,您可以获得相同的结果(通过/失败取决于Bash是否已更新)x='() { :;}; echo OOPS' bash -c "echo this is a test"。这是因为在命令前加上变量分配会将该变量及其值传递到命令的bash -c "..."环境中(在这种情况下)。
暂停,直到另行通知。

1
...但是在某些较新的补丁程序中可能有必要。事情在不断变化。
暂停,直到另行通知。

4
@DennisWilliamson是否env需要运行,取决于运行测试的外壳,而不是被测试的外壳。(这些可能是相同的。即使到那时,我们仍在测试bash如何处理其自身的环境。)Bourne样式的shell接受NAME=value command语法;C样式的外壳(例如cshtcsh)没有。因此,该测试具有更高的可移植性env(以有时会混淆其工作方式为代价)。
伊利亚·卡根

2

未修补的版本中,bash它将导出的函数定义存储为环境变量。

将函数存储x

$ x() { bar; }
$ export -f x

并检查其定义为

$ env | grep -A1 x
x=() {  bar
}

因此,可以通过定义自己的环境变量来利用这一点,并将其解释为函数定义。例如env x='() { :;}'将被视为

x() { :;
}

检查shellshock的命令有什么作用,

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

来自man env

  1. env -在修改后的环境中运行程序。

  2. :除了退出状态外什么都不做0。看更多

  3. 当新的未修补bash实例启动为时bash -c "echo this is a test",将制作的环境变量视为函数并加载。相应地得到输出

    脆弱的
    这是一个测验

注意:函数定义外的回显在bash启动期间被意外执行。函数定义只是进行评估和利用的一步,函数定义本身和使用的环境变量是任意的。外壳程序查看环境变量,看到x,它看起来像满足其对函数定义的了解的约束,并评估行,无意中还执行了echo(可能是任何命令,无论是否恶意) 。另请参见


我仍然发现,如果已定义的任何bash函数(如果导出的话)都将在补丁版本的bash的子外壳中进行评估。看到这个:chayan @ chayan:〜/ testr $ test(){echo“ anything”; }; 出口-f测试; bash -c test Ouput:任何东西所以您的答案没有正确定向。我认为,kasiyA将错误解释为将变量扩展到其定义之外是正确的。
heemayl 2014年

@heemayl这种行为是自然的。但是,如果尝试,env test='() { echo "anything"; }' bash -c "echo otherthing"您将在输出中看到otherthing。修补程序中已纠正该问题。如果我不清楚,请放心。
souravc

请再说一次。在您的最后一条评论中,我们基本上是在定义函数,然后告诉bash执行echo。在此示例中,我们没有在bash中调用该函数。在修补的和未修补的bash中都不会有相同的输出吗?我的想法是,该错误基本上是因为bash正在执行放置在函数定义之后的命令,而该函数以后再也不会在任何地方被调用,例如,如果执行此操作,则env test ='(){echo“ any” }; echo“ foo”'bash -c“ echo otherthing”。请在这种情况下澄清我。
heemayl 2014年

@heemayl我已经编辑了答案,希望现在清楚了。您在我上次评论的示例中是正确的,我们尚未调用该函数。但是不同之处在于,unpatched bash您可以按定义的方式调用函数,而在修补程序中bash,定义本身不存在。
souravc 2014年

@heemayl:不,那是不正确的。已修补的Bash仍会将函数定义传递到孩子的环境中。补丁的区别在于,echo vulnerable不执行遵循函数定义()的代码。请注意,在最新的修补程序中,传递的函数必须具有特定的前缀(env 'BASH_FUNC_x()'='() { :;}; echo vulnerable' bash -c "echo this is a test")。一些更新的补丁可能会%%代替第一个()
暂停,直到另行通知。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.