Lack of Validation in Input Data - The Case of AffineDeFi
The absence of validated input is a major problem for projects, as shown by the recent breach of the Affince protocol on February 06, 2024. Attacker took advantage of this vulnerability to access the upgrade process and steal around 33 $aEthwstETH, worth $88k. This incident highlights the importance of strict validation procedures in DeFi protocols and emphasizes the need for strong security measures to safeguard digital assets and uphold user trust in blockchain ecosystems. We'll take a closer look at this attack to offer more insights to the community.
Attack Information
Attacker: https://etherscan.io/address/0x09f6be2a7d0d2789f01ddfaf04d4eaa94efc0857
Vulnerable Contract: https://etherscan.io/address/0xcd6ca2f0d0c182c5049d9a1f65cde51a706ae142
Transaction Attack: https://etherscan.io/tx/0x03543ef96c26d6c79ff6c24219c686ae6d0eb5453b322e54d3b6a5ce456385e5
Exploit Analysis
Follow the transaction, the attack so simple he trigger receiveFlashLoan function in vulnerable contract 2 times through the Balancer callback function.
During the first receiveFlashLoan, the userData contains the Loan.divest type, triggering the endPosition branch to withdraw assets from the Aave protocol.
In the second receiveFlashLoan call, the userData trigger to upgrade branch and trigger call to _payDebtAndTransferCollateral internal function.
It's evident that the contract transfers all collateral tokens based solely on user input without any verification steps during the receiveFlashLoan call. Up to this point, there has been no verification of the tx.origin or newStrategy address. This oversight is a clear mistake, as anyone can trigger this logic branch and abscond with the collateral tokens. Perhaps the developer assumed that the Balancer matching msg.sender sufficed and that only the STRATEGIST_ROLE could access this branch.
Recommendation
In this scenario, the project can restrict the initial call to prevent user-triggered upgrades by verifying that the tx.origin matches the operator in the allowlist (the user who creates the transaction) or by ensuring that the newStrategy matches the governance set. Furthermore, the upgrade flow should be segregated into an individual feature and only accessible via the STRATEGIST_ROLE.
Conclusion
As developers, it's crucial to remember the principle of "not trusting any input," including input from partners and support contracts. Any compromise in contract administration or mistakes in other contracts can lead to vulnerabilities within your own contract. Allowing user input to dictate actions means providing users with the opportunity to access administrative logic, and it's certain they will not always adhere to your rules.
Moreover, it is highly advisable to conduct a thorough security audit for all projects, regardless of whether they involve smart contracts, backends, wallets, or decentralized applications (dapps).