| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | API Testing |
| Difficulty | Practitioner |
| Objective | Discover and exploit an undocumented PATCH endpoint on the product price API to set the jacket's price to $0 and purchase it |
Finding and Exploiting an Unused API Endpoint¶
I logged in as wiener:peter — $0 balance, jacket costs several hundred dollars.
Adding the jacket to the cart triggered a GET /api/products/1/price in Burp's history. The page source pointed to a JS file:
<script type="text/javascript" src="/resources/js/api/productPrice.js"></script>
Fetching the script revealed a setPrice() function — the front-end was designed to receive price updates from the API. That's not proof the back-end accepts external writes, but it's a strong signal worth testing. Sending OPTIONS to confirm supported methods:
OPTIONS /api/products/1/price HTTP/2
Allow: GET, PATCH
PATCH is listed — the UI only uses GET, but the endpoint accepts partial updates. OPTIONS is the fastest way to discover this: the Allow header lists every method the endpoint accepts, no guessing required.
Testing PATCH:
PATCH /api/products/1/price HTTP/2
{"type":"ClientError","code":400,"error":"Only 'application/json' Content-Type is supported"}
The error message is a recon asset — it told us the exact format required without any guessing. Resending with the correct content type:
PATCH /api/products/1/price HTTP/2
Content-Type: application/json
Content-Length: 11
{"price":0}
{"price":"$0.00"}
Accepted. The cart updated to $0.00. Changing product prices is clearly an admin operation — but the endpoint had no authentication check. Any authenticated user could call PATCH /api/products/1/price. The "unused" means unused by the UI, not absent from the back-end; the attack surface was always there.
Placing the order:
Lab solved :P