GNU/Linux >> Tutoriales Linux >  >> Linux

¿Permitir Setuid en scripts de Shell?

El setuid El bit 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 solo 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

Wikipedia dice:

Debido a la mayor probabilidad de fallas 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 forma 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í, ¿hay una solución común para este problema? Mi solución actual es agregar un sudoers entrada para permitir ALL para ejecutar un script dado como el usuario que quiero que se ejecute, con NOPASSWD para evitar la solicitud de contraseña. La principal desventaja de eso es la necesidad de un sudoers entrada cada vez que quiero hacer esto, y la necesidad de que la persona que llama haga sudo some-script en lugar de solo some-script

Respuesta aceptada:

Linux ignora el bit setuid¹ en todos los ejecutables interpretados (es decir, ejecutables que comienzan con un #! línea). Las preguntas frecuentes de comp.unix.questions explican 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, deberá parchear el kernel. A partir de los kernels 3.x, creo que debe agregar una llamada a install_exec_creds en el load_script función, antes de la llamada a open_exec , pero no lo he probado.

Setuid shebang

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

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

Si se permiten secuencias de comandos setuid con esta implementación, un atacante puede invocar una secuencia de comandos arbitraria creando un enlace simbólico a una secuencia de comandos setuid existente, ejecutándola y cambiando el enlace después de que el núcleo 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 setuid cuando detectan un shebang.

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 solo desvincular o sobrescribir el archivo, sino también cambiar el nombre de cualquier directorio en la ruta). Pero los sistemas Unix tienden a rehuir los bloqueos obligatorios, y los enlaces simbólicos harían que una característica 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 una configuración de kernel para habilitarse) implementan setuid shebang seguro usando una función adicional:la ruta /dev/fd/N se refiere al archivo ya abierto en el descriptor de archivo N (entonces abriendo /dev/fd/N es más o menos equivalente a dup(N) ). Muchos sistemas Unix (incluido Linux) tienen /dev/fd pero no secuencias de comandos setuid.

  1. El kernel abre el ejecutable y encuentra que comienza con #! . Digamos que el descriptor de archivo para el ejecutable es 3.
  2. El núcleo abre el intérprete.
  3. El kernel inserta /dev/fd/3 la lista de argumentos (como argv[1] ), y ejecuta el intérprete.
Relacionado:¿Cuáles son los parámetros/variables especiales de shell (Bash)?

Página de tinglado de Sven Mascheck tiene mucha información sobre shebang a través de unices, incluido el soporte de setuid.

Intérpretes de setuidas

Supongamos que ha logrado que su programa se ejecute como root, ya sea porque su sistema operativo es compatible con setuid shebang o porque ha utilizado un contenedor binario nativo (como sudo ). ¿Ha abierto un agujero de seguridad? Tal vez . El problema aquí es no sobre programas interpretados vs compilados. El problema es si su sistema de tiempo de ejecución se comporta de forma segura si se ejecuta con privilegios.

  • Cualquier ejecutable binario nativo enlazado dinámicamente es interpretado de alguna manera por el cargador dinámico (por ejemplo, /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_PATH es 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.so especialmente diseñado en $LD_LIBRARY_PATH (entre otras tácticas). Todos los sistemas sanos ignoran el LD_* variables en ejecutables setuid.

  • En conchas como 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 , IFS , y muchos más, el invocador del script tiene muchas oportunidades para ejecutar código arbitrario en el contexto de los scripts de shell. Algunos shells configuran estas variables a valores predeterminados sanos si detectan que el script se ha invocado con privilegios, pero no sé si hay alguna implementación en particular en la que yo confíe.

  • La mayoría de los entornos de tiempo de ejecución (ya sean nativos, bytecode o interpretados) 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 elegante que la vinculación dinámica (que sí toma precauciones).

  • Perl es una notable excepción. Soporta explícitamente scripts setuid de 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 se entrega con un asistente de 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 el manual de perlsec. Antes, los scripts setuid perl necesitaban #!/usr/bin/suidperl -wT en lugar de #!/usr/bin/perl -wT , pero en la mayoría de los sistemas modernos, #!/usr/bin/perl -wT es suficiente.

Tenga en cuenta que el uso de un contenedor binario nativo no hace nada por sí mismo para evitar estos problemas . De hecho, puede hacer que la situación empeore , porque podría evitar que su entorno de tiempo de ejecución detecte que se invoca con privilegios y pase por alto su configuración de tiempo de ejecución.

Un contenedor binario nativo puede hacer que un script de shell sea seguro si el contenedor desinfecta el entorno . La secuencia de comandos debe tener cuidado de no hacer demasiadas suposiciones (por ejemplo, sobre el directorio actual), pero esto funciona. Puede usar sudo para esto siempre que esté configurado para desinfectar el entorno. La inclusión de variables en la lista negra es propensa a errores, así que siempre incluya en la lista blanca. Con sudo, asegúrese de que env_reset está activada, que setenv está desactivado, y ese env_file y env_keep solo contienen variables inocuas.

Relacionado:¿Cómo escapar de las comillas en Shell?

TL,DR:

  • Setuid shebang es inseguro pero normalmente se ignora.
  • Si ejecuta un programa con privilegios (ya sea a través de sudo o setuid), escriba código nativo o perl, o inicie el programa con un contenedor que desinfecte el entorno (como sudo con env_reset opción).

¹ Esta discusión se aplica igualmente si sustituye "setgid" por "setuid"; ambos son ignorados por el kernel de Linux en los scripts


Linux
  1. ¿Ejecutar scripts de Shell a través de un sitio web?

  2. ¿Extensiones de archivo para secuencias de comandos de Unix Shell?

  3. Parámetros de línea de comando en scripts de shell

  4. Ejecutar scripts desde otro directorio

  5. ¿Por qué SUID está deshabilitado para scripts de shell pero no para binarios?

Cómo crear scripts de shell

Matrices en scripts de Shell

¿Cómo usar if-else en Shell Scripts?

Comprender el bucle for en los scripts de Shell

El bucle while en los scripts de Shell

Ejecute todos los scripts de shell en la carpeta