
Mira este programa en 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;
}
Mi cuenta de usuario es "prueba" (id 1000). Tengo una segunda cuenta de usuario: "test2" (id 1001).
Esto es lo que he hecho:
gcc test.c -o ./a.out
sudo chown test2 ./a.out
sudo chmod u+s ./a.out
Ahora, esto es lo que sucede si ejecuto ./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),...
No entiendo por qué no veo uid=1001 en la segunda parte...
He probado esto:
#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
Esto es lo que obtengo cuando ejecuto ./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),...
Ahora funciona.
Entonces no entiendo por qué setuid no funciona con el usuario test2 y funciona con los usuarios root...
Alguna idea ?
Gracias
Respuesta1
Están sucediendo un par de cosas aquí que causan esta confusión. Setuid funciona en ambos casos, simplemente no funciona como crees. En primer lugar, está la cuestión de las identificaciones de usuario reales y efectivas. De acuerdo con lapágina de manual setuid(2)
Si el proceso que llama tiene privilegios (más precisamente: si el proceso tiene la
CAP_SETUID
capacidad en su espacio de nombres de usuario), también se configuran el UID real y el conjunto de ID de usuario guardado.
Esto también significa que el proceso de llamada nonotiene la CAP_SETUID
capacidad (que es el caso de sus usuarios normales), entonces el UIDnocambio, solo el EUID.
Entonces pasamos a la system()
llamada. Llamar system("anything")
es el equivalente a llamar a esto:
execl("/bin/sh", "sh", "-c", "anything", (char *) NULL);
Entonces genera /bin/sh
, proporcionando argumentos para que un nuevo shell ejecute su comando. Supongo que usas bash como tu shell, porque bash funciona así
Si el shell se inicia con la identificación del usuario (grupo) efectivo que no es igual a la identificación del usuario (grupo) real, y no se proporciona la opción -p, (...) la identificación del usuario efectivo se establece en la identificación del usuario real.
Entonces, si su proceso no tiene la CAP_SETUID
capacidad, no cambiará la ID de usuario real, solo la UID efectiva. Luego, cuando se genera bash, elimina el UID efectivo, ya que UID no es igual a EUID. Puedes confirmarlo llamando
system("touch /tmp/blah")
y agregando
FILE *file = fopen("/tmp/blah2", "w+");
fclose(file);
a su programa. Verá que el propietario de /tmp/blah
será "test" (ya que el EUID se eliminará durante la ejecución del shell), pero /tmp/blah2
pertenecerá a "test2".