The expected behavior is that each user can only withdraw their own failed transfer credits stored in the failedTransferCredits mapping.
However, the withdrawAllFailedCredits function incorrectly references the mapping key and pays the caller regardless of the _receiver value.
This allows anyone to repeatedly withdraw the ETH that belongs to another user, while the victim’s credit balance never decreases.
Likelihood:
Any external account can call withdrawAllFailedCredits at any time.
No authentication or signature checks are performed. Only the victim must have a non-zero credit.
Because the victim’s failedTransferCredits is never reduced, the attacker can call the function repeatedly.
Impact:
Each call transfers real ETH from the contract’s balance to the attacker.
The attacker can drain all ETH held by the contract, not just the victim’s credits, until the contract balance is empty.
This PoC demonstrates that an attacker can repeatedly call withdrawAllFailedCredits with another user's address
and drain ETH that belongs to that user. The vulnerability exists because the contract incorrectly resets the caller's
balance instead of the victim's, allowing repeated withdrawals.
Steps:
Assume victim has 10 ETH in failedTransferCredits.
Attacker calls withdrawAllFailedCredits(victim).
Attacker receives 10 ETH, victim’s credit is not cleared.
Attacker can repeat the call indefinitely until the contract runs out of ETH.
This proves the critical nature of the bug and its exploitability.
Safer to use msg.sender as the receiver and remove the _receiver parameter to avoid confusion:
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.