File Upload Vulnerabilities
Last updated
Last updated
En esta sección aprenderé sobre la función de cargar archivos como una fuente o vector poderoso para crear ataques que tomen el control del servidor web subiendo una webshell ahora bien, las formas de eludir los controles para subir webshell’s son especificas ya que los controles de seguridad en algunos casos pueden detectar archivos maliciosos.
Dado lo comunes que son las funciones de carga de archivos, saber cómo probarlas correctamente es un conocimiento esencial.
Las vulnerabilidades de carga de archivos ocurren cuando un servidor web permite a los usuarios cargar archivos en su sistema de archivos sin validar suficientemente elementos como su nombre, tipo, contenido o tamaño. No aplicar adecuadamente las restricciones sobre estos podría significar que incluso una función básica de carga de imágenes pueda usarse para cargar archivos arbitrarios y potencialmente peligrosos. Esto podría incluso incluir archivos de script del lado del servidor que permitan la ejecución remota de código.
En algunos casos, el hecho de cargar el archivo es por sí solo suficiente para causar daños. Otros ataques pueden implicar una solicitud HTTP de seguimiento del archivo, normalmente para activar su ejecución por parte del servidor.
El impacto de las vulnerabilidades se puede medir generalmente por 2 factores medibles los cuales son:
Que aspecto del archivo el sitio web no valida correctamente, puede ser por ejm: (Contenido, tamaño, tipo).
Que restricciones se imponen al archivo una vez se ha cargado, dependiendo de las restricciones que se le den al archivo de esa misma forma se podrá medir el nivel de gravedad o impacto que tendrá el archivo que se subió en la pagina.
En el peor de los casos el tipo de archivo que se sube no podría validar correctamente y entonces se podrian subir archivos como por ejm (.php, .jsp), y estos archivos a su vez pueden contener codigo malicioso que permite poder ejecutar conexión remota por medio de la web shell que se ejecuta dentro del archivo con codigo malicioso .php por ejm. otorgandole control total del servidor.
Otro factor importante a tener en cuenta y peligroso es que si por ejm: No se valida correctamente el nombre del archivo que se sube, se puede llegar a reemplazar por otros directorios internos del servidor que pueden llegar a comprometer la seguridad de la aplicacion, o si por ejm tampoco valida y es vulnerable al path traversal, se puede nombrar el archivo con un nombre arbitrario propio del servidor que son archivos criticos importantes del servidor en ese caso entraría en super peligro la aplicación, además eso significa que también se pueden subir archivos sobrescritos en ubicaciones imprevistas que se supone deben ser inmodificables.
No asegurarse de que el tamaño del archivo esté dentro de los umbrales esperados también podría permitir una forma de ataque de denegación de servicio (DoS), mediante el cual el atacante llena el espacio disponible en el disco.
Dados los peligros bastante obvios, es raro que los sitios web no tengan restricciones de ningún tipo sobre los archivos que los usuarios pueden cargar. Más comúnmente, los desarrolladores implementan lo que creen que es una validación sólida que es inherentemente defectuosa o puede ser fácilmente eludida.
Por ejemplo, pueden intentar incluir en la lista negra tipos de archivos peligrosos, pero no tienen en cuenta las discrepancias en el análisis al verificar las extensiones de los archivos. Al igual que con cualquier lista negra, también es fácil omitir accidentalmente tipos de archivos más oscuros que aún pueden ser peligrosos.
En otros casos, el sitio web puede intentar comprobar el tipo de archivo verificando propiedades que un atacante pueda manipular fácilmente utilizando herramientas como Burp Proxy o Repetidor.
En última instancia, incluso medidas de validación sólidas pueden aplicarse de manera inconsistente en toda la red de hosts y directorios que forman el sitio web, lo que genera discrepancias que pueden explotarse.
Antes de ver cómo explotar las vulnerabilidades de carga de archivos, es importante que tenga un conocimiento básico de cómo los servidores manejan las solicitudes de archivos estáticos.
Históricamente, los sitios web consistían casi en su totalidad en archivos estáticos que se entregaban a los usuarios cuando los solicitaban. Como resultado, la ruta de cada solicitud se podría asignar 1:1 con la jerarquía de directorios y archivos en el sistema de archivos del servidor. Hoy en día, los sitios web son cada vez más dinámicos y la ruta de una solicitud a menudo no tiene ninguna relación directa con el sistema de archivos. Sin embargo, los servidores web todavía procesan solicitudes de algunos archivos estáticos, incluidas hojas de estilo, imágenes, etc.
El proceso para manejar estos archivos estáticos sigue siendo prácticamente el mismo. En algún momento, el servidor analiza la ruta en la solicitud para identificar la extensión del archivo. Luego usa esto para determinar el tipo de archivo que se solicita, generalmente comparándolo con una lista de asignaciones preconfiguradas entre extensiones y tipos MIME. Lo que sucede a continuación depende del tipo de archivo y de la configuración del servidor.
Si este tipo de archivo no es ejecutable, como una imagen o una página HTML estática, el servidor puede simplemente enviar el contenido del archivo al cliente en una respuesta HTTP.
Si el tipo de archivo es ejecutable, como un archivo PHP, y
el servidor está configurado para ejecutar archivos de este tipo, asignará variables según los encabezados y parámetros de la solicitud HTTP antes de ejecutar el script. El resultado resultante se puede enviar al cliente en una respuesta HTTP.
💡 Consejo: el
Content-type:
encabezado dentro de una solicitud HTTP de respuesta puede proporcionar pistas sobre que tipo de archivo cree el servidor que ha entregado. Si este encabezado no ha sido establecido explicitamente por el codigo de la aplicación, entonces normalmemente tendrá el estandar de extensiones archivo/tipo MIME, que es un estandar de archivos para mandar contenido a través de la red. Singificado de internet abajo ⬇️
Desde una perspectiva de seguridad, el peor escenario posible es cuando un sitio web le permite cargar scripts del lado del servidor, como archivos PHP, Java o Python, y también está configurado para ejecutarlos como código. Esto hace que sea trivial crear su propio shell web en el servidor.
⚠️ Shell Web
Un web shell es un script malicioso que permite a un atacante ejecutar comandos arbitrarios en un servidor web remoto simplemente enviando solicitudes HTTP al punto final correcto.
Si podemos cargar con exito una web shell con exito podremos decir y saber que podremos ejecutar comandos dentro del directorio del servidor, crear archivos, ejecutar scripts que puedan comprometer drasticamente la seguridad interna de la infraestructura de la empresa o tomar poder del servidor para realizar ataques externos, también super importante podríamos filtrar información confidencial interna que se supone es confidencial vendría siendo una mezcla de disclosure information con RCE (Remote Code Execution).
Por ejemplo, el siguiente resumen PHP podría usarse para leer archivos arbitrarios del sistema de archivos del servidor:
Una vez cargado, enviar una solicitud para este archivo malicioso devolverá el contenido del archivo de destino en la respuesta.
Aunque pueden existir web shell más versatiles, como:
Este script le permite pasar un comando del sistema arbitrario a través de un parámetro de consulta de la siguiente manera:
En la practica en la vida real será probable que no lleguemos a encontrar paginas o sitios web que no implementen una defensa contra la carga o subida de archivos, sin embargo crear una defensa no significa que sea solida, podemos llegar a subir archivos más avanzados que puedan permitirnos ejecutar una web shell para la RCE (Ejecución Remota de Codigo).
Al enviar formularios HTML, el navegador normalmente envía los datos proporcionados en una POST
solicitud con el tipo de contenido application/x-www-form-url-encoded
. Esto está bien para enviar texto simple como su nombre o dirección. Sin embargo, no es adecuado para enviar grandes cantidades de datos binarios, como un archivo de imagen completo o un documento PDF. En este caso, multipart/form-data
se prefiere el tipo de contenido.
Considere un formulario que contenga campos para cargar una imagen, proporcionar una descripción de la misma e ingresar su nombre de usuario. Enviar un formulario de este tipo podría dar como resultado una solicitud similar a esta:
Como puede ver, el cuerpo del mensaje se divide en partes separadas para cada una de las entradas del formulario. Cada parte contiene un Content-Disposition
encabezado, que proporciona información básica sobre el campo de entrada con el que se relaciona. Estas partes individuales también pueden contener su propio Content-Type
encabezado, que le indica al servidor el tipo MIME de los datos que se enviaron utilizando esta entrada.
Una forma en que los sitios web pueden intentar validar la carga de archivos es verificar que este Content-Type
encabezado específico de entrada coincida con un tipo MIME esperado. Si el servidor solo espera archivos de imagen, por ejemplo, es posible que solo permita tipos como image/jpeg
y image/png
. Pueden surgir problemas cuando el servidor confía implícitamente en el valor de este encabezado. Si no se realiza ninguna validación adicional para verificar si el contenido del archivo realmente coincide con el supuesto tipo MIME, esta defensa se puede eludir fácilmente utilizando herramientas como Burp Repeater.
Vamos a ver un lab para entender bien todo esto:
Si bien es claramente correcto evitar que un server no permita ejecutar archivos de subida de una aplicación, por añadidura de esto también es importante sanitizar que no se puedan ejecutar archivos (scripts) arbitrarios en cualquier lugar (directorio fuera de la red) de la aplicación ya que es otra forma de eludir controles robustoz de seguridad.
Como precaución los servidores solo ejecutan archivos o scripts MIME los cuales han sido explicitamente aceptados de lo contrario es posible que devuelva un error con un mensaje o retorne el script o archivo sin formato plano de texto.
Este comportamiento es importante en sí mismo ya que puede proporcionar una forma de filtrar el codigo fuente
Esto se debe a que cuando un servidor devuelve un script o archivo sin formato de texto, en realidad está revelando el código fuente de ese script o archivo. Este código fuente puede contener información sensible, como claves de API, contraseñas o lógica empresarial importante. Un atacante puede aprovechar esta información para entender mejor cómo funciona la aplicación y encontrar posibles vulnerabilidades para explotar. Por lo tanto, es crucial que los servidores estén configurados para no devolver archivos de script sin procesar para mitigar este riesgo.
Sin embargo se anula toda posibilidad de crear una web shell.
Para protegerse contra esto, los administradores de servidores a menudo deshabilitan la ejecución de scripts en todos los directorios que pueden ser escritos por el usuario. Esto puede incluir cualquier lugar donde los usuarios puedan cargar archivos. Sin embargo, si un atacante logra identificar un directorio que ha sido mal configurado y permite la ejecución de scripts, aún podrían aprovechar la carga de archivos para implementar una shell web.
Este tipo de configuración suele diferir entre directorios. Un directorio en el que se cargan archivos proporcionados por el usuario probablemente tendrá controles mucho más estrictos que otras ubicaciones del sistema de archivos que se supone están fuera del alcance de los usuarios finales. Si puede encontrar una manera de cargar un script en un directorio diferente que no debe contener archivos proporcionados por el usuario, el servidor puede ejecutar su script después de todo.
⚠️ Los servidores web suelen utilizar el
filename
campo enmultipart/form-data
las solicitudes para determinar el nombre y la ubicación donde se debe guardar el archivo.
También debe tener en cuenta que, aunque puede enviar todas sus solicitudes al mismo nombre de dominio, esto a menudo apunta a un servidor proxy inverso de algún tipo, como un equilibrador de carga. Sus solicitudes a menudo serán manejadas por servidores adicionales detrás de escena, que también pueden configurarse de manera diferente.
Es cierto que las aplicaciones usan listas negras con el fin de eludir la subida de archivos posiblemente peligrosos (Scripts maliciosos, exploits) .php
por ejm, en una aplicación sin embargo la practica de incluir en una lista negra los tipos de archivos peligrosos es intrinsecamente defectuosa ya que puede ser dificil bloquear todos los tipos de archivos peligrosos que pueden haber, por ejm se pueden usar archivos alternativos que pueden igualmente ejecutar el mismo codigo que aun que son menos conocidas pueden ejecutar lo mismo, por ejm tipo .php5, .shtml
etc…
En este caso, los servidores web de una aplicacion normalmente no ejecutan archivos a menos que hayan sido configurados para hacerlo, por ejm: antes de que un servidor Apache ejecute las funcionalidades .PHP creadas para el cliente, los desarrolladores primero tienen que poner las siguientes directivas de archivos de configuración para cargar el modulo de PHP, en el directorio /etc/apache2/apache2.conf
archivo:
Muchos servidores tambien permiten a los desarrolladores crear archivos de configuracion especiales dentro de directorios individuales para anular o agregar más configuraciones globales.
Los servidores Apache por ejm: cargarán una configuración especifica del directorio en un archivo llamado .htaccess
si hay uno presente.
Ocurre de manera un poco parecida con los servidores de internet los IIS que le permiten a los desarrolladores utilizar un web.config con una configuracion especifica en un directorio, eso a su vez permitiria añadir directivas o configuraciones que pueden permitir en este caso entregar archivos JSON a los usuarios.
Los servidores web utilizan este tipo de archivos de configuración cuando están presentes, pero normalmente no se le permite acceder a ellos mediante solicitudes HTTP. Sin embargo, es posible que ocasionalmente encuentre servidores que no le impidan cargar su propio archivo de configuración malicioso. En este caso, incluso si la extensión de archivo que necesita está en la lista negra, es posible que pueda engañar al servidor para que asigne una extensión de archivo personalizada y arbitraria a un tipo MIME ejecutable.
Incluso si las listas negras han sido muy exhaustivamente reforzadas y tienen muchos tipos de extensiones prohibidas para subir y evitarse ataques, igualmente siempre hay una forma de saltarse la seguridad, en este caso se pueden ofuscar las extensiones de archivos para bypasear el control de la lista negra, Por ejm digamos que el codigo de validación de archivos junto con sus extensiones valida y verifica la extensiones, distingue entre MAYUSCULAS Y Minusculas sin embargo no reconoce el archivo: exploit.pHp
y ahí eso ya es un problema ya que aunque distingue entre may y minusculas no distingue entre ambas juntas, eso es problema porque se puede subir el archivo malicioso, y eso solo es un ejemplo clasico, vamos a ver otros.
Si el código que posteriormente asigna la extensión del archivo a un tipo MIME no distingue entre mayúsculas y minúsculas, esta discrepancia le permite pasar la validación de archivos PHP maliciosos que eventualmente pueden ser ejecutados por el servidor.
Otros ejemplos:
Proporcionar múltiples extensiones. Dependiendo del algoritmo utilizado para analizar el nombre del archivo, el siguiente archivo puede interpretarse como un archivo PHP o una imagen JPG:exploit.php.jpg
Añade caracteres finales. Algunos componentes eliminarán o ignorarán los espacios en blanco finales, puntos y cosas por el estilo:exploit.php.
Intente utilizar la codificación de URL (o codificación de URL doble) para puntos, barras diagonales y barras diagonales invertidas. Si el valor no se decodifica al validar la extensión del archivo, pero luego se decodifica en el lado del servidor, esto también puede permitirle cargar archivos maliciosos que de otro modo se bloquearían:exploit%2Ephp
Agregue punto y coma o caracteres de bytes nulos codificados en URL antes de la extensión del archivo. Si la validación está escrita en un lenguaje de alto nivel como PHP o Java, pero el servidor procesa el archivo usando funciones de nivel inferior en C/C++, por ejemplo, esto puede causar discrepancias en lo que se trata como el final del nombre del archivo: exploit.asp;.jpg
oexploit.asp%00.jpg
Intente utilizar caracteres Unicode multibyte, que pueden convertirse en bytes y puntos nulos después de la conversión o normalización Unicode. Secuencias como xC0 x2E
, xC4 xAE
o xC0 xAE
pueden traducirse a x2E
si el nombre del archivo se analiza como una cadena UTF-8, pero luego se convierte a caracteres ASCII antes de usarse en una ruta.
Podrías intentar agregar caracteres especiales al nombre del archivo. Por ejemplo, podrías llamar al archivo "exploit.asp;.jpg" o "exploit.asp%00.jpg". Algunos sistemas podrían ignorar los caracteres después del punto y coma o el carácter de byte nulo (%00), y tratarlo como un archivo ASP.
Finalmente, podrías intentar usar caracteres Unicode multibyte, que pueden convertirse en bytes y puntos nulos después de una conversión o normalización Unicode. Estos podrían ser interpretados de manera diferente por el sistema de seguridad y el servidor, permitiéndote subir un archivo malicioso.
Otras defensas implican eliminar o reemplazar extensiones peligrosas para evitar que se ejecute el archivo. Si esta transformación no se aplica de forma recursiva, puede colocar la cadena prohibida de tal manera que al eliminarla aún quede una extensión de archivo válida. Por ejemplo, considere lo que sucede si elimina .php
el siguiente nombre de archivo:
Esta es sólo una pequeña selección de las muchas formas en que es posible ofuscar las extensiones de archivos.
Puede ocurrir que la subida de un archivo no sea de forma local, sino mediante una URL esto proporcionado mediante HTTP, entonces al momento de la validación los desarrolladores no puedan usar los metodos dentro de su marco web de la aplicación para validar el archivo ya que viene de una URL del mismo internet, por ello a veces se tiende a crear un directorio temporal donde el servidor web aloja el archivo que fue subido por URL para procesarlo y validarlo o para denegarlo, ahora bien esto puede ser peligroso y contraproducente para la app si el atacante conoce el directorio donde está.
Por ejemplo, si el archivo se carga en un directorio temporal con un nombre aleatorio, en teoría, debería ser imposible para un atacante aprovechar las condiciones de carrera. Si no conocen el nombre del directorio, no podrán solicitar el archivo para activar su ejecución. Por otro lado, si el nombre del directorio aleatorio se genera utilizando funciones pseudoaleatorias como PHP uniqid()
, potencialmente puede ser forzado de forma bruta.
Para facilitar ataques como este, puede intentar ampliar la cantidad de tiempo necesario para procesar el archivo, alargando así la ventana para forzar el nombre del directorio por fuerza bruta. Una forma de hacerlo es cargando un archivo más grande. Si se procesa en fragmentos, puede aprovechar esto creando un archivo malicioso con la carga útil al principio, seguido de una gran cantidad de bytes de relleno arbitrarios.
En los ejemplos que hemos visto hasta ahora, hemos podido cargar scripts del lado del servidor para la ejecución remota de código. Esta es la consecuencia más grave de una función de carga de archivos insegura, pero estas vulnerabilidades aún pueden explotarse de otras maneras.
Aunque es posible que no pueda ejecutar scripts en el servidor, aún puede cargar scripts para ataques del lado del cliente. Por ejemplo, si puede cargar archivos HTML o imágenes SVG, potencialmente puede usar <script>
etiquetas para crear cargas útiles XSS almacenadas.
Si el archivo cargado aparece en una página visitada por otros usuarios, su navegador ejecutará el script cuando intente mostrar la página. Tenga en cuenta que debido a las restricciones de la política del mismo origen, este tipo de ataques solo funcionarán si el archivo cargado proviene del mismo origen en el que lo cargó.
Si el archivo cargado parece estar almacenado y entregado de forma segura, el último recurso es intentar explotar las vulnerabilidades específicas del análisis o procesamiento de diferentes formatos de archivo. Por ejemplo, si sabe que el servidor analiza archivos basados en XML, como archivos de Microsoft Office .doc
o .xls
archivos, esto puede ser un vector potencial para ataques de inyección XXE.
Vale la pena señalar que algunos servidores web pueden estar configurados para admitir PUT
solicitudes. Si no existen las defensas adecuadas, esto puede proporcionar un medio alternativo para cargar archivos maliciosos, incluso cuando la función de carga no esté disponible a través de la interfaz web.
Puede intentar enviar OPTIONS
solicitudes a diferentes puntos finales para probar si hay alguno que anuncie soporte para el PUT
método.
Cuando envías una solicitud OPTIONS a un punto final (endpoint), estás solicitando información sobre qué métodos HTTP son permitidos en ese recurso específico. Aquí hay un ejemplo de cómo sería una solicitud OPTIONS:
Y una respuesta típica podría ser algo así:
Permitir a los usuarios cargar archivos es algo común y no tiene por qué ser peligroso siempre que se tomen las precauciones adecuadas. En general, la forma más eficaz de proteger sus propios sitios web de estas vulnerabilidades es implementar todas las prácticas siguientes:
Verifique la extensión del archivo con una lista blanca de extensiones permitidas en lugar de una lista negra de extensiones prohibidas. Es mucho más fácil adivinar qué extensiones es posible que desee permitir que adivinar cuáles podría intentar cargar un atacante.
Asegúrese de que el nombre del archivo no contenga ninguna subcadena que pueda interpretarse como un directorio o una secuencia transversal ( ../
).
Cambie el nombre de los archivos cargados para evitar colisiones que puedan provocar que se sobrescriban los archivos existentes.
No cargue archivos al sistema de archivos permanente del servidor hasta que hayan sido completamente validados.
En la medida de lo posible, utilice un marco establecido para preprocesar la carga de archivos en lugar de intentar escribir sus propios mecanismos de validación.
Eso sería lo relacionado a Subida de archivos vulnerables, si llegaste hasta aquí gracias por leerme. :)
Si el tipo de archivo es ejecutable, pero el servidor no está configurado para ejecutar archivos de este tipo, generalmente responderá con un error. Sin embargo, en algunos casos, es posible que el contenido del archivo aún se entregue al cliente como texto sin formato. En ocasiones, estas configuraciones erróneas pueden aprovecharse para filtrar el código fuente y otra información confidencial. Puede ver un de esto en nuestros materiales de aprendizaje.