An attacker can drain any ERC20 token in the platform due to the protocol's method of sending ETH to users during withdrawals.
When the protocol sends ETH to users, it converts its WETH back to native ETH before transferring it to the user, as seen in TokenManager::withdraw
.
This approach of transferring ETH to users is generally frowned upon due to its exploitable nature. If the user is a malicious smart contract, they can exploit this vulnerability using the following strategy outlined in the proof of concept.
Pretext: The attacker deploys a smart contract with no receive
function but a payable
fallback function that uses delegateCall
to interact with a predetermined ERC20 contract. When the TokenManager
sends ETH to this malicious contract, the fallback function is triggered, executing the following logic.
Fallback Code Logic:
Upon execution, the fallback function makes a delegateCall
to the XXX token contract with the transferFrom()
function signature and params that transfer tokens from the CapitalPool contract to the attacker's desired address.
XXX token is an ERC20 token currently held in the CapitalPool contract.
Why does this work?
This works because the CapitalPool
always grants the TokenManager
full allowance to move assets from its balance. The use of delegateCall
preserves the msg.sender
of the calling contract, enabling the attack.
The attacker can drain the contract of its assets.
Manual
Transfer users' ETH in ERC20 format (WETH) to avoid interacting with potentially malicious contracts.
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.