-
Notifications
You must be signed in to change notification settings - Fork 60
/
Copy pathreadonly_reentrancy_test.sol
276 lines (224 loc) · 7.19 KB
/
readonly_reentrancy_test.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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
// Definition for read-only reentrancy:
// There is a read-only function that returns a value which is affected
// by the state of the contract that was modified after potential reentrant call.
import "./interfaces/IERC721.sol";
contract MinimalReeentrant {
uint256 private _number;
function vulnarableGetter() public view returns (uint256) {
return _number;
}
function reentrancyExploitable() public {
msg.sender.call("");
_number++;
}
}
contract MinimalVictim {
address public reentrant;
function doSmth() public {
MinimalReeentrant reentrant = MinimalReeentrant(reentrant);
uint256 x = reentrant.vulnarableGetter() + 1;
}
}
contract Reeentrant {
mapping(uint256 => uint256) private _mapping;
uint256 private _number;
uint256 private _number2;
uint256 private _number_ok;
function vulnarableGetter() public view returns (uint256) {
return _number;
}
function vulnarableGetter2() public view returns (uint256) {
return _number + 10;
}
function vulnarableGetter3() public view returns (uint256) {
return vulnarableGetter();
}
function vulnarableGetter4() public view returns (uint256) {
return vulnarableGetter3();
}
function vulnarableGetter5() public view returns (uint256) {
return _mapping[_number];
}
function notVulnarableGetter() public view returns (uint256) {
return 1;
}
function notVulnarableGetter2() public view returns (uint256) {
return _mapping[1];
}
function notVulnarableGetter3(uint256 x) public view returns (uint256) {
return _mapping[x];
}
function notVulnarableGetter4() public view returns (uint256) {
return _number_ok;
}
function reentrancyExploitable() public {
msg.sender.call("");
_number++;
}
function reentrancyExploitable2() public {
IERC721(msg.sender).safeTransferFrom(
address(this),
address(msg.sender),
1
);
_number++;
}
function reentrancyExploitable3() public {
msg.sender.call("");
_changeState();
}
function reentrancyExploitable4() public {
reentrancyExploitable3();
_number2++;
}
function _changeState() internal {
_number++;
}
function ok() public {
_number_ok++;
msg.sender.call("");
}
function ok2() public {
_changeState();
_number_ok++;
}
}
contract Victim {
function vulnarableVictimGetter(
address reentrant
) public view returns (uint256) {
return Reeentrant(reentrant).vulnarableGetter();
}
function vulnarableVictimGetter2(
address reentrant
) public view returns (uint256) {
return internalRead(reentrant);
}
function internalRead(address reentrant) internal view returns (uint256) {
return Reeentrant(reentrant).vulnarableGetter();
}
modifier vulnarableVictimModifier(address reentrant) {
_;
require(Reeentrant(reentrant).vulnarableGetter() == 0);
}
function vulnarableFunction(address reentrant) public {
Reeentrant reentrant = Reeentrant(reentrant);
uint256 x;
reentrant.notVulnarableGetter();
reentrant.notVulnarableGetter2();
reentrant.notVulnarableGetter3(1);
reentrant.vulnarableGetter();
x = reentrant.vulnarableGetter2();
x = reentrant.vulnarableGetter3();
x = reentrant.vulnarableGetter4();
x = reentrant.vulnarableGetter5();
reentrant.notVulnarableGetter4();
}
function vulnarableFunctionWithModifier(
address reentrant
) public vulnarableVictimModifier(reentrant) {}
function notVulnarableFunction(Reeentrant reentrant) public {
uint256 x;
x = reentrant.notVulnarableGetter();
}
}
contract SecondaryVictim {
uint256 private _numberS;
function vulnarableSecondaryVictim(
address victim,
address reentrant
) public returns (uint256) {
_numberS = Victim(victim).vulnarableVictimGetter(reentrant);
return _numberS;
}
function vulnarableSecondaryVictim2(
address victim,
address reentrant
) public returns (uint256) {
_numberS = Victim(victim).vulnarableVictimGetter2(reentrant);
return _numberS;
}
}
contract DummyToken {
mapping(address => uint256) private _balances;
uint256 private _totalSupply;
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) public returns (bool) {
_balances[msg.sender] -= amount;
_balances[recipient] += amount;
return true;
}
function burn(uint256 amount) public {
_balances[msg.sender] -= amount;
_totalSupply -= amount;
}
}
contract ReentrantComplex {
address[] private _tokenAddresses;
address private _lpTokenAddress;
function getVirtualPrice() public view returns (uint256) {
uint256 sum;
for (uint256 i = 0; i < _tokenAddresses.length; i++) {
sum += DummyToken(_tokenAddresses[i]).balanceOf(address(this));
}
return sum / DummyToken(_lpTokenAddress).balanceOf(address(this));
}
function complexReentrantExploitable() public {
DummyToken(_lpTokenAddress).burn(1);
msg.sender.call("");
for (uint256 i = 0; i < _tokenAddresses.length; i++) {
DummyToken(_tokenAddresses[i]).transfer(address(msg.sender), 100);
}
}
}
contract VictimForComplex {
uint256 private _x;
function vulnarableComplexVictim(
address reentrant
) public returns (uint256) {
_x = ReentrantComplex(reentrant).getVirtualPrice();
return _x;
}
}
contract FalsePositive {
function notVulnarable() public {
DummyToken(msg.sender).balanceOf(msg.sender);
}
uint256 balance0;
uint256 balance1;
uint256 managerBalance0;
uint256 managerBalance1;
DummyToken token0 = DummyToken(zero);
DummyToken token1 = DummyToken(zero);
address zero = address(0);
function withdrawManagerBalance() external {
uint256 amount0 = managerBalance0;
uint256 amount1 = managerBalance1;
managerBalance0 = 0;
managerBalance1 = 0;
if (amount0 > 0) {
DummyToken(zero).transfer(zero, amount0);
}
if (amount1 > 0) {
DummyToken(zero).transfer(zero, amount1);
}
}
function _applyFees(uint256 _fee0, uint256 _fee1) private {
balance0 += _fee0 / 100;
balance1 += _fee1 / 100;
managerBalance0 += _fee0 / 100;
managerBalance1 += _fee1 / 100;
}
function _rebalance() public {
uint256 leftover0 = token0.balanceOf(address(this)) - managerBalance0;
uint256 leftover1 = token1.balanceOf(address(this)) - managerBalance1;
uint256 feesEarned0 = leftover0;
uint256 feesEarned1 = leftover1;
_applyFees(feesEarned0, feesEarned1);
}
}
//TODO(yhtiyar): add example with Balancer Vault read-only exlpoit (setiment hack)