如何绕过名为“command”、“builtin”和“unset”的 bash 函数?

如何绕过名为“command”、“builtin”和“unset”的 bash 函数?

我知道可以通过引用命令本身来绕过别名。

但是,似乎如果内置命令被具有相同名称的函数“隐藏”,则无法执行底层内置命令,除非......通过使用内置命令。如果你能做到的话。

引用 bash 手册页(位于LESS='+/^COMMAND EXECUTION' man bash):

COMMAND EXECUTION
       After a command has been split into words, if it results  in  a  simple
       command  and  an  optional list of arguments, the following actions are
       taken.

       If the command name contains no slashes, the shell attempts  to  locate
       it.   If  there  exists a shell function by that name, that function is
       invoked as described above in FUNCTIONS.  If the name does not match  a
       function,  the shell searches for it in the list of shell builtins.  If
       a match is found, that builtin is invoked.

那么,是否可以在不启动新 shell 的情况下从以下情况中恢复?

unset() { printf 'Haha, nice try!\n%s\n' "$*";}
builtin() { printf 'Haha, nice try!\n%s\n' "$*";}
command() { printf 'Haha, nice try!\n%s\n' "$*";}

我什至没有添加readonly -f unset builtin command。如果它可以从上面恢复,请考虑这是一个额外的问题:如果所有三个函数都标记为只读,您仍然可以恢复吗?


我在 Bash 中提出了这个问题,但我也对它对其他 shell 的适用性感兴趣。

答案1

如果所有三个函数都标记为只读,您还能恢复吗?

是的,通常可以,但这并不意味着您应该这样做。

尽你所能取消设置只读变量通过附加调试器并调用,unbind_variable如下所示anishsane 对这个问题的回答,您还可以取消设置只读函数,将其名称传递给unbind_func使用调试器。

当它们不是只读的(如果确实是只读的)时,这不是一个合理的方法。在这种情况下你应该使用cuonglm的解决方案,它利用了unsetPOSIX 模式中的处理方式。 那个解决方案是您在现实生活中可能实际使用的东西。

由于无法实际保证您的 shell 在readonly使用调试器规避后会正常运行,因此我建议只要有更合理的替代方案(例如退出并重新启动您的 shell 或使用新的 shell 替换您的 shell)exec可用,就避免使用它。

话虽如此,这是阿尼什萨尼方法适应于取消设置函数而不是变量:

cat <<EOF | sudo gdb
attach $$
call unbind_func("unset")
call unbind_func("builtin")
call unbind_func("command")
detach
EOF

注意$$ 被扩展进入 shell 的进程 ID,因为EOFin的任何部分都没有<<EOF被引用。

我在 Ubuntu 16.04 LTS 上的 Bash 4.3.48(1)-release 上对此进行了测试,并且它有效。您需要gdb这个,尽管它可以适应其他调试器。作为阿尼什萨尼 评论,管道 from 的cat目的是避免死锁,即向其提供输入的进程gdbgdb停止。我相信它实现了这个目标,因为在两个或多个命令的管道中,Bash 在子 shell 中运行每个命令。但我不确定这是否是最可靠的方法。然而,最终并不能真正保证这种方法有效,因为 Bash 假设只读变量和函数不会改变是完全合理的。在实践中,我的猜测是这实际上总是有效的。

要按照所写的方式使用此技术,您需要sudo安装并且需要能够sudoroot。当然,您可以将其替换为其他特权提升方法。根据您运行的操作系统及其配置方式,您可能可以sudo完全省略并以您自己的身份运行gdb,而不是 root。例如,Linux 内核将参考 的值/proc/sys/kernel/yama/ptrace_scope,其中可以通过sysctl来设置并且可以读取或(作为 root)写入,以确定哪些进程可以调试其他进程。如果值为1,则只有进程的直接父进程(或以 root 身份运行的任何进程)可以调试它。大多数最新的 GNU/Linux 系统都将其设置为1,这就是我将其包括在内的原因sudo

Linux 内核行为的描述有些过于简单化,因为允许其他值并且可以调整ptrace_scope所需的关系。1相关文件了解完整详情。

答案2

bash处于 posix 模式时,一些内置函数被认为是特殊的,这符合 POSIX 标准。

这些特殊内置函数的一个特别之处是,它们在命令查找过程中的函数之前找到。利用这个优势,您可以尝试:

$ unset builtin
Haha, nice try!
builtin
$ set -o posix
$ unset builtin
$ builtin command -v echo
echo

set但如果被名为 的函数覆盖,它就不起作用set

$ set() { printf 'Haha, nice try!\n%s\n' "$*";}
$ set -o posix
Haha, nice try!

在这种情况下,您只需设置POSIXLY_CORRECTbash进入 posix 模式,然后您就拥有了所有特殊的内置命令:

$ POSIXLY_CORRECT=1

相关内容