$ cat writeup.md…
$ cat writeup.md…
alfactf
Task: a Next.js chocolate shop stores balances in server-side SQLite sessions and exposes a promo redemption endpoint. Solution: recover the valid coupon from source, then exploit JavaScript string-plus-number coercion to turn the balance into Infinity and buy the flag item.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
Шоколадный дроп
English summary: the target is a Next.js shop application with server-side SQLite-backed sessions, a promo code feature, and a hidden expensive flag item. The goal is to abuse the promo flow to raise the stored balance high enough for checkout.
Source inspection revealed a valid promo code embedded directly in the frontend or bundled source. The code decodes from base64 into the JSON object below:
{"amount":5000,"coupon":"TREAT5000"}
The interesting API route was POST /api/promocode. The handler updated session.balance before fully validating the promo payload:
promo.amount to session.balancecatchfinallyThat sequence is unsafe in JavaScript because + is both numeric addition and string concatenation.
After redeeming the legitimate 5000 coupon once, the stored balance is the number 5000. The bug appears when the next promo uses a malformed amount value such as the string "1e305".
At that point the application effectively does:
session.balance += "1e305";
Instead of producing a larger number, JavaScript concatenates and the balance becomes the string:
"50001e305"
Later the error handler tries to roll the change back with -=. That operator forces numeric coercion first:
session.balance -= "1e305";
Now "50001e305" is parsed as an enormous scientific-notation value, which overflows to Infinity. Then the rollback becomes:
Infinity - 1e305 === Infinity
So the failed promo does not restore the original balance. It upgrades it to Infinity.
...
$ grep --similar