Se você alterar o código desse modo, poderá ver os UIDs efetivos e reais:
#include <string>
#include <stdlib.h>
int main(int argc, char *argv[]){
system("id");
system("bash -c id");
return 0;
}
No meu sistema, isso retorna essas duas linhas (usei ... para pular grupos irrelevantes):
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)
Como você pode ver, a chamada bruta para id retorna um UID efetivo de 0 (raiz), mas o UID real ainda é meu. Isso é o que você esperaria.
No entanto, você pode ver que a chamada bash -c id removeu o UID efetivo para que ele não seja mais executado como raiz. Isso está documentado em man bash da seguinte maneira:
If the shell is started with the effective user (group) id not equal to the real user (group) id, and the
-poption is not supplied, no startup files are read, shell functions are not inherited from the environment, theSHELLOPTS,BASHOPTS,CDPATH, andGLOBIGNOREvariables, if they appear in the environment, are ignored, and the effective user id is set to the real user id. If the-poption is supplied at invocation, the startup behavior is the same, but the effective user id is not reset.
Portanto, a solução aqui deve ser incluir o sinalizador -p .
(Você pode saber mais sobre o processo pelo qual bash redefine seu UID em bit Setuid parece não ter efeito sobre o bash .)
No entanto, a história não está terminada aqui porque sei que você vai dizer que você não invocou bash . Infelizmente para você, isso é basicamente o que o system() faz em seu nome e não permite que você especifique -p .
strace descarta os privilégios de root, mas aqui está o suficiente da saída strace -f ./a.out para você ver o que está acontecendo:
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 ...>
Neste ponto, o processo filho inicia, pronto para executar nosso 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
Agora temos um shell em execução e descartamos nosso UID efetivo. Em seguida, você verá a inicialização do comando id e a gravação de sua saída para stdout :
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)
...
A solução para você aqui é usar diretamente uma das famílias exec*() ou incluir uma chamada para setuid(0) ou configurar uma ferramenta como sudo para permitir que você chame seu programa de destino diretamente e (presumivelmente) sem uma senha.
Entre essas opções, eu pessoalmente incluo a solução sudo . Os autores disso gastaram muito tempo garantindo que o código fosse seguro contra ataques de privilégio (in) planejados (a).