#StackBounty: #linux #security #setuid Root privileges can be restored after setuid(1000) in musl libc

Bounty: 50

musl libc allows you to change uid to root even after supposedly dropping permissions with setuid(1000). I am not able to reproduce the problem with glibc.

Code:

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

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

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

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

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

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

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

    return 0;
}

which, after compiling with gcc -o setuidtest setuidtest.c, produces the following output when running as root

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

I am running Void Linux with kernel version 4.18_1 and musl version 1.1.20_2, glibc version 2.28_3

Where does this problem come from? Is it my kernel, musl libc or is my testing code just incorrect?
Is this problem reproducible by other people?

strace output (musl unpriviledged)

$ 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) succededn1000 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 output (musl as 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) succededn1000 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 output (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, "177ELF21133>136042"..., 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 1000nsetuid(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 output (glibc)

# 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, "177ELF21133>136042"..., 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 0nsetuid(1000) succededn1000"..., 750 0 0
setuid(1000) succeded
1000 1000 1000
setuid(0) failed
1000 1000 1000
) = 75
exit_group(0)                           = ?
+++ exited with 0 +++


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.