電腦關(guān)機(jī)了,內(nèi)存就沒數(shù)據(jù)了嗎?
大家好,我是周杰倫。
提到網(wǎng)絡(luò)攻擊技術(shù),你腦子里首先想到的是什么?
是DDoS
?
是SQL注入、XSS
?
還是棧溢出、RCE
(遠(yuǎn)程代碼執(zhí)行)?
這些最常見的網(wǎng)絡(luò)攻擊技術(shù),基本上都是與網(wǎng)絡(luò)、軟件、代碼、程序這些東西相關(guān)。
這也好理解,計(jì)算機(jī)網(wǎng)絡(luò)安全,不跟這有關(guān),還與什么有關(guān)系?
今天跟大家介紹一下,攻擊技術(shù),除了這些,還有一些腦洞大開的方式,它們可能跟時(shí)間、震動(dòng)、頻率、溫度
等各種物理相關(guān)的元素相關(guān),看完絕對(duì)讓你瞠目結(jié)舌!
這些不同于傳統(tǒng)軟件安全方向,而是從其他側(cè)面進(jìn)行的網(wǎng)絡(luò)攻擊,稱作側(cè)信道攻擊,也叫邊信道攻擊。
內(nèi)存冷凍
請(qǐng)大家思考一個(gè)問題:如果計(jì)算機(jī)突然斷電,內(nèi)存里的數(shù)據(jù)是不是瞬間就沒了?
內(nèi)存大家都知道,計(jì)算機(jī)運(yùn)行離不開這東西,程序指令和數(shù)據(jù)都存儲(chǔ)在這里,但你知道內(nèi)存條存儲(chǔ)數(shù)據(jù)的原理嗎?

看到圖中那一塊塊黑色的東西了嗎?那就是內(nèi)存條存儲(chǔ)數(shù)據(jù)的地方:內(nèi)存顆粒。
在內(nèi)存顆粒里面,是超大規(guī)模集成電路,里面是密密麻麻的存儲(chǔ)單元格,每一個(gè)單元格存儲(chǔ)一個(gè)比特位:

這個(gè)單元格中的核心部件是一個(gè)電容,電容有電壓,如果電壓在一定范圍內(nèi),這個(gè)單元格就是1,否則就是0。通過無數(shù)個(gè)這樣的單元格,構(gòu)成了GB級(jí)的存儲(chǔ)空間。
但大家學(xué)過物理應(yīng)該知道,電容是一個(gè)不穩(wěn)定的元件,隨著時(shí)間的流逝,電荷會(huì)泄漏,如果不加以控制,內(nèi)存條中的電壓最終就會(huì)變成0,內(nèi)存中的數(shù)據(jù)也就不準(zhǔn)確了。
因此,內(nèi)存在工作的時(shí)候,需要定時(shí)的進(jìn)行數(shù)據(jù)刷新,對(duì)電容進(jìn)行充電,一般最多64ms就得充一次電,才能保存數(shù)據(jù)的準(zhǔn)確性。
好了,內(nèi)存的背景知識(shí)交代完畢,現(xiàn)在回到本節(jié)一開始提到的那個(gè)問題:計(jì)算機(jī)突然斷電了,內(nèi)存中的數(shù)據(jù)瞬間就沒了嗎?
內(nèi)存中的數(shù)據(jù)是通過電容這種電子元件在承載,電容充放電都是需要時(shí)間的,所以可以大膽猜測(cè),即使斷電了,內(nèi)存中的數(shù)據(jù)全部消失也是需要時(shí)間的。
國(guó)外有人腦洞大開,做了一項(xiàng)實(shí)驗(yàn),測(cè)試內(nèi)存條斷電后,里面的數(shù)據(jù)隨著時(shí)間的流逝,消失的情況。
普通數(shù)據(jù)不方便觀察,用圖片最直接,實(shí)驗(yàn)中將一張蒙娜麗莎的圖片載入內(nèi)存中,然后斷電,然后計(jì)算不同時(shí)間后,這張圖的變化情況:

可以看到,在斷電后的5s內(nèi),圖片數(shù)據(jù)肉眼上看不到變化,30s后也影響不大,一分鐘后也能依稀可見,5分鐘后才徹底看不到。
而更重要的是,如果溫度降低時(shí),電容中電荷遺漏的速度將更加緩慢。下面這個(gè)表格是測(cè)試內(nèi)存條數(shù)據(jù)錯(cuò)誤率在正常情況和冷凍情況下的比較。

如上圖所示,紅色框是不冷凍情況,藍(lán)色框是在零下50攝氏度情況,可以看到,在冷凍情況下,一分鐘后,數(shù)據(jù)的錯(cuò)誤率是0,即便是在300秒(5分鐘)后,錯(cuò)誤率也只有驚人的0.0095%。
所以,一種新的攻擊方式就出來了:使用內(nèi)存冷凍的方式,可以從內(nèi)存條中提取數(shù)據(jù)。

