如何在 bash 中显式且安全地强制使用内置命令

如何在 bash 中显式且安全地强制使用内置命令

有一个类似的问题处理“包装”场景,例如您想要替换cd为调用内置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 使用内置命令,或者至少检测到命令不是内置命令,而无需清除整个环境?

我意识到,如果有人控制你的环境,你可能会被搞砸,但至少对于别名,你可以选择通过在别名\之前插入 a 来不调用别名。

答案1

奥利维尔·D·几乎正确,但必须POSIXLY_CORRECT在运行之前设置unset。 POSIX 有一个概念特殊内置, 和bash 支持这个unset就是这样一个内置的。在 bash 源中搜索SPECIAL_BUILTINinbuiltins/*.c查找列表,其中包括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[[是保留字,不需要预防措施。不能=在别名或函数名中使用,因此设置时不需要预防措施POSIXLY_CORRECT。)请注意,我们使用unset -f来确保取消设置函数,尽管变量和函数共享同一个命名空间,两者可以同时存在(感谢 Etan Reisner),在这种情况下取​​消设置两次也可以解决问题。你将函数标记为只读,bash 不会阻止您取消设置 bash-4.2 及之前版本的只读函数,bash-4.3 确实会阻止您,但在POSIXLY_CORRECT设置时它仍然尊重特殊的内置函数。

只读POSIXLY_CORRECT不是真正的问题,这不是布尔值或标志在场启用 POSIX 模式,因此如果它作为只读存在,您可以依赖 POSIX 功能,即使该值为空或 0。您只需要以与上面不同的方式取消设置有问题的函数,也许需要进行一些剪切和粘贴:

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

(并忽略任何错误)或从事其他一些事情脚本学


其他注意事项:

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

感谢下面评论者的额外建议。

答案2

我意识到如果有人控制了你的环境你可能就完蛋了

对,就那个。如果您在未知环境中运行脚本,则各种情况都可能出错,首先是LD_PRELOAD导致 shell 进程在读取脚本之前执行任意代码。尝试从脚本内部防范敌对环境是徒劳的。

十多年来,Sudo 一直通过删除任何看起来像 bash 函数定义的内容来净化环境。自从炮弹休克,在不完全受信任的环境中运行 shell 脚本的其他环境也纷纷效仿。

您无法在不受信任的实体设置的环境中安全地运行脚本。因此担心函数定义是没有成效的。清理你的环境,并在这样做时 bash 会将变量解释为函数定义。

答案3

您可以使用该命令unset -f 删除函数builtincommandtype

相关内容