A same suid + system program, different outputs on different systems

466 Views Asked by At

I write a simple program named suid.c:

int main(int argc, char* argv[]) {
        system(argv[1]);
}

Compile it with gcc then set suid permission (root user now):

# gcc suid.c -o suid
# chmod +s suid

I tried to run ./suid id with the www-data user on different virtual machines, but the output is different.

  • The output in kali 2019: id=33(www-data) gid=33(www-data) groups=33(www-data)
  • The output in ubuntu trusty(a old linux): uid=33(www-data) gid=33(www-data) euid=0(root) egid=0(root) groups=0(root),33(www-data)

Why is that? Has anything changed in Linux?

2

There are 2 best solutions below

0
Gilles 'SO- stop being evil' On BEST ANSWER

It depends whether /bin/sh is bash or dash on your distribution. Bash always drops setuid/setgid privileges unless invoked with the option -p. Dash doesn't do anything special about privileges.

On Ubuntu, the system function invokes sh, which is /bin/sh, which is dash, which doesn't drop privileges. On Kali, the system function invokes sh, which is /bin/sh, which is bash, which drops privileges. It's a matter of which shell the distribution installs as sh, not of how recent the distribution is.

Bash's behavior can be a security countermeasure, but it isn't a very effective one. It's a second line of defense against misguided configurations or badly written programs that run with privileges that they shouldn't have, and allow untrusted input to functions such as system. However, such programs usually have other holes that allow the potential attacker to attack anyway, such as the ability to write to files.

0
Steve Friedl On

As noted in a comment above, you're seeing the effects of system() dropping setuid on purpose (indirectly via bash).

My Amazon Linux instance notes for system(3):

Do not use system() from a program with set-user-ID or set-group-ID privileges, because strange values for some environment variables might be used to subvert system integrity. Use the exec(3) family of functions instead, but not execlp(3) or execvp(3). system() will not, in fact, work properly from programs with set-user-ID or set-group-ID privileges on systems on which /bin/sh is bash version 2, since bash 2 drops privileges on startup. (Debian uses a modified bash which does not do this when invoked as sh.)

Running system() from a setuid program is super dangerous because it's way, way easier to subvert your intentions than you think (hijacking $IFS would be a good place to start).

Using one of the exec functions at least gets the shell out of the way, but there's often another shell down the road.

If you absolutely have to do this, you can call setuid(geteuid()); prior to system(), which essentially throws away the fact that you're in a setuid environment - where real and effective users are different - by saying that both are the elevated user. Likewise with group. This is probably a terrible idea.

I might suggest that if you have to ask SO how to do setuid, you may not be in the best position to do it safely.