⬆️Filtros de tipo
Última actualización
Última actualización
Hasta ahora, solo hemos trabajado con filtros de tipo que solo consideran la extensión del archivo en el nombre del archivo. Sin embargo, como vimos en la sección anterior, aún podemos obtener el control sobre el servidor back-end incluso con extensiones de imagen (por ejemplo, shell.php.jpg
). Además, podemos utilizar algunas extensiones permitidas (por ejemplo, SVG) para realizar otros ataques. Todo esto indica que solo probar la extensión del archivo no es suficiente para prevenir ataques de carga de archivos.
Por este motivo, muchos servidores web y aplicaciones web modernos también prueban el contenido del archivo cargado para asegurarse de que coincida con el tipo especificado. Si bien los filtros de extensión pueden aceptar varias extensiones, los filtros de contenido generalmente especifican una sola categoría (por ejemplo, imágenes, videos, documentos), por lo que no suelen utilizar listas negras o listas blancas. Esto se debe a que los servidores web brindan funciones para verificar el tipo de contenido del archivo y, por lo general, este pertenece a una categoría específica.
Existen dos métodos comunes para validar el contenido de un archivo: Content-Type Header
o Contenido del archivo
. Veamos cómo podemos identificar cada filtro y cómo evitarlos.
En este ejercicio intentaremos cargar un script PHP:
Vemos que recibimos un mensaje que dice Only images are allowed
. El mensaje de error persiste y nuestro archivo no se carga incluso si intentamos algunos de los trucos que aprendimos en las secciones anteriores. Si cambiamos el nombre del archivo a shell.jpg.phtml
o shell.php.jpg
, o incluso si usamos shell.jpg
con un contenido de webshell, nuestra carga fallará. Como la extensión del archivo no afecta el mensaje de error, la aplicación web debe estar probando el contenido del archivo para la validación de tipo. Como se mencionó anteriormente, esto puede estar en el Content-Type Header
o en el Contenido del archivo
.
El siguiente es un ejemplo de cómo una aplicación web PHP prueba el encabezado Content-Type
para validar el tipo de archivo:
El código establece la variable ($type
) del encabezado Content-Type
del archivo cargado . Nuestros navegadores establecen automáticamente el encabezado Content-Type al seleccionar un archivo a través del cuadro de diálogo del selector de archivos, que generalmente se deriva de la extensión del archivo. Sin embargo, dado que nuestros navegadores lo establecen, esta operación es una operación del lado del cliente y podemos manipularla para cambiar el tipo de archivo percibido y, potencialmente, omitir el filtro de tipo.
Podemos empezar por analizar el encabezado Content-Type con la lista de palabras Content-Type de SecLists a través de Burp Intruder, para ver qué tipos están permitidos. Sin embargo, el mensaje nos dice que solo se permiten imágenes, por lo que podemos limitar nuestro análisis a los tipos de imágenes, lo que reduce la lista de palabras solo a 45
tipos (en comparación con las 700 originales). Podemos hacerlo de la siguiente manera:
Aquí podríamos realizar un fuzzing con Burp Intruder seleccionando como variable el Content-Type
y usar esta lista de content-types para ver cual nos da una respuesta positiva.
Para simplificar, simplemente seleccionemos un tipo de imagen (por ejemplo image/jpg
), luego interceptemos nuestra solicitud de carga y cambiemos el encabezado Content-Type:
Esta vez obtenemos File successfully uploaded
, y si visitamos nuestro archivo, vemos que se cargó correctamente:
Nota: Una solicitud HTTP de carga de archivo tiene dos encabezados Content-Type, uno para el archivo adjunto (en la parte inferior) y otro para la solicitud completa (en la parte superior). Por lo general, necesitamos modificar el encabezado Content-Type del archivo, pero en algunos casos la solicitud solo contendrá el encabezado Content-Type principal (por ejemplo, si el contenido cargado se envió como datos POST
), en cuyo caso necesitaremos modificar el encabezado Content-Type principal.
El segundo y más común tipo de validación del contenido de un archivo es probar el MIME-Type
. Multipurpose Internet Mail Extensions (MIME)
es un estándar de Internet que determina el tipo de un archivo a través de su formato general y estructura de bytes.
Esto se hace generalmente inspeccionando los primeros bytes del contenido del archivo, que contienen la Firma del Archivo o los Bytes Mágicos . Por ejemplo, si un archivo comienza con ( GIF87a
o GIF89a
), esto indica que es una imagen GIF
, mientras que un archivo que comienza con texto simple generalmente se considera un archivo Text
. Si cambiamos los primeros bytes de cualquier archivo a los bytes mágicos GIF, su tipo MIME se cambiaría a una imagen GIF, independientemente de su contenido restante o extensión.
Consejo: Muchos otros tipos de imágenes tienen bytes no imprimibles para sus firmas de archivo, mientras que una imagen GIF
comienza con bytes imprimibles ASCII (como se muestra arriba), por lo que es la más fácil de imitar. Además, como la cadena GIF8
es común entre ambas firmas GIF, suele ser suficiente para imitar una imagen GIF.
Tomemos un ejemplo básico para demostrarlo. El comando file
en sistemas Unix encuentra el tipo de archivo a través del tipo MIME. Si creamos un archivo básico con texto en su interior, se lo considerará un archivo de texto, de la siguiente manera:
Como vemos, el tipo MIME del archivo es ASCII text
, aunque su extensión sea .jpg
. Sin embargo, si escribimos GIF8
al principio del archivo, se considerará una imagen GIF
, aunque su extensión siga siendo .jpg
:
Los servidores web también pueden utilizar este estándar para determinar los tipos de archivos, lo que suele ser más preciso que comprobar la extensión del archivo. El siguiente ejemplo muestra cómo una aplicación web PHP puede comprobar el tipo MIME de un archivo cargado:
Como podemos ver, los tipos MIME son similares a los que se encuentran en los encabezados Content-Type, pero su origen es diferente, ya que PHP utiliza la mime_content_type()
función para obtener el tipo MIME de un archivo. Intentemos repetir nuestro último ataque, pero ahora con un ejercicio que prueba tanto el encabezado Content-Type como el tipo MIME:
Una vez que enviamos nuestra solicitud, notamos que recibimos el mensaje de error Only images are allowed
. Ahora, intentemos agregar GIF8
antes de nuestro código PHP para intentar imitar una imagen GIF mientras mantenemos nuestra extensión de archivo como .php
, de modo que se ejecute el código PHP de todos modos:
Esta vez obtenemos File successfully uploaded
, y nuestro archivo se carga exitosamente al servidor:
Ahora podemos visitar nuestro archivo cargado, y veremos que podemos ejecutar exitosamente los comandos del sistema:
Nota: Vemos que la salida del comando comienza con GIF8
, ya que esta fue la primera línea en nuestro script PHP para imitar los bytes mágicos GIF, y ahora se genera como texto simple antes de que se ejecute nuestro código PHP.
Podemos utilizar una combinación de los dos métodos analizados en esta sección, lo que puede ayudarnos a eludir algunos filtros de contenido más robustos. Por ejemplo, podemos intentar utilizar un MIME type con un Content-Type no permitido
, un MIME/Content-Type permitido con una extensión no permitida
, o un MIME/Content-Type no permitido con una extensión permitida
, y así sucesivamente. De manera similar, podemos intentar otras combinaciones y permutaciones para intentar confundir al servidor web y, según el nivel de seguridad del código, es posible que podamos eludir varios filtros.
El servidor mencionado anteriormente emplea filtros Client-Side, Blacklist, Whitelist, Content-Type y MIME-Type para garantizar que el archivo cargado sea una imagen. Intente combinar todos los ataques que aprendió hasta ahora para eludir estos filtros y cargar un archivo PHP y leer la flag en "/flag.txt
".
La cosa se nos complica, pero vamos al lio!
Esta aplicación ya la conocemos más que de sobra. Vamos a realizar todas las técnicas que conocemos para bypassear la subida de una shell.
Como ya sabemos, debemos eliminar desde el inspector el script de validación y los formatos aceptados:
Subimos la webshell phpbash.php
y lo primero que nos da es un error Extension not allowed
:
Vamos a enviar la petición a Intruder
para fuzzear posibles extensiones, al igual que vimos en la sección anterior. Vamos a usar la wordlists de extensiones de Seclists
Lo primero añadimos la extensión como variable en Intruder:
En la pestaña Payloads
pegamos las extensiones de Seclists y desactivamos la opción de Payload encoding
:
Iniciamos el fuzzing y observamos los resultados. Tenemos 2 tipos de respuestas:
Lenght 225: Extension not allowed
Lenght 227/226: Only images are allowed
Podríamos probar por ejemplo esa extensión .phar
con una inyección de caracteres con una doble extensión, lo que podría bypassear los filtros.
Vamos a probar a subir una imagen legítima para probar los tipos de content-types con status 200, y vamos a añadir el Content-Type
como variable:
En la pestaña de Payloads vamos a pegar la lista de palabras de Content-Type de Seclists, pero para que el fuzzing sea más efectivo vamos a filtrar solamente las de formato imagen, reduciendo considerablemente el tiempo del fuzzing:
Importante también desmarcar la casilla Payload encoding.
Lanzamos el fuzzing y vemos los resultados. Vemos un montón de extensiones de imagen permitidas, como gif
, jpeg
, png
...
Vemos que nos permite subir GIFs
, con lo que podríamos probar a cambiar el MYME-Type
del archivo para intentar cargar una shell.php
que imite a un GIF
Vamos a probar a poner GIF8
delante de nuestra shell.php:
Lo subimos y capturamos la petición. Nos da un extension not allowed
, pero fijaros que pasa si ponemos una doble extensión a la shell como shell.gif.phar
:
Nota: En archivos ejecutables es importante poner la extensión al final (.phar, .php...), ya que sino el servidor dará un error al abrir el archivo
Se sube la shell correctamente!
Ahora si accedemos a la siguiente url comprobamos que funciona la shell y nos devuelve el ID del usuario:
Obtenemos la flag de la misma manera, y como vemos antes de la flag se inserta el texto GIF8 que se corresponde con los Magic Bytes del formato GIF: