如何在bash中明确和安全地强制使用内置命令


19

还有一个类似的问题涉及“包装”方案,您想在其中替换为例如cd调用buildin的命令cd

但是,鉴于shellshock等人,并且知道bash从环境中导入功能,所以我进行了一些测试,但找不到cd从脚本中安全调用内置函数的方法。

考虑一下

cd() { echo "muahaha"; }
export -f cd

在此环境中使用调用的所有脚本cd都会中断(请考虑类似的效果cd dir && rm -rf .)。

有用于检查命令类型的命令(通常称为type)和用于执行内置版本而不是功能(builtincommand)的命令。但是,瞧,这些也可以使用函数覆盖

builtin() { "$@"; }
command() { "$@"; }
type() { echo "$1 is a shell builtin"; }

将产生以下内容:

$ type cd
cd is a shell builtin
$ cd x
muahaha
$ builtin cd x
muahaha
$ command cd x
muahaha

有什么方法可以安全地强制bash使用内置命令,或者至少在不清除整个环境的情况下检测到该命令不是内置命令?

我知道,如果有人控制了您的环境,您可能还是很烦,但是至少对于别名,您可以选择不插入别名来调用\它。


您可以使用env命令之前运行脚本,如下所示:env -i <SCRIPT.sh>
Arkadiusz Drabczyk 2015年

仅当env未将其也重新定义为函数时。这太可怕了。我首先想到特殊字符会有所帮助-使用完整路径调用,包括/.用于来源等。但是这些也可以用于函数名称!您可以重新定义所需的任何函数,但是很难回到调用原始命令的位置。
Orion

1
是的,但是如果您要在受感染的计算机上运行任何脚本,无论如何都会被搞砸。或者,#/bin/sh如果这不是默认的交互式外壳程序,请编写脚本。
Arkadiusz Drabczyk 2015年

Answers:


16

