If you change the code like this you can see the effective and real UIDs:
#include <string>
#include <stdlib.h>
int main(int argc, char *argv[]){
system("id");
system("bash -c id");
return 0;
}
On my system this returns these two lines (I've used ... to skip irrelevant groups):
uid=1001(roaima) gid=1001(roaima) euid=0(root) groups=1001(roaima),24(cdrom),...,103(vboxsf)
uid=1001(roaima) gid=1001(roaima) groups=1001(roaima),24(cdrom),...,103(vboxsf)
As you can see, the raw call to id
returns an Effective UID of 0 (root), but the Real UID is still my own. This is what you would expect.
However, you can see that the bash -c id
call has stripped the Effective UID away so it is no longer running as root. This is documented under man bash
as follows:
If the shell is started with the effective user (group) id not equal to the real user (group) id, and the -p
option is not supplied, no startup files are read, shell functions are not inherited from the environment, the SHELLOPTS
, BASHOPTS
, CDPATH
, and GLOBIGNORE
variables, if they appear in the environment, are ignored, and the effective user id is set to the real user id. If the -p
option is supplied at invocation, the startup behavior is the same, but the effective user id is not reset.
So the solution here should be to include the -p
flag.
(You can find out about the process by which bash
resets its UID at Setuid bit seems to have no effect on bash.)
However, the story's not finished here because I know you're going to say you didn't invoke bash
. Unfortunately for you, that's pretty much what system()
does on your behalf, and it doesn't allow you to specify -p
.
strace
discards the root privileges, but here's enough of the strace -f ./a.out
output for you to see what's going on:
execve("./a.out", ["./a.out"], [/* 44 vars */]) = 0
brk(0) = 0x24f1000
...
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7ffee0d42a1c) = 4619
wait4(4619, Process 4619 attached
<unfinished ...>
At this point the child process kicks off, ready to run our id
[pid 4619] rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f100eb270e0}, NULL, 8) = 0
[pid 4619] rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7f100eb270e0}, NULL, 8) = 0
[pid 4619] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
[pid 4619] execve("/bin/sh", ["sh", "-c", "id"], [/* 44 vars */]) = 0
[pid 4619] brk(0) = 0x7f849dd71000
[pid 4619] brk(0) = 0x7f849dd71000
...
[pid 4619] clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f849d1d89d0) = 4620
Now we have a shell running, and it will have discarded our Effective UID. Next you'll see it starting the id
command and writing its output to stdout for you:
Process 4620 attached
[pid 4619] wait4(-1, <unfinished ...>
[pid 4620] execve("/usr/bin/id", ["id"], [/* 44 vars */]) = 0
[pid 4620] brk(0) = 0x1785000
...
[pid 4620] write(1, "uid=1001(roaima) gid=1001(roaim"..., 149) = 149
uid=1001(roaima) gid=1001(roaima) groups=1001(roaima),24(cdrom),...,103(vboxsf)
...
The solution for you here will be either to use one of the exec*()
family directly, or to include a call to setuid(0)
, or to configure a tool such as sudo
to allow you to call your target program directly and (presumably) without a password.
Of these options I'd personally go with the sudo
solution. The authors of that spent a long time ensuring the code was safe against (un)intended escalation of privilege attacks.
findmnt -T ~ | cat
. – sourcejedi Jun 08 '17 at 07:02