Skip to content

Latest commit

 

History

History
113 lines (82 loc) · 4.88 KB

Phish.md

File metadata and controls

113 lines (82 loc) · 4.88 KB

Phish

Medium

Description

Shou is so dumb that he leaks his password (flag) to a phishing website.

Note: The flag contains underscore

Host 1 (San Francisco): phish.sf.ctf.so
Host 2 (Los Angeles): phish.la.ctf.so
Host 3 (New York): phish.ny.ctf.so
Host 4 (Singapore): phish.sg.ctf.so

Source Code

Solution

Discovery

Source Code

We browse main.py, and find the code that is responsible for sending the data to the database

@app.route("/add", methods=["POST"])
def add():
    username = request.form["username"]
    password = request.form["password"]
    sql = f"INSERT INTO `user`(password, username) VALUES ('{password}', '{username}')"
    try:
        db.execute_sql(sql)
    except Exception as e:
        return f"Err: {sql} <br>" + str(e)
    return "Your password is leaked :)<br>" + \
           """<blockquote class="imgur-embed-pub" lang="en" data-id="WY6z44D"  ><a href="//imgur.com/WY6z44D">Please 
        take care of your privacy</a></blockquote><script async src="//s.imgur.com/min/embed.js" 
        charset="utf-8"></script> """

More importantly, focus on this section of the code:

sql = f"INSERT INTO `user`(password, username) VALUES ('{password}', '{username}')"

This is vulnerable to SQL Injection attacks


SQL Injection

A quick example of using SQL Injection with the above style of code is shown below

>>> password = "abc"
    username = "(SELECT * FROM xyz)') -- "
    sql = f"INSERT INTO `user`(password, username) VALUES ('{password}', '{username}')"
    print(sql)
    
#   INSERT INTO `user`(password, username) VALUES ('abc', '(SELECT * FROM xyz)') -- ')

As we can see, it will allow us to SELECT columns from tables, or perform other actions




Exploit

Acquring characters present in password

We intend to acquire the characters present in the password by brute-forcing it through SQL queries like this, where ---HERE--- is the character we are testing

INSERT INTO `user`(password, username) VALUES ('a',(SELECT password FROM user WHERE username = "shou" AND password LIKE "%---HERE---%")); --', 'abc'')

To explain how this works, we will first analyse the `SELECT` statement
SELECT password FROM user WHERE username = "shou" AND password LIKE "%---HERE---%"

We know that there is a username shou with the password as the flag
Hence, we can check if a character is present in the password by adding a condition where password LIKE "%---HERE---%"
This works by using the LIKE condition, along with the multiple character wildcard, %

If the character is present, our query will return: UNIQUE constraint failed: user.username
This is because the SELECT statement returns shou, but shou has already been phished, and his username is present, thus we are unable to create a new row with the username shou

If the character is not in the password, our query will return: NOT NULL constraint failed: user.username
This is because SQLite expects a string to be there, yet nothing is selected since shou's password does not contain the character

To implement this, we send this as the POST data, where {i} the character we are testing:

  • username : abc,
  • password : a',(SELECT password FROM user WHERE username = "shou" AND password LIKE "%{i}%")); --




Brute-forcing password

After acquring all the characters present in the password, we need to figure out how the characters are strung together
At first, we tried a very similar approach using LIKE "---HERE---%" as a condition in the SELECT statement

However, LIKE is case insensitive. ie, "a" LIKE "a" is True, and "A" LIKE "a" is also True
The flag is case sensitive, hence we needed to find another operator

Introducing GLOB, it is similar to LIKE, just that it is case sensitive
GLOB has * as a wildcard, and can accept REGEX queries

We then use a similar injection as the first portion, where we want the following query to run, where ---HERE--- is a portion of the flag:

INSERT INTO `user`(password, username) VALUES ('a',(SELECT password FROM user WHERE username = "shou" AND password GLOB "---HERE---*"));--', 'abc')

Similar to the first portion, if the segment of the flag is correct, it returns UNIQUE constraint failed: user.username

If the segment is incorrect, it returns NOT NULL constraint failed: user.username




Script

Hence, we created a short Python script to solve for the flag
Since we are not allowed to send >100 requests per second, we added time.sleep(0.1) to limit our requests to at most 10 per second

we{e0df7105-edcd-4dc6-8349-f3bef83643a9@h0P3_u_didnt_u3e_sq1m4P}