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

fal*_*tro 20 security bash

有一个类似的问题涉及“包装”场景,例如您想cd用调用 builtin 的命令替换cd

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

考虑这个

cd() { echo "muahaha"; }
export -f cd
Run Code Online (Sandbox Code Playgroud)

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

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

builtin() { "$@"; }
command() { "$@"; }
type() { echo "$1 is a shell builtin"; }
Run Code Online (Sandbox Code Playgroud)

将产生以下结果:

$ type cd
cd is a shell builtin
$ cd x
muahaha
$ builtin cd x
muahaha
$ command cd x
muahaha
Run Code Online (Sandbox Code Playgroud)

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

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

mr.*_*tic 16

Olivier D几乎是正确的,但您必须POSIXLY_CORRECT=1在运行之前进行设置unset。POSIX 有一个Special Built-ins的概念,而bash 支持这个unset就是这样一种内置。搜索SPECIAL_BUILTINbuiltins/*.c在bash的源列表,它包括setunsetexportevalsource

$ unset() { echo muahaha-unset; }
$ unset unset
muahaha-unset
$ POSIXLY_CORRECT=1
$ unset unset
Run Code Online (Sandbox Code Playgroud)

流氓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 "*" )
Run Code Online (Sandbox Code Playgroud)

( while, do,done[[是保留字,不需要预防措施。) 请注意,我们使用unset -f的是确保取消设置函数,尽管变量和函数共享相同的命名空间,但两者可能同时存在(感谢 Etan Reisner)在这种情况下取消设置两次也可以解决问题。您可以将函数标记为只读,bash 不会阻止您取消设置只读函数直到并包括 bash-4.2,bash-4.3 确实会阻止您,但它在POSIXLY_CORRECT设置时仍然尊重特殊的内置函数。

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

\help -s "*" | while IFS=": " read cmd junk; do echo \\unset -f $cmd; done
Run Code Online (Sandbox Code Playgroud)

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


其他注意事项:

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


Gil*_*il' 6

我意识到如果有人控制了你的环境,你可能无论如何都被搞砸了

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

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

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