解析FrozenCache抵內(nèi)存威脅

字號:

Cold boot 攻擊對全磁盤加密解決方案所提供的數(shù)據(jù)安全保護是一個重大威脅,大部分計算機都容易受到這種攻擊,而到目前為止還沒有通用方法可以解決這個問題。
    為什么這篇文章名為“凍結(jié)緩存(Frozen Cache)”呢?因為我們決定采用CPU緩存來抵御Cold boot 攻擊(至少對于所有X86系統(tǒng))。
    這個概念很簡單:將緩存切換到特殊模式,該模式下可以強迫數(shù)據(jù)在緩存中而不是被寫入支持RAM位置。因此,加密密鑰不能從RAM提取,其實這種技術(shù)并不新鮮:LinuxBIOS/CoreBoot將這種技術(shù)稱為Cache-as-RAM,并使用這種技術(shù)來允許“RAM訪問”,即使是在初始化內(nèi)存控制器前。
    下列步驟加載并維護CPU緩存中的256位的加密密鑰,并假定加密密鑰存放在RAM的線性地址X中:
    1. 從RAM將加密密鑰加載到一些CPU注冊器中(如SSE注冊器):
    movq [X], %xmm0
    movq [X+8], %xmm1
    movq [X+16], %xmm2
    movq [X+24], %xmm3
    2. 使用0值復(fù)寫RAM中加密密鑰
    movq 0, [X]
    movq 0, [X+8]
    movq 0, [X+16]
    movq 0, [X+24]
    3. 刷新緩存(從而真正覆蓋RAM中的加密密鑰)
    wbinvd
    4. 添加需要的RAM區(qū)域到CPU的MTRR(4K部分包含密鑰)
    movl (X | MEMORY_TYPE_WRITEBACK), %eax
    xorl %edx, %edx
    movl 0x200, %ecx
    wrmsr
    movl ( ~(1) | MTRR_VALID ), %eax
    movl 0x1, %edx
    movl 0x201, %ecx
    wrmsr
    5. 禁用/凍結(jié)CPU的緩存(CR0.CD=1)
    movl %cr0, %eax
    orl 0x40000000, %eax
    movl %eax, %cr0
    6. 從CPU注冊器向RAM寫入加密密鑰(數(shù)據(jù)仍然在緩存中,不會寫入內(nèi)存)
    movq %xmm0, [X]
    movq %xmm1, [X+8]
    movq %xmm2, [X+16]
    movq %xmm3, [X+24]
    “禁用/凍結(jié)”CPU的緩存嚴重降低了性能,但是,這似乎也是可以接受的,因為考慮到只需在屏幕鎖定時設(shè)置特殊模式(如果筆記本被盜,那么所以努力都毫無意義)。在Linux系統(tǒng)的首次概念證明測試結(jié)果表明,需要對解鎖GUI進行很多必要的性能優(yōu)化(從性能/可用性角度考慮)。
    這段文字只是描述了基本的概念,下面將涉及多CPU/Core問題、性能考慮和優(yōu)化等問題。
    性能方面
    如上所述,性能問題是將緩存切換到“凍結(jié)”模式的主要關(guān)注問題,CPU架構(gòu)的廣泛范圍(多CPU、多核和多線程)和各自的緩存配置讓問題變得更加復(fù)雜。
    首先:只有一個CPU緩存需要“凍結(jié)”以有效保護加密密鑰。其他CPU允許執(zhí)行政策緩存模式。只要每個(邏輯或者虛擬的)CPU完全使用自己的緩存:采用線程技術(shù)(如英特爾的超線程HyperThreading)CPU會作為兩個(或者可能更多)邏輯CPU,但是這兩個CPU共享相同的緩存,而它們必須同時切換至凍結(jié)緩存模式。對于多核CPU,可能情況更加復(fù)雜,如果內(nèi)核都有自己的(L1和L2)緩存。
    加密密鑰僅位于單個CPU的緩存中,只有這個CPU必須執(zhí)行加密和解密線程,在全磁盤加密解決方案中最常見的架構(gòu)就是采用內(nèi)核模塊,它會產(chǎn)生一個為加密和解密邏輯指定的內(nèi)核線程,內(nèi)核線程是可調(diào)度實體,因此可以綁定在CPU上,CPU緩存中有加密密鑰。
    回到更加“傳統(tǒng)的”性能考慮,怎樣做可以減少凍結(jié)CPU緩存帶來的影響呢?將最常用的內(nèi)存區(qū)域加載到緩存(凍結(jié)前)是個不錯的開始,其中最適合的包括:系統(tǒng)調(diào)用入口端點、計時器中斷線程及其“幫助”功能,以及內(nèi)核線程執(zhí)行的加密/解密功能。目前L2緩存通??梢匀菁{所有這些代碼,但是同時還需要考慮緩存的結(jié)合問題,以避免相關(guān)問題。另一個好辦法就是將所有其他進程的時間表分配給其他可用的CPU(不使用凍結(jié)緩存的CPU):這樣可以以正常的速度執(zhí)行,另一個很重要的原因就是,我們能獲得更多時間。
    現(xiàn)在已經(jīng)很明顯了,將需要部署確定具體(運行中的)CPU/緩存組件的程序并有針對性的管理他們。
    缺乏緩存控制
    一旦緩存被凍結(jié),對這些緩存內(nèi)容的管理就沒有結(jié)束:同樣重要的是,緩存中的數(shù)據(jù)(加密密鑰)并沒有寫回內(nèi)存。不幸的是,英特爾架構(gòu)只允許非常少的緩存控制:
    · 啟用/禁用緩存(系統(tǒng)范圍:CR0.CD,每個內(nèi)存區(qū)域:MTRR,每頁:PAT)
    · 清除緩存(wbinvd
    就是如此,根本沒有查詢緩存狀態(tài)(目前在單個緩存線中的RAM位置)處理器指示或者其他先進的緩存管理功能。因此,根本可能驗證加密密鑰真正只出現(xiàn)在CPU緩存中。隨著凍結(jié)緩存設(shè)置,幾乎可以肯定密鑰將出現(xiàn)在緩存中,但是這并不意味著數(shù)據(jù)將同步到RAM中。不管wbinvd指示何時執(zhí)行都會發(fā)生(在凍結(jié)緩存步驟中),該指示可以由任何在ring 0(內(nèi)核)運行的代碼執(zhí)行。因此,有必要盡量減少在CPU(加密密鑰在其緩存中)運行的內(nèi)核模式,這也是為什么綁定其他可調(diào)度實體(至少是所有其他內(nèi)核線程)到其他CPU(如果有的話)的原因。
    減少“無意的”緩存清除帶來的影響的方法是重復(fù)緩存凍結(jié)程序以減輕“無意”緩存清除(wbinvd)的影響。對于Linux而言有個更好的解決方法:修改執(zhí)行/包裝在內(nèi)核中觸發(fā)重新執(zhí)行緩存凍結(jié)的invd/wbinvd指示的函數(shù)/宏。
    保護加密密鑰
    加密密鑰不是需要保存在CPU緩存的數(shù)據(jù)類型。
    密鑰調(diào)度是現(xiàn)代密碼加密的典型:加密和解秘例程并不直接使用加密密鑰,而是采用“輪流密鑰”,這些都來自加密密鑰(根據(jù)密碼算法)并且用于不同算*次的加密與解密。
    AES標準定義了128位的AES密鑰用于生成/獲得10輪128位的密鑰,可以推測這些輪密鑰通過某些不可逆的類似哈希的函數(shù)從加密密鑰中計算出,不過這對于AES標準是不正確的:加密密鑰很容易從任何10/12/14輪密鑰中重新計算出。因此,這些輪密鑰也需要保存在CPU緩存中(至少對于經(jīng)常使用的AES是這樣的)。
    理論上聽起來簡單,但找到一個好的架構(gòu)方法確實是挑戰(zhàn),特別對于Linux系統(tǒng)。
    鎖定屏幕
    另一個重要問題就是,只有當屏幕被鎖定時才會凸顯對性能造成的影響,不過也可能在鎖定屏幕的情況下而不受到性能的影響。
    有兩個策略可以維持原有系統(tǒng)性能:
    1. 配置選項,確定緩存何時不應(yīng)該被凍結(jié)
    2. 屏幕鎖定后的時間窗口允許用戶防止緩存凍結(jié)以進行用戶交互操作等
    配置選項可以是“當系統(tǒng)在充電時不要凍結(jié)緩存”或者名為gcc的程序正在運行的時候不要凍結(jié)緩存等。
    時間窗口的方法有點像倒數(shù)計時,從屏幕鎖定開始(用戶可能仍然在電腦前)。在倒計時點擊“不凍結(jié)緩存”可以使密鑰安全受到威脅,不點擊可以繼續(xù)保護密鑰安全(因此當計算機自動鎖定屏幕時,只會增加一個加密密鑰的額外窗口詢問用戶是否凍結(jié)緩存)。
    保護所有需要保密的數(shù)據(jù)
    本來我的建議是將加密密鑰從RAM移到CPU緩存中進行保護,然而情況并非如此。另外,輪密鑰也需要保存在CPU緩存中。
    很明顯,加密密鑰需要受到保護,其次,密鑰時間表(前文所說的輪密鑰)也需要保護,密鑰時間表是直接從加密密鑰提取的,可以看作是加密密鑰的“擴張版”。第三,應(yīng)該保護“Initialization Vector(IV,初始化向量)”,IV是否需要獲得保護取決于IV的生成方式。例如,“Encrypted Salt-Sector Initialization Vector(ESSIV)”就必須獲得保護,ESSIV是加密密鑰的hash,是Linux系統(tǒng)中dm-crpt默認使用的IV。第四,任何包含解密內(nèi)容的緩沖都需要保護,以避免已知的純文本攻擊,然而保護這種內(nèi)存緩沖非常麻煩。最后,在加密解密過程中計算的數(shù)據(jù)值必須安全地存儲在CPU緩存中以避免密鑰分析。
    控制失控的緩存
    失控緩存是指不確定CPU緩存中的數(shù)據(jù)是否被清除到RAM,這種清除可能是由CPU指示(如invd、wbinvd和clflush,或者外部事件等)。
    以下方法可以避免這種失控緩存問題的發(fā)生:
    減少無意緩存清除的方法之一就是定期重復(fù)緩存凍結(jié)程序以消除這種無意緩存清除的影響。
    有個新方法可以將這種風(fēng)險一起消除,不過還沒有證實這種方法是否可以部署。大家有必要理解物理/線性和虛擬內(nèi)存位置的區(qū)別。
    這種新想法其實很簡單:將數(shù)據(jù)存儲在物理/線性位置(不是由系統(tǒng)RAM支持的),這能夠保證數(shù)據(jù)不會離開CPU緩存,即使觸發(fā)緩存清除。
    雖然還沒有證實過是否能執(zhí)行,可能的安裝程序如下:
    1. 加載數(shù)據(jù)到CPU注冊器
    2. 重寫RAM的數(shù)據(jù)
    3. 將虛擬地址的虛擬-線性映射更改為物理/線性地址(通過修改相應(yīng)的頁面表條目)
    4. 將CPU緩存切換至凍結(jié)模式
    5. 將數(shù)據(jù)從CPU注冊器移到CPU緩存
    還不確定的事情是:第三步或者第四步中CPU是否會發(fā)生故障,這使這一想法無法進行,畢竟,這不太符合CPU內(nèi)存管理的邏輯。不過很快將會有辦法解決這一問題。
    最后說明:很明顯,如果你有4GB的RAM,將不會有無效線性地址。