我對Padding Oracle Attack的分析和思考

weixin_34262482發表於2013-10-28

道哥的《白帽子講web安全》有一章提到Padding Oracle Attack的攻擊方式,據說這貨在2011年的Pwnie Rewards上還被評為"最具價值的伺服器漏洞"。

抱著書看了半天,感覺也不是很理解,和密碼學結合的比較緊,有一些理論的東西在裡面。這裡做個學習筆記,研究一下。

 

1. 相關閱讀材料

https://github.com/GDSSecurity/PadBuster  PadBuster - Automated script for performing Padding Oracle attacks

http://hi.baidu.com/aullik5/item/49ab45de982a67db251f40f6  道哥的分析

http://www.di-mgt.com.au/cryptopad.html  Padding原則

http://hi.baidu.com/306211321/item/faa44923c3c07d98b7326387  分組密碼的連結模式

http://blog.gdssecurity.com/labs/2010/9/14/automated-padding-oracle-attacks-with-padbuster.html  作者寫的分析

http://www.cnblogs.com/JeffreyZhao/archive/2010/09/25/1834245.html  老趙寫的分析(和ASP.NET結合)

http://www.isg.rhul.ac.uk/~kp/secretIV.pdf  國外的牛牛寫的分析(擴充Padding Oracle的知識面)

http://www.icylife.net/yunshu/attachments/Padding-Oracle-Attack.pdf  雲舒寫的分析

http://netifera.com/research/poet/PaddingOraclesEverywhereEkoparty2010.pdf  EKOPARTY 2010的演講PPT

http://netsecurity.51cto.com/art/201101/244089_1.htm  51CT上看到的分析

 

 

 

 

 

2. 前引知識

2.1 分組的填充Padding

分組密碼Block Cipher需要在載入前確保每個每組的長度都是分組長度的整數倍。一般情況下,明文的最後一個分組很有可能會出現長度不足分組的長度:

這個時候,普遍的做法是在最後一個分組後填充一個固定的值,這個值的大小為填充的位元組總數。即假如最後還差3個字元,則填充0x03。

這種Padding原則遵循的是常見的PKCS#5標準。http://www.di-mgt.com.au/cryptopad.html#PKCS5

 

 

2.2 CBC(Cipher Block Chaining CBC)模式

這是一種分組連結模式,目的是為了使原本獨立的分組密碼加密過程形成迭代,使每次加密的結果影響到下一次加密。這行可以強化加密演算法的"敏感性",即實現所謂的"雪崩效應",在香濃理論中這就是"擾亂原則"。

之前在《密碼學》課後做的筆記:http://hi.baidu.com/306211321/item/faa44923c3c07d98b7326387

這裡我個人感覺要注意的一點是:

在連結模式中,初始化IV的長度要和對稱加密演算法的分組長度一致。原因是連結模式中的異或操作是等長操作。

 

 

 

 

 

3. Padding Oracle Attack攻擊的原理

因為Padding Oracle Attack是針對CBC連結模式的攻擊,和具體的加密演算法無關(分組)。所以這裡我們選擇DES為例進行闡述。

假設明文為: LittleHann(明文長度為10  8 < 10 < 16 即使用2個分組)

經過DES加密(CBC模式)後,其密文為: EFC2807233F9D7C097116BB33E813C5E

加密程式: http://pan.baidu.com/s/1e2o7

密文采用了"ASCII十六進位制的表示方法",即兩個字元表示一個位元組的十六進位制數。這是因為密碼學演算法中得到的密文經常會出現不可列印字元,為了保證在網路上傳輸的正確而不受不同系統間編碼方案的影響,就有必要對密文進行"視覺化"轉化(即轉化成可列印字元)。除了"ASCII十六進位制的表示方法"之外,還可以採用"base64編碼方法"。

PS:

這裡插個題外話:PHP的DES及其他密碼學演算法的加密是通過"PHP加密擴充套件庫Mcrypt"來實現的。

http://www.php100.com/cover/php/2651.html

http://baike.baidu.com/link?url=U8OtBP-IcYLRGrfWpSNhHskzrnA0qPNTsIrgGZcZSjLTGBPfpEI35ry51JtAXFxVuVsXKBK_UUZoXbZWgM-xLK

 

整個加密過程如下:

初始化向量IV與明文(第一組明文)XOR後,再經過運算得到的結果作為新的IV,用於下一分組(分組2),如果迭代下去。

