Permitir setuid en scripts de shell

Permitir setuid en scripts de shell

El setuidbit de permiso le dice a Linux que ejecute un programa con la identificación de usuario efectiva del propietario en lugar del ejecutor:

> cat setuid-test.c

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

int main(int argc, char** argv) {
    printf("%d", geteuid());
    return 0;
}

> gcc -o setuid-test setuid-test.c
> ./setuid-test

1000

> sudo chown nobody ./setuid-test; sudo chmod +s ./setuid-test
> ./setuid-test

65534

Sin embargo, esto sólo se aplica a los ejecutables; Los scripts de Shell ignoran el bit setuid:

> cat setuid-test2

#!/bin/bash
id -u

> ./setuid-test2

1000

> sudo chown nobody ./setuid-test2; sudo chmod +s ./setuid-test2
> ./setuid-test2

1000

Wikipediadice:

Debido a la mayor probabilidad de fallos de seguridad, muchos sistemas operativos ignoran el atributo setuid cuando se aplica a scripts de shell ejecutables.

Suponiendo que estoy dispuesto a aceptar esos riesgos, ¿hay alguna manera de decirle a Linux que trate el bit setuid de la misma manera en los scripts de shell que en los ejecutables?

Si no es así, ¿existe una solución común para este problema? Mi solución actual es agregar una sudoersentrada que permita ALLejecutar un script determinado como el usuario con el que quiero que se ejecute, para NOPASSWDevitar que se solicite la contraseña. La principal desventaja de esto es la necesidad de una sudoersentrada cada vez que quiero hacer esto, y la necesidad de que la persona que llama, sudo some-scripten lugar de simplementesome-script

Respuesta1