Olivier D 几乎是正确的,但是您必须POSIXLY_CORRECT=1在运行之前进行设置unset。POSIX具有“ 特殊内置”的概念,bash支持此功能unset是这样一个内置的。搜索SPECIAL_BUILTINbuiltins/*.c在bash的源列表,它包括setunsetexportevalsource

$ unset() { echo muahaha-unset; }
$ unset unset
muahaha-unset
$ POSIXLY_CORRECT=1
$ unset unset

unset现在,如果您未设置command,则流氓已从环境中删除typebuiltin那么您应该可以继续进行操作,但是unset POSIXLY_CORRECT如果您依赖于非POSIX行为或高级bash功能。

并不地址别名,因此,您必须使用\unset,以确保它在交互shell(或总是在情况下expand_aliases有效)。

对于偏执狂,这应该可以解决所有问题,我认为:

POSIXLY_CORRECT=1
\unset -f help read unset
\unset POSIXLY_CORRECT
re='^([a-z:.\[]+):' # =~ is troublesome to escape
while \read cmd; do 
    [[ "$cmd" =~ $re ]] && \unset -f ${BASH_REMATCH[1]}; 
done < <( \help -s "*" )

whiledodone[[是保留字,不需要注意事项。)注意我们使用unset -f就一定要取消设置的功能,尽管变量和函数共享相同的命名空间很可能都存在,在这种情况下,同时(由于伊坦赖斯纳)两次不动也可以解决问题。您可以将一个函数标记为只读,bash不会阻止您将只读函数设置为bash-4.2(包括bash-4.2),bash-4.3可以阻止您使用它,但是设置后它仍然会保留特殊的内置POSIXLY_CORRECT函数。

只读POSIXLY_CORRECT不是一个真正的问题,这不是布尔值或标志,它的存在会启用POSIX模式,因此,如果它以只读形式存在,则即使值为空或0,也可以依靠POSIX功能。您只需要未设置有问题的功能的方式与上述方法不同,可能是通过剪切和粘贴操作:

\help -s "*" | while IFS=": " read cmd junk; do echo \\unset -f $cmd; done

(并忽略任何错误)或从事其他一些技巧工作


其他说明:

  • function是保留字,可以使用别名,但不能用函数覆盖。(混叠有function一点麻烦,因为\function 它不能绕开混叠)
  • [[]]是保留字,可以使用别名(将被忽略),但不能用函数覆盖(尽管可以如此命名函数)
  • (( 不是函数的有效名称,也不是别名

有趣,谢谢!我似乎很奇怪bash会默认保护unsetet等人不被覆盖。
falstro 2015年

这需要-f论证unset吗?否则,如果定义了与内置函数相同的变量和函数,unset则将首先选择要取消设置的变量。如果设置了任何隐藏功能,也会失败readonly吗?(尽管这是可检测的,并且可能会导致致命错误。)这也错过了[内置的功能。
Etan Reisner,2015年

1
我认为这没有\unset -f unset意义,因为它将在读取循环中完成。如果unset是函数\unset,则通常可以使用它代替内置函数,但是\unset只要POSIX模式有效,就可以安全使用。显式调用\unset -f[.以及:可能是虽然好主意,因为你的正则表达式排除。我还将添加-f\unset循环中,并将添加到循环\unset POSIXLY_CORRECT之后,而不是之前。\unalias -a(在之后\unset -f unalias)还允许一个人安全地放弃后续命令的转义。
AdrianGünter'17

1
@AdrianGünterTry:,[[ ${POSIXLY_CORRECT?non-POSIX mode shell is untrusted}x ]]这将导致非交互式脚本退出,并且如果未设置变量则显示错误,如果设置了变量(空或任何值),则继续。
mr.spuratic

1
“您必须使用\unset”。应注意,任何字符都应加引号,以避免别名扩展,而不仅仅是第一个。un"se"t尽管可读性较差,但效果也一样。“检查每个简单命令的第一个单词(如果未引用),以查看其是否具有别名。” – Bash Ref
Robin A. Meade

6

我意识到,如果有人控制了您的环境,您可能还是被搞砸了

对,就那个。如果您在未知的环境中运行脚本,那么所有事情都会出错,首先是LD_PRELOAD使Shell进程在读取脚本之前执行任意代码。试图从脚本内部防止恶意环境是徒劳的。

十多年来,Sudo一直在通过删除所有类似于bash函数定义的内容来对环境进行消毒。自从Shellshock以来,其他在不受信任的环境中运行Shell脚本的环境也纷纷效仿。

您不能在不受信任的实体设置的环境中安全地运行脚本。因此,担心函数定义没有效果。清理您的环境,并在此过程中将bash解释为函数定义的变量清除。


1
我完全不同意这一点。安全不是围绕“消毒”或“未消毒”的二元主张。这是关于消除剥削的机会。不幸的是,现代系统的复杂性意味着需要适当限制许多利用机会。许多利用机会围绕着无害的上游攻击,例如更改PATH变量,重新定义内置函数等。为单个人或程序提供执行此操作的机会,并且整个系统容易受到攻击。
Dejay Clayton 2015年

@DejayClayton除了第一句话外,我完全同意您的评论,因为我看不出它与我的回答有任何矛盾。删除利用机会有所帮助,但不能仅去除其中的一半,只要攻击中的细微调整可以使其继续工作。
吉尔(Gilles)'所以

我的观点是,您不能只是“对环境进行消毒”,然后就不再担心各种攻击媒介。有时值得“从脚本内部防御恶意环境”。即使您解决了“函数定义”问题,您仍然必须担心诸如PATH之类的问题,有人可以将名为“ cat”或“ ls”的自定义脚本放入目录,然后再调用“ cat”的任何脚本“或” ls“代替” / bin / cat“和” / bin / ls“有效地执行了利用程序。
Dejay Clayton

@DejayClayton显然,对环境进行清理无助于与环境无关的攻击媒介。(例如,调用/bin/catchroot 的脚本可能会调用任何东西。)对环境进行清理确实会使您理智PATH(假定您当然是在正确地进行)。我仍然没明白你的意思。
吉尔(Gilles)'所以

考虑到PATH是最大的利用机会之一,并且即使在安全博客中,许多可疑的PATH实践也被记录为推荐建议,并且考虑到有时已安装的应用程序会对PATH重新排序以确保此类应用程序正常工作,我看不到在脚本中进行防御性编码将不是一个好主意。您认为合理的PATH实际上可能容易受到您从未遇到过的攻击的攻击。
Dejay Clayton

1

您可以unset -f用来删除内置函数,命令和类型。


2
抱歉,请unset() { true; }注意。
falstro 2015年

1
该死的!列出定义的函数也依赖于内置函数。我放弃!
odc 2015年
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.