電腦關機了,記憶體就沒資料了嗎?

程式碼熬夜敲發表於2021-10-26

大家好,我是周杰倫。

提到網路攻擊技術,你腦子裡首先想到的是什麼?

DDoS

是SQL隱碼攻擊、XSS

還是棧溢位、RCE(遠端程式碼執行)?

這些最常見的網路攻擊技術,基本上都是與網路、軟體、程式碼、程式這些東西相關。

這也好理解,計算機網路安全,不跟這有關,還與什麼有關係?

今天跟大家介紹一下,攻擊技術,除了這些,還有一些腦洞大開的方式,它們可能跟時間、震動、頻率、溫度等各種物理相關的元素相關,看完絕對讓你瞠目結舌!

這些不同於傳統軟體安全方向,而是從其他側面進行的網路攻擊,稱作側通道攻擊,也叫邊通道攻擊

記憶體冷凍

請大家思考一個問題:如果計算機突然斷電,記憶體裡的資料是不是瞬間就沒了?

記憶體大家都知道,計算機執行離不開這東西,程式指令和資料都儲存在這裡,但你知道記憶體條儲存資料的原理嗎?

在這裡插入圖片描述

看到圖中那一塊塊黑色的東西了嗎?那就是記憶體條儲存資料的地方:記憶體顆粒。

在記憶體顆粒裡面,是超大規模積體電路,裡面是密密麻麻的儲存單元格,每一個單元格儲存一個位元位:

在這裡插入圖片描述

這個單元格中的核心部件是一個電容,電容有電壓,如果電壓在一定範圍內,這個單元格就是1,否則就是0。通過無數個這樣的單元格,構成了GB級的儲存空間。

但大家學過物理應該知道,電容是一個不穩定的元件,隨著時間的流逝,電荷會洩漏,如果不加以控制,記憶體條中的電壓最終就會變成0,記憶體中的資料也就不準確了。

因此,記憶體在工作的時候,需要定時的進行資料重新整理,對電容進行充電,一般最多64ms就得充一次電,才能儲存資料的準確性。

好了,記憶體的背景知識交代完畢,現在回到本節一開始提到的那個問題:計算機突然斷電了,記憶體中的資料瞬間就沒了嗎?

記憶體中的資料是通過電容這種電子元件在承載,電容充放電都是需要時間的,所以可以大膽猜測,即使斷電了,記憶體中的資料全部消失也是需要時間的。

國外有人腦洞大開,做了一項實驗,測試記憶體條斷電後,裡面的資料隨著時間的流逝,消失的情況。

普通資料不方便觀察,用圖片最直接,實驗中將一張蒙娜麗莎的圖片載入記憶體中,然後斷電,然後計算不同時間後,這張圖的變化情況:

在這裡插入圖片描述

可以看到,在斷電後的5s內,圖片資料肉眼上看不到變化,30s後也影響不大,一分鐘後也能依稀可見,5分鐘後才徹底看不到。

而更重要的是,如果溫度降低時,電容中電荷遺漏的速度將更加緩慢。下面這個表格是測試記憶體條資料錯誤率在正常情況和冷凍情況下的比較。

在這裡插入圖片描述

如上圖所示,紅色框是不冷凍情況,藍色框是在零下50攝氏度情況,可以看到,在冷凍情況下,一分鐘後,資料的錯誤率是0,即便是在300秒(5分鐘)後,錯誤率也只有驚人的0.0095%。

所以,一種新的攻擊方式就出來了:使用記憶體冷凍的方式,可以從記憶體條中提取資料。

在這裡插入圖片描述

記憶體中有什麼資料?很多程式的密碼金鑰可能都存在於記憶體之中,最典型的比如Windows的開機密碼。

以為電腦關機了就安全了?可不是這麼回事哦!

熔斷與幽靈

熔斷與幽靈漏洞攻擊大家應該不會陌生,在2017年剛剛爆發出來的時候,佔據了無數媒體的頭條,可見影響力之廣。

在這裡插入圖片描述

在講述這個漏洞之前,先請大家看一個故事。

在這裡插入圖片描述

學過計算機組成原理的朋友可能都知道,CPU有兩個重要的特性:分支預測和亂序執行。

在執行到判斷分支的時候(比如if判斷),CPU會根據它自己的“經驗”來判斷,一會兒可能會走入哪一個分支,從而提前執行一些這個分支的指令,這叫分支預測。

另外,CPU在執行一些指令的時候,可能並不是按照順序一條執行完再執行下一條,而是可能在會預執行一些CPU認為可以提前執行,對程式流程無關的指令,這個叫亂序執行。

在這裡插入圖片描述

但CPU認為可以提前執行,那就真的可以提前執行嗎,執行了可能對程式本身流程沒有影響,但會不會有什麼副作用呢?

先留這個問題,一會兒再說,先來看另一個概念:快取。