內(nèi)存中有什么數(shù)據(jù)?很多程序的密碼密鑰可能都存在于內(nèi)存之中,最典型的比如Windows的開機(jī)密碼。
以為電腦關(guān)機(jī)了就安全了?可不是這么回事哦!
熔斷與幽靈
熔斷與幽靈漏洞攻擊大家應(yīng)該不會(huì)陌生,在2017年剛剛爆發(fā)出來的時(shí)候,占據(jù)了無數(shù)媒體的頭條,可見影響力之廣。

在講述這個(gè)漏洞之前,先請(qǐng)大家看一個(gè)故事。

學(xué)過計(jì)算機(jī)組成原理的朋友可能都知道,CPU有兩個(gè)重要的特性:分支預(yù)測(cè)和亂序執(zhí)行。
在執(zhí)行到判斷分支的時(shí)候(比如if判斷),CPU會(huì)根據(jù)它自己的“經(jīng)驗(yàn)”來判斷,一會(huì)兒可能會(huì)走入哪一個(gè)分支,從而提前執(zhí)行一些這個(gè)分支的指令,這叫分支預(yù)測(cè)。
另外,CPU在執(zhí)行一些指令的時(shí)候,可能并不是按照順序一條執(zhí)行完再執(zhí)行下一條,而是可能在會(huì)預(yù)執(zhí)行一些CPU認(rèn)為可以提前執(zhí)行,對(duì)程序流程無關(guān)的指令,這個(gè)叫亂序執(zhí)行。

但CPU認(rèn)為可以提前執(zhí)行,那就真的可以提前執(zhí)行嗎,執(zhí)行了可能對(duì)程序本身流程沒有影響,但會(huì)不會(huì)有什么副作用呢?
先留這個(gè)問題,一會(huì)兒再說,先來看另一個(gè)概念:緩存。
CPU執(zhí)行程序需要頻繁與內(nèi)存通信,讀取數(shù)據(jù)或者寫入數(shù)據(jù)。但內(nèi)存的響應(yīng)速度比CPU慢多了,于是CPU在其內(nèi)部加了一塊緩存,將最近要用的數(shù)據(jù)放到這里來,避免每一次都從內(nèi)存訪問,提高工作效率。

因此,同樣讀取一個(gè)數(shù)據(jù),從內(nèi)存讀和從緩存讀,時(shí)間上是有差異的。
熔斷與幽靈正是利用這一點(diǎn),來進(jìn)行了系統(tǒng)攻擊。
假如你想讀取操作系統(tǒng)內(nèi)核的數(shù)據(jù),但因?yàn)橄到y(tǒng)安全機(jī)制,應(yīng)用程序是無法直接訪問內(nèi)核空間的,但這個(gè)漏洞就可以幫你實(shí)現(xiàn)內(nèi)核空間數(shù)據(jù)的讀取。
下面這段程序,先執(zhí)行幾十次,每次傳入的x都小于16,每次都走入同一個(gè)分支,訓(xùn)練CPU,讓它獲得一些“經(jīng)驗(yàn)”,讓它認(rèn)為 < 16是大概率要執(zhí)行的分支,然后啟用亂序執(zhí)行,提前執(zhí)行x < 16這個(gè)分支中的指令。
void bad_guy(int x) {
if (x < 16) {
?temp &= array2[array1[x] * 512];
}}
來看一下,在上面這個(gè)例子中,x < 16這個(gè)分支中會(huì)通過array1這個(gè)數(shù)組訪問內(nèi)存,假設(shè)x突然來了一個(gè)很大的數(shù),這樣通過array1[x]訪問的內(nèi)存地址就溢出到了內(nèi)核空間了。
亂序執(zhí)行的后果,會(huì)提前計(jì)算array1[x] * 512,并將其作為下標(biāo)訪問array2數(shù)組的內(nèi)容,然后會(huì)將這個(gè)內(nèi)容從內(nèi)存條加載到CPU緩存中。
突然冷不丁的來一個(gè)大于16的參數(shù),這下它提前執(zhí)行的指令就白費(fèi)了。
而隨后,CPU發(fā)現(xiàn)了這一次x是大于16的,不應(yīng)該走到這個(gè)分支中來,上面描述的這一段活就白干了。
雖然是白干了,但它做了一件事:把a(bǔ)rray2數(shù)組對(duì)應(yīng)下標(biāo)的那一塊數(shù)據(jù)從內(nèi)存搬到緩存了。
這時(shí)再依次訪問array2數(shù)組的每一個(gè)元素,就能知道剛才的下標(biāo),再進(jìn)一步可以推斷出那個(gè)內(nèi)核空間的值。
然后不斷變換x的輸入,可以知道任意內(nèi)核地址空間的數(shù)據(jù)。
這就是熔斷與幽靈漏洞的核心思想:通過分支預(yù)測(cè)+亂序執(zhí)行+緩存內(nèi)存訪問時(shí)間差異來推斷內(nèi)核數(shù)據(jù)。
計(jì)時(shí)攻擊
熔斷與幽靈,其實(shí)是利用了CPU訪問內(nèi)存和訪問緩存的時(shí)間差來透露信息,從而實(shí)現(xiàn)數(shù)據(jù)泄漏,也就是說,利用的核心是時(shí)間這個(gè)物理量。
而利用時(shí)間的另一個(gè)經(jīng)典攻擊方式就是計(jì)時(shí)攻擊。
給大家舉個(gè)例子,如果要使用C語言編寫一個(gè)函數(shù),用來判斷輸入的密碼是否正確,有人可能會(huì)這樣寫:
bool check_passwd(char* input) { ?bool result = false; ?const char* passwd = "XiaoBaiGe2021"; ?if (input) { ? ?if (strlen(input) != strlen(passwd)) { ? ? ?return false;
? ?} ?
? ?const char* p1 = input; ? ?const char* p2 = passwd; ? ?while (*p1 && *p2) { ? ? ?if (*p1 == *p2) {
? ? ? ?p1++;
? ? ? ?p2++;
? ? ?} else { ? ? ? ?break;
? ? ?}
? ?} ? ?
? ?if (*p1 == '\0' && *p2 == '\0') {
? ? ?result = true;
? ?}
?} ?
?return result;
}
上面這個(gè)函數(shù)中,在正式的字符串比較之前,先進(jìn)行了長(zhǎng)度的比較,如果長(zhǎng)度都不同,那就不用比較了,節(jié)省時(shí)間。
但這個(gè)看上去很聰明的做法,實(shí)際上可能會(huì)給攻擊者提供了信息參考。
通過輸入不同長(zhǎng)度的字符串,發(fā)現(xiàn)程序驗(yàn)證所花費(fèi)的時(shí)間不同,攻擊者可能猜測(cè)出真正密碼的長(zhǎng)度。
接下來的驗(yàn)證過程中,開始逐位比較字符串的每一位,乍一看也沒毛病,但同樣的問題,如果第一位就比較錯(cuò)誤,程序很早就退出,而如果比較的時(shí)間相對(duì)較長(zhǎng),則說明密碼的前幾位可能是正確的。
通過這些信息,然后進(jìn)行不斷嘗試,密碼可以在短時(shí)間內(nèi)破解出來,是不是覺得不可思議?這是曾經(jīng)發(fā)生過的真實(shí)案例。
正則表達(dá)式

