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
-p
option is not supplied, no startup files are read, shell functions are not inherited from the environment, theSHELLOPTS
,BASHOPTS
,CDPATH
, andGLOBIGNORE
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.
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).