CPU執行程式需要頻繁與記憶體通訊,讀取資料或者寫入資料。但記憶體的響應速度比CPU慢多了,於是CPU在其內部加了一塊快取,將最近要用的資料放到這裡來,避免每一次都從記憶體訪問,提高工作效率。

在這裡插入圖片描述

因此,同樣讀取一個資料,從記憶體讀和從快取讀,時間上是有差異的。

熔斷與幽靈正是利用這一點,來進行了系統攻擊。

假如你想讀取作業系統核心的資料,但因為系統安全機制,應用程式是無法直接訪問核心空間的,但這個漏洞就可以幫你實現核心空間資料的讀取。

下面這段程式,先執行幾十次,每次傳入的x都小於16,每次都走入同一個分支,訓練CPU,讓它獲得一些“經驗”,讓它認為 < 16是大概率要執行的分支,然後啟用亂序執行,提前執行x < 16這個分支中的指令。

void bad_guy(int x) {
 if (x < 16) {
  temp &= array2[array1[x] * 512];
 }
}

來看一下,在上面這個例子中,x < 16這個分支中會通過array1這個陣列訪問記憶體,假設x突然來了一個很大的數,這樣通過array1[x]訪問的記憶體地址就溢位到了核心空間了。

亂序執行的後果,會提前計算array1[x] * 512,並將其作為下標訪問array2陣列的內容,然後會將這個內容從記憶體條載入到CPU快取中。

突然冷不丁的來一個大於16的引數,這下它提前執行的指令就白費了。

而隨後,CPU發現了這一次x是大於16的,不應該走到這個分支中來,上面描述的這一段活就白乾了。

雖然是白乾了,但它做了一件事:把array2陣列對應下標的那一塊資料從記憶體搬到快取了。

這時再依次訪問array2陣列的每一個元素,就能知道剛才的下標,再進一步可以推斷出那個核心空間的值。

然後不斷變換x的輸入,可以知道任意核心地址空間的資料。

這就是熔斷與幽靈漏洞的核心思想:通過分支預測+亂序執行+快取記憶體訪問時間差異來推斷核心資料。

計時攻擊

熔斷與幽靈,其實是利用了CPU訪問記憶體和訪問快取的時間差來透露資訊,從而實現資料洩漏,也就是說,利用的核心是時間這個物理量。

而利用時間的另一個經典攻擊方式就是計時攻擊。

給大家舉個例子,如果要使用C語言編寫一個函式,用來判斷輸入的密碼是否正確,有人可能會這樣寫:

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;
}

上面這個函式中,在正式的字串比較之前,先進行了長度的比較,如果長度都不同,那就不用比較了,節省時間。

但這個看上去很聰明的做法,實際上可能會給攻擊者提供了資訊參考。

通過輸入不同長度的字串,發現程式驗證所花費的時間不同,攻擊者可能猜測出真正密碼的長度。

接下來的驗證過程中,開始逐位比較字串的每一位,乍一看也沒毛病,但同樣的問題,如果第一位就比較錯誤,程式很早就退出,而如果比較的時間相對較長,則說明密碼的前幾位可能是正確的。

通過這些資訊,然後進行不斷嘗試,密碼可以在短時間內破解出來,是不是覺得不可思議?這是曾經發生過的真實案例。

正規表示式

在這裡插入圖片描述

正規表示式在字串校驗、文字提取、格式化解析等領域有非常廣泛的應用,基本上所有主流的程式語言都有對應的程式庫。

但你知道正規表示式的解析引擎是如何工作的嗎,你知道如果傳入一些特定的字串,可能讓解析引擎陷入巨大的計算陷阱嗎?

正規表示式的解析引擎有一個重要的流派就是基於NFA,這是一種狀態機。隨著解析引擎的逐字元解析,狀態機可能會有不同的下一個狀態。由於每一個狀態都有許多個下一個狀態,解析引擎可能會在這條鏈路上不斷前行,直到找到一個匹配的。

比如在 《白帽子講Web安全》 一書中所提到的例子:

點選下方領取《白帽子講Web安全》 高清PDF版

【點我領取】

有一個這樣的正規表示式:^(a+)+$

如果輸入四個字元'aaaa',解析引擎的執行過程是這樣的:

它只有16條路徑,很快就能走完。

但如果輸入的字元a大量增加時,執行的路徑將會暴增。

在這裡插入圖片描述

可以看到,隨著後續字串長度的增加,花費的時間開始呈2倍增長趨勢。如果輸入64個字元a,那將會是什麼後果?

CPU飛轉,程式失去響應,服務拒絕攻擊DOS!

沒想到吧,你只是輸入了一個正規表示式字串,就能讓伺服器失去響應。

點選下方領取《白帽子講Web安全》 高清PDF版

【點我領取】

總結

以上就是今天給大家介紹的一些側通道攻擊技術。

相關文章