89832 bc critical unbounded transaction tips bypass high security containment and drain compromised accounts
Submitted on Aug 21st 2026 at 12:11:12 UTC by @Grant4969 for Audit Comp | Quantus
Report ID: #89832
Report Type: Blockchain/DLT
Report severity: Critical
Target: https://github.com/immunefi-team/audit-comp-quantus-chain
Impacts:
Direct loss of funds
Description
Brief/Intro
Quantus high-security mode expressly exists to contain a compromised account signing key: after enrollment, immediate value exits must remain blocked in favor of delayed transfers that a guardian can cancel. However, ReversibleTransactionExtension enforces this policy only over RuntimeCall. A transaction tip is separate signed data inside ChargeTransactionPayment, so the same restricted signer can attach an almost balance-sized tip to the explicitly whitelisted Vesting::claim. The payment is withdrawn and finalized even when the claim fails, immediately reducing the high-security account to its existential deposit without creating any reversible hold. The full-runtime PoC also proves that transaction-pool validation accepts the extrinsic and that later deposits can be drained repeatedly.
Vulnerability Details
This is a high-security policy escape, not ordinary use of an unrestricted key
The competition's general exclusions mention impacts requiring leaked keys or credentials. This report does not claim that key compromise itself is a protocol vulnerability, nor that an unrestricted account owner used authority that the protocol intended to grant.
High-security is a Quantus-specific containment control whose documented purpose is to keep enforcing restrictions after the ordinary signing key is compromised. Its pallet documentation states that enrollment is permanent so that an attacker who gains access to the account cannot disable the protections and that funds subsequently sent to a compromised account remain protected and recoverable by the guardian.
Consequently, possession of the signing key intentionally grants only restricted-signer authority. The vulnerability is that this restricted signer escapes the Quantus-enforced boundary through another value-bearing field in the same signed extrinsic that the custom policy never inspects. This is analogous to a constrained session key bypassing its spending limit through an unchecked signed field. The missing enforcement and affected security control are Quantus-owned runtime integration code; standard transaction-payment behavior only exposes the gap.
This does not rely on a trusted role or an accepted off-chain safeguard
No allocator, curator, approver, guardian, governance actor, operator, or other permissioned role participates in the exploit. The attacker has only the ordinary account key that high-security mode expressly treats as an untrusted, restricted signer after enrollment.
Nor is the report asking Quantus to introduce a new solvency invariant or replace a documented off-chain trust assumption. Quantus already enforces this exact containment invariant on-chain: ReversibleTransactionExtension rejects signed transactions whose RuntimeCall is outside the high-security whitelist. The exploit supplies an explicitly allowed call but moves value through an unchecked sibling field in the same signed payload. It therefore bypasses an existing code-enforced constraint rather than relying on the absence of a constraint the protocol deliberately left to trusted actors.
The competition's disclosed Known Issues do not describe transaction tips, ChargeTransactionPayment, or this split policy boundary.
The Quantus policy checks only RuntimeCall
ReversibleTransactionExtension implements TransactionExtension<RuntimeCall>. Its validation logic passes only call to the high-security policy:
The Quantus whitelist explicitly admits Vesting::claim because the call cannot redirect a legitimate vesting payout:
However, the production signed-extension tuple also contains:
The tip is an arbitrary compact-encoded balance stored in that separate extension, outside RuntimeCall. ReversibleTransactionExtension has no reference to it and therefore cannot enforce the high-security invariant over it. Reordering the two extensions would not fix this because one extension does not automatically receive another extension's value.
Transaction payment immediately settles the unchecked value exit
During transaction preparation, the predicted fee including the caller-selected tip is withdrawn from the signer. During post-dispatch settlement, standard transaction-payment logic computes and deposits the actual fee including the same tip irrespective of whether dispatch succeeded. This is normal behavior for unrestricted accounts and is not alleged to be an upstream Substrate bug. The Quantus vulnerability is composing this value-bearing field with a custom security policy that examines only the call.
Therefore, a restricted signer can submit an allowed Vesting::claim { schedule_id: 0 } with:
The high-security extension accepts the call. The payment extension withdraws the amount. The nonexistent schedule makes the claim fail, which demonstrates that the business call produces no payout or useful state transition, but the full tip remains charged. Failure is not required for exploitation; it is only the cleanest minimal reproduction because any successful whitelisted call can carry the same tip.
No pending reversible transfer is created, so the guardian has no delay window or cancellation target. Quantus' TransactionFeesCollector records the payment in MiningRewards::CollectedFees; MiningRewards::on_finalize subsequently remints the collected transaction fees to the current block author.
Attack sequence
The attack requires no guardian key, governance privilege, valid vesting schedule, malicious token, majority hash power, reorganization, race, or arithmetic edge case.
Impact Details
The selected in-scope impact is Direct loss of funds, rated Critical by the Quantus competition. The victim's high-security balance is immediately and irreversibly reduced regardless of which miner includes the transaction. The loss is limited only by the account's balance, bypasses both the guardian intervention window and the reversible hold, and can be repeated against every later deposit.
Miner receipt is secondary evidence rather than a prerequisite for the Critical impact. If the attacker produces an otherwise normal block and privately includes the transaction, Quantus remints the collected payment to that block author. This does not require majority hash power or a chain reorganization. Even when an unrelated miner includes it, the victim suffers the same deterministic direct loss.
References
High-security's compromised-key guarantee: https://github.com/immunefi-team/audit-comp-quantus-chain/blob/3b243b870a5442a6e5f443056f67bbba87d472f5/pallets/reversible-transfers/src/lib.rs#L339-L366
Call-only high-security transaction extension: https://github.com/immunefi-team/audit-comp-quantus-chain/blob/3b243b870a5442a6e5f443056f67bbba87d472f5/runtime/src/transaction_extensions.rs#L60-L80
High-security whitelist including
Vesting::claim: https://github.com/immunefi-team/audit-comp-quantus-chain/blob/3b243b870a5442a6e5f443056f67bbba87d472f5/runtime/src/configs/mod.rs#L660-L699Production
TxExtensioncomposition: https://github.com/immunefi-team/audit-comp-quantus-chain/blob/3b243b870a5442a6e5f443056f67bbba87d472f5/runtime/src/lib.rs#L153-L176Standard tip storage and settlement mechanics: https://github.com/immunefi-team/audit-comp-quantus-chain/blob/3b243b870a5442a6e5f443056f67bbba87d472f5/pallets/transaction-payment/src/lib.rs#L794-L808
Quantus fee collection and miner reminting: https://github.com/immunefi-team/audit-comp-quantus-chain/blob/3b243b870a5442a6e5f443056f67bbba87d472f5/pallets/mining-rewards/src/lib.rs#L192-L217
Recommended Fix
Enforce the high-security invariant over the complete signed extrinsic, not only RuntimeCall. At minimum, reject every nonzero ChargeTransactionPayment::tip() from a high-security signer. A per-transaction cap is insufficient because a compromised key can submit repeated transactions; any permitted allowance must be cumulative over the guardian delay. Architecturally, the policy should inspect every signed extension capable of debiting the origin, or the tip restriction should be integrated directly into transaction payment. Ordinary inclusion fees may remain payable, but discretionary tips must be forbidden or routed into a guardian-cancellable hold.
Link to Proof of Concept
https://gist.github.com/Grant3124/fdb8e665a48f363316dabed94a9f023b
Proof of Concept
The PoC runs against the exact in-scope commit:
It uses an ML-DSA-65 account and the production runtime's complete signed extrinsic, transaction-extension tuple, transaction-pool validation, dispatch pipeline, transaction-payment adapter, and mining-rewards finalization.
Steps to reproduce
Apply the patch
Download the raw file from the linked secret Gist as immunefi-c01-poc.patch, place it in the repository root, and apply it:
The patch adds audit_poc_high_security_balance_can_be_drained_via_tip, its maximum-tip helper, and the required imports to runtime/tests/transactions/ml_dsa_65.rs.
What the test proves
The tip=0 control isolates the discretionary tip as the drain vector. Transaction-pool validation rules out a block-author-only invalid extrinsic. The second drain directly tests the pallet's promise that funds later sent to a compromised high-security account remain protected. The exact miner event distinguishes fee reminting from the separate base block reward.
Was this helpful?