it-swarm-es.tech

Permitir setuid en scripts de Shell

El bit de permiso setuid 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 aplican a los scripts ejecutables de Shell.

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

195
Michael Mrozek

Linux ignora el bit setuid¹ en todos los ejecutables interpretados (es decir, ejecutables que comienzan con una línea #!). El comp.unix.questions FAQ explica 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 núcleos 3.x, creo que debe agregar una llamada a install_exec_creds en la función load_script , 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 se implementa Shebang (#!):

  1. El núcleo abre el ejecutable y descubre que comienza con #!.
  2. El kernel cierra el ejecutable y abre el intérprete en su lugar.
  3. El kernel inserta la ruta del script a 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 equipos 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 renombrar 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 sea 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) implementan setbanid seguro Shebang usando un adicional característica: la ruta /dev/fd/N se refiere al archivo ya abierto en el descriptor de archivo [~ # ~] n [~ # ~] (¡entonces abrir /dev/fd/N es aproximadamente equivalente a dup(N)). Muchos sistemas unix (incluido Linux) tienen /dev/fd Pero no scripts setuid.

  1. El núcleo abre el ejecutable y descubre 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.

La página Shebang de Sven Mascheck tiene mucha información sobre Shebang a través de unidades, incluyendo soporte de setuid .


Intérpretes setuid

Supongamos que ha logrado hacer que su programa se ejecute como root, ya sea porque su sistema operativo admite Shebang setuid o porque ha utilizado un contenedor binario nativo (como Sudo). ¿Has abierto un agujero de seguridad? Quizás . El problema aquí es ¡no sobre programas interpretados vs compilados. El problema es si su sistema de tiempo de ejecución se comporta de manera segura si se ejecuta con privilegios.

  • Cualquier ejecutable binario nativo vinculado dinámicamente es interpretado de alguna manera por el cargador dinámico (ej. /lib/ld.so), Que carga las bibliotecas dinámicas requeridas por el programa. En muchos dispositivos, puede configurar la ruta de búsqueda para 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 las variables LD_* En los ejecutables setuid.

  • En shells 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 establecen estas variables en valores predeterminados razonables si detectan que el script ha sido invocado con privilegios, pero no sé si hay alguna implementación en particular en la que pueda confiar.

  • 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 el enlace dinámico (que sí toma precauciones).

  • Perl es una notable excepción. Es explícitamente admite scripts setuid de forma segura. De hecho, su script puede ejecutar setuid incluso si su sistema operativo ignoró el bit setuid en los scripts. Esto se debe a que Perl se entrega con un ayudante raíz setuid que realiza las comprobaciones necesarias y revoca al intérprete en los scripts deseados con los privilegios deseados. Esto se explica en el manual perlsec . Solía ​​ser que los scripts de Perl setuid 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 en sí mismo para evitar estos problemas . De hecho, puede empeorar la situación ¡peor, porque podría evitar que su entorno de tiempo 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 contenedor desinfecta el entorno . El script debe tener cuidado de no hacer demasiadas suposiciones (por ejemplo, sobre el directorio actual), pero esto continúa. Puede usar Sudo para esto siempre que esté configurado para desinfectar el medio ambiente. Las variables de la lista negra son propensas a errores, por lo que siempre debe incluirlas en la lista blanca. Con Sudo, asegúrese de que la opción env_reset Esté activada, que setenv esté desactivada y que env_file Y env_keep Solo contengan variables inocuas.


TL, DR:

  • Setuid Shebang es inseguro pero generalmente ignorado.
  • 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 la opción env_reset).

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

Una forma de resolver este problema es llamar al script de Shell desde un programa que puede 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ía poder ejecutarlo, y verá que su script se ejecuta sin permisos de nadie.
Pero aquí también necesita codificar la ruta del script o pasarla como argumento de línea de comando al exe anterior.

57
Hemant

Prefiero algunos scripts que están en este barco así:

#!/bin/sh
[ "root" != "$USER" ] && exec Sudo $0 "[email protected]"

Tenga en cuenta que esto no usa setuid sino que simplemente ejecuta el archivo actual con Sudo.

24
rcrowley

Si quieres evitar llamar a Sudo some_script solo puedes hacer:

  #!/ust/bin/env sh

  Sudo /usr/local/scripts/your_script

Los programas SETUID deben diseñarse con extremo cuidado ya que se ejecutan con privilegios de root y los usuarios tienen un gran control sobre ellos. Necesitan comprobar la cordura de todo. No puede hacerlo con scripts porque:

  • Los shells son grandes piezas de software que interactúan fuertemente con el usuario. Es casi imposible verificar todo correctamente, especialmente porque la mayoría del código no está diseñado para ejecutarse en ese modo.
  • Las secuencias de comandos son en su mayoría una solución rápida y sucia y, por lo general, no están preparadas con tanto cuidado como para permitir setuid. Tienen muchas características potencialmente peligrosas.
  • Dependen en gran medida de otros programas. No es suficiente que se haya verificado el Shell. sed, awk, etc. también tendrían que verificarse

Tenga en cuenta que Sudo proporciona algunas comprobaciones de cordura, pero no es suficiente: verifique cada línea en su propio código.

Como última nota: considere usar capacidades. Le permiten otorgar a un proceso que se ejecuta como usuario privilegios especiales que normalmente requerirían privilegios de root. Sin embargo, por ejemplo, aunque ping necesita manipular la red, no necesita tener acceso a los archivos. Sin embargo, no estoy seguro si se heredan.

12
Maciej Piechotka

comando super [-r reqpath] [args]

Super permite que usuarios específicos ejecuten scripts (u otros comandos) como si fueran root; o puede establecer los grupos uid, gid y/o suplementarios por comando antes de ejecutar el comando. Está destinado a ser una alternativa segura a la creación de scripts de raíz setuid. Super también permite a los usuarios comunes proporcionar comandos para que otros los ejecuten; estos se ejecutan con el uid, gid y grupos del usuario que ofrece el comando.

Super consulta un archivo `` super.tab '' para ver si el usuario puede ejecutar el comando solicitado. Si se concede el permiso, super ejecutará pgm [args], donde pgm es el programa asociado con este comando. (La ejecución de raíz está permitida de manera predeterminada, pero aún se puede denegar si una regla excluye la raíz. Los usuarios normales no pueden ejecutar de manera predeterminada).

Si el comando es un enlace simbólico (o también un enlace duro) al superprograma, escribir% command args es equivalente a escribir% super command args (El comando no debe ser super o super no reconocerá que se invoca a través de un enlace.)

http://www.ucolick.org/~will/RUE/super/README

http://manpages.ubuntu.com/manpages/utopic/en/man1/super.1.html

5
Nizam Mohamed

Puede crear un alias para Sudo + el nombre del script. Por supuesto, eso es aún más trabajo de configurar, ya que también debes configurar un alias, pero te ahorra tener que escribir Sudo.

Pero si no le importan los horribles riesgos de seguridad, use un setuid Shell como intérprete para el script de Shell. No sé si eso funcionará para ti, pero supongo que podría funcionar.

Permítanme decir que desaconsejo hacer esto, sin embargo. Solo lo menciono con fines educativos ;-)

4
wzzrd

Si por alguna razón Sudo no está disponible, puede escribir un script de envoltura delgada 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 compila, configúrelo como setuid con chmod 4511 wrapper_script.

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

Tenga en cuenta que esto descarta el entorno por completo. Si desea usar algunas variables ambientales sin abrir vulnerabilidades, realmente solo necesita usar Sudo.

Obviamente, desea asegurarse de que el script solo se pueda escribir de raíz.

2
Chris