
Veja este programa c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
printf("res=%d\n", setuid(1001));
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
return 0;
}
Minha conta de usuário é "teste" (id 1000). Tenho uma segunda conta de usuário: “test2” (id 1001).
Aqui está o que eu fiz:
gcc test.c -o ./a.out
sudo chown test2 ./a.out
sudo chmod u+s ./a.out
Agora, aqui está o que acontece se eu iniciar ./a.out:
UID: 1000
EUID: 1001
uid=1000(test) gid=1000(test) groups=1000(test),...
res=0
UID: 1000
EUID: 1001
uid=1000(test) gid=1000(test) groups=1000(test),...
Não entendo porque não vejo uid=1001 na segunda parte...
Eu tentei isso:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
printf("res=%d\n", setuid(0)); // <- Here is the change
printf("UID: %d\n", getuid());
printf("EUID: %d\n", geteuid());
system("id");
return 0;
}
gcc test.c -o ./a.out
sudo chown root ./a.out
sudo chmod u+s ./a.out
Aqui está o que recebo ao executar ./a.out:
UID: 1000
EUID: 0
uid=1000(test) gid=1000(test) groups=1000(test),...
res=0
UID: 0
EUID: 0
uid=0(root) gid=1000(test) groups=1000(test),...
Agora, funciona.
Então não entendo porque o setuid não funciona com o usuário test2 e funciona com usuários root ...
Qualquer ideia ?
Obrigado
Responder1
Há algumas coisas acontecendo aqui que causam essa confusão. Setuid funciona em ambos os casos, mas não funciona da maneira que você pensa. Primeiro, há a questão dos IDs de usuário reais e efetivos. De acordo compágina de manual setuid(2)
Se o processo de chamada for privilegiado (mais precisamente: se o processo tiver a
CAP_SETUID
capacidade em seu namespace de usuário), o UID real e o set-user-ID salvo também serão definidos.
Isso também significa que o processo de chamada nãonãotiver a CAP_SETUID
capacidade (que é o caso de seus usuários comuns), então o UID iránãoalterar, apenas o EUID.
Então passamos para a system()
chamada. Chamar system("anything")
é o equivalente a chamar isto:
execl("/bin/sh", "sh", "-c", "anything", (char *) NULL);
Então ele gera /bin/sh
, fornecendo argumentos para que um novo shell execute seu comando. Presumo que você use o bash como seu shell, porque o bash funciona assim
Se o shell for iniciado com o ID do usuário (grupo) efetivo diferente do ID do usuário (grupo) real e a opção -p não for fornecida, (...) o ID do usuário efetivo será definido como o ID do usuário real.
Portanto, se o seu processo não tiver CAP_SETUID
capacidade, ele não alterará o ID do usuário real, apenas o UID efetivo. Então, quando o bash é gerado, ele descarta o UID efetivo, já que o UID não é igual ao EUID. Você pode confirmar isso ligando para
system("touch /tmp/blah")
e adicionando
FILE *file = fopen("/tmp/blah2", "w+");
fclose(file);
ao seu programa. Você verá que o proprietário de /tmp/blah
será "test" (já que o EUID será descartado na execução do shell), mas /tmp/blah2
pertencerá a "test2".