最近發現的嚴重漏洞 (CVE-2018-17144) 是如何工作的?
如果您是一名礦工,您將採取哪些步驟來創建額外的(第 21,000,012.5 個)比特幣?
這在原始碼中到底在哪裡(連結)?
為什麼非礦工不能做到這一點?
此外,哪些分叉是/易受攻擊的?
如果您是一名礦工,您將採取哪些步驟來創建額外的(第 21,000,012.5 個)比特幣?
這在原始碼中到底在哪裡(連結)?
CVE-2018-17144 有兩個組成部分。有一個崩潰錯誤和一個通貨膨脹錯誤。兩者都是由幾乎相同的場景觸發的:一個事務多次包含一個輸入。
一般來說,這是如何工作的:假設一個礦工有一個未使用的輸出 A 用於 1 BTC。他們使用該輸入兩次創建交易,因此輸入 1 從輸出 A 花費,輸入 2也從輸出 A 花費。該交易的輸出具有 2 BTC 的價值。請注意輸出的值如何大於輸出 A 的值,但如果您有兩次輸出 A,則該值是正確的。
然後礦工將接受此交易並將其包含在他正在探勘的區塊中。一旦礦工找到一個包含他的交易的區塊,他就會將其廣播到比特幣網路。
當一個 Bitcoin Core 0.14.x 節點接收到這個塊時,它會驗證這個塊,但是由於這一行的
false
參數,它會跳過重複輸入檢查。所以礦工進行的交易將通過這一步驗證,以及其他交易驗證步驟,包括輸入腳本驗證,直到它到達這個循環。在這個循環中,交易的輸入在 UTXO 數據庫中被標記為已花費。第一次看到重複的輸入時,它被標記為已用。但是第二次看到時,硬幣已經被標記為已用過,所以coins->vout[nPos].IsNull()
這是真的。這意味著它將進入這個 if 語句並隨後命中後面的assert
語句. 斷言導致軟體崩潰。對於 Bitcoin Core 0.15.0 - 0.16.2,行為是不同的。這是由於 UTXO 數據庫的結構發生了變化。在到達相同的循環之前,一切都基本相同。在這裡,實際上不是返回輸出是否被花費,而是返回
SpendCoin
輸入是否存在於數據庫中。所以第一次,它會按預期通過,但第二次false
,它仍然沒有返回,而是返回true
。查看
SpendCoin
,您可以看到它僅在無法從數據庫中獲取硬幣(表示 UTXO 的對象)時才返回 false。使用新的數據庫結構,這是有道理的,因為輸出應該在使用時從數據庫中刪除。但是,如果你往下看幾行,你會發現它只有在標記為 時才會刪除硬幣FRESH
。在硬幣為 的情況下FRESH
,SpendCoin
將在第一遍刪除對象,因此第二遍將找不到硬幣,因此將返回 false。這會觸發以下導致節點關閉assert
的函式呼叫。如果 coin 不是
FRESH
,則不會刪除 coin 對象本身,但會清除其內容。這意味著第二次看到輸入時,如果 coin is not ,FRESH
仍然會返回 true,因為該對象仍然存在於記憶體中SpendCoin
,這意味著它通過了緊跟. 然後驗證繼續正常進行,並將此交易創建的輸出添加到 UTXO 數據庫中,這意味著 UTXO 數據庫中現在存在不應該存在的錢。SpendCoin``FRESH
所以現在的問題是,UTXO 什麼時候標記為
FRESH
?FRESH
它們在添加到 UTXO 數據庫時會被標記。但是 UTXO 數據庫仍然只在記憶體中(作為記憶體)。當它被保存到磁碟時,記憶體中的條目不再被標記為FRESH
。這種保存到磁碟的過程發生在每個塊之後(以及其他時間,但這並不重要)。因此,如果礦工的輸出是已經確認的交易的一部分,並且他在同一交易中花費了兩次輸出(因此交易有兩個輸入引用相同的輸出),並且該交易不被廣播到網路,而是包含在他探勘的塊中,他能夠創建一個新的輸出,其價值是他花費的輸出的兩倍,從而創造了硬幣。
為什麼非礦工不能做到這一點?
非礦工無法做到這一點的原因是,仍然會檢查在塊外接收到的交易是否存在重複輸入。該交易將被視為無效而被拒絕,並且不會添加到節點的記憶體池中,因此該交易永遠不會進入區塊。只有具有重複輸入的交易才會觸發此漏洞,因此只有礦工可以這樣做,因為他們必須故意將無效交易插入他們的塊中。
此外,哪些分叉是/易受攻擊的?
任何軟體包含 commit 的分叉
eecffe50efc3944d713c701fa375dacbf17fb7cf
。這意味著任何軟體在 2016 年 11 月 10 日之後從 Bitcoin Core 分叉或提取更改。