Padding Oracle Attack學習筆記
前言
最近看到Padding Oracle Attack,是對加密算法的CBC模式的破解,很是好奇,找了些資料,動手實現了破解算法。 但是過程有點點曲折,記錄下心得體會。
原理
以DES的CBC模式爲例,分組加密算法在加解密時,都需要把消息分組,一般爲8或16字節。以解密爲例:
理解破解算法的幾個關鍵點:
關鍵點1:padding bytes
padding bytes,即明文不足分組長度時,進行填充的字節。以8字節分組,PKCS5模式爲例,當明文爲HELLO時,不滿8個字節,剩餘3個字節的填充字節爲0x03,即:
padding byte = padding length = block size - block input length
//填充字節 等於 填充長度 等於 分組長度 - 分組輸入長度
因此,填充字節只能是0x01, 0x02…0x08。 0x08表示輸入長度爲8的倍數字節時,後面也會有8個字節的填充數組。 也就是說,填充字節肯定存在,且範圍是1~8。
(其他分組長度類似原理)
關鍵點2:padding error
當構造的輸入錯誤時,會產生padding error,如java中是BadPaddingException,其他語言可自行嘗試。 這點很關鍵,因爲Padding Oracle Attack的核心就是這個,利用padding error來不斷進行輸入嘗試。 編寫破解工具時,可能看不到異常,但是一定要能根據返回信息判斷是不是產生了padding error,如果不能確定,破解是進行不下去的。
關鍵點3:算法
與解密示意圖對應,我們將分組密文進行 block ciper decryption後得到的字節數組稱爲Intermdiate bytes,將與Intermediate bytes進行XOR的字節數組稱爲IV,分組密文用block表示,block[i]表示第i個分組密文, plain block[i]表示第i個分組明文,intermediate[i]表示第i個分組密文的Intermdiate bytes。
ok,讓我們捋一捋流程:
-
block[i] decrypt得到 Intermediate bytes, 這個操作跟key(密鑰)有關,每個密文分組得到的Intermediate bytes是固定的
-
Intetermedaite bytes 異或 IV 得到明文。注意圖中的解密過程,前面分組的密文會與後面一個分組的解密密文異或,最終得到後面一個分組的明文。即對應的block[i]的IV爲block[i-1]。因此,解密一定從最後一個分組開始。
-
從最後一個分組開始,用全0數組pseudo block[i-1]代替真實的block[i-1], 通過不斷的調整psdudo block[i-][7](最後一個字節),通過判斷是否返回padding error來確定填充字節是否正確,即0x01,如果正確,則意味着可以得到明文:
if intermediate[i][7] XOR pseudo block[i-1][7] == 0x01: //得到intermedaite bytes intermediate[i][7] = pseudo block[i-1][7] XOR 0x01 //從而得到分組明文 plain block[i][7] = intermediate[i][7] XOR block[i-1][7]
因爲一個字節的範圍是從0~255,所以對於一個字節的嘗試最多是256次,
-
如果構造數組得到正確的填充字節0x01,則意味着intermediate[i]的最後一個字節確定了,從而可以繼續調整,當填充字節爲0x02時:
pseudo block[i-1][7] = intermediate[i][7] XOR 0x02
調整pseudo block[i-1][6]的字節值,可以得到block[i][6]的明文字節;以此類推,block[i]就可以全部解密。
-
重複步驟3、4,即可以得到全部分組的明文。
遇到的坑
看完上面的原理,像我這種弱雞會覺得,“靠,這麼簡單,我要試一試”。 事實會再次證明你too young。。。
坑1
算法的第3點,爲毛要用全0數組充當僞造的數組,而且要從最後一個字節開始嘗試,且得到的填充字節就一定是0x01?
抱歉,我也沒有確定的答案,我找到的源碼和資料都是直接這麼搞的,更狠的是原作者的PPT上說的是random bytes(希望是我英語不好,讀的不仔細吧)。 但是我有個推測:block[i]密文decrypt操作得到的intermediate[i],字節重複且在0~8之前的概率非常之小。
因此,當從全0數組的最後一個字節開始嘗試時,遇到非padding error的情況,就極大概率對應填充字節爲0x01。 因爲,假設此時正確填充字節爲0x03,則意味着block[i]的5、6、7這3個字節對應的intermediate都是0x03。
當然,我覺得就算出現此類極端情況,也是有補救措施的。 比如對於填充字節0x01得出一個錯誤的pseudo block[i][7],那很可能對於0x02,得不出正確的pseudo block[i][6],這個時候退回去,跳過當前的pseudo block[i][7]值往後嘗試;以此類推,也可以推導出正確的結果。
當然這個是推測,需要翻算法和源碼驗證。
坑2
算法第5點,看似正確,轉念一想,對於第一個密文分組,咱是不知道原始IV的啊,怎麼破?
抱歉,這個真的無解。。。翻的資料都說“初始IV不重要,一般放在第一個分組,明文傳輸”。。。
剛開始看算法的時候,糾結了很久,因爲推不出第一個分組的明文,才一直覺得自己是沒有透徹理解原理,就是因爲沒有注意到這句話。。。
總結
看安全的材料,感覺搞安全的同學們還是挺NB的,佩服。。。不過,慣例,槽還是要吐的,上面那兩個坑,尤其第一個,搜到的材料都沒有解釋,看來大家都比較忙。。。
附錄:
- 《白帽子講WEB安全》
- 破解算法PPT
- PadBauster源碼地址