【问题标题】:setuid program doesn't work on 2.6 kernelsetuid 程序在 2.6 内核上不起作用
【发布时间】:2016-09-13 01:53:52
【问题描述】:

我无法理解为什么我的 setuid 程序看起来并没有真正提升权限,即使 id 看起来是正确的。这是在 2.6 内核上运行并失败,但在 Ubuntu 14.04 上完全按照预期工作,做同样的事情。我需要一个程序,在执行期间的某些时候需要提升权限,而最低权限是默认设置。

#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

static uid_t _orig_euid;

void save_privilege(void){
    _orig_euid = geteuid();
    printf("saved privilege: %d\n", _orig_euid);
}

void drop_privileges(void){
    if(seteuid(getuid()) == -1){
        exit(0);
    }
    printf("dropped privileges %d %d\n", getuid(), geteuid());
}

void reacquire_privileges(void){
    if(setuid(_orig_euid) == -1){
        exit(0);
    }
    printf("reacquired privileges %d %d\n", getuid(), geteuid());
}

void do_privileged(int rw){
    switch(rw){
        case 0:
            //read from driver
            system("dd if=/dev/myrandom bs=10 count=1");
        case 1:
            //write to driver
            system("dd if=/dev/zero of=/dev/myrandom");
        case 2:
            //change something in proc fs
            system("echo 3 > /proc/sys/vm/drop_caches");
        default:
            break;
    }
}

int main(int argc, char *argv[]){
    int i;

    if(argc != 2){
        printf("usage: %s testno\n", argv[0]);
        return 0;
    }

    i = atoi(argv[1]);

    save_privilege();

    do_privileged(i);

    drop_privileges();

    do_privileged(i);

    reacquire_privileges();

    do_privileged(i);

    return 0;
}

我的程序权限设置为:

ls -l
-rwsr-xr-x    1 root     root         6547 Sep 13 00:35 test

我当前的用户 ID 是:

id
uid=1000(user) gid=1000(user)

我要写入的 procfs 条目是:

ls -l /proc/sys/vm/drop_caches
-rw-r--r--    1 root     root            0 Sep 13 00:36 /proc/sys/vm/drop_caches

当我运行程序时,我得到:

./test 2
saved privilege: 0
sh: cannot create /proc/sys/vm/drop_caches: Permission denied
dropped privileges 1000 1000
sh: cannot create /proc/sys/vm/drop_caches: Permission denied
reacquired privileges 1000 0
sh: cannot create /proc/sys/vm/drop_caches: Permission denied

但是,在 Ubuntu 14.04 上运行相同的程序可以正常工作 - 只有当权限被删除时,它才无法修改 procfs 条目。

这是一个 strace(./test_perm 与 ./test 相同)。

