側信道攻擊,從喊666到入門之——錯誤注入攻擊白盒


作者:backahasten

上文中,我們介紹了有關Unicorn的使用,為了避免只造輪子不開車的現象出現,我們就用Unicorn來親手攻擊一個AES白盒。

我選取了CHES 2016 競賽中的AES白盒,這個白盒非常白,甚至給了源碼,代碼和程序可以在這里找到。這個鏈接還包含里Writeup,但是其中使用的工具很老了,還是python2的代碼,並且使用的執行引擎是PIN,如果是ARM的安卓APP里的白盒就沒辦法了。

我選取了CHES 2016 競賽中的AES白盒,這個白盒非常白,甚至給了源碼,代碼和程序可以在這里找到。這個鏈接還包含里Writeup,但是其中使用的工具很老了,還是python2的代碼,並且使用的執行引擎是PIN,如果是ARM的安卓APP里的白盒就沒辦法了。

 

啊嗚,數學

!!!!!!!!!所有計算均在——有限GF(2^8)!!!!!!!!

在進行白盒破解之前,我們看一下錯誤注入的原理是什么。

對於AES128來說,錯誤注入的目標在第九輪的MixColumns計算之前,第九輪的MixColumns計算之前的數據假設是這個樣子的:



假設我們的錯誤正好命中了第一個字節,則數據流變成了:



之后,數據流會依次進入

  • MixColumns
  • AddRoundKey K9
  • SubBytes
  • ShiftRows
  • AddRoundKey K10

中間的過程就不寫了,有興趣的通過可以自己推一下,如果熟悉AES的計算過程,不難推算。這篇文章有詳細的推導過程。

最后,AddRoundKey *K*10 結束之后輸出,應該是這個樣子的:

如果成功錯誤注入的話,會變成這個樣子:(其中的+號表示異或)

以第一個字節為例,我們設:

之后把OO’進行Xor計算:

得到:

設:

原式變為:

把剩下3個字節補齊,得到:


四個Y的取值都是0-255,遍歷四個Y,就可以得到Z的一個取值范圍 。得到Z的取值范圍了之后,可以對應一組Y。(再說一次,乘法和加法都在GF(2^8)上)

之后通過關系公式:

推導出一組K10(0,7,10,13)密鑰的值。

這只是錯誤出現在第一個Byte的情況,通過多組錯誤輸出,可以唯一的推導所有的K10,之后通過密鑰擴展算法,推導出AES的密鑰。

這個地方有點繞,我們舉個例子:

假設O13 ^O’13 = 0x55,我們嘗試求一下:

sbox=(
    0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16) a=[] for y3 in range(256): #假設O13 ^ O'13 = 0x55 a.append(sbox.index(0x55 ^ sbox[i]) ^ y3) print(a) 

計算得到了256個數,但是由於其中有的數是重復的,所以可以縮小Z的取值范圍。拿到了Z之后,就可以推導出對應的Y,由於O是已知的,所以可以推出密鑰或者縮小密鑰的范圍。

那么2Z3Z怎么計算?

這里的2,3與Z的關系是有限域中的乘法,所以計算求Z的時候,並不是簡單的除一下就行了,而是域中的計算。需要涉及到生成多項式0x11B求逆元的計算。(推薦一本書,密碼編碼學與網絡安全—原理與實踐-第六版,寫的很好。)

 

實踐操作

我沒有像參考文章一樣用fridaidapython去做,這樣不太靈活。我使用了unicorn 引擎,一般來說,白盒AES算法都會被封裝在一個或者連續的幾個函數中,這樣對於Unicorn 是十分方便的。

我的目標沒有選擇上文中的wbDES,它太老了,而且DES的使用越來越少,我也沒有使用參看文獻中的基於OLLVM的實現,因為OLLVM混淆之后的AES也不是嚴格意義上的白盒,我選用了CHES 2016 CTF上的一道題。

首先對程序使用GDB進行分析,發現程序的main函數主要是獲取輸入和輸出,加密過程chow_aes3_encrypt_wb函數中,在chow_aes3_encrypt_wb下斷點。

