Usando el bit setuid correctamente


45

Tengo un proceso que necesita privilegios de root cuando lo ejecuta un usuario normal. Aparentemente puedo usar el "bit setuid" para lograr esto. ¿Cuál es la forma correcta de hacer esto en un sistema POSIX?

Además, ¿cómo puedo hacer esto con un script que utiliza un intérprete (bash, perl, python, php, etc.)?

Respuestas:


53

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 uid efectivo (ID de usuario) y uid real .

Algunas utilidades comunes, tales como passwd, son propiedad de root y se configuran de esta manera por necesidad ( passwdnecesita acceso /etc/shadowque solo puede leerse desde 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, establezca el uid efectivo del proceso en su uid real. En POSIX C:

#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void report (uid_t real) {
    printf (
        "Real UID: %d Effective UID: %d\n",
        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 los sistemas POSIX:

  • getuid(): Obtén el uid real .
  • geteuid(): Obtenga el uid efectivo .
  • seteuid(): Establece el uid efectivo .

No puede hacer nada con el último inapropiado para el uid real, excepto en la medida en que el bit setuid esté configurado en el ejecutable . Para probar esto, compile gcc test.c -o testuid. Entonces necesita, con privilegios:

chown root testuid
chmod u+s testuid

El último establece el bit setuid. Si ahora se ejecuta ./testuidcomo un usuario normal, verá que el proceso se ejecuta por defecto con uid 0, root efectivo.

¿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 usar el bit setuid a menos que esté configurado en el intérprete (lo cual sería muy, muy estúpido). Aquí hay una secuencia de comandos 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 ( $>) y uid real ( $<) efectivos . Pero si intenta lo mismo chowny lo chmodutiliza con el ejecutable compilado (de C, ejemplo anterior), no habrá ninguna diferencia. El script no puede obtener privilegios.

La respuesta a esto es usar un binario setuid para ejecutar el script:

#include <stdio.h>
#include <unistd.h> 

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, entonces chown root perlsuid && chmod u+s perlsuid. Ahora puede ejecutar cualquier script 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, NO DEJES este tipo de cosas por ahí . Lo más probable es que realmente desee compilar el nombre del script como una ruta absoluta , es decir, reemplazar todo el código main()con:

    const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
    return execv("/usr/bin/perl", (char * const*)args);

Asegúrate de que /opt/suid_scriptstodo lo que contiene sea 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 script permiten que las variables de entorno cambien la forma en que ejecutan un script . Por ejemplo, una variable de entorno puede hacer que se cargue una biblioteca suministrada por la persona que llama, 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 HAGA 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 se 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.


1
muchos shells se comportarán de manera diferente si se llama como un -privilegedshell y, si se llama desde un script responsable, también se puede invocar de suid rootesa manera de forma segura . Sin embargo, me temo que la noción de un guión responsable es una especie de sueño en el mundo real. De todos modos, es probable que vale la pena mencionar lo importante que es para las escrituras de evitarse de todo tipo, si es posible, mientras que los permisos son elevados ...
mikeserv

@mikeserv No estoy familiarizado con los conceptos de " -privilegedshell" y "script responsable". ¿Quieres hacer otra respuesta sobre conchas? No puedo prometerle la marca, por supuesto;) No hay muchas buenas razones para hacerlo, sudoes una opción mucho mejor si es posible, lo escribí como una respuesta genérica derivada de una pregunta temprana sobre la autenticación de contraseña del sistema en php .
Ricitos

1
Realmente no quiero hacer eso, los guiones responsables son realmente difíciles , probablemente más allá de mí. Pero debo decir que no estoy de acuerdo con su análisis de sudo- considero sudocomo psicótico en el que aparenta no ser root. Incluso puedes poner globos de concha en tu sudoers. sues mejor en mi opinión para fines de secuencias de comandos, aunque sudoes bueno para aplicaciones en vivo. Pero tampoco es tan explícita como una secuencia de comandos con un #!/bin/ksh -pbangline o cualquiera que sea el suid kshde $PATHes.
mikeserv

Si bien esta respuesta aborda la pregunta, las secuencias de comandos setuid son una receta para abrirse a una vulnerabilidad de escalada de privilegios local. Hay una razón por la cual los scripts no se pueden configurar fácilmente. La respuesta correcta aquí es sudo.
mdadm

Me tomé la libertad de editar su respuesta para al menos mencionar la gran vulnerabilidad con las variables de entorno. No me gusta tanto como una respuesta canónica porque entra en muchas digresiones en un método inseguro (el contenedor setuid para scripts). Sería mejor recomendar sudo y dejar la discusión extendida para otro hilo (lo cubrí allí , por cierto).
Gilles 'SO- deja de ser malvado'

11

Sí, puedes, pero probablemente sea 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 regulares ejecuten programas (¡y especialmente scripts!) Como root. La mayoría de ellos tienen que hacer eso a menos que el programa esté escrito específicamente y con mucho cuidado para manejar eso, los usuarios malintencionados podrían usarlo para obtener shell shell fácilmente.

Entonces, incluso si lo hiciera, sería una buena idea desinfectar TODAS las entradas al programa que desea ejecutar como root, incluido su entorno, parámetros de línea de comandos, STDIN y cualquier archivo que procese, datos provenientes de conexiones de red se abre, etc., etc. Es extremadamente difícil de hacer, y aún más difícil de hacer bien.

Por ejemplo, puede permitir que los usuarios solo editen algunos archivos con el editor. Pero el editor permite la ejecución de comandos de ayudantes externos o la suspensión, por lo que los usuarios pueden hacer lo que deseen. O puede estar ejecutando scripts de shell que le parecen muy estrictos para la seguridad, pero el usuario puede ejecutarlo con $ IFS modificado u otras variables de entorno que cambien completamente su comportamiento. O podría ser un script PHP que se supone que ejecuta "ls", pero el usuario lo ejecuta con $ PATH modificado y, por lo tanto, hace que ejecute un shell que él llama "ls". O docenas 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 solo ejecute (después de desinfectar tanto como sea posible) la parte que absolutamente necesita root a través del programa auxiliar de suid.

Tenga en cuenta que incluso si confía completamente en todos sus usuarios locales (como, les da una contraseña de root), es una mala idea, ya que cualquier supervisión de seguridad involuntaria por CUALQUIERA de ellos (como tener un script PHP en línea o una contraseña débil, o lo que sea) de repente permite que el atacante remoto comprometa por completo toda la máquina.


1
secundado vea el ejemplo de invocación segura en la serie del programador POSIX para man sh, e incluso eso no puede hacer un command -p env -i .... Es bastante difícil de hacer después de todo. Aún así, si se hace correctamente, para fines explícitos, puede ser mejor para suidun intérprete capaz que usar suo sudo, ya que el comportamiento de cualquiera de ellos siempre estará fuera del control del guión (aunque sues menos probable que arroje bolas curvas, ya que tiende ser un poco menos loco) . Agrupar todo el paquete es la única apuesta segura: es mejor estar seguro de que es todo.
mikeserv
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.