setuid bash 脚本和 binfmt_misc 凭据标志

问题描述

在问这个问题时,我不想被告知我不应该这样做或它不安全。这些问题超出了本问题的范围。

当脚本在 binfmt_misc 文件系统中使用凭据标志 (setuid) 注册时,Linux 尊重脚本上的 setgidC 位。 This 就是一个例子。我正在尝试使用相同的工具来制作可与 setuid/setgid 一起使用的 shell 脚本,特别是 bash 脚本。为此,我制作了一个使用文件扩展名匹配的 binfmt 字符串:

:pbash:E::pbash::/bin/bash:C

为了测试这一点,一个名为 test.pbash 的文件没有 shebang 行,归 root 所有,模式为 6755:

echo "Effective UID: $(id -u)"
echo "Real UID: $(id -ru)"

运行 test.pbash 输出:

Effective UID: 1000
Real UID: 1000

有效 UID 应显示为 0,因此我不得不在网上搜索有关 bash 和有效 UID 的信息。我发现 bash 会将有效 UID 更改为实际 UID,除非它在设置 -p 标志的情况下运行。这需要我更改 binfmt 并创建一个包装脚本。

包装脚本 /bin/pbash,为了以防万一,多次调用 -p

#!/bin/bash -p
set -p
exec bash -p -- "$@"

删除旧的 binfmt 并加载新的新 binfmt 字符串:

:pbash:E::pbash::/bin/pbash:C

运行 test.pbash,我得到:

Effective UID: 1000
Real UID: 1000

还是不行。我想我会重写 /bin/pbash 以输出一些信息:

#!/bin/bash -p
set -p
echo "Executing '$0' '$1'" > /tmp/log.txt
exec bash -p -- "$@"

我再次运行 test.pbash 并且 /tmp/log.txt 不存在,就好像 /bin/pbash 根本没有运行一样。我从 binfmt 字符串中删除 C 标志,再次运行 test.pbash,并获得相同的输出。改变的是 /tmp/log.txt 存在!它说:

Executing '/bin/pbash' '/bin/test.pbash'

所以当我设置了凭证标志时,我的包装器脚本甚至没有运行。为什么 bash 或 Linux 会这样?如何强制它运行包装器脚本?

解决方法

kernel doc page 有一个可能很重要的提示:

If you want to pass special arguments to your interpreter,you can
write a wrapper script for it ....

您可能会遇到以下两个问题中的一个或两个:

  • 解释器 /bin/bash -p 可能从字面上理解为寻找名为 "/bin/bash\ -p" 的文件
  • echo "Effective UID: $(id -u)"echo -n "Effective UID: " ; id -u 相同。
    • 具体来说,$(id -u) 从另一个 id 调用中调用 /bin/bash(可能没有 -p 参数)。

我没有深入到一个最小的例子,但这种组合对我有用。您或许可以将其缩减为更短的内容,但这给出了一个解决上述两个潜在问题的示例:尝试将此代码 (pbash.c) 作为解释器:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc,char **argv) {
    char **args = calloc(sizeof(char *),argc+2);
    memcpy(args+2,argv+1,(argc-1)*sizeof(char *));
    args[0] = strdup("bash");
    args[1] = strdup("-p");
    execv("/bin/bash",args);
    perror("exec failed");
}

编译安装:

$ gcc -o pbash pbash.c
$ sudo cp pbash /usr/local/bin/
$ ls -l /usr/local/bin/pbash
-rwxr-xr-x. 1 root root 24832 Jul 16 21:35 /usr/local/bin/pbash
$ sudo -s
# echo ':pbash:E::pbash::/usr/local/bin/pbash:C' > /proc/sys/fs/binfmt_misc/register
# exit
$ ls -l /proc/sys/fs/binfmt_misc/
total 0
-rw-r--r--. 1 root root 0 Jul 16 21:29 pbash
--w-------. 1 root root 0 Jul 16 21:29 register
-rw-r--r--. 1 root root 0 Jul 16 20:55 status

并准备一个 try.pbash 脚本:

echo -n "Effective UID: " ; id -u
echo -n "Real UID: " ; id -ru

并使其setuid并运行它:

$ sudo chown root.root  try.pbash
$ sudo chmod +xs try.pbash
$ ls -l try.pbash
-rwsr-sr-x. 1 root root 64 Jul 16 21:52 try.pbash
$ ./try.pbash
Effective UID: 0
Real UID: 1000

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...