Answers:
此答案是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”
回。或者,攻击者可能会做得比仅仅打印事情还要糟糕得多。
env
不是必需的。使用不带以下命令的命令,您可以获得相同的结果(通过/失败取决于Bash是否已更新)x='() { :;}; echo OOPS' bash -c "echo this is a test"
。这是因为在命令前加上变量分配会将该变量及其值传递到命令的bash -c "..."
环境中(在这种情况下)。
在未修补的版本中,bash
它将导出的函数定义存储为环境变量。
将函数存储x
为
$ x() { bar; }
$ export -f x
并检查其定义为
$ env | grep -A1 x
x=() { bar
}
因此,可以通过定义自己的环境变量来利用这一点,并将其解释为函数定义。例如env x='() { :;}'
将被视为
x() { :;
}
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
来自man env
,
env
-在修改后的环境中运行程序。
:
除了退出状态外什么都不做0
。看更多
当新的未修补bash实例启动为时bash -c "echo this is a test"
,将制作的环境变量视为函数并加载。相应地得到输出
脆弱的 这是一个测验
注意:函数定义外的回显在bash启动期间被意外执行。函数定义只是进行评估和利用的一步,函数定义本身和使用的环境变量是任意的。外壳程序查看环境变量,看到x,它看起来像满足其对函数定义的了解的约束,并评估行,无意中还执行了echo(可能是任何命令,无论是否恶意) 。另请参见本
env test='() { echo "anything"; }' bash -c "echo otherthing"
您将在输出中看到otherthing
。修补程序中已纠正该问题。如果我不清楚,请放心。
unpatched bash
您可以按定义的方式调用函数,而在修补程序中bash
,定义本身不存在。
echo vulnerable
不执行遵循函数定义()的代码。请注意,在最新的修补程序中,传递的函数必须具有特定的前缀(env 'BASH_FUNC_x()'='() { :;}; echo vulnerable' bash -c "echo this is a test"
)。一些更新的补丁可能会%%
代替第一个()
。