The TryHack3M: Subscribe room hosted by TryHackMe challenges to help Hack3M reach 3M subscribers. More details can be found here: https://tryhackme.com/r/room/subscribe
I used a Kali Linux VM in VirtualBox and connected to the TryHackMe machine via OpenVPN.
We have good news and bad news! The good news is that we are about to hit 3 million users on our platform, and the bad news is;Well, last night, the UnderGround (UG) Hackers attacked our website, hackme.thm, and took complete control. They were able to turn off the signup page, so there won’t be any new registrations. Given this, our user count is stuck at 2.99 Million.
Can you help us restore the registration panel on our site to reach our 3 million user milestone?
Room Objectives
To assist the HackM3.thm company in fixing the application, you need to:
Explore the web server and find the attack vectors leveraged by the attacker.
Regain access and restore the signup functionality for the new users.
Investigate the web application logs and track down the root cause.
Lab Connection
Before moving forward, start the lab by clicking the Start Machine button. It will take 3-5 minutes to load properly. We can’t wait to get our site restored and resume our 3M celebrations!
Good luck!
I have successfully started the machine.
After having started the machine, I just added hackme.thm to my hosts file to make the following hacking a bit more comfortable.
Sometimes, the attacker leaves footprints that allow you to regain access to the server. Can you help HackM3 restore server access and get 3M subscribers?
As usual, I started doing an nmap scan using nmap -sV hackme.thm.
Doing a basic nmap scan.
Apart from ports 22 and 80, port 8000 and 8089 were open for later tasks.
What is the invite code for the hackme.thm website?
I started by visiting the webservice on port 80.
First impression of the website.
Clicking “Join NOW”, I was prompted to enter an Invite Code.
Seeing the Sign up page.
Trying random codes didn’t work. Before trying an SQL injection, I checked for some client-side hints and found an interesting file.
http://hackme.thm/js/invite.js
functione(){var e = window.location.hostname;if(e ==="capture3millionsubscribers.thm"){var o =newXMLHttpRequest;
o.open("POST","inviteCode1337HM.php",true);
o.onload=function(){if(this.status ==200){
console.log("Invite Code:",this.responseText)}else{
console.error("Error fetching invite code.")}};
o.send()}elseif(e ==="hackme.thm"){
console.log("This function does not operate on hackme.thm")}else{
console.log("Lol!! Are you smart enought to get the invite code?")}}
As can be seen on line 14 at the bottom, if the domain of the request is “hackme.thm”, nothing is done, but on line 3 it says something should be done if the domain is capture3millionsubscribers.thm. A POST request to inviteCode1337HM.php is made to receive an invite code. I thought about just doing a POST request myself, but I assumed there would be a server-side check, too, and adding capture3millionsubscribers.thm to my hosts file was easy enough with echo "10.10.219.64 capture3millionsubscribers.thm" >> /etc/hosts
Then I just had to execute the e() function from http://capture3millionsubscribers.thm.
Trying to run the e() function from the wrong domain.Running the e() function from the correct domain.
This revealed the Invite Code.
Answer: VkXgo:Invited30MnUsers
What is the password for the user guest@hackme.thm?
Entering the invite code revealed the credentials including the password:
Entering the Invite Code.
Credentials: guest@hackme.thm:wedidit1010
Answer: wedidit1010
What is the secure token for accessing the admin panel?
Entering the guest credentials, this was the first impression:
First impression after entering the guest credentials.
The FREE tier room looked like a typical TryHackMe room.
Viewing the FREE tier room.
Clicking on “Complete” did not mark the task as complete, though, so the site was not functional.
To enter the VIP training room, I would need a subscription, which is precisely what is impossible as per the overall task description.
Trying to enter the VIP room.
Doing some investigation, I realized there was a cookie for the VIP status.
Investigating the cookies.
Simply setting that to “true” in my Firefox browser allowed access to the VIP room.
Entering the VIP room.
Interestingly, the “Start Machine” button actually worked in this room, but only for VIP users:
Trying to start the machine.
When I wanted to capture the request of this interaction by copying it from the Network tab in my browser, I realized there was another interesting request independent of clicking the button (to /BBF813FA941496FCE961EBA46D754FF3.php), but more on that later. And clicking the button did not send a request, but instead, that check was done on client-side again:
script in advanced_red_teaming.php
// sc-drFUgV fXEjrf$(document).ready(function(){$('#start_machine').click(function(e){var isVIPE = document.getElementById("isVIP");var isVIP =(isVIPE.value.toLowerCase()==='true');if(isVIP){$("#splitScreenRight").attr("class","sc-drFUgV bROZdw");$("#main1").attr("class","sc-bKNmIE bYiuLB");$("#main2").attr("class","sc-hZDbVM bksodH");$("#nav1").attr("class","sc-krITIZ gMgnKr");}else{alert("This page is only for VIP users")}});});$(document).ready(function(){$('#exit_split').click(function(e){$("#splitScreenRight").attr("class","sc-drFUgV fXEjrf");$("#main1").attr("class","sc-bKNmIE ipXaXG");$("#main2").attr("class","sc-hZDbVM hgVIhb");$("#nav1").attr("class","sc-krITIZ hoLoqS");});});
On line 13, the message for non-VIP users is printed. This is done, when the “Start Machine” button is clicked and when the value of the “isVIP” element is not true on line 5 and 7. Getting the content of this element:
Viewing the “isVIP” element.
Setting it to “true”:
Changing the “isVIP” element value.
Then clicking the “Start Machine” button actually worked:
Starting the machine and getting a terminal.
Now about the request made to /BBF813FA941496FCE961EBA46D754FF3.php. That request is made in an iframe to get the terminal.
<iframeid="remote-window"allow="fullscreen; clipboard-read; clipboard-write"title="Training Room 2: Advanced Red Teaming"src="./BBF813FA941496FCE961EBA46D754FF3.php"style="background-color:rgb(255, 255, 255);"width="100%"height="100%"></iframe>
Accessing this PHP file directly works, too, instead of changing the “isVIP” variable. Accessing the terminal directly.
Accessing the terminal directly.
Doing some basic reconnaissance there, I realized I was “www-data”, “pwd” was not allowed, and this was the output for “ls”:
Listing all files in the current directory.
I assumed the token for accessing the admin panel was to be found somewhere here, so I searched for it by starting to look into the config.php with cat config.php.
Viewing the contents of config.php.
Not only did this reveal the token, but also a new domain and port.
Answer: ACC#SS_TO_ADM1N_P@NEL
What is the flag value after enabling the registration feature and getting 3M subscribers on the platform?
First, I tried to access http://admin1337special.hackme.thm:40009 in my browser by adding admin1337special.hackme.thm to my hosts file using echo "10.10.219.64 admin1337special.hackme.thm" >> /etc/hosts, even though my initial nmap scan did not show port 40009, but to be fair, I think the basic scan doesn’t cover that port.
Upon trying to visit that page, I got an error, but the URL being changed to /public/html showed me that I was on the right track:
Trying to visit the new URL.
Since I had an access token for the website, I just needed to figure out how to use it. Unfortunately, no cookie was set to manipulate this time. So I guessed I had to find a login page first. Before using Gobuster, I manually tried http://admin1337special.hackme.thm:40009/public/html/login.php and was lucky.
Viewing the login page to enter the access token.
Upon entering the token, another login mask was presented:
Viewing the login form.
I tried my guest credentials from earlier, but was not lucky this time.
Trying to access the admin portal using the guest credentials.
Also, the client-side login code was not vulnerable this time:
login.js
functionlogin(){var username = document.getElementById('username').value;var password = document.getElementById('password').value;fetch('../../api/login.php',{method:'POST',headers:{'Content-Type':'application/json',},body:JSON.stringify({username: username,password: password,}),}).then(response=> response.json()).then(data=>{if(data.error){alert(data.error);}else{// Redirect to the dashboard or the admin page based on the role
window.location.href = data.role =='admin'?'dashboard.php':'dashboard.php';}}).catch((error)=>{
console.error('Error:', error);});}
I thought about getting some more info in my other terminal via BBF813FA941496FCE961EBA46D754FF3.php, but no other file besides config.php was cat’able.
So, I resorted to trying an SQL injection. Entering ' OR 1==1' as the username did not grant me access, but it also did not show the usual message about an invalid username or password, so I knew that something was possible here. I copied the Request Headers and Post Data on my browser and saved them in req.txt to use in sqlmap.
Database: hackme
Table: users
[1 entry]
+----+------------------+------------+--------+----------+--------------+----------+
| id | email | name | role | status | password | username |
+----+------------------+------------+--------+----------+--------------+----------+
| 1 | admin@hackme.thm | Admin User | admin | 1 | adminisadm1n | admin |
+----+------------------+------------+--------+----------+--------------+----------+
Easy enough, I found the credentials for the admin user of the admin portal. After entering admin and adminisadm1n on http://admin1337special.hackme.thm:40009/public/html/login.php, I got in.
Viewing the admin panel after entering the admin credentials.
The available actions were “Invite Code” and “Sign up”. I wanted to activate the “Sign up” a. k. a. registration feature. I clicked the “Set Options” button a couple of times because nothing seemed to happen, but going back to http://hackme.thm, I saw fireworks.
Viewing the flag after enabling the registration feature.
Flag: TryHack3M{3MSUBSCRIBERS}
Detection
Investigating the Attack
Our security department detected an alert about a web attack on the 4th of April, 2024. They have ingested the logs into Splunk, which can be accessed using the following credentials:
Username
admin
Password
splunklab
URL
10.10.219.64:8000
Your task is to analyse the logs and track the attacker’s footprints.
Good luck.
Now it was time to go to port 8000 and do some investigation.
First impression of the splunk login page.First impression of the splunk home page.
How many logs are ingested in the Splunk instance?
I wasn’t too firm with Splunk, but when I entered “index” into the search, I got some suggestions already:
Seeing some past search queries/suggestions.
So, to get the overall number of logs, I searched for index=*. Unfortunately, the splunk license was expired was was already shown in the login screen.
Getting a notice about the expired splunk license.
I went to the TryHackMe discord to find a solution and they confirmed this was an unintended issue.
Since I didn’t want to wait for the issue to be resolved and since the necessary searches were already visible in splunk, I looked up another write-up to get the screenshots and answers.
Getting all events in splunk.
Answer: 10530
What is the web hacking tool used by the attacker to exploit the vulnerability on the website?
Quite a few events. Since I got into the website using sqlmap, I already assumed the attacked did the same. But looking at the different User Agents that made the requests confirmed that:
Listing all different User Agents that made requests.
Answer: sqlmap
How many total events were observed related to the attack?
In the above screenshot, the event count made by sqlmap is 158, which is the answer.
Answer: 158
What is the observed IP address of the attacker?
To get the IP address of the attacker, I would’ve searched for the IP address used with the sqlmap User Agent like this: index=* user_agent="sqlmap/1.2.4#stable (http://sqlmap.org)"
Viewing the source IP address of the sqlmap User Agent.
The “source_ip” of the events is the one used by the attacker.
Answer: 83.45.212.17
How many events were observed from the attacker’s IP?
Now, the attacker did not only make requests using sqlmap, but also using other User Agents. So to find all events for the attacker, I would’ve searched for all events with that same source IP address like this: index=* source_ip="83.45.212.17"
Viewing all events made by the attacker’s IP address.
Answer: 184
What is the table used by the attacker to execute the attack?
First, I assumed it would just be the “users” table like I did to get the credentials of the admin user, but that was not what was meant here. The attacker did use sqlmap, but not the same way I did to hack the website back. To see all requests made by sqlmap, I could’ve searched through them manually, but splunk also lists interesting fields including the different URI requested by the attacker’s IP address.
Viewing an interesting request made by sqlmap.
Within on of these requests, the payload used by sqlmap can be seen. That URI shows a request where username and password are selected from “TryHack3M_users” where the role is “admin”.
Answer: TryHack3M_users
Conclusion
Congratulations on making it this far! You have clearly completed the challenge and helped HackM3 reach 3M subscribers. We are thrilled that you’ve had the opportunity to learn some fascinating exploitation techniques while gaining valuable detection insights for this kind of attack. Keep up the great work!
That concludes this CTF. Too bad the splunk part was not functional in the way it was intended to be, but I liked the different methods used to exploit the webservice in the first part, like changing the host name, cookies, understanding basic JavaScripts and all that.