Bit SUID en shell script (I)

Hace unos días, después de realizar una instalación de una red de detección de intrusos dedicada, nos quedamos unos cuantos compañeros hablando sobre temas de seguridad. Durante la charla, hubo un apartado acerca de los scripts con bit suid y cómo, mientras en Solaris con la shell ksh se permitía la ejecución de un script suidado, el núcleo de Linux evitaba la ejecución de un script con suid. Antes de continuar con la entrada en el blog, es recomendable conocer el funcionamiento del bit suid. Veámoslo brevemente:

El bit suid es un permiso que se puede conceder tanto a directorios como ficheros, en el caso a tratar nos interesará cuando éste es concedido a ficheros.

El suid en ficheros es un permiso muy especial, que permite a un usuario con permisos de ejecución para dicho fichero, tomar los permisos del dueño del fichero durante su ejecución. Un ejemplo sencillo para entender su funcionamiento el el binario passwd, que permite cambiar las contraseñas de los usuarios en entornos Unix/Linux. Este binario tiene como dueño el usuario root (administrador) y el bit suid activado, permitiendo de esta forma, a un usuario no privilegiado poder cambiar su contraseña. Sino sería imposible/muy inseguro que un usuario cambiara su contraseña, puesto que necesitaría permisos para leer y escribir en el fichero de contraseñas.

Para conceder los permisos suid a un fichero se realiza mediante el comando chmod con flag u+s o valor 4XXX de forma octal. Por ejemplo chmod 4711 binario. Cuando se lista un fichero, este bit se identifica con la letra “S” o “s” en la posición de ejecución del fichero para el usuario. Valdrá “s” cuando aparte del bit suid, tenga activado el bit de ejecución, mientras que valdra “S” cuando no tenga el bit de ejecución activado.

Volviendo a lo que les comentaba, durante los intercambios de opiniones, surgieron aquellas que pensaban que no tendría que ser el núcleo el encargado de permitir o no la ejecución de scripts en shell, sino el administrador el encargado de tomas las medidas oportunas y permitir la ejecución de aquellos scripts con suid que requieran.

Por otro lado, los que indicaban que se corría un gran riesgo al permitir la ejecución de estos scripts por la cantidad de problemas de seguridad (escalada de privilegios) que se podrían dar. Debido sobretodo, a un historial de vulnerabilidades pasadas muy grande. En relación con estos fallos históricos a los que se referían mis compañeros, me vienen a la mente dos vulnerabilidades que se hicieron famosas, y que expondré a continuación:

La primera se trataba de una condición de carrera. Veremos un ejemplo para entenderlo con mayor facilidad. Imaginen que ustedes, como usuarios no privilegiados y con no muy buenas intenciones, ven que hay un script de nombre “script.sh” con bit stuid cuyo contenido es:

#!/bin/sh
echo “Hola mundo”

Entonces crean un script que es una shell de nombre malvado.sh como la siguiente:

#!/bin/sh -i

A continuación, nos creamos un enlace estático al “script.sh” que lo llamaremos “racecond” de la siguiente forma:

ln /dir/script.sh /home/usuario_malvado/racecond

Y para finalizar un programa en C que va intentar forzar una condición de carrera. El cuyo pseudocódigo del script es el siguiente:

if (fork()) { 
           lanzamos racecond 
}
else { 
          eliminamos enlace racecond;
          mv malvado.sh racecond
}

Analicemos entonces lo que puede ocurrir cuando ejecutemos el programa en C. Al ejecutar la sentencia fork tendremos dos procesos, el proceso padre o original y el hijo. En el caso del proceso padre este ejecutara “racecond”, que es el enlace estático al script “script.sh” con suid, por lo que al ejecutarse este se cargará la shell requerida (/bin/sh).

Mientras esta shell se carga, hay una pequeña probabilidad que el proceso hijo creado elimine el enlace “racecond” y mueva el script malvado.sh a donde se encontraba racecond. Por lo que puede ocurrir que el proceso padre, una vez cargada la shell con bit del suid, no ejecute el “script.sh” sino “malvado.sh”, dando como resultado una shell de root.

Un tipo de vulnerabilidad parecida a ésta afecto a los Unix Darwin 10.3.9 (Mac OsX). Para eliminar está condición de carrera se emplearon los descriptores de ficheros (entre muchos otros motivos), por lo que esta vulnerabilidad no debería afectar a los núcleos más modernos.

La segunda vulnerabilidad que me viene a la cabeza era producida por no tomar las oportunas medidas de seguridad en la creación de scripts para shell. Cuando se crea un script, se indica en la primera línea que se trata de un script o ejecución (#!) y a continuación la shell con la que queremos ejecutarlo (por ejemplo, /bin/sh) dando lugar al conocido #!/bin/sh. Las siguientes lineas del fichero son el código que queremos que interprete nuestra shell.

¿Y cual es el problema de esto? El problema reside en que de esta forma se permite la inclusión de opciones en la shell. Veámoslo con otro ejemplo, imaginen que yo tuviera el mismo script “script.sh” de la vulnerabilidad anterior, cuando se ejecuta el script éste es interpretado de la siguiente forma:

/bin/sh script.sh

Si creo un enlace estático del script con suid cuyo nombre sea “-i” (ln – miscript.sh -i), borro la variable PATH asignando el valor “.” (PATH=.) y ejecuto el script (-i), lo que se ejecuta con permisos de suid es lo siguiente:

/bin/sh -i

Cuyo resultado será una shell interactiva con permisos de root, muy distinto al objetivo inicial que era mostrar “Hola mundo”. Por ello, se recomienda que se cierren las opciones de la shell mediante la incursión del guion “-”. En el script de ejemplo sería: “#!/bin/sh -”. Actualmente las shell protegen de este fallo incluyendo “./” o “/” delante del nombre del script por lo que ya no afecta a las shell modernas.

Como ven, independientemente de cómo esté construido el propio script de shell y los fallos que se hayan podido cometer en su construcción, las vulnerabilidades en scripts con el suid pueden ser enormes y es de entender que el kernel linux no permita la ejecución de un script con bit suid, ya que para dicha función ya existe el sudo. ¿Por qué seguro que no hay ninguna forma de ejecutar un script mediante el uso del bit suid?

Eso lo veremos en la siguiente entrada.

Comments

  1. Muchas gracias! Hoy pasé 2 horas intentando saber dónde estaba el fallo en un script que había ‘setuidado’,y gracias a tu explicación ya sé que por qué!.Muchas gracias,un saludo.