Rating: 5.0

# 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](Assets/phish)

# Solution
## Discovery
### Source Code
We browse [main.py](Assets/phish/main.py), and find the code that is responsible for sending the data to the database

```Python
@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}
" + str(e)
return "Your password is leaked :)
" + \
"""<blockquote class="imgur-embed-pub" lang="en" data-id="WY6z44D" >Please
take care of your privacy
</blockquote><script async src="//s.imgur.com/min/embed.js"
charset="utf-8"></script> """
```
More importantly, focus on this section of the code:
```Python
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
```Python
>>> 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
```SQL
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

```SQL
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:
```SQL
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](Assets/Phish/solve.py) 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}

Original writeup (https://github.com/StrixGoldhorn/CTF-Writeups/blob/main/WeCTF%202021/Phish.md).