Solución:
El bit setuid se puede establecer en un archivo ejecutable para que cuando se ejecute, el programa tenga los privilegios del propietario del archivo en lugar del usuario real, si son diferentes. Esta es la diferencia entre eficaz uid (identificación de usuario) y verdadero uid.
Algunas utilidades comunes, como passwd
, son de propiedad raíz y se configuran de esta manera por necesidad (passwd
necesita acceder /etc/shadow
que solo puede ser leído por root).
La mejor estrategia al hacer esto es hacer lo que sea necesario como superusuario de inmediato y luego reducir los privilegios para que sea menos probable que ocurran errores o mal uso mientras se ejecuta root. Para hacer esto, configura el proceso eficaz uid a su uid real. En POSIX C:
#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include
#include
#include
void report (uid_t real)
printf (
"Real UID: %d Effective UID: %dn",
real,
geteuid()
);
int main (void)
uid_t real = getuid();
report(real);
seteuid(real);
report(real);
return 0;
Las funciones relevantes, que deberían tener un equivalente en la mayoría de los lenguajes de nivel superior si se usan comúnmente en sistemas POSIX:
getuid()
: Consigue el verdadero uid.geteuid()
: Consigue el eficaz uid.seteuid()
: Selecciona el eficaz uid.
No puedes hacer nada con el último inapropiado para el fluido real. excepto en la medida en que el bit setuid se estableció en el ejecutable. Así que para probar esto, compila gcc test.c -o testuid
. Luego necesita, con privilegios:
chown root testuid
chmod u+s testuid
El último establece el bit setuid. Si ahora corres ./testuid
como usuario normal, verá que el proceso se ejecuta de forma predeterminada con el uid 0 efectivo, root.
¿Qué pasa con los guiones?
Esto varía de una plataforma a otra, pero en Linux, las cosas que requieren un intérprete, incluido el código de bytes, no pueden hacer uso del bit setuid a menos que esté configurado en el intérprete (lo que sería muy, muy estúpido). Aquí hay un script de perl simple que imita el código C anterior:
#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);
print "Real UID: $< Effective UID: $>n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>n";
Fiel a sus raíces * nixy, perl ha incorporado variables especiales para uid efectivo ($>
) y uid real ($<
). Pero si intentas lo mismo chown
y chmod
usado con el ejecutable compilado (de C, ejemplo anterior), no hará ninguna diferencia. El script no puede obtener privilegios.
La respuesta a esto es usar un binario setuid para ejecutar el script:
#include
#include
int main (int argc, char *argv[])
if (argc < 2)
puts("Path to perl script required.");
return 1;
const char *perl = "perl";
argv[0] = (char*)perl;
return execv("/usr/bin/perl", argv);
Compila esto gcc --std=c99 whatever.c -o perlsuid
, luego chown root perlsuid && chmod u+s perlsuid
. Ahora puedes ejecutar alguna script de perl con un uid efectivo de 0, independientemente de quién sea el propietario.
Una estrategia similar funcionará con php, python, etc. Pero...
# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face
POR FAVOR, POR FAVOR NO deje este tipo de cosas por ahí. Lo más probable es que desee compilar en el nombre del script como camino absoluto, es decir, reemplace todo el código en main()
con:
const char *args[] = "perl", "/opt/suid_scripts/whatever.pl"
return execv("/usr/bin/perl", (char * const*)args);
Ellos se aseguran /opt/suid_scripts
y todo lo que contiene son de solo lectura para usuarios no root. De lo contrario, alguien podría cambiar cualquier cosa por whatever.pl
.
Además, tenga en cuenta que muchos lenguajes de secuencias de comandos permiten que las variables de entorno cambien la forma en que ejecutan una secuencia de comandos. Por ejemplo, una variable de entorno puede hacer que se cargue una biblioteca proporcionada por el llamador, lo que le permite ejecutar código arbitrario como root. Por lo tanto, a menos que sepa que tanto el intérprete como el script en sí son robustos frente a todas las posibles variables de entorno, NO HAGAS ESTO.
Entonces, ¿qué debo hacer en su lugar?
Una forma más segura de permitir que un usuario no root ejecute un script como root es agregar una regla sudo y hacer que el usuario ejecute sudo /path/to/script
. Sudo elimina la mayoría de las variables de entorno y también permite al administrador seleccionar con precisión quién puede ejecutar el comando y con qué argumentos. Consulte ¿Cómo ejecutar un programa específico como root sin una solicitud de contraseña? para un ejemplo.
Si puedes, pero lo es probablemente una muy mala idea. Por lo general, no establecería el bit SUID directamente en su ejecutable, sino que usaría un sudo (8) o su (1) para ejecutarlo (y limitar quién puede ejecutarlo)
Sin embargo, tenga en cuenta que existen muchos problemas de seguridad al permitir que los usuarios habituales ejecuten programas (¡y especialmente scripts!) Como root. La mayoría de ellos tienen que hacer eso, a menos que el programa esté específicamente y con mucho cuidado escrito para manejar eso, los usuarios malintencionados podrían usarlo para obtener root shell fácilmente.
Entonces, incluso si lo hiciera, sería una muy buena idea desinfectar primero TODAS las entradas al programa que desea ejecutar como root, incluido su entorno, parámetros de línea de comando, STDIN y cualquier archivo que procese, datos provenientes de conexiones de red se abre, etc. Es extremadamente difícil de hacer, y aún más difícil de hacerlo bien.
Por ejemplo, puede permitir que los usuarios solo editen algún archivo con el editor. Pero el editor permite la ejecución de comandos de ayudantes externos o la suspensión, dando así a los usuarios root shell para hacer lo que deseen. O puede estar ejecutando scripts de shell que le parecen muy estrictos en cuanto a seguridad, pero el usuario puede ejecutarlo con $ IFS modificado u otras variables de entorno que cambian completamente su comportamiento. O podría ser un script PHP que se supone que debe ejecutar "ls", pero el usuario lo ejecuta con $ PATH modificado y, por lo tanto, lo hace ejecutar un shell al que llama "ls". O decenas de otros problemas de seguridad.
Si el programa realmente debe hacer parte de su trabajo como root, probablemente sería mejor modificarlo para que se ejecute como usuarios normales, pero simplemente ejecute (después de desinfectar tanto como sea posible) la parte que absolutamente necesita root a través del programa auxiliar suid.
Tenga en cuenta que incluso si confía completamente en todos sus usuarios locales (por ejemplo, les da una contraseña de root), es una mala idea, ya que cualquier descuido de seguridad no intencional por parte de CUALQUIERA de ellos (por ejemplo, tener un script PHP en línea, una contraseña débil o lo que sea) permite de repente que un atacante remoto comprometa por completo toda la máquina.