解密過程是加密過程的逆過程:

這裡要注意,前幾個分組的解密結果對我們都沒有意義,我們重點關注的是最後一個分組的解密結果。看這張圖可能會清楚一點:

注意到最後一個分組的末尾的數值為0x04,即表示填充了4個Padding。如果最後的Padding不正確(值和數量不一致),則解密程式往往會丟擲異常(Padding Error)。而利用應用的錯誤回顯,我們就可以判斷出Paddig是否正確。

 

這裡有幾個概念要先理清一下:

1. 基於密碼學演算法的攻擊,往往第一個要搞清楚的是,我們在攻擊誰,或者準確的說我們的攻擊點在哪裡?在一個密碼學演算法中,有很多的引數(指攻擊者可以控制的引數),攻擊者往往是針對其中某一個或某一些引數進行破解,窮舉等攻擊。

在Padding Oracle Attack攻擊中,攻擊者輸入的引數是IV+Cipher,我們要通過對IV的"窮舉"來請求伺服器端對我們指定的Cipher進行解密,並對返回的結果進行判斷。

 

2. 和SQL隱碼攻擊中的Blind Inject思想類似。我覺得Padding Oracle Attack也是利用了這個二值邏輯的推理原理,或者說這是一種"邊通道攻擊(Side channel attack)"。http://en.wikipedia.org/wiki/Side_channel_attack

這種漏洞不能算是密碼學演算法本身的漏洞,但是當這種演算法在實際生產環境中使用不當就會造成問題。

和盲注一樣,這種二值邏輯的推理關鍵是要找到一個"區分點",即能被攻擊者用來區分這個的輸入是否達到了目的(在這裡就是尋找正確的IV)。

比如在web應用中,如果Padding不正確,則應用程式很可能會返回500的錯誤(程式執行錯誤);如果Padding正確,但解密出來的內容不正確,則可能會返回200的自定義錯誤(這只是業務上的規定),所以,這種區別就可以成為一個二值邏輯的"注入點"。

 

3. 攻擊成立的兩個重要假設前提:

1. 攻擊者能夠獲得密文(Ciphertext),以及附帶在密文前面的IV(初始化向量)
2. 攻擊者能夠觸發密文的解密過程,且能夠知道密文的解密結果

 

4. 可能出現的情況

明文分組和填充就是Padding Oracle Attack的根源所在,但是這些需要一個前提,那就是應用程式對異常的處理。當提交的加密後的資料中出現錯誤的填充資訊時,不夠健壯的應用程式解密時報錯,直接丟擲"填充錯誤"異常資訊(這個錯誤資訊在不同的應用中是不同的體現,在web一般是報500錯誤)。

攻擊者就是利用這個異常來做一些事情,假設有這樣一個場景,一個WEB程式接受一個加密後的字串作為引數,這個引數包含使用者名稱、密碼。引數加密使用的最安全的CBC模式,每一個block有一個初始化向量IV(注意:這個IV在伺服器第一次生成這個密文的時候就產生了,並儲存在伺服器上,攻擊者需要在提交資料的時候也提交這個IV,後面我們會看到,攻擊者實際上就是在"窮舉"這個IV)。

當提交引數時,服務端的返回結果會有下面3種情況:
a. 引數是一串正確的密文,分組、填充、加密都是對的(程式執行本身沒出問題),包含的內容也是正確的(業務邏輯是對的),那麼服務端解密、檢測使用者許可權都沒有問題,返回HTTP 200。
b. 引數是一串錯誤的密文,包含不正確的bit填充(程式執行本身出現致命錯誤),那麼服務端解密時就會丟擲異常,返回HTTP 500 server error。
c. 引數是一串正確的密文(程式執行本身沒出問題),包含的使用者名稱是錯誤的(業務邏輯是錯的),那麼服務端解密之後檢測許可權不通過,但是依舊會返回HTTP 200戒者HTTP 302,而不是HTTP 500。

攻擊者無需關心使用者名稱是否正確,只需要提交錯誤的密文(因為這裡有4中變數情況,為了構造出二值邏輯推理,我們要定住其中2個情況,即讓業務邏輯恆錯,對Bit Padding的情況進行邏輯推理),根據HTTP Code即可做出攻擊。

 

 

我們繼續回到原理分析上來。

