hunt-fintech-graphql – Security skill for AI agents
Hunt fintech-specific GraphQL vulnerabilities
Install the hunt-fintech-graphql skill with one command
Pick the tool you use, copy the command and paste it in your terminal. The skill lands in that tool's skills folder and is ready in your next session.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-fintech-graphql-d8816.tgz | tar xz -C ~/.claude/skills
Call it in Claude Code: /hunt-fintech-graphql – then describe the task you want done.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-fintech-graphql-d8816.tgz | tar xz -C ~/.codex/skills
Call it in Codex: $hunt-fintech-graphql – then describe the task you want done.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-fintech-graphql-d8816.tgz | tar xz -C ~/.hermes/skills
Call it in Hermes Agent: /hunt-fintech-graphql – then describe the task you want done.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/hunt-fintech-graphql-d8816.tgz | tar xz -C ~/.deepcode/skills
Call it in DeepSeek: /hunt-fintech-graphql – then describe the task you want done.
SKILL.md content
> ⚠️ AUTHORIZED USE ONLY
> This skill is for educational purposes or authorized security assessments only.
> You must have explicit, written permission from the system owner before using this tool.
> Misuse of this tool is illegal and strictly prohibited.
> Mandatory confirmation gate
> Before running any command that probes, exploits, changes, persists on, extracts data from, or attempts credential access against a target:
> 1. Ask the user to state the exact target URL, IP, account, or resource.
> 2. Ask the user to confirm written authorization and the permitted scope.
> 3. Show the exact command(s) and explain their expected effect.
> 4. Wait for explicit confirmation in the current conversation.
>
> Without that confirmation, remain read-only and provide defensive guidance only. Prefer a sandbox, disposable VM, or controlled lab.
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 may resolve 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.
UserorAccount
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/graphqlField/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, clientMutationIdTech-stack tells specific to this vertical:
- Plaid/Stripe/Dwolla/Marqeta wrapped behind an internal GraphQL gateway (
bankLink,plaidLinkTokenmutations) - Apollo Federation with a dedicated
ledgerorpaymentssubgraph — check for the subgraph's own introspection being reachable directly, bypassing the gateway's stitched-down schema - Custom
Money/Decimal/BigDecimalGraphQL 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
Use synthetic accounts and non-settling sandbox balances. Real-money transfers, settlement, KYC collection and repeated/race traffic need explicit owner-approved amounts, request caps, stop conditions and recovery; ordinary bounty scope does not imply those permissions. Stop at the smallest evidence of an invariant failure. Reproduce from clean state only in a resettable lab, never by repeatedly debiting a live account. A safe inconclusive report is preferable to moving value to prove impact.
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:
mutation {
transferFunds(input: {
idempotencyKey: "test-key-001"
sourceAccountId: "acc_1"
destAccountId: "acc_2"
amount: "10.00"
}) { transactionId status }
}Send twice with the identical idempotencyKey. Two responses/IDs alone are not proof: compare committed sandbox ledger effects and the documented idempotency-key scope/expiry. Duplicate financial effects within that contract establish a failure.
Decimal-precision / rounding probes:
mutation { transferFunds(input: {sourceAccountId:"acc_1", destAccountId:"acc_2", amount: "0.001"}) { transactionId } }
mutation { transferFunds(input: {sourceAccountId:"acc_1", destAccountId:"acc_2", amount: "9999999999999999.99"}) { transactionId } }
mutation { transferFunds(input: {sourceAccountId:"acc_1", destAccountId:"acc_2", amount: "1e2"}) { transactionId } }
mutation { transferFunds(input: {sourceAccountId:"acc_1", destAccountId:"acc_2", amount: "-50.00"}) { transactionId } }Sub-cent amounts test truncate-vs-round handling (repeat N times to accumulate a rounding-error
balance drift); scientific notation and oversized values test whether the Money/Decimal
scalar parser falls back to a native float/int with overflow or precision-loss behavior; negative
amounts test whether the resolver assumes sign server-side or trusts the client's.
Alias-batched double-spend probe (confirm before escalating to parallel HTTP):
mutation {
r1: redeemRewards(input: {rewardId: "rwd_1", accountId: "acc_1"}) { success }
r2: redeemRewards(input: {rewardId: "rwd_1", accountId: "acc_1"}) { success }
r3: redeemRewards(input: {rewardId: "rwd_1", accountId: "acc_1"}) { success }
}Top-level mutation fields execute serially under the GraphQL specification. If more than one alias reports success, inspect
… (full content is in the download package)
Related skills
- hunt-graphql – Hunting skill for graphql vulnerabilities.
- hunt-grpc – Hunt gRPC vulnerabilities
- hunt-idor – Hunting skill for idor vulnerabilities. Built from 26 public bug bounty reports. Use…
- hunt-laravel – Hunt Laravel specific vulnerabilities
- hunt-misc – Hunting skill for misc vulnerabilities. Reference for authorized role, token and…
- hunt-nextjs – Hunt Next.js specific vulnerabilities
- hunt-nodejs – Hunt Node.js specific vulnerabilities
- hunt-oauth – Hunting skill for oauth vulnerabilities. Built from 19 public bug bounty reports. Use…