Double-spend attack in CREDIT mode through collateral-withdrawal race condition
Description
A malicious user can execute a double-spend attack in CREDIT mode by withdrawing collateral between card authorization and settlement, causing the operator's borrowing attempt to fail.
This is the attack sequence:
A user supplies collateral into the service then requests a collateral withdrawal via
executeServiceAction(service, "withdraw", params).Once the operator approves the withdrawal, the user makes a purchase using card payment.
The user immediately calls
executeApprovedServiceAction(actionId, "withdraw")to withdraw collateral from the service to the account.If step 3 executes before the operator processes the authorized borrowing, the operator will fail to borrow on behalf of the account and cannot settle the card payment.
The user calls
requestWithdrawal. Since the account is inCREDITmode and the token is no longer collateral in the service, the withdrawal executes instantly without operator approval.
The user completes a card payment without the operator being able to settle it, effectively receiving goods/services for free.
Impact
This enables direct financial loss where users can obtain goods or services without payment being settled. The operator cannot recover the funds since the collateral has been withdrawn before borrowing could occur. While the card payment backend could potentially prevent this through proper sequencing checks, the contract-level vulnerability allows the attack vector to exist.
The overall impact is indeterminate as it highly depends on backend implementation and operational controls. If the card payment system validates that no pending withdrawals exist before authorizing payments, this vulnerability can be effectively mitigated at the application layer. However, if such controls are absent, this becomes a critical issue enabling systematic double-spending with direct financial losses. The contract-level design permits this attack pattern, making security entirely reliant on off-chain safeguards.
Recommendations
To prevent this attack, consider the following approaches:
The card payment backend should verify that there are no pending withdrawal-service actions before authorizing card payments.
The operator should approve and execute requested collateral withdrawal-service actions in a single call to prevent the race-condition window.
Remediation
Hyperbeat provided the following response to this finding:
Managed on the backend side