Linux ignora el bit setuid¹ en todos los ejecutables interpretados (es decir, ejecutables que comienzan con una #!línea). ElPreguntas frecuentes sobre comp.unix.questionsexplica los problemas de seguridad con los scripts de shell setuid. Estos problemas son de dos tipos: relacionados con shebang y relacionados con shell; Entro en más detalles a continuación.

Si no le importa la seguridad y desea permitir scripts setuid, en Linux, necesitará parchear el kernel. A partir de los kernels 3.x, creo que es necesario agregar una llamada ainstall_exec_credsen elload_scriptfunción, antes de la llamada a open_exec, pero no lo he probado.


asunto setuid

Existe una condición de carrera inherente a la forma en que #!normalmente se implementa shebang ():

  1. El kernel abre el ejecutable y descubre que comienza con #!.
  2. El kernel cierra el ejecutable y en su lugar abre el intérprete.
  3. El kernel inserta la ruta al script en la lista de argumentos (como argv[1]) y ejecuta el intérprete.

Si se permiten scripts setuid con esta implementación, un atacante puede invocar un script arbitrario creando un enlace simbólico a un script setuid existente, ejecutándolo y organizando el cambio del enlace después de que el kernel haya realizado el paso 1 y antes de que el intérprete llegue a abriendo su primer argumento. Por esta razón,la mayoría de los unices ignoran el bit setuidcuando detectan un tinglado.

Una forma de asegurar esta implementación sería que el kernel bloquee el archivo de script hasta que el intérprete lo haya abierto (tenga en cuenta que esto debe evitar no sólo desvincular o sobrescribir el archivo, sino también cambiar el nombre de cualquier directorio en la ruta). Pero los sistemas Unix tienden a evitar los bloqueos obligatorios, y los enlaces simbólicos harían que una función de bloqueo correcta fuera especialmente difícil e invasiva. No creo que nadie lo haga de esta manera.

Algunos sistemas Unix (principalmente OpenBSD, NetBSD y Mac OS X, todos los cuales requieren que se habilite una configuración del kernel) implementanseguro setuid shebangusando una característica adicional: la ruta se refiere al archivo ya abierto en el descriptor de archivo/dev/fd/Nnorte(por lo que la apertura es aproximadamente equivalente a ). Muchos sistemas Unix (incluido Linux) tienen scripts setuid, pero no./dev/fd/Ndup(N)/dev/fd

  1. El kernel abre el ejecutable y descubre que comienza con #!. Digamos que el descriptor de archivo del ejecutable es 3.
  2. El núcleo abre el intérprete.
  3. El kernel inserta /dev/fd/3la lista de argumentos (como argv[1]) y ejecuta el intérprete.

La página de Sven Maschecktiene mucha información sobre shebang en unices, incluidosoporte setuid.


Intérpretes setuidos

Supongamos que ha logrado que su programa se ejecute como root, ya sea porque su sistema operativo admite setuid shebang o porque ha utilizado un contenedor binario nativo (como sudo). ¿Has abierto un agujero de seguridad?Tal vez. El problema aquí esnosobre programas interpretados versus programas compilados. La cuestión es si susistema de ejecuciónse comporta de forma segura si se ejecuta con privilegios.

  • Cualquier ejecutable binario nativo vinculado dinámicamente es interpretado de alguna manera por elcargador dinámico(p. ej. /lib/ld.so), que carga las bibliotecas dinámicas requeridas por el programa. En muchos Unices, puede configurar la ruta de búsqueda de bibliotecas dinámicas a través del entorno ( LD_LIBRARY_PATHes un nombre común para la variable de entorno) e incluso cargar bibliotecas adicionales en todos los archivos binarios ejecutados ( LD_PRELOAD). El invocador del programa puede ejecutar código arbitrario en el contexto de ese programa colocando un libc.socódigo especialmente diseñado $LD_LIBRARY_PATH(entre otras tácticas). Todos los sistemas sensatos ignoran las LD_*variables en los ejecutables setuid.

  • EnconchasComo sh, csh y derivados, las variables de entorno se convierten automáticamente en parámetros de shell. A través de parámetros como PATH, IFSy muchos más, el invocador del script tiene muchas oportunidades de ejecutar código arbitrario en el contexto del script de shell. Algunos shells configuran estas variables con valores predeterminados sensatos si detectan que el script ha sido invocado con privilegios, pero no sé si existe alguna implementación particular en la que pueda confiar.

  • La mayoría de los entornos de ejecución(ya sea nativo, de código de bytes o interpretado) tienen características similares. Pocos toman precauciones especiales en los ejecutables setuid, aunque los que ejecutan código nativo a menudo no hacen nada más sofisticado que el enlace dinámico (que sí toma precauciones).

  • perlaes una notable excepción. Éladmite explícitamente scripts setuidde forma segura. De hecho, su secuencia de comandos puede ejecutar setuid incluso si su sistema operativo ignoró el bit setuid en las secuencias de comandos. Esto se debe a que Perl viene con un asistente raíz setuid que realiza las comprobaciones necesarias y vuelve a invocar al intérprete en los scripts deseados con los privilegios deseados. Esto se explica en elperlsecmanual. Solía ​​ser que se necesitaban scripts setuid perl #!/usr/bin/suidperl -wTen lugar de #!/usr/bin/perl -wT, pero en la mayoría de los sistemas modernos, #!/usr/bin/perl -wTes suficiente.

Tenga en cuenta que usar un binario nativoEl contenedor no hace nada por sí solo para prevenir estos problemas.. De hecho, puede hacer que la situaciónpeor, porque podría impedir que su entorno de ejecución detecte que se invoca con privilegios y omita su configurabilidad de tiempo de ejecución.

Un contenedor binario nativo puede hacer que un script de shell sea seguro si el contenedordesinfecta el medio ambiente. El script debe tener cuidado de no hacer demasiadas suposiciones (por ejemplo, sobre el directorio actual), pero así es. Puedes usar sudo para esto siempre que esté configurado para desinfectar el ambiente. Incluir variables en la lista negra es propenso a errores, por lo que siempre debe incluirse en la lista blanca. Con sudo, asegúrese de que la env_resetopción esté activada, desactivada setenvy que env_filesolo env_keepcontenga variables inocuas.


TL,DR:

  • Setuid shebang es inseguro pero generalmente se ignora.
  • Si ejecuta un programa con privilegios (ya sea mediante sudo o setuid), escriba código nativo o perl, o inicie el programa con un contenedor que desinfecte el entorno (como sudo con la env_resetopción).

¹ Esta discusión se aplica igualmente si sustituye “setgid” por “setuid”;Ambos son ignorados por el kernel de Linux en los scripts.

Respuesta2

Una forma de resolver este problema es llamar al script de shell desde un programa que pueda usar el bit setuid.
es algo así como sudo. Por ejemplo, así es como lograría esto en un programa en C:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    setuid( 0 );   // you can set it at run time also
    system( "/home/pubuntu/setuid-test2.sh" );
    return 0;
 }

Guárdelo como setuid-test2.c.
compilar
Ahora haga el setuid en este programa binario:

su - nobody   
[enter password]  
chown nobody:nobody a.out  
chmod 4755 a.out  

Ahora deberías poder ejecutarlo y verás que tu script se ejecuta sin permisos de nadie.
Pero aquí también es necesario codificar la ruta del script o pasarla como línea de comando arg al exe anterior.

Respuesta3

Prefijo algunos scripts que se encuentran en este barco de la siguiente manera:

#!/bin/sh
[ "root" != "$USER" ] && exec sudo $0 "$@"

Tenga en cuenta que esto no utiliza setuidsino que simplemente ejecuta el archivo actual con sudo.

Respuesta4

Si por alguna razón sudono está disponible, puede escribir un script contenedor delgado en C:

#include <unistd.h>
int main() {
    setuid(0);
    execle("/bin/bash","bash","/full/path/to/script",(char*) NULL,(char*) NULL);
}

Y una vez que lo compilas, configúralo como setuidcon chmod 4511 wrapper_script.

Esto es similar a otra respuesta publicada, pero ejecuta el script con un entorno limpio y lo usa explícitamente /bin/bashen lugar del shell llamado por system(), por lo que cierra algunos posibles agujeros de seguridad.

Tenga en cuenta que esto descarta el medio ambiente por completo. Si desea utilizar algunas variables ambientales sin abrir vulnerabilidades, en realidad solo necesita usar sudo.

Obviamente, desea asegurarse de que el script en sí solo pueda escribirlo el usuario root.

información relacionada