作者:
GoSSIP_SJTU
·
2015/10/10 10:46
0x00 前言
在前兩講中,我們主要對SSL協議中CBC模式的弱安全性進行了系統的介紹。這一講中我們對用於生成SSL中所用金鑰的PRNG做一點簡單介紹。
0x01 什麼是PRNG?
PRNG(pseudo Random Noise Generation),即偽隨機噪聲生成,用於生成各種密碼學操作中所需的隨機數。
一般而言,Linux系統中的PRNG(簡稱為LRNG)可以被分為3個非同步的部分。第一部分把系統事件轉化為bit來表示潛在的熵;第二部分把這些bit加入到隨機數生成池;第三部分使用連續的SHA-1操作處理生成池中的bit來產生輸出,得到的反饋依然被放入生成池中來更新它。
每個從系統事件得到的隨機性都被收集為兩個32-bit的word。第一個word包含事件發生的時間,第二個word是事件的值,通常是一個按鍵、滑鼠移動或者驅動訪問的編碼。
LRNG使用一個計數器來計算加入到生成池中物理隨機性的估算值,這個值用一個不同事件發生頻率的函式來計算。這個值被表示為熵。
Linux系統中PRNG的結構抽象如下圖所示:
Pools and counters:上圖描述了LRNG流程。內部狀態保持在3個熵池裡:primary、secondary、urandom,它們的大小分別為512、128、128位元組。熵源向primary池新增資料;它的輸出再給到secondary和urandom中,LRNG的輸出是從secondary或者urandom中提取的。提取過程中,一個池子的內部狀態會根據反饋行為更新。
每個池子有一個熵值計數器。它是0到池子大小位元值之間的一個整數。輸出從熵池中提取後,這個值就減小提取出熵值的位元數。
熵值的增加比較複雜。(1)如果增加的bit來自某一熵源,那麼會根據這個熵源最近幾次事件的時序(timing)來評估它們的熵,然後相應增加計數器的值。(2)如果增加的bit來自primary,則接收池的計數器就直接增加接收到的bit數。
secondary池的計數器很重要,它用來判斷當前secondary池中的熵是否足夠用來生成隨機數,如果不夠,就進行阻塞並等待,並從primary池中提取熵,直到熵計數器的值大於某一閾值。
Adding physical entropy: 桌面及伺服器PC使用的熵源有4個:滑鼠、鍵盤活動,磁碟輸入輸出以及特殊中斷。事件發生時,會產生一個32bit的word表示時間(系統開機到現在經歷的毫秒數),一個word編碼它的屬性(事件型別、)。此外,同一型別連續事件的時序會被用來估算該事件提供的熵值。
收集到的熵分批加入池子,每分鐘幾次。預設加入primary,滿了的話加入secondary,不會加入urandom。
Generating output:當使用者使用/dev/urandom時,從urandom中提取隨機bit;當使用者使用/dev/random時,從secondary中提取隨機bit;當這兩個池子熵不夠時從primary池提取隨機bit。 /dev/random和/dev/urandom這兩種生成隨機數的方式特點如下:
- /dev/random 會生成非常安全的隨機bit,當隨機bit數不夠時,它可能阻塞使用者直到隨機bit都被生成;
- /dev/urandom 輸出的隨機bit相對不那麼安全,它會根據請求返回任意數量的偽隨機bit,但它的輸出不會被阻塞。
熵提取過程包括3個步驟:(1)更新熵池內容;(2)提取隨機bit到輸出;(3)減少熵池計數器值。
0x02 Linux中PRNG的運作原理
Linux中PRNG的初始化 作業系統啟動時會初始化LRNG,使用固定的作業系統引數和當前時間,以及額外的磁碟操作或系統事件。這樣的操作序列很容易被攻擊者預測,並且如果沒有額外事件發生,LRNG的熵會很有限。導致LRNG的輸出具有可預測性。
可以使用一個指令碼解決這個問題,在系統關閉時儲存一個隨機種子,在開機時把它寫回到池子中。關機時,指令碼從/dev/urandom中讀取512個位元組並寫入到一個檔案,開機時再把這寫位元組寫回到/dev/urandom裝置。寫回這些位元組改變primary池的狀態,效果類似於發生一系列事件。然後由primary池更新secondary及urandom池。
這個指令碼是Linux發行包中的一部分,而不是核心的一部分。在KNOPPIX及OpenWRT發行版中沒有這個指令碼,他們的LRNG初始狀態是可預測的,安全性較低。
估算熵值 LRNG僅適用一個時間函式估算事件的熵,而與事件型別無關。
事件的熵為:
更新熵池 熵池的更新機制基於TGFSR(Twisted Generalized Feedback Shift Register)。
TGFSR的唯一輸入是狀態的初始值(一個p*w bit的種子),每個迴圈中內部狀態被用來生成新的狀態。
LRNG中用的移位暫存器是基於TGFSR的,區別在於它在每次迴圈中增加熵。LRNG中的熵池被定義為長度為m個word的陣列。
熵透過執行add(pool, j, g)演算法來增加,g是新的熵word。
每個池子基於一個本原多項式來更新。多項式根據池子大小來選,secondary和urandom用的多項式相同。
從熵池中提取隨機bit的方式為:(1)對提取的bit做hash;(2)修改池子狀態;(3)減小計數器。 依據如下演算法:
0x03 弱PRNG對SSL的影響
OpenSSL的PRNG是一個確定性函式,知道輸入和呼叫次序的攻擊者可以預測輸出。為了使PRNG安全,熵池必須從/dev/random或者其它攻擊者不知道的熵源中取種。2006年,在Debian Linux釋出版本中的OpenSSL增加了一個漏洞修復,而這個漏洞修復引入了一個新的漏洞,它在修復另一個漏洞時把生成隨機數選種過程的程式碼給刪掉了,這導致初始選種時使用到的唯一的隨機值是當前程式ID-pid,Linux平臺預設最大程式號是32,768,所以生成隨機數的種子值範圍很小。這個漏洞到2008年才被發現,受影響的Debian Linux版本的可用熵都很有限,產生的公私鑰對是可預測的,這樣導致生成的SSL協議中使用的伺服器私鑰具有脆弱性。由於漏洞存在,攻擊者可以事先生成公私鑰對,一旦發現有跟公鑰匹配的就知道了對應私鑰。加州大學聖地亞哥分校的研究人員在漏洞釋出不久對流行的SSL伺服器進行掃描,發現1.5%左右的伺服器使用了弱金鑰證書。
事實上,因為弱PRNG生成的RSA和DSA弱金鑰在TLS和SSH伺服器中廣泛存在。在2012年的一項調查中發現,0.75%的TLS證書因為熵不足共享了金鑰,其他還有1.7%的裝置採用了相同實現,很可能也存在此類問題。0.5%的TLS主機和0.03%的SSH主機RSA私鑰可被獲取,因為它們的公鑰存在共享公因子現象。RSA公鑰的分解是很困難的,但是如果兩個公鑰共享了某一素因子,那麼只要計算出這個素因子,就可以將這兩個公鑰分解,從而計算出對應的私鑰。
這一現象是由於PRNG在生成隨機數時,如果用於產生RSA公鑰的兩個隨機數都是在熵源不足的情況下生成的,那麼採用相同實現的裝置就可能生成兩個相同的素因子,導致對應的私鑰相同。如果兩個隨機數中,僅有一個是在熵源不足的情況下生成,那麼兩臺採用相同實現的不同裝置生成的RSA公鑰就可能共享一個素因子,導致這兩個RSA公鑰均可很容易地被分解。
弱PRNG導致的弱RSA和DSA金鑰問題在嵌入式裝置如路由器、閉路電視等中也廣泛存在,因為這類裝置的熵源有限(缺少滑鼠鍵盤等熵源)。關於嵌入式裝置中弱金鑰的問題具體可以參考USENIX Security 14年的文章A Large-Scale Analysis of the Security of Embedded Firmwares,由於導致弱金鑰的原理相同,這裡就不多做介紹了。
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!