Root: withdrawAllFailedCredits(address _receiver) reads credits for _receiver but zeroes msg.sender’s slot and sends ETH to msg.sender, not to _receiver. No access-control check ties the withdrawal to the credited address. No reentrancy guard.
Impact: Any user can drain another user’s failedTransferCredits. With a reentering fallback, an attacker can repeatedly withdraw the same victim’s balance (since the victim’s slot is never cleared).
Normal behavior:
When a direct ETH transfer in _payout fails, the amount is credited to failedTransferCredits[recipient]. Later, that recipient should withdraw their own credits safely.
Issue:
withdrawAllFailedCredits(address _receiver) reads the victim’s balance from failedTransferCredits[_receiver], but sets to zero failedTransferCredits[msg.sender] and pays msg.sender. Thus, anyone can pass a victim address as _receiver and steal the victim’s credits. Because the victim’s slot is never cleared, a reentrant attacker can loop withdrawals in one tx.
Likelihood:
Reason 1 // Any user can call withdrawAllFailedCredits and supply an arbitrary _receiver (no auth checks are present).
Reason 2 // Credits are easy to create in real usage (any failed _payout), and popular markets frequently accumulate such balances.
Impact:
Impact 1 // Full theft of another user’s credited ETH (loss of funds).
Impact 2 // With a reentering fallback, repeated drains within a single transaction because the victim’s slot is never cleared.
Some transfer to RevertingReceiver fails in _payout -> market.failedTransferCredits[RevertingReceiver] = X.
Deploy Attacker with victim = address(RevertingReceiver).
Call Attacker.steal():
market.withdrawAllFailedCredits(victim) reads victim's X, zeroes Attacker slot instead, sends X to Attacker.
Attacker's receive() reenters while victim's credit remains untouched -> repeated thefts.
Bind withdrawal to the caller, not an arbitrary address; clear before external call; guard reentrancy.
withdrawAllFailedCredits allows any user to withdraw another account’s failed transfer credits due to improper use of msg.sender instead of _receiver for balance reset and transfer.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.