(注:我沒有使用github中提供的Shared object文件,而是使用源碼,在makefile中添加了-no-pie參數,重新編譯了一個executable文件)

之后在函數的執行結束的位置下斷點。

發現程序使用RSIRDI進行傳參,RSI保存的指針是輸出緩沖區,RDI保存的指針是函數輸出數據的位置。

之后開始棧空間的構建,具體構建的方法和調試請參考上一篇文章。

有錯誤注入攻擊,一定需要能量分析攻擊,在針對芯片的攻擊中,SPA和DPA可以提供攻擊位置信息,針對白盒的攻擊也差不多,白盒的實現是針對查找表實現的,所以我們首先需要打印出來所有內存讀的位置,這也是為了后續的攻擊做准備。在hook中添加篩選條件:

if access == UC_MEM_READ and size == 1 and address<0xb0000000: 

其中,size==1表示每次讀取的大小是一個字節,因為AES是以字節為單位進行計算的;address<0xb0000000是為了排除程序在操作棧中數據時的誤觸發。

(左邊數值是地址值的十進制表示,實際是從0x61a800(6400000)到0x6acfc0(7000000))

這個圖可以看出,查找表隨着時間是從高地址向低地址分布的。

接下來,我們開始hook並錯誤注入,我們需要注入的是第九輪,也就是說,在時間上是比較靠后的位置,在這個過程中,我們需要不停的改變注入的位置,通過分析錯誤輸出,來了解是否注入對了地方,如果正好輸出了四個錯誤,並且位置符合,就是注入對了地方;如果錯誤的位置過多,表示注入的靠前了,如果錯誤的位置只有一個,說明注入的位置靠后了。

我們拿到了符合條件的錯誤輸出值:

628caf41f9a2f7a51c57b9e23e137365
628cf341f961f7a5c157b9e23e137366
628c2f41f91ff7a5b557b9e23e13730e
628c1541f9caf7a56e57b9e23e1373b8
628c6e41f9b0f7a5d857b9e23e137323
628c1b41f961f7a5c457b9e23e1373aa
628c3d41f93ff7a57857b9e23e13730e
628c1e41f902f7a5bb57b9e23e13732b
628c8c41f9e4f7a5a757b9e23e137319
628ca341f948f7a56057b9e23e1373a2
628cc241f950f7a50f57b9e23e137319
628cbc41f9aef7a58157b9e23e13735a
628c4e41f9a1f7a50057b9e23e1373e3
628ccf41f914f7a57f57b9e23e137317
628cbc41f9aef7a58157b9e23e13735a
628caf4af9a286a51cf9b9e2d7137365
628cafd9f9a22aa51c3bb9e205137365
628cafd9f9a22aa51c3bb9e205137365
628cafd9f9a22aa51c3bb9e205137365
628caf85f9a27aa51cb4b9e2d4137365
628cafc2f9a245a51ce9b9e2f4137365
628cafd9f9a22aa51c3bb9e205137365
f88caf41f9a2f7441c5782e23ef47365
6c8caf41f9a2f7c71c57c6e23e297365
a68caf41f9a2f7781c57e6e23eb97365
828caf41f9a2f7391c5739e23ef27365
d48caf41f9a2f7931c57f7e23e8b7365    
3d8caf41f9a2f7061c5736e23ee87365
6236af4122a2f7a51c57b9b83e138f65
62faaf41bba2f7a51c57b9483e133365
6236af4122a2f7a51c57b9b83e138f65    
6296af41b6a2f7a51c57b9f93e138a65
6272af41b3a2f7a51c57b9493e13bb65
62f4af41ada2f7a51c57b9ef3e13bd65
6272af41b3a2f7a51c57b9493e13bb65
62c5af4167a2f7a51c57b93e3e133f65

拿到足夠多的錯誤輸出后,我使用了工具https://github.com/SideChannelMarvels/JeanGrey/tree/master/phoenixAES進行上述的數學計算推導過程

得到第10輪密鑰之后,使用工具aes_keyschedule推算出AES-128的密鑰。

參考:

[1]https://blog.quarkslab.com/differential-fault-analysis-on-white-box-aes-implementations.html

[2]https://bbs.pediy.com/thread-254042.htm


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM