Lactf_web_penguins_login_writeup

I fold an orignal origami penguin from a Gum Wrapper.

peng

there is no particular intention of referring to this photo

Postgre SQLi

I solved this SQLi challenge with an unintended solution.

[web] penguins login
peng

I’ve already shared this method on lactf offcial discord, and I’ll share it in this article as well.
Because I was able to come up with an unintended and little known interesting technique for postgresql’s sqli.

The challenge summary consists of regular SQL and Python given by the following file: It’s a standard difficulty problem, as all you need to see to solve the challenge is a simple blacklist mistake.

allowed_chars = set(string.ascii_letters + string.digits + " 'flag{a_word}'")
forbidden_strs = ["like"]

with app.app_context():
    conn = get_database_connection()
    create_sql = """
        DROP TABLE IF EXISTS penguins;
        CREATE TABLE IF NOT EXISTS penguins (
            name TEXT
        )
    """
    with conn.cursor() as curr:
        curr.execute(create_sql)
        curr.execute("SELECT COUNT(*) FROM penguins")
        if curr.fetchall()[0][0] == 0:
            curr.execute("INSERT INTO penguins (name) VALUES ('peng')")
            curr.execute("INSERT INTO penguins (name) VALUES ('emperor')")
            curr.execute("INSERT INTO penguins (name) VALUES ('%s')" % (flag))
        conn.commit()

@app.post("/submit")
def submit_form():
    conn = None
    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:
            curr.execute("SELECT * FROM penguins WHERE name = '%s'" % username)
            result = curr.fetchall()

        if len(result):
            return "We found a penguin!!!!!", 200
        return "No penguins sadg", 201

    except Exception as e:
        return f"Error: {str(e)}", 400

    # need to commit to avoid connection going bad in case of error
    finally:
        if conn is not None:
            conn.commit()

Once you realize that you can use similer to since it’s postgre sql, the rest can be solved by scripting.
However, I became suspicious about the ambiguity of the flag format in the rules, so I wanted to dump the data in non-ASCII representation as well.

In conclusion, a payload such as ' union select name from penguins where name similar to '{x39{x30....' escape '{, worked effectively. This use of {x39 as \x39 involved replacing the escape character with {, allows the brute force discovery of all ASCII characters through hex encoding.

Although it was not needed in this problem, setting escape dynamically could potentially allow various queries to be issued even on a more restricted challenges.

Outro

It’s a small memorandum.
I recently learned a lot of attack techniques specific to postgresql, so I’d like to introduce them someday. See you in the next post.

douro

Software Developper, Security Researcher more


2024-03-21