LaCTF - LA Housing Portal Walkthrough

An interesting CTF challenge created by LaCTF that focuses mainly on SQL Injection, yet if you are feeling stuck as a beginner, it is fine because, despite the obvious SQLi vulnerability in the code, it may require time and extra tweaking to get the payload to function properly for the flag to be extracted. Feel free to read and learn more as you go.

Enumeration

As always, open the website first to la-housing.chall.lac.tf and see how the interface is…

UI for La-Housing challenge

Hm, so when we click Get Matches!, it will send a request to /submit

"Testing the interface"

Now let’s check the source code and see if the code for /submit route is vulnerable.

@app.route("/submit", methods=["POST"])
def search_roommates():
    data = request.form.copy()

    if len(data) > 6:
        return "Invalid form data", 422

    for k, v in list(data.items()):
        if v == "na":
            data.pop(k)
        if (len(k) > 10 or len(v) > 50) and k != "name":
            return "Invalid form data", 422
        if "--" in k or "--" in v or "/*" in k or "/*" in v:
            return render_template("hacker.html")

    name = data.pop("name")

    roommates = get_matching_roommates(data)
    return render_template("results.html", users=roommates, name=name)

so it calls the get_matching_roommates and passes our data into it. After checking the code of the get_matching_roommates function, it seems like it’s vulnerable to SQLi due to the following lines…

...
	query = """
    select * from users where {} LIMIT 25;
    """.format(
        " AND ".join(["{} = '{}'".format(k, v) for k, v in prefs.items()])
    )
    print(query)
    conn = sqlite3.connect("file:data.sqlite?mode=ro", uri=True)
    cursor = conn.cursor()
    cursor.execute(query)
...

Get The Flag

So after opening Burpsuite and seeing how the request is formatted, it looks like the final parameter of the request is the awake parameter, which is the easiest one to inject.

Burpsuite

For this request, the payload with the following content seems to be promising

'+UNION+SELECT+1,(SELECT+*+FROM+flag),2,3,4,'

To test it, I made a new request and appended that to the end of the parameter

Capturing the flag

And BOOM !!! I found the flag.