假設有這樣一個應用 http://sampleapp/home.jsp?UID=0000000000000000EFC2807233F9D7C097116BB33E813C5E

(中間用箭頭隔開了,前面的16個字母(即8位元組 ASCII十六進位制表示法兩個字母為一個位元組)為攻擊者輸入的IV。後面的32個字母(即16位元組)為攻擊者輸入的密文)

 

我們向伺服器傳送這樣一個請求:

Request: http://sampleapp/home.jsp?UID=0000000000000000EFC2807233F9D7C097116BB33E813C5E

Respose: 500 - Internal Server Error

此時在解密時Padding是不正確的(填充的值和填充的數量不一致)

例如:

程式判斷Padding是否出錯一般是去檢驗末尾的那個位元組的值,這裡是0x3D,顯然不對。這裡我們再次回憶一下Padding原則:

1個位元組的Padding為0x01

2個位元組的Padding為0x02

3個位元組的Padding為0x03

4個位元組的Padding為0x04

5個位元組的Padding為0x05

6個位元組的Padding為0x06

7個位元組的Padding為0x07

8個位元組的Padding為0x08(當原始的明文正好是分組的整數倍的時候,Padding一個整組的填充值)

也就是說,Padding的值只可能是0x01~0x08之間。

 

接下來就是最關鍵的部分了,也是我一開始看比較難理解的地方。

我們接下來要利用選擇密文攻擊的思想,不斷調整,修正IV。來對Intermediary Value進行猜測。

1) Padding 0x01

我們不斷地調整IV的值,以希望解密後,最後一個位元組的值為正確的Padding Byte,這裡是0x01。因為Intermediary Value是固定的(我們此時不知道Intermediary Value),因此從0x00~0xFF之間,只可能有一個IV的值與Intermediary Value的最後一個位元組進行XOR後,結果是0x01(思考: 因為0x01只有最後1 bit為1,其他都是0,所以根據XOR的性質,只能存在一個值能XOR得到0x01)。攻擊者通過遍歷這255個值,可以找出IV需要的最後一個位元組。

Request: http://sampleapp/home.jsp?UID=0000000000000066EFC2807233F9D7C097116BB33E813C5E

Respose: 200 OK

通過XOR運算,可以馬上推匯出此Intermediary Byte的值:

if(Inermediary Byte) ^ 0x66  == 0x01
{
       Inermediary Byte = 0x66 ^ 0x01                 
}
so:
        Inermediary Byte = 0x67

在回過頭來看看加密過程:

初始化向量IV與明文進行XOR運算得到了Inermediary Value,因此將剛才得到的Inermediary Byte(0x67)與"真實"的IV的最後一個位元組0x0F(攻擊者事先獲取到的)進行XOR運算,即能得到明文:

 

0x67 ^ 0x0F = 0x68 : H

 

即得到明文(第一個分組)的最後一個字母H!

這裡我們們要稍微停一下,把思路理一理:

1. 我們在得出明文的那次XOR計算中用到的IV(0x0F)和我們攻擊者不斷"注入"的IV(0x01~0xFF)不是一回事,要區分開來。在計算明文的那個IV(0x0F)是我們事先就獲取到的,回想我們之前說的這個Padding Oracle Attack攻擊的成立條件:

這個IV(0x0F)是伺服器端在傳送密文的時候(可能是cookie形式)附帶在密文的頭部發給我們的。 一般情況下,如果跨系統傳送這種"帶鹽"的密文,都要把"鹽(IV)"附帶在密文的頭部或其他位置一起傳送給接收方。這裡我們接收到的IV就是0x0F,在不同的環境中這個IV必然是不一樣的,關鍵是要理解原理。

 

2. 為什麼我們可以不斷嘗試IV?

按理來說,IV不是在伺服器第一次生成這段密文的時候就生成好了嗎?然後在每次傳送的時候都附帶在密文的頭部,不會再變了......

的確是這樣,但這說的是在正常的解密情況下發生的事,而我們攻擊者現在做的事並不是在解密(事實上攻擊者這個時候也不知道IV和KEY),我們只是在通過不斷的修改IV來對目標解密系統進行"試探",從返回的結果來進行"側通道攻擊",從而進行二值邏輯推理。

這就是一種所謂的"選擇密文攻擊"方式,好像也有叫Bit-Flipping Attack:

http://www.vnsecurity.net/2010/03/codegate-2010-challenge-8-bit-flipping-attack-on-cbc-mode/

 