$ strace ./test_perm 2
execve("./test_perm", ["./test_perm", "2"], [/* 8 vars */]) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x40005000
open("/lib/libc.so.0", O_RDONLY)        = 3
fstat(3, {st_mode=S_IFREG|0755, st_size=310348, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000000, -1, 0) = 0x40006000
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0(\0\1\0\0\0\260\256\0\0004\0\0\0"..., 4096) = 4096
mmap2(NULL, 360448, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x4000e000
mmap2(0x4000e000, 303968, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED, 3, 0) = 0x4000e000
mmap2(0x40060000, 4972, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x4a) = 0x40060000
mmap2(0x40062000, 15112, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x40062000
close(3)                                = 0
munmap(0x40006000, 4096)                = 0
stat("/lib/ld-uClibc.so.0", {st_mode=S_IFREG|0755, st_size=21200, ...}) = 0
mprotect(0x40060000, 4096, PROT_READ)   = 0
mprotect(0x4000c000, 4096, PROT_READ)   = 0
ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
geteuid32()                             = 1000
write(1, "saved privilege: 1000\n", 22saved privilege: 1000
) = 22
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [], 0}, 8) = 0
vfork(sh: cannot create /proc/sys/vm/drop_caches: Permission denied
)                                 = 1183
--- SIGCHLD (Child exited) @ 0 (0) ---
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
wait4(1183, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}], 0, NULL) = 1183
rt_sigaction(SIGQUIT, {SIG_DFL, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, 8) = 0
getuid32()                              = 1000
setresuid32(-1, 1000, -1)               = 0
getuid32()                              = 1000
geteuid32()                             = 1000
write(1, "dropped privileges 1000 1000\n", 29dropped privileges 1000 1000
) = 29
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_DFL, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_DFL, [INT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, 8) = 0
vfork(sh: cannot create /proc/sys/vm/drop_caches: Permission denied
)                                 = 1184
--- SIGCHLD (Child exited) @ 0 (0) ---
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
wait4(1184, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}], 0, NULL) = 1184
rt_sigaction(SIGQUIT, {SIG_DFL, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, 8) = 0
setuid32(1000)                          = 0
getuid32()                              = 1000
geteuid32()                             = 1000
write(1, "reacquired privileges 1000 1000\n", 32reacquired privileges 1000 1000
) = 32
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_DFL, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_DFL, [INT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, 8) = 0
vfork(sh: cannot create /proc/sys/vm/drop_caches: Permission denied
)                                 = 1185
--- SIGCHLD (Child exited) @ 0 (0) ---
rt_sigaction(SIGQUIT, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
wait4(1185, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}], 0, NULL) = 1185
rt_sigaction(SIGQUIT, {SIG_DFL, [QUIT], SA_RESTART|0x4000000}, {SIG_IGN, [QUIT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [INT], SA_RESTART|0x4000000}, {SIG_IGN, [INT], SA_RESTART|0x4000000}, 8) = 0
rt_sigaction(SIGCHLD, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, {SIG_DFL, [CHLD], SA_RESTART|0x4000000}, 8) = 0
exit(0)                                 = ?

【问题讨论】:

  • 2.6 版中的下一个数字是多少?与 3.x 和 4.x 内核系列不同,它有一种感觉。无论如何,2.6.x内核系列都比较老了,你确定要支持吗?此外,在现代发行版下编译旧内核可能会导致内核内核和 libc 之间不兼容。你可以在没有你的程序(使用susudo /bin/sh -c &lt;command&gt;)的情况下执行相同的操作(echo 3 &gt; /proc/sys/vm/drop_caches)吗?
  • @Tsyvarev,即使是 y2.6.x.y 版本控制中也有意义。
  • 输出重定向 '>' 可能通过 system() 函数导致问题。 'echo 3 > ...' 是唯一不起作用的命令吗?如果是这样,请尝试进入特权模式,实际通过 open(2) 和 write(2) 打开 proc 文件。
  • @Tsyvarev 内核是 2.6.28.9,这是用于具有自定义发行版的嵌入式平台。绝对计划在未来迁移到更新的内核,但现在,必须让它适用于现有项目。执行相同命令的 sudo 效果很好,我不明白。为了解决 kaiwan 的问题,这不是唯一不起作用的命令,它只是一个示例,我也无法使用系统调用 system("insmod testdriver.ko") 加载驱动程序,基本上我能做的任何事情作为root,我不能使用上面的程序。
  • 一般来说,此语句:exit(0); 不适合在出现错误情况后退出。那是因为 0 表示成功。建议使用(来自 stdlib.h)exit( EXIT_FAILURE );

标签: c permissions linux-kernel setuid


【解决方案1】:

在 strace 下调用您的程序会导致 setuid 位被忽略。

您尝试使用 ruid == 调用用户 ID 运行 setuid 二进制文件。这不是很好。但这似乎也不是您的主要问题。

不要永远从 setuid 程序调用 system(),否则有人会使用 SHELL=/tmp/evil your_setuid_program 并在早餐时使用 root。

我什至想弄清楚你在这里真正做错了什么导致 setuid 位对你不起作用,因为你显然不知道如何编写 setuid 二进制文件。

【讨论】:

  • 好吧,我有一个在嵌入式 Linux 中运行的应用程序,它需要能够进行 system() 调用,但是在不需要 system() 调用时降低权限,我一直在阅读的所有内容都说让它一个 setuid 程序,在应用程序启动时立即降低权限,然后仅在必要时重新获得 root 权限(当我需要进行 system() 调用时),然后再次放弃权限,您对如何执行此操作有更好的建议吗?
  • @wireless_freedom:尝试编写一个以 root 身份运行的程序,并且在不调用 system() 的情况下完成它需要做的事情,然后我们就可以交谈了。如果你不能做到这一点,那就没有意义了。对于除 insmod 之外的所有示例,您根本不应该启动任何程序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-03
  • 1970-01-01
  • 2017-09-06
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 2014-01-08
相关资源
最近更新 更多