Neurona's picture
Duplicate from Krypto-Whitehat/qwen3.8-9b-cyber-exploit-agent
dd70e57
Raw
History Blame Contribute Delete
24.3 kB
# -*- coding: utf-8 -*-
"""Track B part 2: six-test boundary family, VALID ledger teacher (#6906, closed completed, fix verified
in ConfidentialMPTSend.cpp:196-199), SponsorFee self-deal FP boundary (#6890, closed not_planned),
tefINVARIANT/tec families, temporal FP (PD-modified-by-failed-tx, invariant in develop:
PermissionedDomainInvariant.cpp:104)."""
from trackb_part1 import SYSTEM, PREAMBLE, S, add
# ============================================================ #6906 — REAL VALID TRACK:ledger (closed completed, fix in develop)
V6906_USER = """## Report: Transfer fee bypass via confidential sends
ConfidentialMPTSend completely ignores the sfTransferFee set on the MPToken issuance. Regular MPT payments between holders (via Payment.cpp) call transferRate() to calculate and apply the transfer fee, but the confidential send path performs no such check or deduction. Furthermore, MPTokenIssuanceCreate has no validation preventing both sfTransferFee > 0 and tfMPTCanConfidentialAmount from being enabled simultaneously. Holders can bypass transfer fees entirely by converting to confidential form, sending confidentially, and having the recipient convert back.
Root cause: ConfidentialMPTSend::preclaim and doApply contain zero references to sfTransferFee or transferRate. MPTokenIssuanceCreate::preflight validates sfTransferFee and tfMPTCanConfidentialAmount completely independently with no cross-validation. Since confidential amounts are encrypted, the fee cannot be calculated in the current design, but there is no check preventing the incompatible feature combination.
Impact: issuer revenue loss on every confidential hop; transfer fee becomes unenforceable."""
add("V6906",
V6906_USER,
"""White-Box: Six ledger tests on the reported chain.
(1) Reachable: ConfidentialMPTSend is a supported transactor on develop (ConfidentialMPTSend.cpp exists; ConfidentialTransfer amendment family). No preflight kills a well-formed confidential send: sender holds MPT, issuance has lsfMPTCanTransfer + lsfMPTCanHoldConfidentialBalance, ElGamal keys present. Reachable.
(2) Committed: the send consumes sender confidential balance and credits recipient inbox/inbox-convert state in the validated ledger - real state change, no invariant rolls it back. Committed.
(3) Third-party forceable: any two holders transact; the victim is the ISSUER who set sfTransferFee > 0 expecting revenue on holder-to-holder transfers. Issuer does not act in the attack. Third-party forceable.
(4) Not by-design: XLS-89d (confidential MPT) does not grant a fee exemption to confidential hops; the fee is a property of the issuance, not of the transport. Not by-design.
(5) Not live-rechecked-away: there is no later re-check that claws the fee - the hop is final once committed. Not neutralized.
(6) Novel residual: not a recombination of dead premises - it is a single missing cross-check between two independently-validated features. Novel.
Six tests: all pass -> VALID_BUG TRACK:ledger.
Confirm-gate (what makes it valid and testable): with an issuance that has sfTransferFee > 0 AND confidential transfers enabled (pre-fix: nothing forbids the combination), a confidential send moves value holder->holder with no transfer-fee deduction anywhere in ConfidentialMPTSend apply - contrast Payment.cpp's transferRate() application on the open path.
Fix verification note (develop): the gate now exists - ConfidentialMPTSend.cpp:196-199 returns tecNO_PERMISSION when sfTransferFee > 0 (sanity check), and the comment states MPTokenIssuanceCreate/MPTokenIssuanceSet enforce the incompatibility at issuance time. A report against CURRENT develop is therefore already fixed; against the pre-fix tree it is a real, six-tests-pass VALID_BUG.""",
"""Pre-fix tree (or git checkout before the cross-validation was added); local rippled develop standalone.
1. Issuer: MPTokenIssuanceCreate with tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance (confidential enabled) and sfTransferFee = 1000 (basis points).
2. Holder A: ConfidentialMPTConvert amount -> A holds confidential balance.
3. A: ConfidentialMPTSend to holder B, amount X (encrypted).
4. B: ConfidentialMPTConvertBack (or MergeInbox after convert).
5. Compare issuer fee revenue: open-path transfer of X (Payment with MPT) deducts transfer fee per transferRate(); confidential round-trip deducted zero.
6. Post-fix expectation on develop: step 1 (issuance create/set with fee>0 + confidential) is rejected at preflight, and step 3 returns tecNO_PERMISSION (ConfidentialMPTSend.cpp:196-199) - the fix gate, named and observable.""",
"""Root cause: two features validated independently (transfer fee on issuance; confidential-enable flag) with no cross-field rule; the confidential send path (ConfidentialMPTSend.cpp) never consulted transferRate(), so the fee existed on the open path only. Source -> sink: issuance flags (source) -> confidential hop apply (sink) with fee application absent.
Reachability: any two holders of a fee-bearing confidential-enabled issuance; no privilege needed beyond holding.
Primitive: economic-logic bypass (fee evasion), committed and final - a ledger-level value-movement rule failure, not memory corruption.
Chain to impact: repeatable hops -> issuer revenue reduced to zero on confidential traffic -> direct economic harm to a third party (the issuer). Rung reached: full chain to fund-flow impact (not extraction from a victim's balance, but en-masse evasion of a contractual fee).
What is missing to the next rung: nothing - the chain is complete as a ledger-validity/economics bug. (For "critical" it would need extraction of third-party funds or a broken invariant; systematic fee evasion is high.)
Stability: deterministic; no races; amendment state only affects availability of the confidential features.
Fix sketch: (a) reject sfTransferFee > 0 combined with confidential-enable at MPTokenIssuanceCreate/Set preflight; (b) defense in depth: tecNO_PERMISSION in ConfidentialMPTSend when fee > 0. Both are present on current develop - report stands as VALID against pre-fix trees and as FIXED against develop.""",
"VALID_BUG TRACK:ledger", "high",
"""REASONING: Six ledger tests pass on the pre-fix tree: reachable (supported transactor, no tem kill), committed (real balance moves, no invariant rollback), third-party forceable (holders act; issuer is the victim), not by-design (XLS confidential spec grants no fee exemption), not live-rechecked (no claw-back), novel residual (single missing cross-check, not a remix). Confirm-gate: fee>0 + confidential-enabled issuance allowed a zero-fee hop vs transferRate() on the open path. Fix verified on develop: ConfidentialMPTSend.cpp:196-199 (tecNO_PERMISSION) + issuance-time enforcement.""")
V6906_USER_V2 = """## Report: Issuer transfer fees are optional if you use confidential MPTs
Found a nice trick: enable confidential amounts on an MPT issuance that charges a transfer fee, then move tokens between holders via ConfidentialMPTSend instead of Payment. The confidential path never calls transferRate() and nothing stops an issuance from having both features on at once, so the fee is simply never charged. Issuers lose revenue silently - balances all reconcile, no errors, nothing in the ledger shows a fee was skipped."""
add("V6906v2",
V6906_USER_V2,
"""White-Box: Same mechanism as the confidential transfer-fee bypass; run the six tests quickly.
(1) Reachable: yes - confidential sends are first-class on develop; no preflight stops the hop.
(2) Committed: yes - balances move and reconcile (as the reporter notes: the silence is the bug).
(3) Third-party forceable: yes - any holders; victim is the fee-setting issuer.
(4) By-design? No - the fee attaches to the issuance; the confidential transport is not a fee exemption in the XLS design.
(5) Live re-check? No claw-back exists.
(6) Novel: single missing cross-check, not a remix.
All six pass -> VALID_BUG TRACK:ledger. Current develop already carries the fix (issuance-time incompatibility enforcement + ConfidentialMPTSend.cpp:196-199 tecNO_PERMISSION sanity gate), so the live window is the pre-fix tree.""",
"""Pre-fix standalone: create fee+confidential issuance, convert A->send->convert-back B, diff issuer revenue vs an equivalent open-path Payment (which applies transferRate()). Post-fix: issuance rejected / send tecNO_PERMISSION.""",
"""Root cause: absent cross-validation between sfTransferFee and confidential-enable; confidential apply path never consults transferRate().
Reachability: any holders; silent by nature (no error anywhere).
Primitive: committed fee-evasion (economic logic bypass).
Chain: complete to issuer revenue impact (high; not critical - no third-party balance extraction or invariant break).
Missing to next rung: none for classification.
Stability: deterministic.
Fix sketch: issuance-time incompatibility check + apply-time sanity rejection (both present on develop).""",
"VALID_BUG TRACK:ledger", "high",
"""REASONING: six tests pass (reachable, committed, third-party, not by-design, not re-checked, novel); confirm-gate = zero-fee confidential hop vs transferRate() on open path; fixed on develop via issuance-time enforcement + ConfidentialMPTSend.cpp:196-199.""")
# ============================================================ #6890 — SponsorFee self-deal (closed not_planned) = FP boundary
V6890_USER = """## Report: SponsorFee granular permission lets delegate self-deal, draining principal's XRP as fees
A delegate granted the SponsorFee granular permission for SponsorshipSet can create a sponsorship where the principal is the sponsor and the delegate is the sponsee. This transfers XRP from the principal's balance into the sponsorship object's sfFeeAmount. The delegate then submits their OWN (non-delegated) transactions referencing sfSponsor=principal with spfSponsorFee, so the fee is paid from the principal's XRP. The delegate chose not to set lsfSponsorshipRequireSignForFee, so no co-signature is required. The principal's XRP is burned as transaction fees for the delegate's benefit.
Root cause: the SponsorFee granular permission template includes sfSponsee as an allowed field, but SponsorshipSet has no checkGranularSemantics override (default returns tesSUCCESS). No semantic check prevents sfSponsee = delegate's own address.
Impact: principal's funds drained as fees."""
add("V6890",
V6890_USER,
"""White-Box: Who granted what, and what did the victim sign?
(1) Chain of consent: the PRINCIPAL granted the delegate the SponsorFee granular permission via DelegateSet (a signed action by the principal). The principal thereby authorized the delegate to perform SponsorshipSet actions in its name within that permission's field template.
(2) The harmful step - creating a sponsorship (principal sponsors, delegate sponsee) and letting fees draw from it - is executed under exactly that granted permission. The "drain" is capped by what the principal's own grant enables: XRP moved into sfFeeAmount by a SponsorshipSet the delegate was authorized to make.
(3) The counter-control the report itself names exists: lsfSponsorshipRequireSignForFee. The principal omitted it. A provided-but-unused safety flag is a hardening/documentation matter, not a dead protection: nothing prevented the principal from setting it, and the delegation UI/spec surface exposes it.
(4) F10: victim must grant the privilege itself, and the extraction is bounded to fee-shaped amounts through the granted vehicle - this is the privilege-grant family: mostly hygiene/by-design. F6-adjacent: principal can cause (grant) and repair (revoke delegation, set the flag, drain/expire the sponsorship).
(5) Six-test quick pass: reachable+committed yes, but test 3 (third-party forceable) fails - every enabling action is the principal's own signed grant; test 4 (not by-design): delegation semantics put field-template boundaries in the principal's hands by design. Two tests fail -> not VALID.
(6) Not a dead-protection case (F22/D-family): no silent fail-open control; the control (co-sign flag) exists and was simply not activated by its owner.""",
"""Owned accounts: principal P, delegate D.
1. P signs DelegateSet granting D the SponsorFee permission (this signature is the consent boundary - step 1 is P's action).
2. D creates sponsorship via the granted permission: sponsor=P, sponsee=D, no lsfSponsorshipRequireSignForFee.
3. D submits own txs with sfSponsor=P + spfSponsorFee -> fees draw from the sponsorship (P's XRP).
4. Demonstrated boundary: between step 1 and 2 nothing was taken from P that P's grant did not enable; with the flag set in step 2, every fee draw needs P's co-signature (re-run and observe the difference).
5. P repairs anytime: revoke delegation / update sponsorship; exposure is bounded by sfFeeAmount D could fund.""",
"""Root cause: delegation working as designed - a granular permission whose field template includes sfSponsee; the semantic risk (self-sponsee) is governed by the principal's choice of the co-sign flag. Not a missing check in the protocol's threat model: the delegating account IS the security boundary.
Reachability: fully reachable, and the harm is real in magnitude only if the principal granted broad permission and funded generously - which is the principal's own configuration.
Primitive: misuse of granted authority (fee-shaped extraction), not a protocol bypass.
Chain to impact: grant -> self-dealing sponsorship -> fee draw. The first link is the victim's signed action: chain broken for third-party forceability (test 3), by-design (test 4).
Missing for VALID: a self-deal shape reachable WITHOUT the principal's enabling grant, or ignoring lsfSponsorshipRequireSignForFee when set - neither is shown.
Stability: deterministic.
Fix sketch (hygiene/hardening, not security class): document loud defaults for lsfSponsorshipRequireSignForFee in delegation tooling; optional wallet-side warning when a SponsorFee template includes sfSponsee; spec note on least-privilege templates.""",
"FALSE_POSITIVE", "none",
"""REASONING: every enabling action is the principal's own signed grant (DelegateSet) and the harm runs through exactly the granted vehicle, bounded by sfFeeAmount (F10 privilege-grant family); the named counter-control (lsfSponsorshipRequireSignForFee) exists and was left unset by its owner - no dead protection (F22 inapplicable). Third-party-forceability (test 3) and not-by-design (test 4) both fail.""")
# ============================================================ tefINVARIANT_FAILED family
TEFINV_USER = """## Report: Invariant check failure on my transaction proves ledger corruption is possible
My crafted Payment/Batch combination gets tefINVARIANT_FAILED on a mainnet-identical config. The transaction applied, changed state, and THEN the invariant caught something - which means the bad state was already written before the check ran. If an invariant can fire, the bad state exists, so a variant that slips past the invariant corrupts the ledger. I consider this a committed-state security bug with the invariant as the only thing standing between my tx and ledger corruption.
Impact: ledger corruption / consensus risk."""
add("TEFINV",
TEFINV_USER,
"""White-Box: What does tefINVARIANT_FAILED actually guarantee in rippled's apply pipeline?
(1) Invariant checks run at transaction finalize (post-doApply) on the effects; on violation the transaction result becomes tefINVARIANT_FAILED and the transaction's effects are NOT included in the validated ledger - the open-ledger view for that transaction is discarded/rolled back. tef* results are not-eligible-for-ledger: nothing commits.
(2) Therefore "the bad state was already written" is true only transiently inside the candidate view of one transaction on one server - it never reaches the validated ledger, never reaches other servers, never becomes consensus input.
(3) The report's inference "an invariant CAN fire -> a variant CAN slip past" is speculation without a path: it needs a specific second bug (an invariant gap for a specific effect class). No such gap is identified. F5: a tec/tel/tem/tef result alone is not a bug; tefINVARIANT_FAILED is the system working (detect + rollback), the opposite of committed corruption.
(4) Six-test: test 2 (committed) fails categorically - the result code itself certifies non-commit. One failed test -> not VALID.
(5) If the reporter believes the invariant is WRONG (fires on legitimate state), that would be a correctness issue about the invariant, not a security exploit - different report, needs the specific invariant named and a legitimate state that trips it.""",
"""Owned node / repro harness.
1. Submit the crafted tx -> observe tefINVARIANT_FAILED (as reported).
2. Immediately read the validated ledger (ledger_entry / account_info at the next validated index): none of the tx's effects are present. The rollback is observable.
3. Submit a normal Payment afterwards -> succeeds; no state desync between servers (compare two nodes' ledger hashes - identical).
4. Demonstrated: the result code certifies non-commit; the "already written" state exists only inside the discarded candidate view.""",
"""Root cause: misreading of the apply pipeline - invariant failure = detected-and-rolled-back, not committed-then-noticed.
Reachability: the trigger (invariant firing) is reachable; the claimed harm (committed corruption) is categorically unreachable through this result.
Primitive: none - rollback is the primitive the system applies.
Chain: dead at test 2 (committed). Missing for VALID: a named invariant GAP plus a state that (a) violates ledger rules and (b) passes every invariant - not provided.
Stability: deterministic result semantics.
Fix sketch: none for protocol; if the invariant itself seems wrong, file the specific invariant + legitimate-but-rejected state as a correctness report.""",
"FALSE_POSITIVE", "none",
"""REASONING: tefINVARIANT_FAILED certifies the effects were NOT committed (rollback at finalize; nothing enters the validated ledger) - test 2 (committed) fails categorically; the leap to "a variant slips past" identifies no invariant gap (F5: result code alone is not a bug).""")
# ============================================================ tec-only family
TEC_USER = """## Report: tecPATH_DRY / tecUNFUNDED_* results are bugs - my transaction eats fees without effect
When I submit a Payment with a path that later dries up, the transaction is INCLUDED in the ledger with tecPATH_DRY and the fee is still charged. Same for tecUNFUNDED_PAYMENT variants. The transaction "succeeded" enough to claim a fee but did nothing - that is a bug: money for nothing. Attackers can also spam tec results to burn other people's fees (batch of doomed payments from a funded account).
Impact: fee theft / spam vector."""
add("TEC",
TEC_USER,
"""White-Box: tec semantics are specified XRPL behavior, not a defect.
(1) Result-code families: tem/tel/tef = not included in ledger (no fee); tes/tec = included; tec = "transaction included, fee charged, non-success effect". Charging the fee for an included-but-failed tx is exactly the documented contract: the network did the work of including and processing it. The reporter's normative claim ("fee without effect = bug") contradicts the specified cost model, not the implementation.
(2) F5: a result code alone is not a protocol bug. The spec question would be "does some XLS promise fee-refund on tec?" - no.
(3) Six-test quick: no state deviation exists at all (ledger state matches spec for tec) - tests 1-2 vacuous, test 4 (not-by-design) fails: by-design.
(4) The spam angle: an account burning ITS OWN fees on doomed txs is self-priced spam - the fee mechanism is the defense working (each tec costs the sender). Third-party fee burning would require signing txs for other accounts - not shown.
(5) Where a REAL bug could hide (honest boundary): a tec that charges a fee while applying partial effects, or a tec path that skips fee escalation - those would be concrete state deviations. Not claimed here.""",
"""Any ledger explorer.
1. Submit Payment with drying path -> tecPATH_DRY included, fee charged (observe both).
2. Compare ledger state before/after: zero effect besides fee - matching the specified tec contract.
3. Attempt "burn others' fees": requires their signatures -> impossible; own-account spam is self-funded (each tec costs the sender).""",
"""Root cause: normative disagreement with the documented tec cost model, not implementation deviation.
Reachability: behavior reachable and specified.
Primitive: none (spec-conformant accounting).
Chain: no deviation, no rung. Missing for VALID: a tec path with partial effects or fee-escalation skip (not claimed).
Stability: deterministic, spec-level.
Fix sketch: none (spec behavior); if the cost model is disliked, that is a standards discussion (XLS), not a security fix.""",
"FALSE_POSITIVE", "none",
"""REASONING: tec = included + fee + no effect is the specified contract (F5: result code alone is not a bug); no state deviation exists; spam burns the sender's own fees (no third-party path). Not-by-design test fails: by design.""")
# ============================================================ temporal FP — PD modified by failed tx (fixed by invariant)
PDFIX_USER = """## Report (historical): failed transactions can modify Permissioned Domains
I noticed that under some paths a transaction whose result is a tec/tef failure still leaves modifications to a PermissionedDomain object in the ledger (credential lists changed). A failed transaction modifying domain state breaks the atomicity contract: only successful transactions should change domain state. Reproduced on an older develop build from earlier this year. Is this still live?"""
add("PDFIX",
PDFIX_USER,
"""White-Box: Temporal triage - is the reported behavior live on CURRENT develop?
(1) The invariant exists now: PermissionedDomainInvariant.cpp:104 - "No permissioned domains should be affected if the transaction failed". A failed-tx domain modification is checked at finalize; violation -> tefINVARIANT_FAILED -> effects not committed.
(2) Release-note confirmation: rippled 3.1.3 added exactly this invariant ("ensure Permissioned Domains aren't modified by failed transactions") - matching the historical report's window.
(3) Current-develop answer: a NEW report of this shape dies at test 2 (committed) - the invariant certifies rollback. FALSE_POSITIVE against current develop, with the honest note that pre-3.1.3 it was a real class (worth a one-line history, not a verdict change).
(4) F5+test-2: the kill is the invariant finalize gate, named: PermissionedDomainInvariant (failed-tx clause).
(5) If the reporter claims a CURRENT repro, the verdict flips to UNPROVEN pending their exact build + tx - but as stated ("older build from earlier this year") the class is closed by the named gate.""",
"""Current develop node.
1. Attempt the reported shape (any tx failing with tec/tef that would touch PD credential state).
2. Observe: either the tx's domain effects do not occur at all, or the tx result is tefINVARIANT_FAILED with effects rolled back (PermissionedDomainInvariant failed-tx clause).
3. Confirm invariant presence in the running build (PermissionedDomainInvariant.cpp:104 family).
4. On the old build the repro may show the historical behavior - that is versioned history, not a live bug.""",
"""Root cause: historically real class (failed tx leaving PD modifications), closed by the 3.1.3 invariant; current develop enforces at finalize with rollback.
Reachability: the trigger txs are reachable; the modification does not commit anymore.
Primitive: none on current develop.
Chain: killed at test 2 by the named invariant gate.
Missing for VALID: a current-build repro where the modification survives finalize.
Stability: deterministic; amendment-gated invariant (check amendment state on the target network).
Fix sketch: already shipped (invariant); regression tests live with the invariant suite.""",
"FALSE_POSITIVE", "none",
"""REASONING: PermissionedDomainInvariant.cpp:104 ("No permissioned domains should be affected if the transaction failed") + 3.1.3 release note close this class; any current repro ends tefINVARIANT_FAILED (not committed, test 2). Historical validity acknowledged; current verdict FALSE_POSITIVE.""")