繼續回到思路分析上來:

在正確"匹配"了Padding 0x01之後,需要做的是繼續推匯出剩下的Intermediary Byte。根據Padding 原則,在需要Padding兩個位元組的時候,其值應該是0x02。

 

2) Padding 0x02

這個時候我們要注意,之前說過,這個攻擊過程是個迴圈迭代的過程,上一步的結果就是作為下一步的基礎。

我們之前已經知道Intermediary Byte的最後一個位元組為0x61,因此可以"更新"IV(攻擊者輸入的IV)的第8個位元組為 0x66 ^ 0x02 = 0x64。

(思考: 這個Padding Oracle Attack的過程中,攻擊者需要不斷地調整輸入IV的值,之前因為在Padding 0x01中,我們只是在假設Padding 0x01的情況,在這個假設下,我們通過得出IV的最位元組,從而計算出Intermediary Byte,進而算出明文的最後一個位元組"H"。這裡要注意的是,這個假設的IV沒有任何意義,只是我們進行"選擇密文攻擊"過程中的一個路人甲而已。而接下來我們要繼續假設Padding 0x02的情況,為了使在假設Padding 0x02中,Intermediary Value的最後一個位元組依然為"0x67(之前算出來的)",所以我們要對IV的最後一個位元組進行迭代更新: 0x66 ^ 0x02 = 0x64)。

這個時候,本質上攻擊者是固定住了IV的最後一個位元組不變,開始迴圈"盲注"倒數第二個位元組。開始依照第一步時的方法對倒數第二個位元組進行"盲注"邏輯判斷。

Request: http://sampleapp/home.jsp?UID=0000000000007064EFC2807233F9D7C097116BB33E813C5E

Respose: 200 OK

通過遍歷可以得出,IV的第7個位元組為0x70。

if(Inermediary Byte) ^ 0x70  == 0x02
{
       Inermediary Byte = 0x70 ^ 0x02                
}
so:
        Inermediary Byte = 0x72

對應的Intermediary Byte為0x72。知道了Intermediary Byte的倒數第二個位元組為0x72,就可以得出明文的的倒數第二個位元組:

0x72 ^ 0x17 = 0x65 : e

(這裡的IV: 0x17是我們從服務端接收到的附帶在密文頭部的IV的倒數第二個位元組)
接下來,要繼續對IV進行假設,同理,這次"選擇密文攻擊"的假設是Padding 0x03,對IV進行迭代更新,然後對IV的倒數第三個位元組進行"窮舉"迴圈探測。

Padding 0x03

Padding 0x04

Padding 0x05

Padding 0x06

Padding 0x07

 

Padding 0x08

最終得到這個分組的明文LittleH。這是第一個分組的明文。

注意,Padding Oracle Attack是以單個分組進行的。到了這一步,我們會發現,我們的攻擊目標其實就是那個臨時中間值變數Intermediary Value,得到了這個值,再加上我們本來就可以獲取到IV(伺服器端生成的附在密文頭部的那個IV),我們可以通過XOR運算得到這個分組的明文。

 

對於多個分組的密文來說,我們繼續觀察一下CBC的解密流程:

第二個分組使用的IV(對於第一組來說是附帶在密文頭部的那段)是第一組分組的密文。因此我們就把第一組的密文帶入帶第二組的計算中。繼續第對二組的Intermediary Value進行邏輯推導,最終得到第二組的密文:ann。

多分組的密文可以依次類推,由此即可以僅根據密文和IV還原出明文。

 

 

 

 

4. Padding Oracle Attack的攻擊利用場景

一旦我們通過暴力破解得到中間值Intermediary Value之後,IV便可以用來生成我們想要的任意值。新的IV可以被放在前一個示例的前面,這樣便可以得到一個符合我們要求的,包含兩個資料塊的密文了。這個過程可以不斷重複,這樣便能生成任意長度的資料了。

使用PadBuster加密任意的值

https://github.com/GDSSecurity/PadBuster

 

 

 

5. 模擬實驗

這是道哥寫的python指令碼,可以用來模擬實驗出Padding Oracle Attack的原理:

http://pan.baidu.com/s/1eitwK

可以參考原始碼來進行深入的學習,加深理解。

 

 

6. 後記

自己按照書上講的理解了一下,如有不對之處,望指正。

 

相關文章