Can apply the discount multiple times?
Another challenge down! The hint for today’s CTF was quite direct: Can apply the discount multiple times? Knowing this, I figured I would have to find a way to stack or duplicate a discount code during the checkout process. I started the challenge and immediately fired up Burp Suite to keep an eye on the HTTP history.
I created an account, and as soon as I landed on the homepage of “Cheesy Does It,” the discount code was staring right at me:
Use the discount code PIZZA-10 for a 10% discount today only!
I tossed a few pizzas into my cart and proceeded to the checkout page.
Enumeration
I filled in my delivery details and the discount code. Before hitting the checkout button, I turned on Intercept in Burp Suite so I could catch the exact flow of the transaction.
When I clicked checkout, I noticed the application made a sequence of POST requests:
- First, it sent a request to validate my card information. There was nothing related to the discount code here, so I forwarded it without issues.
- The next request was another
POST to process the payment amount and card details. Again, no discount code logic, so I forwarded it.
- The third request was the payload I was looking for. It was a
POST request to /api/orders that contained the full order details in JSON format, including the "discount":"PIZZA-10" field.
Initial Access / Exploitation
I immediately sent this third request over to Burp Repeater so I could tinker with the payload and see how the backend handled the discount code.
My first step was to just send the unmodified request to establish a baseline and see what a normal 200 OK response looked like. Once I had that, it was time to experiment.
Attempt 1: Duplicating the JSON Key
My first idea was to simply list the discount key twice in the main JSON body to see if the server would apply both:
"payment_method": "card",
"notes": "",
"discount": "PIZZA-10",
"discount": "PIZZA-10"
}
I sent the request and got a 200 OK response, but no flag. This actually makes sense; most standard JSON parsers will simply overwrite the first duplicate key with the second one, so the server likely only processed it once.
Attempt 2: Moving the Discount Key
Since duplicating the key didn’t work, I tried placing the "discount": "PIZZA-10" key inside each individual pizza object within the "items" array, hoping it would apply the discount per item instead of on the total order. I received another 200 OK response, but still no flag. I needed a different approach.
The Breakthrough: Type Confusion
I took a step back and looked closely at the structure of the JSON request. Something caught my eye: the toppings field was structured as a JSON array ("toppings":["Tomatoes","Extra Mozzarella"]).
I hypothesized:
What if I change the data type of the discount field from a string to an array?
If the backend lacks strict type validation, it might loop through an array and apply every code inside it.
I modified the bottom of my payload to look like this:
"payment_method": "card",
"notes": "",
"discount": ["PIZZA-10", "PIZZA-10"]
}
I sent the request, and sure enough, that was the solution! The server accepted the array, applied the discount multiple times, and the response revealed the flag. Another challenge solved, plus free pizza, what more could you want?
- Burp Suite (Community Edition): Proxy, Intercept, and Repeater to catch the multi-step checkout process and manipulate the JSON payload.
Summary
- Key Steps: I intercepted the final order creation request during checkout, analyzed the JSON structure, and manipulated the
"discount" field by changing it from a string to an array containing multiple instances of the code.
- What I Learned: This was a great practical example of JSON type confusion. Just because an API expects a string doesn’t mean it won’t execute dangerous logic if you feed it an array or an object. Looking at how other fields are structured (like the
toppings array) can provide massive clues on what the backend parser might accept.
- Crucial Mistakes/Takeaways: My first attempt at duplicating the JSON key was a good reminder of how JSON RFCs and parsers work, duplicate keys are usually just collapsed into the last defined value. Recognizing that limitation pushed me to find the actual array-based vulnerability.