![如何绕过名为“command”、“builtin”和“unset”的 bash 函数?](https://linux22.com/image/121442/%E5%A6%82%E4%BD%95%E7%BB%95%E8%BF%87%E5%90%8D%E4%B8%BA%E2%80%9Ccommand%E2%80%9D%E3%80%81%E2%80%9Cbuiltin%E2%80%9D%E5%92%8C%E2%80%9Cunset%E2%80%9D%E7%9A%84%20bash%20%E5%87%BD%E6%95%B0%EF%BC%9F.png)
我知道可以通过引用命令本身来绕过别名。
但是,似乎如果内置命令被具有相同名称的函数“隐藏”,则无法执行底层内置命令,除非......通过使用内置命令。如果你能做到的话。
引用 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的解决方案,它利用了unset
POSIX 模式中的处理方式。 那个解决方案是您在现实生活中可能实际使用的东西。
由于无法实际保证您的 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,因为EOF
in的任何部分都没有<<EOF
被引用。
我在 Ubuntu 16.04 LTS 上的 Bash 4.3.48(1)-release 上对此进行了测试,并且它有效。您需要gdb
这个,尽管它可以适应其他调试器。作为阿尼什萨尼 评论,管道 from 的cat
目的是避免死锁,即向其提供输入的进程gdb
已gdb
停止。我相信它实现了这个目标,因为在两个或多个命令的管道中,Bash 在子 shell 中运行每个命令。但我不确定这是否是最可靠的方法。然而,最终并不能真正保证这种方法有效,因为 Bash 假设只读变量和函数不会改变是完全合理的。在实践中,我的猜测是这实际上总是有效的。
要按照所写的方式使用此技术,您需要sudo
安装并且需要能够sudo
root。当然,您可以将其替换为其他特权提升方法。根据您运行的操作系统及其配置方式,您可能可以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_CORRECT
为bash
进入 posix 模式,然后您就拥有了所有特殊的内置命令:
$ POSIXLY_CORRECT=1