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.
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 |
![]() |
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
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.
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á...)
No hay comentarios:
Publicar un comentario