hunt-fintech-graphql
Hunt fintech-specific GraphQL vulnerabilities: money-movement mutations (transfers, redemptions, withdrawals, card top-ups), ledger/balance/portfolio query IDOR, decimal-precision and rounding abuse, idempotency-key bypass enabling double-spend, KYC/PII field-level authorization gaps, and admin-override mutations reachable via mass assignment. Distinct from hunt-graphql, which owns generic GraphQL discovery and IDOR/mutation methodology — this skill owns the delta introduced when a GraphQL layer sits in front of a ledger, wallet, payments, banking, brokerage, or lending backend, where a resolver bug moves real money instead of just leaking data. Use when hunting a fintech, banking, payments, wallet, neobank, brokerage, or lending target that exposes a GraphQL API, or when a schema/response includes balance, transfer, ledger, redeem, quote, KYC, or account-linking fields.
git clone --depth 1 https://github.com/elementalsouls/Claude-BugHunter /tmp/hunt-fintech-graphql && cp -r /tmp/hunt-fintech-graphql/skills/hunt-fintech-graphql ~/.claude/skills/hunt-fintech-graphqlSKILL.md
## Why Fintech GraphQL Is a Different Risk Class
Generic GraphQL bugs (IDOR, mass assignment, introspection, batching abuse — see `hunt-graphql`)
still apply here, but the blast radius changes completely: a resolver bug in a SaaS app leaks
data, the same class of bug in a ledger mutation **moves money**. Three properties make fintech
GraphQL backends a distinct hunting surface:
- **Money-movement mutations are almost always resolvers over a double-entry ledger.** A single
GraphQL mutation (`transferFunds`, `redeemRewards`, `withdrawToBank`) can trigger multiple
ledger writes (debit + credit + fee) that must be atomic. GraphQL's flexible input shape and
alias batching make it easy to desynchronize those writes.
- **Decimals are attacker-controlled input, not display formatting.** Amounts, exchange rates,
interest, and rewards points are usually passed as GraphQL scalars (`Float`, `String`, custom
`Decimal`/`Money` scalar). How the resolver parses and rounds that value is exploitable surface
in its own right — this barely exists in non-financial GraphQL APIs.
- **KYC/PII fields sit next to routine account fields in the same type.** `User` or `Account`
types commonly expose `ssnLast4`, `routingNumber`, `kycStatus`, `governmentIdUrl`, or
`linkedBankAccount` alongside `displayName` and `email` — one missing field-level authorization
check on a type used everywhere in the schema fans out to every query that touches it.
---
## Attack Surface Signals
**URL / schema naming patterns (in addition to `hunt-graphql`'s generic `/graphql` list):**
```
/graphql/ledger
/graphql/payments
/api/wallet/graphql
/internal/ledger-graphql
/banking/graphql
```
**Field/type names worth grepping schema introspection or JS bundles for:**
```
balance, availableBalance, pendingBalance, ledgerEntry, ledgerEntries
transferFunds, withdraw, redeem, topUp, reverseTransaction, adjustBalance
kycStatus, ssnLast4, routingNumber, accountNumber, governmentIdUrl
quoteExchangeRate, interestAccrued, rewardsPoints, portfolioValue
idempotencyKey, clientMutationId
```
**Tech-stack tells specific to this vertical:**
- Plaid/Stripe/Dwolla/Marqeta wrapped behind an internal GraphQL gateway (`bankLink`, `plaidLinkToken` mutations)
- Apollo Federation with a dedicated `ledger` or `payments` subgraph — check for the subgraph's own introspection being reachable directly, bypassing the gateway's stitched-down schema
- Custom `Money`/`Decimal`/`BigDecimal` GraphQL scalar in the schema (`scalar Money`) — the parser for this scalar is worth fuzzing directly
Run `hunt-graphql`'s discovery + introspection methodology first to get the schema; everything
below assumes you already have (or have partially enumerated) a schema with money-movement types.
---
## Step-by-Step Hunting Methodology
1. **Map every mutation that touches balance, whether directly or as a side effect.** Not just
`transfer*`/`withdraw*` — also `redeemRewards`, `applyCoupon`, `upgradeTier`,
`closeAccount` (often refunds a balance), `disputeTransaction` (often provisionally credits).
2. **For each money-movement mutation, identify the ledger write shape.** Does one mutation call
produce one ledger entry or several (debit sender, credit receiver, fee entry)? Multi-entry
writes are the ones worth racing — see Stage 4.
3. **Test idempotency-key handling.** Send the identical mutation (same `idempotencyKey` /
`clientMutationId`) twice, back-to-back and with a delay. A ledger write on the second call
means idempotency isn't enforced server-side — replay = double-execute.
4. **Test decimal/precision edge cases** on every amount-accepting argument — see Payload section.
Confirm server-side rounding matches client-displayed rounding; a mismatch is directly
monetizable.
5. **Probe cross-account IDOR on account/portfolio node IDs**, same as `hunt-idor`/`hunt-graphql`,
but specifically test whether a `transferFunds`-style mutation validates that the
**source account belongs to the authenticated caller** — not just that *some* account with
that ID exists. This is the fintech-specific IDOR: authz on the *source* of a debit is easy to
forget when authz on the *destination* of a credit was correctly implemented (crediting an
arbitrary account "looks safe" to a developer; debiting one clearly isn't, so it gets checked
— but sometimes only one direction does).
6. **Check field-level authorization on KYC/PII fields** by querying the shared `User`/`Account`
type from every context that returns it — not just the profile screen. A `transaction` type
that embeds `counterparty { ssnLast4 }` is a common place for the check to be missing, because
the developer authorized the top-level `transaction` query but didn't re-check field access on
the nested `counterparty`.
7. **Look for admin-tier mutations reachable via mass assignment**, not just a missing auth
check — e.g. an input object with a client-settable `status` or `override` field that a normal
user's mutation shouldn't expose but that the resolver accepts anyway
(`updateTransaction(input: {id, status: "COMPLETED", amount: "..."})`).
8. **Test currency-argument consistency.** Send a transfer/quote mutation with mismatched
`sourceCurrency`/`targetCurrency` combinations the UI never generates (e.g. self-transfer with
a currency conversion) and check whether the resolver's FX-rate lookup and the ledger write use
the same rate — a TOCTOU window here is a direct arbitrage bug.
9. **Combine alias batching with money-movement mutations** to test for double-spend — see
`hunt-race-condition` for the parallel-HTTP escalation once alias batching alone confirms the
resolver isn't serializing writes per-account.
---
## Payload & Detection Patterns
**Idempotency-key replay test:**
```graphql
mutation {
transferFunds(input: {
idempotencyKey: "test-key-001"
sourceAccountId: "acc_1"
destAccountId: "acc_2"
amount: "10.00"
}) { transRun autonomous hunt loop on a target — scope check → recon → rank surface → hunt → validate → report with configurable checkpoints. Usage: /autopilot target.com [--paranoid|--normal|--yolo]
Build an exploit chain — given bug A, finds B and C to combine for higher severity and payout. Knows common chain patterns: IDOR→ATO, SSRF→cloud metadata, XSS→ATO, open redirect→OAuth theft, S3→bundle→secret→OAuth. Usage: /chain
Active vulnerability hunting. Two-track dispatcher — asks Red Team vs WAPT, hands off to hunt-dispatch skill and sibling commands. Usage: /hunt target.com | /hunt *.target.com | /hunt targets.txt [--vuln-class X] [--source-code P] [--chrome]
On-demand intelligence fetch for a target — CVEs, disclosed reports, new features. Pulls NVD/GitHub-Advisory CVEs + bundled disclosed reports + hunt memory context. Usage: /intel target.com
Inspect or rotate the autopilot ledger JSONL files (findings.jsonl, negatives.jsonl). Caps file size and keeps N rotated backups so memory does not grow unbounded.
Pick up a previous hunt on a target — shows hunt history and untested surface from the autopilot ledger. Usage: /pickup target.com
Run full recon pipeline on a target — subdomain enum (Chaos API + subfinder), live host discovery (dnsx + httpx), URL crawl (katana + waybackurls + gau), gf pattern classification, nuclei scan. Outputs to recon/<target>/ directory. Usage: /recon target.com
Optional manual note on a target or the last confirmed finding. Capture is automatic during autopilot; this is for extra context. Usage: /remember