iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 15
1
Security

網路安全概述系列 第 15

談 Debian OpenSSL 的問題與亂數產生器

  • 分享至 

  • xImage
  •  

預計下幾篇會講到:

  • Heartbleed
  • SQL Injection 和其帶來的災害(包括最近出現的巨大漏洞)

前言

我們今天要談 Debian 的 OpenSSL 在約莫十二年前發生的 bug,這個 bug 持續了半年之久,並導致許多人產生了一些壞掉的金鑰。

主要原因是由於開發者的不慎,導致將亂數產生器的 變得相當得少,所以產生出來的亂數,隨機性會非常的低。

熵 (Entropy) 在密碼學中,可以指一個亂數的隨機度有多大。
例如說,一顆公正的硬幣,每擲一次可以產生 1 bit 的熵(若硬幣不公正則更少)。理論上,一組理想中的亂數,其熵的數量會與其長度相等,不過日常生活中,熵的數量不高(例如 CPBL 的全壘打率,因為這球壞掉了,常常打出全壘打,故熵不高)。

在電腦處理上,當需要產生一個足夠好的亂數時,也會遇到熵太少的問題。通常來說,這些熵的來源,是來自滑鼠操作、雜訊等等。一般來說,這些熵都會先存起來,然後放進熵庫,這樣當要需要用亂數的時候,才會有足夠的熵可以拿來產亂數(否則你每次產亂數都要等)。不過,當需要更好的亂數,也需要夠快的產生出亂數的時候,就需要拿出 偽隨機亂數產生器,它會將這些不夠格的熵,拿去一個函式做計算,將其轉換成足夠長的亂數。這些熵,只要一個字變了,其產生出的最終結果將會有很大的不同(參考 MD5/SHA1)。
因此,即使攻擊方可以預測滑鼠移動,但是因為有多個不同的來源,且只要某一來源變動一小點數值,其輸出即會有巨大的變化,所以不太能夠預測最終的結果。

不過,亂數產生器有個問題:實務上沒有辦法量測出一個亂數的熵有多大。

OpenSSL 的問題

OpenSSL 有個函式,RAND_add(buf, n, e),即將長度 n 的隨機數值 buf 與其估計的熵大小 e 加進熵池。RAND_add 內部會再呼叫 MD_update,把一些種子給放進熵池。

當 OpenSSL 需要更多熵時,會使用 rand_unix.c/RAND_poll

char buf[100];
fd = open("/dev/random", O_RDONLY);
n = read(fd, buf, sizeof buf);
close(fd);
RAND_add(buf, sizeof buf, n);

註:有 /dev/random/dev/urandom,前者在熵不夠時會停止輸出,後者不會。

read() 是返回 ssize_int,做 read 動作讀了多少 bytes。
不過最後呼叫 RAND_add 時,buf 只有 100 bytes,而且這個長度被傳進去當參數,所以有可能 buf 只有裝滿 50 bytes,剩下的 50 bytes 是未初始化的,實際上卻跟 RAND_add 說這長度有 100 bytes(或反過來)的狀況。

另一邊 RAND_load_file (讓人讀檔時也來增加熵的函式)也是這樣幹的,而且還特別寫註解:

i=fread(buf,1,n,in);
if (i <= 0) break;
/* even if n != i, use the full array */
RAND_add(buf,n,i);

還有一個函式 RAND_bytes,是反過來,從熵池中產生一些亂數,然後再把這些產生過的亂數塞回去熵池中。

理論上,使用未初始化的數值,然後還「說謊」告知不正確的熵數,也還是能夠增加亂數池中的熵。不過,通常這樣使用未初始化數值是不太好的,例如 code 不好讀(每個人都要思考一下為啥會這樣做)、有可能讀一讀結果出現 segfault,也有可能發生可怕的事情:

Debian 維護者幫改

結果某一天,Debian 套件的維護者拿 valgrind (註:一個可以測看看有沒有 leak 等等的工具)去測 OpenSSL,然後 valgrind 抱怨說,某處用了未初始化的數值。

然後一路追回來,發現問題出在呼叫 MD_update 時,會發生這個使用未初始化數值的問題。
因為他不曉得原本這邊是發生什麼事情,於是他發信去 mailing list 問說這邊是啥問題,能不能先註解掉。
後來兩個路人(其中一個人是 OpenSSL 的開發者)說,如果 valgrind 一直抱怨,你要除錯,就先註解掉。

結果這維護者就先把呼叫 MD_update 的程式碼都給拿掉,然後沒有放回去。最後導致 Debian 中整個 OpenSSL 的亂數產生器變成一個殘廢,唯一的熵來源就只剩下 RAND_bytes 的某處,會將當前最大的程序 ID 加進去亂數池,導致這亂數產生器最多也只能產生出 32767 種組合。

影響範圍

這個 bug 持續了半年之久,所以許多人發現他們用 OpenSSL 產生出的金鑰是壞掉的(而且還會撞 key)。

舉例來說,以下的東西是依靠 OpenSSL 的亂數產生器,而且需要有足夠強的亂數產生器才會算夠安全的:

  • SSH 金鑰
  • DNSSEC 金鑰
  • SSL/TLS session key 與憑證

這個 bug 其實也不能怪罪到任何人頭上,畢竟當初 Debian 的維護者有發文問人(不過他沒有寫完整,若把 code 貼完整一點可能就有差),然後路人可能看了覺得沒差,就和他講,結果就導致這種問題的發生。另一方面,如果說 OpenSSL 有回來問結果如何,要不要把 patch 給他們,可能就會有人幫他 review,然後提早發現東西被改壞了。

關於這件事情,可以參考 Debian 的說明文件


上一篇
你的軟體是否足夠安全?談「軟體」的安全,以 CVE-2017-5638 (Struts2) 為例
下一篇
談 Heartbeat 協定與 CVE-2014-0160 (Heartbleed)
系列文
網路安全概述31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言