【譯】 沙箱中的間諜 - 可行的 JavaScript 快取記憶體區攻擊
王龑 — MAY 27, 2015 原文連線 The Spy in the Sandbox – Practical Cache Attacks in Javascript
相關論文可在 https://github.com/wyvernnot/cache_attack_in_javascript 下載
作者和單位
- Yossef Oren (yos AT cs.columbia.edu)
- Vasileios P. Kemerlis (vpk AT cs.columbia.edu)
- Simha Sethumadhavan (simha AT cs.columbia.edu)
- Angelos D. Keromytis (angelos AT cs.columbia.edu)
哥倫比亞大學計算機系
摘要
我們將展示首個完全執行在瀏覽器裡的針對微架構的邊通道攻擊手段。與這個領域裡的其它研究成果不同,這一手段不需要攻擊者在受害者的電腦上安裝任何的 應用程式來展開攻擊,受害者只需要開啟一個由攻擊者控制的惡意網頁。這種攻擊模型可伸縮性高,易行,貼近當今的網路環境,特別是由於絕大多數桌面瀏覽器連線到 Internet 因此幾乎無法防禦。這種攻擊手段基於 Yarom 等人在 文[23] 提出的 LLC攻擊
,可以讓攻擊者遠端獲得屬於其它程式、使用者甚至是虛擬機器的資訊, 只要它們和受害者的瀏覽器執行在執行在同一臺物理主機上。我們將闡述這種攻擊背後的基本原理,然後用一種高頻寬的隱藏通道驗證它的效果,最後 用它打造了一個覆蓋整個系統的滑鼠和網路活動記錄器。抵禦這種攻擊是可能的,但是所需的反制措施對瀏覽器和電腦的正常使用所產生的代價有點不實際。
1 引言
邊通道分析是裡一種非常強大的密碼分析攻擊。攻擊者通過分析安全裝置內部在進行安全運算時所產生的的物理訊號(功率,輻射,熱量等)來取得祕密資訊[15]。 據說在二戰中便有情報部門在使用,Kocher 等人在1996年首次在學術情境下討論了這個問題[14]。邊通道分析被證實可以用來侵入無數的現實世界中的系統,從汽車報警器 到高安全性的密碼協處理器[8][18]。快取攻擊(Cache Attack)是和個人電腦相關的一種邊通道攻擊,因為高速緩衝區被不同的程式和使用者使用而導致了資訊的洩露[17][11]。
雖然邊通道攻擊的能力無可置疑,但是要實際應用到系統上還是相對受限的。影響邊通道攻擊可行性的一個主要因素是對不確定的攻擊模型的假設:除了基於網路的 時序攻擊,大部分的邊通道攻擊都要求攻擊者非常接近受害者。快取攻擊一般會假設攻擊者能夠在受害者的機器上執行任意的二進位制的程式碼。雖然這個假設 適用於像 Amazon 雲端計算平臺這樣的 IaaS 或者 PaaS 環境,但是對於其它環境就不那麼貼切了。
在這篇報告裡,我們用一種約束更少、更可行的攻擊者模型挑戰了這一限制性的安全假設。在我們的攻擊模型裡,受害者只需要訪問一個網頁,這個網頁 由攻擊者所擁有。我們會展示,即使在這麼簡單的攻擊者模型裡,攻擊者依然能夠在可行的時間週期裡,從被攻擊的系統中提取出有意義的資訊。為了和這樣的 計算設定保持一致,我們把注意力集中在了跟蹤使用者行為而不是獲取密匙上。報告中的攻擊方式因此是高度可行的:對於攻擊者的假設和限定是 實際的;執行的時間是實際的;給攻擊者帶來的好處也是實際的。據我們瞭解,這是首個可以輕鬆擴充套件至上百萬個目標的邊通道攻擊方式。
我們假設攻擊中受害者使用的個人電腦配備有較新型號的的 Intel CPU ,並進一步假設使用者用支援 HTML5 的瀏覽器訪問網頁。這覆蓋了絕大部分連線到 Internet 的 個人電腦,見 章節5.1 。使用者被強迫訪問一個頁面,這個頁面上有一個由攻擊者控制的元素比如廣告。攻擊程式碼自己會執行基於 JavaScript 的 快取攻擊,見 章節2,持續地訪問被攻擊系統的 LLC 。因為所有的 CPU 核心,使用者,程式,保護環等共享同一個快取記憶體區,所以可為攻擊者提供被攻擊使用者和系統的詳細資訊。
1.1 現代 Intel CPU 的記憶體架構
現代的計算機系統通常會採用一個高速的中央處理器(CPU)和一個容量大但是速度較慢的隨機存取器(RAM)。為了克服這兩個模組的效能鴻溝,現代的計算機系統會採用 快取記憶體 - 一種容量小但是速度更快的記憶體結構,裡面儲存了 RAM
中最近被 CPU
訪問過的子集。快取記憶體通常會採用** 分層** 設計,即在CPU
和 RAM
之間 分層 放置一些列逐漸變大和變慢的記憶體結構。圖1 取自 文[22],展示了 Intel Ivy Bridge 的快取結構,包括:較小的 level 1(L1) cache,大一些的** level 2 (L2) cache**,最下方是最大的 level 3 (L3) cache 並和 RAM 相連。Intel 目前代號為 Haswell 的新一代 CPU 採用了另一種嵌入式的 DRAM(eDRAM) 設計,所以不在本文討論範圍內。如果 CPU 需要訪問的資料當前不在快取裡,會觸發一個 未命中 ,當前快取裡的一項必須被 **淘汰 **來給新元素騰出空間。
圖1:Intel Ivy Bridge
Intel 的快取微架構是 **巢狀的 **- 所有一級快取裡的資料必須同時存在二級和三級快取裡。倒過來,如果某個元素在三級快取裡被淘汰,那它也會立刻被 從二級和一級快取裡移走。需要注意的是 AMD 快取微架構的設計是非巢狀的,所以本文描述的方法並不能立刻應用到該平臺上。
本文重點關注第三級快取,通常也被稱為 LLC。由於 LLC 較大,CPU 訪問記憶體的時候如果把整個 LLC 的內容都搜尋一遍效率會很低。 為了避免這個問題,LLC 通常被分成不同的 組,每一組都對應者記憶體空間的一個固定的子集。每個組包含若干快取線。例如,Intel Haswell 系列中的 Core i7-3720QM 處理器擁有 8192 = 2^13 個組,每個組有 12 條 64 = 2^6 位元組的快取線,共同組成 8192 x 12 x 64 = 6 MB 的快取記憶體。 CPU 如果要檢查一個給定的實體地址是否在三級快取裡,會先計算出組,然後只檢查組內的快取線。結果就是,某個實體地址的快取未命中,會導致同一個組的 為數不多的快取線中的一條被淘汰,這個事實會被我們的攻擊反覆運用。由64位元長度的實體地址到13位元長度的組索引的對映方法已經被 Hund 等人在2013年 通過逆向工程得出來[12]。在表示實體地址的64個位元位裡,5到0被忽略,16到6被直接用來作為組索引的低11位,63到17雜湊後得到組索引的高2位。 LLC 被所有的核心、執行緒、程式、使用者,乃至執行在同一塊 CPU 晶片上的虛擬機器所共享,而不論特權環或其它類似的保護措施。
現代的個人電腦採用了一種 虛擬記憶體機制,在這種機制裡,使用者程式一般無法直接得到或訪問系統的實體記憶體。取而代之的是,程式會被分配不同的虛擬記憶體頁。 如果某個虛擬記憶體頁被當前執行的程式訪問,作業系統會在實體記憶體裡動態地分配一個頁框。CPU 的記憶體管理單元(MMU)負責把不同程式對虛擬記憶體地址的訪問 對映到實體記憶體。Intel 處理器的頁和頁框大小一般是4KB,並且頁和頁框的是按照頁對齊的 - 每頁的開始地址是頁大小的倍數。這意味著,任何虛擬地址 的低12位和對應的虛擬地址是一一對應的,這一事實也在我們的攻擊中用到。
1.2 快取攻擊
快取攻擊是針對微架構的攻擊手段中一個典型的代表, Aciamez 在他的出色的調查中將這類攻擊定義為利用 “信任架構邊界下方的底層處理器結構” 從不同的安全系統中 獲取祕密資訊。快取攻擊基於這樣的事實:儘管在上層有諸多像沙箱、虛擬記憶體、特權環,宿主這樣的保障機制,安全和不安全程式通過快取記憶體區的共用可以互相影響。 攻擊者構造一個“間諜”程式後可以通過被共用的快取來測量和干擾其它安全程式的內部狀態。Hu 在1992年的首次發現[11],在隨後的一些研究成果裡顯示了邊通道攻擊可被用來獲取 AES密匙[17][4],RSA密匙[19],甚至可以允許一臺虛擬機器侵入宿主上的其它機器。
我們的攻擊建立在 填充+探測 模型的基礎上,這個方法由 Osvik 在[17]中首次描述,不過是針對一級快取的。之後由 Yarom 等人在[23]中擴充套件到啟用了 較大記憶體頁系統的 LLC。我們把這個方法擴充套件了一下支援更加常見的 4K 的頁大小。總的來說,填充+探測有四個步驟。第一步,攻擊者建立一個 或多個移除集。移除集是記憶體中的一系列地址,這些地址被訪問的時候會佔據受害者程式使用的一條快取線。第二步,攻擊者通過訪問移除集填充整個組。 這會強制受害者的程式碼或指令被從組中淘汰,並使組進入一個已知的狀態。第三步,攻擊者觸發或只是等待受害者執行和可能使用組。最後,攻擊者通過再次訪問移除集來探測組。如果訪問的延遲比較低,意味著攻擊者的指令或資料還在快取裡。否則,較高的訪問延遲意味著受害者的程式碼用到了組,因此攻擊者可以瞭解受害者的內部狀態。 實際的時間測量是用非特權的彙編指令RDTSC進行的,這個指令可以得到處理器非常準確的週期數。再次遍歷連結串列還有第二個目的,那就是強制組進入 受攻擊者控制的狀態,為下一輪的測量做好準備。
1.3 Web 執行環境
JavaScript 是一種擁有動態型別,基於物件的執行時求值的指令碼語言,它支撐著現代網際網路的客戶端。JavaScript 程式碼以原始碼的形式傳到瀏覽器端,由瀏覽器 即時編譯(JIT) 機制來編譯和優化。不同瀏覽器廠商之間的激烈競爭使不斷改進 JavaScript 效能備受關注。結果就是,在某些場景下,JavaScript 執行的效率已經 可以和機器語言相媲美。
JavaScript 語言的核心功能是由 ECMA 產業協會在 ECMA-262 標準中定義的。語言標準由全球資訊網協會(W3C)定義的一系 API 所補充,因此適合開發 Web 內容。 JavaScript API 的集合是不斷演進的,瀏覽器廠商依照自己的開發計劃不斷增加新的 API 支援。我們的工作中用到兩個具體的API: 第一個是型別陣列的定義9,通過它可以高效地訪問非結構化的二進位制陣列。 第二個是高精度時間API16,讓應用程式可以進行毫秒以下時間的測量。 如 章節5.1 所示,大部分當今主流的瀏覽器都同時支援這兩個API。
JavaScript 程式碼執行在高度沙箱化的環境裡 - 用 JavaScript 交付的程式碼對系統的訪問非常受限。例如,JavaScript 程式碼如果沒有使用者的允許不能開啟和讀取檔案。 JavaScript 程式碼不能執行機器語言或者載入本地的程式碼庫。 最值得注意的是,JavaScript 程式碼沒有指標的概念,所以你連一個 JavaScript 變數虛擬地址都沒法知道。
1.4 我們的工作
我們的目的是構造一個可以通過 Web 部署的 LLC 攻擊。這個過程是充滿挑戰的,因為 JavaScript 程式碼沒法載入共享庫或者執行本機語言的程式, 並且由於無法直接呼叫專用的彙編指令而被迫呼叫指令碼語言的函式進行時間的測量。儘管有這些挑戰,我們還是成功地把快取攻擊擴充套件到了基於 Web 環境:
我們展示了一種用來在 LLC 上的建立 非典型移除集 特別方法。與[23]不同,我們的方法不要求系統配置成支援較大的記憶體頁,所以能夠很快的 應用到廣泛的桌面和伺服器系統。我們展示了該方法雖然是使用 JavaScript 實現的,但是依然可以在實際的時間週期裡完成。
我們展示了一種 功能完善的用無需特權的 JavaScript 發動 LLC 攻擊 的方法。我們用隱藏通道的方式,評估了它的效能,包括在同一個機器、不同程式之間 和在虛擬機器與它的主機之間。基於 JavaScript 的通道與[23]中用機器語言實現的方法類似,都可以達到每秒幾百 kb 的速度。
我們展示了怎麼利用基於快取的方法來有效地跟蹤使用者行為。快取攻擊的這一應用與我們的攻擊模型更相關,這與密碼分析在其它成果中的應用不同。
最後,我們分析了針對攻擊可能的反制措施和整個系統的代價。
文件結構: 第二章,攻擊方法不同階段的設計與實現。 第三章,基於攻擊方法建立起來的隱藏通道,這個通道也被用來驗證方法的效能。 第四章,快取攻擊如何被用來跟蹤使用者在瀏覽器內外的行為。 第五章,總結,提出反制措施和仍未解決的研究挑戰。
2 攻擊方法
正如前文所訴,一次成功的 填充+探測 攻擊包含幾個步驟:為一個或多個相關組建立移除集,填充快取,觸發受害者的操作,最後再次探測組。 雖然填充和探測實現起來很簡單,但是要找到對應於某個系統操作的組並且為它建立移除集就不那麼容易了。在本章裡,我們描述了這幾個步驟用 JavaScript 如何實現。
2.1 建立一個移除集
2.1.1 設計
正如[23]寫到,填充+探測攻擊方法的第一步是為某個與被攻擊程式共享的組建立一個移除集。這個移除集包含一系列的變數,而且這些變數都被 CPU 對映到相同的 組裡。根據 文[20] 的建議,使用連結串列可以避免 CPU 的記憶體預讀和流水線優化。我們首先展示如何為任意一個組建立一個移除集,然後解決尋找與受害者共享組的問題。
文[17]指出,一級快取是依據虛擬地址的低位的位元來決定組分配的。假設攻擊者知道變數的虛擬地址,那麼在基於一級快取的攻擊模型裡建立移除集很容易。 但是,LLC 裡變數的組分配是依照實體記憶體的地址進行的,而且一般情況下,非特權程式無法知道。文[23]的作者為了規避這個問題,假設系統用的是 頁較大的模型,在這個模型裡,實體地址和虛擬地址的低21位是相同的,並通過迭代演算法來獲得組索引的高位。
在我們所考慮的攻擊模型裡,系統執行在 4K 的頁大小模型下,實體地址和虛擬地址只有最低的12位是相同的。然而更大的難題是,JavaScript沒有指標的概念, 所以即使是自己定義的變數,虛擬地址也是不知道的。
從64位實體地址到13位的組索引的對映關係已經被 Hund 等人研究過[12]。他們發現,當訪問實體記憶體裡一段連續的、8MB大小的 “淘汰緩衝區” 時會讓三級快取裡的所有組 都失效。雖然我們在使用者態下沒有辦法分配這樣的一個“淘汰緩衝區” (實際上,文章[12]是通過核心模式的驅動實現的),我們用 JavaScript 在 虛擬記憶體裡分配了一個 8MB 大小的陣列(這其實是由系統分配的隨機、不連續的 4K 大小實體記憶體頁的集合),然後測量遍歷這個緩衝區在全系統造成的影響。 我們發現在迭代訪問了這個淘汰緩衝區後如果立即訪問記憶體中其它不相關的變數,訪問的延遲會顯著的增加。 另外一個發現是,即使不訪問整個緩衝區而是每隔64位元組去訪問它,這個現象依然存在。但是,我們所訪問的 131K 個偏移值到8192個可能的組的對映關係 並沒有立刻清晰起來,因為我們不知道緩衝區裡各個頁在實體記憶體中的地址。
解決這個問題一個不太靠譜的做法是,給定一個任意的“受害者”在記憶體中的地址,通過暴力手段從 131K 個偏移值中找到12個與這個地址共享組的地址。要完成這點, 我們可以從 131K 個偏移量中選取幾個作為子集,在迭代了所有的偏移量後再測量下訪問的延遲有沒有變化。如果延遲增加了,意味著含有12個地址的子集與 受害者地址共享相同的組。如果延遲沒有變化,那子集裡的12個地址中的任何一個都不在組裡,這樣受害者地址就還在快取裡。把這個過程重複8192遍,每次用 一個不同的受害者地址,我們就可以識別每個組並且建立自己的資料結構。
受此啟發而立刻寫出來的程式會執行非常長的時間。幸運的是, Intel MMU 的頁幀大小(章節1.1)非常有幫助,因為虛擬地址是頁對齊的,每個虛擬地址的 低12位和每個實體地址的低12位是一致的。據 Hund 等人所稱,12個位元中的6個被用來唯一決定組索引。因此,淘汰緩衝區中的一個偏移會和其它 8K 個偏移 共享12到6位,而不是所有 131K 個。此外,只要找到一個組就能立刻知道其它的63個在相同頁幀裡的組的位置。再加上 JavaScript 分配大的資料快取區的時是 和頁幀的邊界對齊的,所以可以用演算法1中的貪心演算法。
演算法1 Profiling a cache set Let S be the set of unmapped pages, and address x be an arbitrary page-aligned address in memory
1. Repeat k times:
(a) Iteratively access all members of S
(b) Measure t1 , the time it takes to access x
(c) Select a random page s from S and remove it
(d) Iteratively access all members of S\s
(e) Measure t2 , the time it takes to access x
(f) If removing page s caused the memory access to speed up considerably (i.e., t1 − t2 > thres),
then this page is part of the same set as x. Place it back into S.
(g) If removing page s did not cause memory access to speed up considerably,
then this address is not part of the same set as x.
2. If |S| = 12, return S. Otherwise report failure.
通過多次執行 演算法1,我們可以逐漸的建立一個移除集並覆蓋大部分的快取,除了那些被 JavaScript 執行時本身所使用的。我們注意到, 與[23]中的演算法建立的淘汰緩衝區不同,我們的移除集是非典型的 - 因為 JavaScript 沒有指標的概念,所以如果我們發現了一個移除集 我們並沒有辦法知道它對應著 CPU 快取記憶體的哪個組。此外,在相同的機器上每次執行這個演算法都會得到不同的對映。這也許是因為用了傳統的 4K 頁大小 而不是 2MB 的頁大小的原因,這個問題即使不用 JavaScript 用機器語言也存在。
2.1.2 驗證
我們用 JavaScript 實現了 演算法1 並且在安裝了 Ivy Bridge, Sandy Bridge,Haswell 系列 CPU 的機器上進行驗證,機器上裝有 Safari 和 Firefox 對應執行在 Mac OS Yosemite 和 Ubuntu 14.04 LTS 作業系統上。系統並沒有被配置使用大的頁而是用預設的 4K 頁大小。列表1 顯示了實現 演算法1.d 和 演算法1.e 的程式碼,展示了 JavaScript 下怎麼遍歷連結串列和測量時間。演算法如果要執行在 Chrome 和 Internet Explorer 下,需要額外的幾個步驟,在 章節5.1 中。
列表1
// Invalidate the cache set
var currentEntry = startAddress;
do {
currentEntry = probeView.getUint32(currentEntry);
} while (currentEntry != startAddress);
// Measure access time
var startTime = window.performance.now();
currentEntry = primeView.getUint32(variableToAccess);
var endTime = window.performance.now();
圖2 效能分析演算法的累積表現
圖2顯示了效能分析的結果,執行在 Intel i7-3720QM CPU 上, 裝有 Firefox 35.0.1 和 Mac OS 10.10.2 。我們很高興地發現在30秒內就 對映了超過25%的組,1分鐘內就達到了50%。這個演算法想要並行執行是非常簡單的,因為大部分的執行時間花在了資料結構的維護上,只有一小部分花在讓快取失效和 測量上。整個演算法用不到500行 JavaScript 程式碼就可以完成。
圖3 Haswell 上兩種方法訪問延遲的概率分佈
為了驗證我們的演算法能夠辨別不同的組,我們設計了一個實驗來比較一個變數被 flush 前後的訪問延遲。圖3 顯示了兩種方式訪問變數的概率分佈函式。 灰色的代表用我們的方式從快取中 flush 出去的變數的訪問時間;而黑色是駐留在快取裡的變數的訪問時間。時間的測量用到 JavaScript 的高精度計時器, 所以還包括了 JavaScript 執行時的延遲。兩者的不同是顯而易見的。圖4 顯示的是在較早版本的 Sandy Bridge CPU 上捕捉到的結果,該型號每個組有16個條目。
通過選取一些列的組,並且不斷的測量它們的訪問延遲,攻擊者可以獲得快取實時活動非常詳細的圖。我們把這種視覺呈現稱作 “記憶體譜圖”,因為它看起來很像聲音的譜圖。
圖4 Sandy Bridge 上兩種方法訪問延遲的概率分佈 圖5 記憶體譜圖示例 圖5顯示的是每隔400ms抓取一次的記憶體譜圖。其中X軸對應時間,Y軸對應不同的組。例子中的時間解析度是250微秒,檢測了一共128個組。每個點的密度 代表了這個組在這個時間的訪問延遲。黑色代表延遲較低,意味從上次測量到現在沒有其它程式訪問過這個組。白色意味著攻擊者的資料在上次測量之後被淘汰了。
細看這個記憶體譜圖可以得到幾個顯而易見的事實。首先,雖然沒用機器語言指令而是用了 JavaScript 的計時器,測量的抖動很小,活躍和不活躍的組 很容易被區分。圖中有幾條明顯的垂直的線,意味著同一時間間隔裡有多個相鄰的組被訪問。因為連續的組對應的實體記憶體的地址也是連續的,所以我們 相信這個訊號代表著一個超過 64 位元組的彙編指令。還有一些聚在一起的組被同時訪問。我們推斷這代表著變數的訪問。最後,橫著的白線預示著一個變數 被不斷地訪問。這個變數可能是屬於測量程式碼的或屬於當前的 JavaScript 執行時。從一個沒有任何特權的網頁能得到這麼多資訊真是太了不起了。
2.2 在快取裡識別意思的區域
移除集讓攻擊者能夠監控任意一個組的活動。因為我們得到的移除集是非典型的,因此攻擊者必須想辦法把分析過的組和受害者的資料或是程式碼的地址 關聯起來。這個學習/分類的問題已經由 Zhang 和 Yarom 分別在 文章[25] 和 文章[23] 裡提出了,他們採用了不同的諸如 SVM 的機器學習的演算法試圖從 快取延遲的測量資料裡找到規律。
為了有效地展開學習過程,攻擊者需要誘導受害者做一些操作,然後檢查哪些組被這個操作訪問到,詳見 演算法2。
Let Si be the data structure matched to eviction set i
1. For each set i:
(a) Iteratively access all members of Si to prime the cache set
(b) Measure the time it takes to iteratively access all members of Si
(c) Perform an interesting operation
(d) Measure once more the time it takes to iteratively access all members of Si
(e) If performing the interesting operation caused the access time to slow down considerably, then the operation was
associated with cache set i.
因為 JavaScript 受到一系列的許可權限制,實現步驟(c)是很有挑戰的。與之形成對比的是 Apecechea 等人能夠用一個空的 sysenter 呼叫來觸發一次細小的核心操作。為了實現這個步驟,我們必須調查 JavaScript 的執行時來發現有哪些函式會觸發有意思的行為,例如檔案訪問,網路訪問,記憶體分配等等。 我們還對那些執行時間相對較短,不會產生遺留的函式感興趣。因為遺留可能導致垃圾回收,進而影響步驟(d)的測量。Ho 等人在 文章[10] 中已經找到了這樣的 幾個函式。另外一種方式是誘導使用者代替攻擊者執行一個特定的操作(比如在鍵盤上按一個鍵)。這個例子裡的學習過程可能是結構化的(攻擊者知道受害者 將要執行的時機),也可能是非結構化的(攻擊者只能假設系統一段時間內的響應緩慢是由受害者的操作導致的)。這兩種方法都被使用,詳見 章節4。
因為我們的程式會一直檢測到由 JavaScript 執行時產生的活動,比如高效能的計時器的程式碼,瀏覽器其它那些與當前執行呼叫無關的模組的程式碼,實際上我們 通過呼叫兩個相似的函式並** 對比** 它們兩次活動效能分析的結果,以此來尋找相關的組。
3 基於快取記憶體區的隱藏通道之 JavaScript 實現
3.1 動機
正如 文章[23] 所示,LLC 訪問模式可被用來建立一個高頻寬的隱藏通道,有效的用來在同一臺宿主上的兩個虛擬機器之間滲透敏感的資訊。在我們的攻擊模型裡, 攻擊者雖然不在同一臺宿主上的虛擬機器裡,而是在一個網頁中,隱藏通道的動機不一樣,但是也很有意思。
經由動機,我們假設某個安全部門在追蹤犯罪大師 Bob 的蹤跡。該部門通過釣魚專案在 Bob 的個人電腦上裝了一個被稱作 APT( Advanced Persistent Threat ) 的 軟體。APT 被設計用來記錄 Bob 的犯罪記錄併傳送到部門的祕密伺服器上。然而 Bob 非常的警覺,他使用了啟用了強制資訊流跟蹤 (Information Flow Tracking ) [24] 的作業系統。作業系統的這一功能阻止了 APT 在訪問了可能含有使用者隱私資料的檔案後再連上網路。
在這種情況下,只要 Bob 能被誘導訪問一個由安全部門控制的網頁,這個部門就可以立刻採用基於 JavaScript 的快取記憶體區攻擊。APT 可以利用基於快取記憶體區 的邊通道和惡意網站通訊,這樣就不用通過網路傳輸使用者的隱私資料,進而不會觸發作業系統的資訊流跟蹤功能。
這個研究案例受到了來自某個安全部門的 “RF retro-reflector” 設計的啟發,在這個設計裡一臺諸如麥克風的收集器,並不會把接收到的訊號直接傳送出去, 而是把接受的訊號調製到由一個外部 ”收集裝置“ 傳送給它的 “照射訊號” 上去。
3.1.1 設計
隱藏通道的設計有兩個需求:第一,保持傳送端的簡單,我們尤其不想讓它執行 章節2.1 中的移除集演算法。第二,因為接收端的移除集是非典型的, 它應該足夠簡單,這樣接收端就可以搜尋到傳送端的訊號調製到了哪一個組。
為了滿足這些需求,我們的發射器/ APT 在自己的記憶體中分配了 4K 大小的陣列,並且不斷地把收集到的資料轉換成對與這個陣列的記憶體訪問的模式。這個 4K 大小 的陣列覆蓋了快取的 64 個組,這樣 APT 在每個時間週期裡就能傳送 64位元 的資料。為了能保證記憶體訪問能夠被接收端定位,相同的訪問模式被重複 運用到陣列的幾個拷貝上。 因此,高速緩衝區的大部分都會被執行到,與之形成對比的是 文章[23] 中的方法使用了典型的移除集,因此只會啟用兩條快取線。
接收端的程式碼會對作業系統的記憶體做一個效能分析,然後搜尋含有被 APT 調製後的資料所在的頁框。真正的資料會被從記憶體訪問的模式裡解調出來然後傳回伺服器, 整個過程都不會違背作業系統對資訊流跟蹤的保護。
3.1.2 評估
我們的攻擊模型假設傳送端使用(相對較快的)機器語言編寫,而接收端是用 JavaScript 編寫。所以,我們假設整個系統效能的限制因素是惡意網站的 取樣速度。
為了評估隱藏通道的頻寬,我們寫了一個小程式用預先設定好的模式來遍歷系統的記憶體(即含有單詞"Usenix"的位元圖)。 接下來,我們用 JavaScript 快取記憶體攻擊來嘗試尋找這一訪問模式,並測量 JavaScript 程式碼所能達到的最大的頻率。
圖6 主機到主機的隱藏通道
圖6 顯示的記憶體譜圖捕捉到了這一隱藏通道的執行。隱藏通道的理論頻寬通過測量大約是 320kbps,這和 文章[23] 中用機器語言實現的跨虛擬機器的隱藏通道 1.2Mbps 的頻寬比較吻合。
圖7 主機到虛擬機器的隱藏通道 圖7 中的記憶體譜圖比較相似,但並不是由執行在相同主機上的接收端程式碼得到的,而是在一臺虛擬機器上得到的(Firefox 34 瀏覽器,Ubuntu 14.01 系統, VMWare Fusion 7.1.0 )。儘管在這個場景下的高峰頻率只能到大約 8kbps,但是一個虛擬機器中的網頁竟然能夠探測到底層的硬體,這著實讓人驚訝。
4 利用快取記憶體區攻擊跟蹤使用者行為
絕大多數關於快取記憶體區攻擊的研究都假設攻擊者和受害者位於雲端計算供應商資料中心裡的同一臺機器上。這樣的機器一般不會配置成接收互動式的輸入, 所以該領域的大部分研究都聚焦於如何獲得加金鑰匙或其它保密的狀態資訊,譬如隨機數生成器的狀態[26]。 本文將研究怎麼利用快取記憶體區攻擊來跟蹤使用者的行為 ,這和我們的攻擊模型更相關。我們注意到 文章[20] 已經嘗試了利用 CPU 的一級快取對系統負載進行細粒度的度量,以此跟蹤按鍵事件的方法。
本案例將演示一個惡意網站怎麼用快取記憶體區攻擊去跟蹤使用者的活動。在接下來展示的的攻擊裡,我們會假設使用者在一個背景標籤頁或視窗裡開啟 了一個惡意網站的頁面,並且在另一個標籤頁或是另外一個完全沒有網際網路連線的應用裡執行了一些敏感的操作。
我們選擇了把焦點集中在滑鼠操作和網路活動上,因為作業系統負責處理它們的程式碼沒有辦法被忽略不計。所以,我們期待這些操作會在快取記憶體區留下比較大的腳印。 而且正如下文所述,它們也很容易被 JavaScript 處處受限的安全模型所觸發。
4.1 設計
兩種攻擊的結構比較類似。首先,進行效能分析,攻擊者用 JavaScript 探測每一個組。接著,在訓練階段,待檢測的活動(網路活動或滑鼠操作)被觸發, 伴隨著對快取記憶體區的高精度的取樣。在訓練階段一方面通過測量指令碼直接觸發網路活動(執行一個網路請求),另一方面是不停地在網頁上搖晃滑鼠。
通過比較在訓練階段快取區在閒時和忙時的活動,攻擊者可以知道使用者操作會對應啟用哪部分的組,並且訓練出一個關於組的分類器。最後, 在分類階段,攻擊者不停地監視這些有意思的組從而掌握使用者的活動。
我們用一個基本的非結構化的訓練過程,即假設訓練過程中系統進行的最集中的操作就是被測量的。為了利用這點,我們計算了隨著時間的 每次測量的 Hamming 權重(等於在某個週期內活躍組的個數),之後應用 k-meas 演算法對測量資料做聚類。 然後計算每個簇中每個組的平均訪問延遲,從而算出每個簇的中心。遇到未知的測量向量,我們會計算這個向量和各個中心的歐幾里得距離, 並把它歸到最近的那一類。
在分類階段,我們用命令列工具 wget 生成網路流量,並且將滑鼠移動到視窗以外。為了獲得網路活動的真實資料,我們同時用 tcp-dump 來測量系統的流量,然後把 tcp-dump 記錄的時間戳和分類器所檢測到時間戳聯絡起來。為了獲得滑鼠操作的真實資料,我們寫了一個頁面記錄所有 滑鼠事件及其時間戳。需要強調的是,記錄滑鼠活動的頁面並不在執行著測量程式碼的瀏覽器(Firefox),而是執行在另一個瀏覽器裡(Chrome)。
4.2 驗證
圖8 檢測到的網路活動 圖9 檢測到的滑鼠活動
活動測量的結果見 圖8 和 圖9 。兩個圖片的頂端都顯示了快取記憶體區一個子集的實時活動。圖片的底端是分類器的輸出結果和外部收集的真實資料。 正如圖片展示的那樣,我們異常簡單的分類器在識別滑鼠操作和網路活動方面非常有效。毫無疑問,使用更加 高階的訓練和分類技巧可以進一步提高攻擊的效果。需要強調的是,滑鼠操作的檢測器並不會檢測網路活動,反之亦然。
分類器的測量頻率只有 500Hz。結果就是,它沒有辦法統計單個的包,而只能說明在一個階段裡活躍還是不活躍。另一方面,檢測滑鼠活動的程式碼要比 記錄真實資料的程式碼採集到的事件多。這是因為 Chrome 瀏覽器對滑鼠事件的頻率做了限制,大約是 60Hz。
Chen 等人在一篇著名的文章[5]中證明了對網路活動的監測可以作為深度挖掘使用者行為的奠基石 。雖然 Chen 等人假設攻擊者可以在網路層 監控受害者所有流入和流出的資料,但是這裡所展示的技術本質上可以讓惡意網站監控使用者同時進行的網路操作。攻擊可以被更多的指標增強, 例如記憶體分配(見文[13]),DOM 佈局事件,磁碟寫操作等。
5 結論
本文顯示了邊通道攻擊的範圍要比預期的大很多。本文提出的攻擊可針對網際網路上的大部分機器而不侷限於某些特定的攻擊場景。如此眾多的系統突然間易受 邊通道攻擊意味著防止邊通道攻擊的演算法和系統應當被廣泛使用,而不能只是在某些特定情況下。
5.1 易被攻擊系統的普遍性
我們的攻擊需要一臺個人計算機,並配有 Intel CPU,使用了 Sandy Bridge, Ivy Bridge, Haswell 或者 Broadwell 的微架構。據 IDC 的資料顯示,2011年 以後售出的個人計算機80%都滿足這一條件。更進一步,假設使用者使用的瀏覽器支援 HTML5 高精度計時器和型別陣列的規範。表1 列舉了各個瀏覽器廠商支援這些 API 的最早的版本和易被攻擊的版本佔全球網際網路流量的比重,統計資料來自 StatCounter GlobalStatas 2015年一月份的報告。如表所示,目前市場上 80% 的瀏覽器 都無法抵禦此類攻擊。
攻擊能否取得效果取決於能不能用 JavaScript 高精度時間API 進行精準的測量。雖然 W3C 對這個API規範定義了高精度時間的單位是 “毫秒,並精確到千分之一”, 但是它並沒有給出該值的最高解析度,實際上瀏覽器不同、作業系統不同這個值也會有所區別。舉個例子,我們在測試過程中發現 MacOS 上的 Safari 瀏覽器可以精確 到納秒,而 Windows 上的 IE 瀏覽器只能精確到 0.8 微秒。 另一方面, Chrome 瀏覽器在所有我們測試的作業系統上給出的解析度都是 1 微秒。
所以 圖3 中,單次快取命中和快取未命中的差別大概是 50 納秒,效能分析和測量的指令碼在時間解析度力度更細的作業系統上要稍加改動。 在效能分析階段,我們沒有統計單次未命中的時間,而是重複讀取記憶體來放大時間的差別。 在測量階段,我們雖然沒有辦法放大每次快取未命中的時間,但是我們可以利用來自相同頁框的程式碼通常會讓相鄰的組失效這一點。只要同一個頁框的64個組裡有20個 產生了快取未命中,我們的攻擊就可以在哪怕是毫秒級的解析度上進行。
我們提出的這種攻擊還可以很容易的運用到手機、平板等移動裝置上。值得一提的是,安卓瀏覽器從 4.4 版本開始支援 高精度時間API 和 型別陣列,但在 本文撰寫的時候 iOS Safari (8.1) 還不支援 高精度時間API。
5.2 反制措施
本文描述的攻擊之所以可行,是因為它聚集了從微架構這一層到最終的 JavaScript 執行時設計和實現的的一些決定: 怎麼把實體記憶體的地址對映到組, 巢狀的快取記憶體區架構,JavaScript 高速的記憶體訪問和高精度計時器;最後是 JavaScript 的許可權模型。這裡的每一點上都可以採取一些緩解措施, 但是都會對系統的正常使用產生影響。
在微架構這層,修改實體記憶體到快取線的對映方式可以非常有效地阻止我們的攻擊,即不再用地址底12位元中的6個直接選擇一個組。類似的,換用非巢狀的 快取微架構而不是用巢狀的,會讓我們的程式碼幾乎不肯能精確地一級快取中淘汰某項,使得測量更加困難。然而,這兩個設計決定當初被選擇正是為了讓 CPU 的設計 和快取記憶體的使用更高效,改變它們會讓其它很多的應用效能受到影響。再說了,修改 CPU 微架構可不是一件小事,因為升級已經部署的硬體是肯定不行的。
在JavaScript這層,似乎降低高精度計時器的解析度就可以讓攻擊更難發動。但是,高精度計時器的建立是為了解決 JavaScript 開發者的實際需要的, 這些應用範圍可從音樂和遊戲再到擴增實境和遠端醫療。
一個可能的權宜之計是限制應用只有在獲得了使用者許可後才能訪問計時器(例如,通過顯示一個確認視窗)或者通過第三方的認可(例如下載自可信的 “app store”)。
一種有意思的方式是使用啟發式的效能分析來檢測和阻止此類攻擊。如 Wang 等人利用大量演算法和按位指令的存在可以預示這密碼學應用元素 [21] 的存在, 可以注意到我們的攻擊裡各種測量步驟訪問記憶體也會有一定的模式。因為現代的 JavaScript 的執行時,作為效能分析引導優化的機制的一部分,已經能夠 詳細的檢查程式碼的執行時效能。所以 JavaScript 執行時應該能夠在執行時發現有效能分析行為的程式碼並且相應的修改返回結果 (例如,在高精度計時器里加上抖動,或者動態的調整陣列在記憶體中的位置等)。
5.3 結論
在這篇論文裡,我們展示瞭如何有效地通過可疑網頁發起針對微架構的邊通道攻擊,這種方式已被認為是非常有效的。 與快取記憶體區攻擊一般被用於密碼分析應用不同,本文介紹了它怎麼被用來有效地跟蹤使用者行為。 邊通道攻擊的範圍已被擴充,這意味著設計新的安全系統時一定要考慮到對邊通道攻擊的反制措施。
致謝
我們很感謝激 Henry Wong 對於 Ivy Bridge 快取淘汰策略的研究和 Burton Rosenberg 對於頁和頁框的講解。
參考文獻
見原文
關於譯文
偶然看到這篇文章 美發現新的瀏覽器攻擊模式:可監控全球八成PC, 非常驚訝 便隨手點開文章裡的原文連線 PDF,讀完有種腦洞大開的感覺。論文知識點涉及計算機組成原理、虛擬化、 效能分析、JavaScript、HTML5規範等,都是我所感興趣的領域,再加上中招的是 老東家 的產品, 因此雖然我是安全領域的小白,但還是嘗試精讀並且斗膽翻譯一下,不足之處歡迎大家批評指正。任何意見和建議請猛戳 issues
本文作者系OneAPM工程師編譯整理。OneAPM是中國基礎軟體領域的新興領軍企業。專注於提供下一代應用效能管理軟體和服務,幫助企業使用者和開發者輕鬆實現:緩慢的程式程式碼和SQL語句的實時抓取。想閱讀更多技術文章,請訪問OneAPM官方技術部落格。
相關文章
- 【譯】JavaScript 的記憶體模型JavaScript記憶體模型
- CPU快取記憶體快取記憶體
- MRAM快取記憶體的組成快取記憶體
- Oracle調整緩衝區快取記憶體Oracle快取記憶體
- Redis的快取穿透、快取雪崩、快取擊穿的區別Redis快取穿透
- javascript中的記憶體管理JavaScript記憶體
- RowHammer 攻擊:記憶體的隱形威脅記憶體
- 調整緩衝區快取記憶體(Buffer Cache)的效能(轉)快取記憶體
- 調整緩衝區快取記憶體(Buffer Cache)的效能(1)快取記憶體
- 調整緩衝區快取記憶體(Buffer Cache)的效能(2)快取記憶體
- 調整緩衝區快取記憶體(Buffer Cache)的效能(3)快取記憶體
- 【Linux】Linux 的快取記憶體Linux快取記憶體
- Glide - 記憶體快取與磁碟快取IDE記憶體快取
- DDD 和 記憶體快取記憶體快取
- 記憶體快取選型記憶體快取
- 多核cpu、cpu快取記憶體、快取一致性協議、快取行、記憶體快取記憶體協議
- iOS 除SDWebImage之外清理記憶體中快取iOSWeb記憶體快取
- Android記憶體優化之記憶體快取Android記憶體優化快取
- 快取穿透、快取擊穿、快取雪崩區別快取穿透
- docker部署redis快取記憶體DockerRedis快取記憶體
- 談談CPU快取記憶體快取記憶體
- CPU快取和記憶體屏障快取記憶體
- django 快取表格到記憶體Django快取記憶體
- 對github的中間人攻擊Github
- JavaScript之記憶體空間JavaScript記憶體
- javascript堆疊記憶體分配的區別JavaScript記憶體
- 【譯】JavaScript的記憶體管理和 4 種處理記憶體洩漏的方法JavaScript記憶體
- [譯] Swift 中的記憶體洩漏Swift記憶體
- Java記憶體快取-通過Google Guava建立快取Java記憶體快取GoGuava
- JavaScript中的記憶體洩露模式JavaScript記憶體洩露模式
- 【譯】JavaScript的工作原理:記憶體管理和4種常見的記憶體洩漏JavaScript記憶體
- 高效能記憶體快取 ristretto記憶體快取
- [譯] JavaScript是如何工作的:記憶體管理 + 如何處理4個常見的記憶體洩漏(譯)JavaScript記憶體
- ASP.NET Core - 快取之記憶體快取(上)ASP.NET快取記憶體
- ASP.NET Core - 快取之記憶體快取(下)ASP.NET快取記憶體
- 如何避免JavaScript中的記憶體洩漏?JavaScript記憶體
- JavaScript的記憶體管理JavaScript記憶體
- Android 記憶體快取框架 LruCache 的原始碼分析Android記憶體快取框架原始碼