正則表達(dá)式在字符串校驗(yàn)、文本提取、格式化解析等領(lǐng)域有非常廣泛的應(yīng)用,基本上所有主流的編程語言都有對(duì)應(yīng)的程序庫(kù)。
但你知道正則表達(dá)式的解析引擎是如何工作的嗎,你知道如果傳入一些特定的字符串,可能讓解析引擎陷入巨大的計(jì)算陷阱嗎?
正則表達(dá)式的解析引擎有一個(gè)重要的流派就是基于NFA,這是一種狀態(tài)機(jī)。隨著解析引擎的逐字符解析,狀態(tài)機(jī)可能會(huì)有不同的下一個(gè)狀態(tài)。由于每一個(gè)狀態(tài)都有許多個(gè)下一個(gè)狀態(tài),解析引擎可能會(huì)在這條鏈路上不斷前行,直到找到一個(gè)匹配的。
比如在?《白帽子講Web安全》?一書中所提到的例子:
點(diǎn)擊下方領(lǐng)取**《白帽子講Web安全》** 高清PDF版
【點(diǎn)我領(lǐng)取】
有一個(gè)這樣的正則表達(dá)式:^(a+)+$
如果輸入四個(gè)字符'aaaa',解析引擎的執(zhí)行過程是這樣的:
它只有16條路徑,很快就能走完。
但如果輸入的字符a大量增加時(shí),執(zhí)行的路徑將會(huì)暴增。

可以看到,隨著后續(xù)字符串長(zhǎng)度的增加,花費(fèi)的時(shí)間開始呈2倍增長(zhǎng)趨勢(shì)。如果輸入64個(gè)字符a,那將會(huì)是什么后果?
CPU飛轉(zhuǎn),程序失去響應(yīng),服務(wù)拒絕攻擊DOS!
沒想到吧,你只是輸入了一個(gè)正則表達(dá)式字符串,就能讓服務(wù)器失去響應(yīng)。
點(diǎn)擊下方領(lǐng)取**《白帽子講Web安全》** 高清PDF版
【點(diǎn)我領(lǐng)取】
總結(jié)
以上就是今天給大家介紹的一些側(cè)信道攻擊技術(shù)。
除了上面這些案例,通過CPU的利用率,CPU電信號(hào)的頻率等等信息,也能泄露出很多有價(jià)值的信息,側(cè)信道攻擊技術(shù),不只是電影中的黑科技,而是可能真實(shí)發(fā)生的攻擊。