martes, 25 de abril de 2017

By Design (5)

Sube que te sube

Una página que permite subir un fichero a un servidor... ¿qué puede salir mal?

No quiero dar por ahora el nombre de la aplicación porque me consta que en estos días está siendo sometida a una importante "reforma" y quisiera antes ver cómo termina la cosa. Pero, sin decir el "pecador", sí voy a mencionar uno de sus muchos, y en ocasiones importantes, "pecados". De modo que en los ejemplos que siguen se mostrará un formulario que he creado aprovechando su código (se trata de software libre) y copiando su comportamiento.

La idea es que el usuario sube un fichero y la aplicación lo almacena en cierto directorio. El nombre con el que el fichero es guardado se determina teniendo en cuenta el valor de una variable de configuración:
  • Si el valor de ésta es verdadero, se usa un nombre predeterminado y se le pone como extensión la del fichero original. Esta extensión se obtiene tomando los caracteres que sigan al último carácter de punto (.). Esta es la opción por defecto.
  • Si es falso, se usa el nombre y la extensión del fichero original.
El primer error de diseño, aunque es algo que no siempre es posible evitar, es que por defecto el directorio en el que se copian los ficheros subidos es un subdirectorio de la propia aplicación web. Peligroso. Muy peligroso. Pero no todo está perdido (¿o sí?): los desarrolladores implementan un control de las extensiones de archivo permitidas con objeto de impedir que alguien cuele algo dañino:

if (preg_match("#\.(exe|com|bat|zip|php|phps|php3|phtml|phtm|cgi)$#i", $uploadFile["name"]))
  // file name has an invalid file name extension (adjust the regex pattern if you want more relaxed
  //file name validation)
   $errors["uploadFile"] = "You cannot upload this type of file!";
  // file name must not end with .exe, .com, .bat, .zip, .php, .phps, .php3, .phtml, .phtm or .cgi

El resultado es que si, por ejemplo, se intenta subir un fichero PHP, la aplicación se niega a obedecer:
Imagen 1 - No puedes subir este tipo de fichero


Así que nada de ".exe, .com, .bat, .zip, .php, .phps, .php3, .phtml, .phtm or .cgi" ¿verdad?

Blanco. Blanco como la nieve

Cuando de medidas de seguridad se trata, es más fácil terminar arrepentiéndose de haber usado una lista negra que de usar una lista blanca.

Y, para muestra, un botón. Porque mirando esas extensiones me pregunto: ¿y si, por ejemplo, la aplicación estuviera corriendo en un servidor Apache que tuviera instalados módulos como mod_mono, que permite la ejecución de páginas ASP.NET? ¿Y si, entonces, alguien subiera un fichero con extensión .ASPX?

Sin necesidad de ir tan lejos, lo que más a mano tenía yo para hacer pruebas era un servidor XAMPP sobre Windows. Y... XAMPP trae Perl activado. Así que probé a subir un fichero con extensión ".PL":
Imagen 2 - Subiendo CGI en Perl
Et voilà:
Imagen 3 - Ejecutando el CGI

Lo mejor es que si se cambia el shebang (eso que empieza por #! en la primera línea) se puede invocar otros programas para interpretar el programa. Eso sí, teniendo que lidiar "a mano" con las complejidades del CGI. Que, por otro lado, tampoco son tantas ni tan grandes. Por ejemplo, ahí va PHP:
Imagen 4 - CGI escrito en PHP


O, puesto que estoy ejecutando Apache como usuario (no como servicio) ¿por qué no acordarnos de nuestra gran amiga, la calculadora?
Imagen 5 - La calculadora es buena. La calculadora es tu amiga

También me llama la atención que, aunque se prohíbe la subida de ficheros .EXE, no se dice nada de los PS1 de Powershell, los .VBS y los .JS de Windows Scripting Host, etc., que pueden proporcionar funcionalidades similares.

Todo ello por no pensar en que alguien suba un fichero HTML y tenga su XSS bien asentadito. O su tienda ilegal de fármacos o de falsificaciones de prendas de moda.

Es lo que tienen las listas negras: o estás al tanto de todas las posibilidades y sus consecuencias y lo haces todo a la perfección o después toca lamentarse.

Pero aún hay más. Hagamos borrón y cuenta nueva y exploremos otro camino.

Regular. O sea: ni bueno ni malo

Las expresiones regulares facilitan mucho las cosas a los programadores. Son potentes y sencillas... pero no siempre fáciles.

Quizá sea una trivialidad, pero resulta que para resolver un problema usando expresiones tienes que dominar dos cosas:
  • El problema que quieres resolver y sus condicionantes
  • Las expresiones regulares
Y cuando todo sale mal, muchas veces no sabes en cuál de ellas se falló. Como dije antes, yo estaba usando XAMPP sobre Windows. Uno de los muchos entornos en los que la aplicación puede ser desplegada. Y, como cada uno de ellos, con sus características propias.

En este caso, basta con fijarse en una de ellas: cuando en Windows creas un fichero cuyo nombre tiene espacios al final, dichos espacios son ignorados. Como una imagen vale más que mil palabras:
Imagen 6 - Espacio

 De modo que, si se intenta subir un fichero PHP...
Imagen 7 - Subiendo PHP


... y se intercepta la petición usando un proxy como ZAP y se modifica el nombre del fichero, añadiendo un espacio al final (y también el tamaño de la petición, que habrá que incrementar en uno como consecuencia de lo anterior)...
Imagen 8 - Modificando la petición

 ... El resultado será
Imagen 9 - Subido...

 Y, si se conoce en qué directorio ha sido colocado, el fichero será de lo más "aprovechable"

Imagen 10 - ...Y funcionando

Podría ser aún peor. Porque si se hubiera configurado la aplicación para no renombrar archivos, en lugar del espacios al final del nombre también valdría con uno o varios puntos:
Imagen 11 - Por si fuera poco

Por ir acabando

Menos mal que la aplicación hace otras comprobaciones y no deja que el nombre del fichero contenga cosas rarunas, como la barra  de directorio o la barra invertida. En realidad, ahí se hace un buen diseño y se limita los caracteres válidos, tanto para ficheros como para directorios, a un conjunto muy preciso: "a-zA-Z0-9+_.-" para los primeros y "a-zA-Z0-9+_-" para los segundos.

Lástima que las comprobaciones se hacen sólo para la parte del "nombre de archivo" y no para la de la extensión.

Por si a alguien le cabía duda: lo del upload era sólo la anécdota. Lo que realmente quería decir con este post era:
  • Eso de que los ficheros subidos vayan a un directorio que está publicado por el servidor web es muy, muy, muy mala idea.
  • En general, para esto de la seguridad: Lista negra= caca. Lista negra= problemas. Lista negra = quebraderos de cabeza.
  • Las expresiones regulares son como las cerillas: son algo bueno, pero tienes que ser responsable y saber lo que estás haciendo antes de usarlas. No hacen magia. No resuelven tus problemas. Sólo son una herramienta rápida para que tú los soluciones. Si tú te equivocas, las expresiones regulares te ayudarán a equivocarte más rápido.
No es éste, el de la subida de ficheros, el único problema de seguridad de la aplicación. Ésa cuyo nombre por ahora callo pero que alguien seguro que intentará, y posiblemente logrará, localizar. Que para eso están los buscadores.

Tiene tantos como para dedicarle un blog entero.

Lo bueno es que de los errores se aprende. Y mejor que sea de los ajenos.
(Continuará...)