Browse CTFs New CTF Sign in

Order status tampering

web_injection_logic Difficulty 1–5 30 min certifiable

Theory

Why This Matters

A 2020 security assessment of a mid-size logistics SaaS platform uncovered a critical workflow flaw: an authenticated customer could POST directly to the order update endpoint with {"status": "delivered"} on any order in a pending state, causing the system to trigger automated downstream actions — including releasing escrow funds to the seller — without any actual shipment occurring. The state transition logic was enforced only in the front-end JavaScript; the API accepted any status string for any order belonging to the authenticated user. Separately, a researcher disclosed a related flaw in a marketplace platform where replaying a completed order's POST body to the checkout endpoint created a duplicate fulfillment record without re-charging the payment method. These incidents share a root cause: order lifecycle as a client-controlled parameter rather than a server-enforced state machine.

Core Concept

An order lifecycle is a finite state machine (FSM): a directed graph of states with defined permitted transitions between them. A canonical e-commerce FSM might be: pending → paid → processing → shipped → delivered, with branches for cancelled and refunded. The FSM invariant is that transitions are only valid along permitted edges — a pending order cannot jump directly to delivered, and a refunded order cannot transition back to shipped.

Order status tampering occurs when the server accepts a client-supplied status value and applies it without verifying that the transition from the current state to the requested state is permitted. The attack surface includes: hidden form fields containing the current status, API parameters like {"status": "delivered"}, URL path segments like /orders/123/delivered, and order replay — resending a previously captured complete order request to trigger a second fulfillment cycle.

Refund replay is a high-value variant: if the refund endpoint does not check whether the order is already in a refunded state, a repeated POST can issue multiple refunds against a single charge. Similarly, setting an unpaid order's status to shipped may trigger inventory deduction and shipping label generation without revenue capture.

The correct fix is server-side FSM enforcement: the server maintains the canonical state in a database column, looks up the current state on every status-change request, validates that the requested transition is in the permitted-transitions set, and rejects out-of-order transitions with a 409 Conflict response.

Technical Deep-Dive

# Normal flow: order is in state "pending", awaiting payment
GET /api/orders/ORD-7821 HTTP/1.1
Host: shop.example.com
Cookie: session=abc123

# Response:
# {"order_id": "ORD-7821", "status": "pending", "total": 199.99, "paid": false}

# Attack: directly set status to "delivered" — skipping paid, processing, shipped
PATCH /api/orders/ORD-7821 HTTP/1.1
Host: shop.example.com
Cookie: session=abc123
Content-Type: application/json

{"status": "delivered"}

# Vulnerable server response (no FSM check):
# {"order_id": "ORD-7821", "status": "delivered", "escrow_released": true}

# Refund replay — order is already refunded
POST /api/orders/ORD-7821/refund HTTP/1.1
Host: shop.example.com
Cookie: session=abc123
Content-Type: application/json

{}

# Vulnerable server response (no idempotency check on refund):
# {"refund_id": "REF-9902", "amount": 199.99, "status": "processed"}
# Second request also returns 200 with a new refund_id — double refund issued
# Automated state transition fuzzer
import requests

BASE = 'https://shop.example.com'
STATUSES = ['pending', 'paid', 'processing', 'shipped', 'delivered',
            'cancelled', 'refunded', 'complete', 'closed', 'approved']

session = requests.Session()
session.cookies.set('session', 'abc123')

order_id = 'ORD-7821'

for status in STATUSES:
    r = session.patch(
        f'{BASE}/api/orders/{order_id}',
        json={'status': status}
    )
    print(f'[{r.status_code}] status={status} -> {r.text[:120]}')

Security Assessment Methodology

  1. Map the order lifecycle — Retrieve an order object and note the current status field. Review the application UI to identify all states and the expected transition sequence.
  2. Attempt invalid forward transitions — From pending, PATCH or POST {"status": "delivered"}. From processing, attempt {"status": "refunded"}. Record which transitions the server accepts without error.
  3. Attempt backward transitions — From delivered, attempt {"status": "pending"}. Some applications allow state reversal that should be forbidden.
  4. Test order replay — Capture the full HTTP request that completes an order (the final checkout or confirmation POST). After the order is complete, replay the identical request. Observe whether a duplicate fulfillment record is created.
  5. Test refund replay — Issue a refund on a completed order. Immediately re-send the identical refund POST. Check whether a second refund_id is issued.
  6. Inspect hidden form fields and cookies — Some legacy applications store order_status in a hidden <input> or a signed (but not encrypted) cookie. Modify the value and re-submit the form.

Defensive Countermeasure — Implement a server-side transition table (e.g., a database table or in-code mapping) that defines the complete set of permitted (from_state, to_state) pairs. On every status-change request, query the current state from the database (never trust client input for current state), look up the requested transition, and return 409 Conflict if it is not in the permitted set. Enforce refund idempotency with a unique constraint on (order_id, refund_type) in the refunds table and use database-level locks when processing refunds.

Common Assessment Errors

  • Testing only the final-state jump — Skipping from pending to delivered is the obvious test. Also test intermediate skips (pending to shipped, paid to delivered) and backward transitions, which may have different code paths.
  • Missing the refund replay vector — Refund endpoints are often developed by a different team and may lack the idempotency controls present in the main checkout flow. Always test refund replay separately.
  • Assuming PATCH is the only attack vector — Some applications use PUT, POST to a sub-resource (/orders/123/deliver), or even GET requests with query parameters (?action=deliver). Test all HTTP methods that modify order state.
  • Not checking downstream side effects — A successful status tamper may not be immediately obvious from the HTTP response. Check inventory records, fulfillment queues, and financial ledgers for unexpected entries.
  • Overlooking multi-tenant isolation — Status tampering may be possible on other users' orders, not just your own. Test with order IDs belonging to other test accounts (with permission in a CTF context).
  • Ignoring the hidden form field surface — Modern SPAs rarely use hidden form fields, but legacy checkout flows still do. Always inspect form HTML for status, total, and order_id fields that may be client-controlled.

NICE Framework Alignment

Code Knowledge/Skill/Task Statement How This Card Develops It
K0007 Knowledge of authentication, authorization, and access control methods Demonstrates how server-side FSM enforcement is an authorization control on state transitions
K0065 Knowledge of web application security testing techniques Develops systematic state transition fuzzing and replay methodologies
K0070 Knowledge of system and application security threats and vulnerabilities Classifies order status tampering as a workflow integrity threat with high financial impact
S0001 Skill in conducting vulnerability scans and recognizing vulnerabilities in security systems Trains enumeration of permitted and forbidden state transitions through directed testing
T0028 Identify and analyze vulnerabilities and risks in web applications Applies FSM invariant analysis to identify missing server-side transition enforcement
T0570 Perform technical security assessments of web applications Structures order status assessment from state mapping through refund replay proof-of-concept

Further Reading

  • OWASP Testing Guide v4.2, OTG-BUSLOGIC-006: Test Business Logic Data Validation — OWASP Foundation
  • The Web Application Hacker's Handbook, 2nd Ed., Chapter 11 — Stuttard & Pinto
  • HackerOne Hacktivity: Order State Machine Bypass Reports — HackerOne Platform (public disclosures)

Challenge Lab

Reinforce your learning with a hands-on generated challenge based on this card's competency.