T09 · Insecure Skill Coding Practices
- Location
scripts/xh_pay.py:104- Finding
Payment Ceiling Bypass Through Unvalidated Payment Asset
- Content
View full analysis
Vulnerability Details
File Location:
scripts/xh_pay.py, lines 104–108 and 173–183
Vulnerability Type: Unvalidated payment asset and incorrect denomination assumptions
Risk Level: HighVulnerable code:
python amt = a.get("maxAmountRequired") or a.get("amount") try: out["price_usdc"] = round(int(amt) / 1e6, 6) if amt else None except Exception: # noqa: BLE001 out["price_usdc"] = Nonepython info = describe(required) if info["price_usdc"] is None or info["price_usdc"] > max_price: return {"ok": False, "payer": acct.address, "error": f"harga {info['price_usdc']} di atas plafon {max_price} — dibatalkan", "quote": info} inner = x402ClientSync() register_exact_evm_client(inner, signer, [NETWORK]) hc = x402HTTPClientSync(inner) payload = hc.create_payment_payload(required) sig = encode_payment_signature_header(payload)Technical Analysis
The
payworkflow obtains its payment requirements from a remote endpoint. The endpoint therefore controls fields includingasset,amount,network, andpayTo.The ceiling check interprets every challenge amount as a six-decimal USDC quantity:
python int(amt) / 1e6However, the code does not require the challenge's
assetfield to equal the documented Base USDC contract stored inUSDC_BASE. After applying the ceiling under this unverified assumption, it passes the original challenge tocreate_payment_payload, which prepares the wallet authorization.Consequently, the value checked against
--maxmay not represent the asset or denomination that the wallet is asked to authorize. Restricting the registered client to Base does not correct this issue because it constrains the chain, not the token contract.The code does correctly reject missing prices and genuine six-decimal amounts above the configured ceiling. Those controls do not prevent a remote seller from selecting another compatib ...[truncated 1534 chars]
- Remediation
View remediation
Remediation Suggestions
- Before constructing a payment payload, require a case-insensitive exact match between the challenge's asset address and
USDC_BASE. - Require the challenge network to equal
eip155:8453and validate the expected payment scheme explicitly. - Compare the requested amount directly in USDC atomic units rather than converting an unverified asset using a hard-coded decimal assumption. For example, convert the configured ceiling to an integer number of six-decimal USDC units and reject amounts above it.
- Reject malformed, negative, fractional, ambiguous, or unexpectedly encoded amount values.
- Where the Skill is used specifically with XH Agents, validate
payToagainst the documented recipient. For payments to other sellers, display the recipient and require explicit approval before signing. - Revalidate all security-relevant challenge fields immediately before signing so that the validated object and the object supplied to
create_payment_payloadcannot diverge. - Add tests covering alternate token contracts, tokens with different decimals, wrong networks, unexpected schemes, oversized atomic amounts, and recipient substitution.
- Before constructing a payment payload, require a case-insensitive exact match between the challenge's asset address and
