Cosmic Components Co. - UniVsThreats26 Quals Web
Category: Web
CTF: UniVsThreats26 Quals
Target: Cosmic Components Co. shop (Flask/Jinja2)
TL;DR
Two coupons (SPACESALE15, NEWCUSTOMER10) stack multiplicatively when alternated. Discounts persist across cart contents, so we stack on a cheap RAM item, swap in expensive gear, and pay pennies. Negative quantities and bundling make some steps cheaper, and loyalty rewards (+BBD 100 each purchase) turn every buy into profit. Final balance grows while purchasing all six items, unlocking Elite Customer and the flag.
Flag: UVT{sp4c3_sh0pp3r_3xtr40rd1n41r3_2026}
Recon
- Flask/Jinja2 shop with login/register; debug hints in HTML:
- Template engine Jinja2 v3.1.2, debug context exposed
- Admin panel at
/admin/dashboard(JWT admin role) - GraphQL at
/graphqlwith introspection - Legacy wallet API
/api/v1/users/{id}/wallet(PATCH)
- Six products with tier gates and starting balance BBD 100.00.
- Product hints suggest coupon games, quantity tricks, and bundling.
- GraphQL introspection and the legacy wallet API existed but were unused because business-logic flaws alone were enough for full compromise.
Vulnerabilities
Coupon Stacking (core)
- Two codes:
SPACESALE15(15%) andNEWCUSTOMER10(10%). - Applying the same code twice is blocked, but alternating applies both, compounding forever (Γ0.85, Γ0.90, ...). ~77 alternations β price β ~BBD 0.01.
- The cart stores a floating discount multiplier in session and multiplies totals by it on each apply; it never clamps to a minimum price nor resets after checkout.
Negative Quantity Acceptance
- Cart accepts
-99to99quantities.-1RAM gives a -300 line item. - Checkout blocks negative totals, but pairing with bundling lowers totals.
- Input validation only checks type/range, not sign, so negatives pass straight to price calculation.
Bundling + Negative Quantities
- Neutrino SSD can be bundled with Quantum RAM.
+1 SSD+-1 RAMdrops from 500 to 200, then coupons shrink further. - Bundling is enforced only for SSD+RAM; other products refuse mixed carts. The SSD uses the same cart total, so RAMβs negative value offsets SSD.
Coupon Persistence Across Products
- Discount factor is stored in session, not recalculated per cart. Stack on RAM, remove RAM, add expensive item: discount still applies.
- The code applies discounts to the cart total, not to individual line items, so swapping items inherits the discount factor.
Loyalty Reward Exploitation
- Each purchase yields +BBD 100 loyalty credit, even if the purchase cost is near zero β every buy nets profit.
- Rewards are awarded before checking payment amount; no cap or proportional scaling.
SSTI (informational)
- Profile bio renders Jinja2 (
{{7*7}}β49). Sandbox blocks dangerous attributes, but context leaks session/config/app data. Not needed for the main chain.
Exploit Steps
Step 1: Register & Login
- Create account β balance 100, tier Rookie.
Step 2: Quantum RAM (Rookie β Silver)
- Add RAM (product 1).
- Alternate coupons ~8 times to drop to ~BBD 87.33.
- Checkout β +100 loyalty; balance 112.67; tier Silver.
Step 3: Neutrino SSD (Silver β Gold)
- Add SSD (product 2) and
-1RAM (bundling allowed) β total 200. - Stack coupons to ~BBD 99.49.
- Checkout β +100 loyalty; balance 113.18; tier Gold.
Step 4: Ion Processor Core (Gold β Platinum)
- Add RAM, stack ~80 alternating coupons (price β ~0.01).
- Remove RAM; add Ion Processor (product 3) β 9000 becomes ~21.10.
- Checkout β +100 loyalty; balance 191.98; tier Platinum.
Why it works: the discount multiplier is stored in session and not tied to a product. After RAM is removed, the multiplier (β0.0023) is applied to the Ion Processorβs 9000 price.
Step 5: Nvidia 8090 Ti GPU (Platinum β Diamond)
- Reuse coupon-stack-and-swap β GPU costs ~0.04.
- Checkout β +100 loyalty; balance 291.94; tier Diamond.
Step 6: Dark Matter PSU (Diamond β Elite)
- Same method β PSU costs ~0.06.
- Checkout β +100 loyalty; balance 391.88; tier Elite.
Step 7: Collect Flag
GET /flagreturnsUVT{sp4c3_sh0pp3r_3xtr40rd1n41r3_2026}.
Key Payloads / Snippets
Stack coupons by alternating:
for i in range(80):
coupon = "SPACESALE15" if i % 2 == 0 else "NEWCUSTOMER10"
requests.post(f"{BASE}/cart/coupon", data={"csrf_token": csrf, "coupon": coupon})
Negative quantity test:
requests.post(f"{BASE}/cart/add", data={"productId": "1", "quantity": "-1"})
# {"success": true, "total": -300.0}
Discount persistence swap:
add_to_cart(1) # RAM
stack_coupons()
remove_from_cart(1)
add_to_cart(3) # Ion Processor β ~21.10 after discounts persist
Quick math check for stacking:
price = 300.0
for i in range(8):
price *= 0.85 if i % 2 == 0 else 0.90
print(round(price, 2)) # 87.33 after 8 alternations on RAM
Final Wallet Ledger
Date | Description | Amount | Balance
-----------|--------------------------------------|-------------|--------
2026-02-27 | Initial Credit | +BBD 100.00 | 100.00
2026-02-27 | Customer Loyalty Reward | +BBD 100.00 | 200.00
2026-02-27 | Purchase: Quantum RAM Stick (Γ1) | -BBD 87.33 | 112.67
2026-02-27 | Customer Loyalty Reward | +BBD 100.00 | 212.67
2026-02-27 | Purchase: Neutrino SSD Drive (Γ1) | -BBD 99.49 | 113.18
2026-02-27 | Customer Loyalty Reward | +BBD 100.00 | 213.08
2026-02-27 | Purchase: Ion Processor Core (Γ1) | -BBD 21.10 | 191.98
2026-02-27 | Customer Loyalty Reward | +BBD 100.00 | 291.98
2026-02-27 | Purchase: Nvidia 8090 Ti GPU (Γ1) | -BBD 0.04 | 291.94
2026-02-27 | Customer Loyalty Reward | +BBD 100.00 | 391.94
2026-02-27 | Purchase: Dark Matter PSU (Γ1) | -BBD 0.06 | 391.88
Total spend: BBD 208.02 for BBD 13,010.00 of goods (β98.4% discount) plus loyalty profit.
Takeaways / Fixes
- Validate quantities as positive integers server-side; reject negatives/zeros.
- Make coupons idempotent and single-apply; track per-cart, not just last code.
- Recompute discounts per cart rebuild; avoid session-level discount factors.
- Remove template rendering of user input; avoid SSTI in bios/comments.
- Clamp loyalty rewards to paid value; decouple rewards from discounted totals.
- Add minimum payable amounts per item and per order; refuse free orders produced by discounts.