strace 输出(musl 非特权)

strace 输出(musl 非特权)

musl libc即使在据称删除权限后,您也可以更改uid为.我无法重现该问题。rootsetuid(1000)glibc

代码:

#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>

int main(void) {
    uid_t r, e, s;

    getresuid(&r, &e, &s);
    printf("%d %d %d\n", r, e, s);

    if (setuid(1000) != 0)
        puts("setuid(1000) failed");
    else
        puts("setuid(1000) succeded");

    getresuid(&r, &e, &s);
    printf("%d %d %d\n", r, e, s);

    if (setuid(0) != 0)
        puts("setuid(0) failed");
    else
        puts("setuid(0) succeded");

    getresuid(&r, &e, &s);
    printf("%d %d %d\n", r, e, s);

    return 0;
}

使用 编译后,gcc -o setuidtest setuidtest.c以 root 身份运行时会产生以下输出

0 0 0
setuid(1000) succeded
1000 1000 1000
setuid(0) succeded
0 0 0

我在跑步虚空Linux有内核版本4.18_1和musl版本1.1.20_2,glibc版本2.28_3

这个问题从何而来?是我的内核、musl libc 还是我的测试代码不正确?其他人可以重现这个问题吗?

strace 输出(musl 非特权)

$ strace ./setuidtest (unpriviledged)
execve("./setuidtest", ["./setuidtest"], 0x7ffdd15f69e0 /* 24 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x7f538925cb28) = 0
set_tid_address(0x7f538925cb68)         = 6299
mprotect(0x7f5389259000, 4096, PROT_READ) = 0
mprotect(0x55e9c5e75000, 4096, PROT_READ) = 0
getresuid([1000], [1000], [1000])       = 0
ioctl(1, TIOCGWINSZ, 0x7ffd2faf3160)    = -1 ENOTTY (Not a tty)
writev(1, [{iov_base="1000 1000 1000", iov_len=14}, {iov_base="\n", iov_len=1}], 21000 1000 1000
) = 15
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1 RT_2], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[], NULL, 8) = 0
setuid(1000)                            = 0
futex(0x7f538925cfd8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
getresuid([1000], [1000], [1000])       = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1 RT_2], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[], NULL, 8) = 0
setuid(0)                               = -1 EPERM (Operation not permitted)
futex(0x7f538925cfd8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
getresuid([1000], [1000], [1000])       = 0
writev(1, [{iov_base="setuid(1000) succeded\n1000 1000 "..., iov_len=69}, {iov_base=NULL, iov_len=0}], 2setuid(1000) succeded
1000 1000 1000
setuid(0) failed
1000 1000 1000
) = 69
exit_group(0)                           = ?
+++ exited with 0 +++

strace 输出(musl 作为 root)

# strace ./setuidtest (as root)
execve("./setuidtest", ["./setuidtest"], 0x7ffe19286eb0 /* 18 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x7f08b2619b28) = 0
set_tid_address(0x7f08b2619b68)         = 6409
mprotect(0x7f08b2616000, 4096, PROT_READ) = 0
mprotect(0x561507eb4000, 4096, PROT_READ) = 0
getresuid([0], [0], [0])                = 0
ioctl(1, TIOCGWINSZ, 0x7fffb222bff0)    = -1 ENOTTY (Not a tty)
writev(1, [{iov_base="0 0 0", iov_len=5}, {iov_base="\n", iov_len=1}], 20 0 0
) = 6
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1 RT_2], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[], NULL, 8) = 0
setuid(1000)                            = 0
futex(0x7f08b2619fd8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
getresuid([1000], [1000], [1000])       = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1 RT_2], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[], NULL, 8) = 0
setuid(0)                               = 0
futex(0x7f08b2619fd8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
getresuid([0], [0], [0])                = 0
writev(1, [{iov_base="setuid(1000) succeded\n1000 1000 "..., iov_len=62}, {iov_base=NULL, iov_len=0}], 2setuid(1000) succeded
1000 1000 1000
setuid(0) succeded
0 0 0
) = 62
exit_group(0)                           = ?
+++ exited with 0 +++

strace 输出(glibc 非特权)

$ strace ./setuidtest
execve("./setuidtest", ["./setuidtest"], 0x7fff673b2c20 /* 14 vars */) = 0
brk(NULL)                               = 0x558b5fbb1000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/x86_64/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls/x86_64/x86_64", 0x7ffc25da1650) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls/x86_64", 0x7ffc25da1650) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls/x86_64", 0x7ffc25da1650) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls", 0x7ffc25da1650)    = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64/x86_64", 0x7ffc25da1650) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64", 0x7ffc25da1650) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64", 0x7ffc25da1650) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3604\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=18248368, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbcd7093000
mmap(NULL, 3921920, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fbcd6ab1000
mprotect(0x7fbcd6c65000, 2097152, PROT_NONE) = 0
mmap(0x7fbcd6e65000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b4000) = 0x7fbcd6e65000
mmap(0x7fbcd6e6b000, 14336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fbcd6e6b000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fbcd7094500) = 0
mprotect(0x7fbcd6e65000, 16384, PROT_READ) = 0
mprotect(0x558b5f5c0000, 4096, PROT_READ) = 0
mprotect(0x7fbcd7095000, 4096, PROT_READ) = 0
getresuid([1000], [1000], [1000])       = 0
fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
brk(NULL)                               = 0x558b5fbb1000
brk(0x558b5fbd2000)                     = 0x558b5fbd2000
setuid(1000)                            = 0
getresuid([1000], [1000], [1000])       = 0
setuid(0)                               = -1 EPERM (Operation not permitted)
getresuid([1000], [1000], [1000])       = 0
write(1, "1000 1000 1000\nsetuid(1000) succ"..., 841000 1000 1000
setuid(1000) succeded
1000 1000 1000
setuid(0) failed
1000 1000 1000
) = 84
exit_group(0)                           = ?
+++ exited with 0 +++

strace 输出(glibc 作为 root)

# strace ./setuidtest
execve("./test", ["./test"], 0x7ffc9176d3f0 /* 15 vars */) = 0
brk(NULL)                               = 0x5653f4910000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/x86_64/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls/x86_64/x86_64", 0x7ffe7d0c9550) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls/x86_64", 0x7ffe7d0c9550) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls/x86_64", 0x7ffe7d0c9550) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/tls", 0x7ffe7d0c9550)    = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64/x86_64", 0x7ffe7d0c9550) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64", 0x7ffe7d0c9550) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/usr/lib/x86_64", 0x7ffe7d0c9550) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\3604\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=18248368, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb18f5af000
mmap(NULL, 3921920, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb18efcd000
mprotect(0x7fb18f181000, 2097152, PROT_NONE) = 0
mmap(0x7fb18f381000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b4000) = 0x7fb18f381000
mmap(0x7fb18f387000, 14336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb18f387000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fb18f5b0500) = 0
mprotect(0x7fb18f381000, 16384, PROT_READ) = 0
mprotect(0x5653f40df000, 4096, PROT_READ) = 0
mprotect(0x7fb18f5b1000, 4096, PROT_READ) = 0
getresuid([0], [0], [0])                = 0
fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
brk(NULL)                               = 0x5653f4910000
brk(0x5653f4931000)                     = 0x5653f4931000
setuid(1000)                            = 0
getresuid([1000], [1000], [1000])       = 0
setuid(0)                               = -1 EPERM (Operation not permitted)
getresuid([1000], [1000], [1000])       = 0
write(1, "0 0 0\nsetuid(1000) succeded\n1000"..., 750 0 0
setuid(1000) succeded
1000 1000 1000
setuid(0) failed
1000 1000 1000
) = 75
exit_group(0)                           = ?
+++ exited with 0 +++

答案1

这个问题确实不是来自内核或 musl,而是来自能力。事实证明,我的两个测试系统之间存在差异 -glibc并且musl- 这使得它看起来与它们有关。

这个问题实际上是由pam_rundir.so模块它设置了SECBIT_NO_SETUID_FIXUP安全位,这使得内核在之后保留程序的功能setuid(因此允许我setuid返回root)。

相关内容