Padding Oracle攻擊實(shí)例分析
在《2010年揚(yáng)名的十大WEB黑客技術(shù)》和《淺談ASP.NET的Padding Oracle攻擊》中,我們都提到了Padding Oracle Attack的相關(guān)內(nèi)容,也對(duì)其進(jìn)行了一些簡(jiǎn)單的了解。接下來(lái),通過(guò)這篇譯文,我們?cè)賮?lái)對(duì)其進(jìn)行一下深入的學(xué)習(xí)吧。
最近出現(xiàn)了許多有關(guān)Padding Oracle Attack的聲音,在今年夏天早些時(shí)候的BlakHat Europe會(huì)議上,Juliano Rizzo和Thai Duong在他們的演講中演示了這種攻擊方式。雖然Padding Oracle是種相對(duì)容易的攻擊方式,但如果您還沒(méi)有對(duì)它的自動(dòng)攻擊原理有一定了解,那么利用它進(jìn)行攻擊還是需要不少時(shí)間的。由于缺少好用的工具以識(shí)別及利用Padding Oracles,我們開(kāi)發(fā)了一個(gè)基于Padding Oracle的內(nèi)部腳本,PadBuster,現(xiàn)在我們打算將它與社區(qū)分享。您可以在這里下載工具,現(xiàn)在我們也會(huì)花些時(shí)間來(lái)討論這個(gè)工具的工作方式,以及它所支持的幾種場(chǎng)景。
一些背景知識(shí)
在討論P(yáng)adBuster之前,我們先來(lái)簡(jiǎn)單討論一下典型的Padding Oracle Attack基礎(chǔ)。故名思義,Padding Oracle Attack背后的關(guān)鍵性概念便是加/解密時(shí)的填充(Padding)。明文信息可以是任意長(zhǎng)度,但是塊狀加密算法需要所有的信息都由一定數(shù)量的數(shù)據(jù)塊組成。為了滿足這樣的需求,便需要對(duì)明文進(jìn)行填充,這樣便可以將它分割為完整的數(shù)據(jù)塊。
加密時(shí)可以使用多種填充規(guī)則,但最常見(jiàn)的填充方式之一是在PKCS#5標(biāo)準(zhǔn)中定義的規(guī)則。PCKS#5的填充方式為:明文的最后一個(gè)數(shù)據(jù)塊包含N個(gè)字節(jié)的填充數(shù)據(jù)(N取決于明文最后一塊的數(shù)據(jù)長(zhǎng)度)。下圖是一些示例,展示了不同長(zhǎng)度的單詞(FIG、BANANA、AVOCADO、PLANTAIN、PASSIONFRUIT)以及它們使用PKCS#5填充后的結(jié)果(每個(gè)數(shù)據(jù)塊為8字節(jié)長(zhǎng))。
請(qǐng)注意,每個(gè)字符串都至少有1個(gè)字節(jié)的填充數(shù)據(jù),因此7字節(jié)的值(如AVOCADO)則使用0x01進(jìn)行填充,而8字節(jié)的值(如PLANTAIN)則會(huì)填充一個(gè)額外的數(shù)據(jù)塊。填充字節(jié)的值也說(shuō)明了填充的字節(jié)數(shù),因此待加密數(shù)據(jù)的最后幾個(gè)字節(jié)必須是以下幾種情況之一:
如果解密后的最后一個(gè)數(shù)據(jù)塊末尾并非這些合法的字節(jié)序列,大部分加/解密程序都會(huì)拋出一個(gè)填充異常。這個(gè)異常對(duì)于攻擊者尤為關(guān)鍵,它是Padding Oracle Attack的基礎(chǔ)。#p#
一個(gè)基本的Padding Oracle Attack場(chǎng)景
作為一個(gè)具體例子,請(qǐng)考慮以下場(chǎng)景:
http://sampleapp/home.jsp?UID=7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6
某個(gè)應(yīng)用程序使用Query String參數(shù)來(lái)傳遞一個(gè)用戶加密后的用戶名,公司ID及角色I(xiàn)D。參數(shù)使用CBC模式加密,每次都使用不同的初始化向量(IV,Initialization Vector)并添加在密文前段。
當(dāng)應(yīng)用程序接受到加密后的值以后,它將返回三種情況:
接受到正確的密文之后(填充正確且包含合法的值),應(yīng)用程序正常返回(200 - OK)。
接受到非法的密文之后(解密后發(fā)現(xiàn)填充不正確),應(yīng)用程序拋出一個(gè)解密異常(500 - Internal Server Error)。
接受到合法的密文(填充正確)但解密后得到一個(gè)非法的值,應(yīng)用程序顯示自定義錯(cuò)誤消息(200 - OK)。
上述的場(chǎng)景體現(xiàn)了一個(gè)典型的Padding Oracle(填充提示),我們可以利用應(yīng)用程序的行為輕易了解某個(gè)加密的值是否填充正確。這里的單詞Oracle代表了一種機(jī)制,用于了解某個(gè)測(cè)試是否通過(guò)。
既然已經(jīng)給出了場(chǎng)景,那么我們便來(lái)查看應(yīng)用程序所使用的一個(gè)加密后的參數(shù)。這個(gè)參數(shù)保存了使用分號(hào)隔離的一系列值,在我們的示例中,則是用戶名(BRIAN),公司ID(12)及角色I(xiàn)D(12):因此這里的明文是“BRIAN;12;2;”。以下則是經(jīng)過(guò)加密的Query String實(shí)例,請(qǐng)注意加密后的UID參數(shù)使用了ASCII十六進(jìn)制表示法。
在實(shí)際情況中,攻擊者并不會(huì)知道這里所對(duì)應(yīng)的明文是多少,不過(guò)作為示例,我們已經(jīng)知道了明文、填充、以及加密后的值(如下表)。正如之前所提到的那樣,IV添加在密文的前段,即最前面8個(gè)字節(jié)。
攻擊者可以根據(jù)加密后值的長(zhǎng)度來(lái)推測(cè)出數(shù)據(jù)塊的大小。由于長(zhǎng)度(這里是24)能被8整除但不能被16整除,因此可以得知數(shù)據(jù)塊的大小是8個(gè)字節(jié)?,F(xiàn)在我們來(lái)觀察下加密和解密的內(nèi)部實(shí)現(xiàn),下圖便展示了字節(jié)級(jí)別的運(yùn)算方式,這對(duì)以后攻擊方式的討論很有幫助。請(qǐng)注意,其中帶圓圈的加號(hào)表示XOR(異或)操作。
加密過(guò)程:
解密過(guò)程:
同樣值得指出的是,解密之后的最后一個(gè)數(shù)據(jù)塊,其結(jié)尾應(yīng)該包含正確的填充序列。如果這點(diǎn)沒(méi)有滿足,那么加/解密程序就會(huì)拋出一個(gè)填充異常。#p#
利用Padding Oracle進(jìn)行解密
我們現(xiàn)在來(lái)關(guān)注一下如何利用Padding Oracle Attack進(jìn)行解密。我們將每次操作一個(gè)單獨(dú)的加密塊,因此我們可以獨(dú)立出第一塊密文(IV后的那塊),在前面加上全為NULL的IV值,并發(fā)送至應(yīng)用程序。以下是URL極其相關(guān)回復(fù):
Request: http://sampleapp/home.jsp?UID=0000000000000000F851D6CC68FC9537 Response: 500 - Internal Server Error
回復(fù)的500錯(cuò)誤是意料之中的,因?yàn)檫@個(gè)值在解密后完全非法。下圖展示了應(yīng)用程序在嘗試解密的時(shí)候究竟做了哪些事情。您會(huì)發(fā)現(xiàn),因?yàn)槲覀冎惶幚韱蝹€(gè)數(shù)據(jù)塊,因此它的結(jié)尾必須包含正確的填充字節(jié),才能避免出現(xiàn)非法填充異常。
如上圖所示,在解密之后,數(shù)據(jù)塊的末尾并沒(méi)有包含正確的填充序列,因此出現(xiàn)了異常?,F(xiàn)在我們將IV加一,并發(fā)送同樣的密文,看看會(huì)發(fā)生什么:
Request: http://sampleapp/home.jsp?UID=0000000000000001F851D6CC68FC9537 Response: 500 - Internal Server Error
與之前一樣,我們得到了500異常。這是因?yàn)樵诮饷芎笪覀冞€是沒(méi)有獲得合法的填充序列。稍有不同的是,我們?cè)谏钊雰?nèi)部之后會(huì)發(fā)現(xiàn),最后一個(gè)字節(jié)的值會(huì)有所變化(變成了0x3C而不是0x3D)。
如果我們重復(fù)發(fā)送這樣的請(qǐng)求,每次將IV的最后一個(gè)字節(jié)加一(直至0xFF),那么最終我們將會(huì)產(chǎn)生一個(gè)合法的單字節(jié)填充序列(0x01)。對(duì)于可能的256個(gè)值中,只有一個(gè)值會(huì)產(chǎn)生正確的填充字節(jié)0x01。遇上這個(gè)值的時(shí)候,你應(yīng)該得到一個(gè)不同于其他255個(gè)請(qǐng)求的回復(fù)結(jié)果:
Request: http://sampleapp/home.jsp?UID=000000000000003CF851D6CC68FC9537 Response: 200 OK
同樣,我們從示意圖中了解一下此時(shí)發(fā)生了什么:
在這個(gè)情況下,我們便可以推斷出中間值(Intermediary Value)的最后一個(gè)字節(jié),因?yàn)槲覀冎浪?x3C異或后的結(jié)果為0x01,于是:
因?yàn)?[Intermediary Byte] ^ 0×3C == 0×01,
得到 [Intermediary Byte] == 0×3C ^ 0×01,
所以 [Intermediary Byte] == 0×3D
現(xiàn)在我們可以更進(jìn)一步。我們已經(jīng)知道了中間值的最后一個(gè)字節(jié),于是我們可以推斷出解密后的值是多少。您可以回憶一下,在解密的過(guò)程中,中間值的每個(gè)字節(jié)都會(huì)與密文中的前一個(gè)數(shù)據(jù)塊(對(duì)于第一個(gè)數(shù)據(jù)塊來(lái)說(shuō)便是IV)的對(duì)應(yīng)字節(jié)進(jìn)行異或操作,于是我們使用之前示例中原來(lái)的IV中的最后一個(gè)字節(jié)(0x0F),與中間值異或一下便可以得到明文。不出意料,我們會(huì)得到0x32,這表示數(shù)字“2”(明文中第一個(gè)數(shù)據(jù)塊的最后一個(gè)字節(jié))。
我們現(xiàn)在已經(jīng)破解了示例數(shù)據(jù)塊中的第8個(gè)字節(jié),是時(shí)候關(guān)注第7個(gè)字節(jié)了。在破解第8個(gè)字節(jié)時(shí),我們使用暴力枚舉IV,讓解密后的最后一個(gè)字節(jié)成為0x01(合法填充)。在破解第7個(gè)字節(jié)的時(shí)候,我們要做的事情也差不多,不過(guò)此時(shí)要求第7個(gè)字節(jié)與第8個(gè)字節(jié)都為0x02(再重復(fù)一遍,這表示合法的填充)。我們已經(jīng)知道,中間值的最后一個(gè)字節(jié)是0x3D,因此我們可以將IV中的第8個(gè)字節(jié)設(shè)為0x3F(這會(huì)產(chǎn)生0x02)并暴力枚舉IV的第七個(gè)字節(jié)(從0x00開(kāi)始,直至0xFF)。
我們?cè)俅卧庥鎏畛洚惓?,直至遇上某個(gè)值,它使得解密后的第7個(gè)字節(jié)成為0x02(正確填充),此時(shí)IV中的字節(jié)為0x24:
使用這種技巧,我們可以從后往前破解中間值里的每個(gè)字節(jié),最終得到解密后的值(盡管每次一個(gè)字節(jié))。下圖展示了完全破解后的IV值,此時(shí)整個(gè)數(shù)據(jù)塊都為填充值(0x08):
使用PadBuster進(jìn)行解密
(譯注:這段內(nèi)容為PadBuster的使用指南,在此略過(guò),如果您對(duì)這部分內(nèi)容感興趣可以閱讀原文《Automated Padding Oracle Attacks with PadBuster》。)
加密任意的值
我們已經(jīng)知道如何利用Padding Oracle和PadBuster來(lái)依次破解每個(gè)加密的數(shù)據(jù)塊?,F(xiàn)在,我們就來(lái)觀察下如何使用同樣的漏洞來(lái)加密任意數(shù)據(jù)。
可能您已經(jīng)發(fā)現(xiàn),一旦我們可以推斷出密文數(shù)據(jù)塊的中間值,我們便能通過(guò)操作IV的值來(lái)完全控制解密所得到的結(jié)果。例如,在前面的示例中,如果想要將密文中第一個(gè)數(shù)據(jù)塊解密為“TEST”這個(gè)值,您可以計(jì)算出它所需要的IV值,只要將目標(biāo)明文與中間值進(jìn)行異或操作即可。因此,只要您將字符串“TEST”(自然,還包括四個(gè)0x04字節(jié)作為填充)與中間值異或之后,便可以得到最終的IV,即0×6D,0×36,0×70,0×76,0×03,0×6E,0×22,0×39:
這種做法對(duì)于單個(gè)數(shù)據(jù)塊來(lái)說(shuō)自然沒(méi)有問(wèn)題,但如果我們想要用它來(lái)生成長(zhǎng)度超過(guò)一個(gè)數(shù)據(jù)塊的值又該怎么辦呢?我們來(lái)看一個(gè)簡(jiǎn)單通俗的實(shí)際案例。這次我們要生成一個(gè)加密的字符串“ENCRYPT TEST”而不僅僅是“TEST”。第一步,還是將文本分拆成數(shù)據(jù)塊,并補(bǔ)上必須的填充字節(jié),如下圖:
在構(gòu)造超過(guò)一個(gè)數(shù)據(jù)塊的值時(shí),我們實(shí)際上是從最后一個(gè)數(shù)據(jù)塊開(kāi)始,向前依次生成所需的密文。在這里,最后的數(shù)據(jù)塊與之前的相同,因此我們已經(jīng)知道以下的IV和密文能夠生成字符串“TEST”:
Request: http://sampleapp/home.jsp?UID=6D367076036E2239F851D6CC68FC9537
接下來(lái),我們需要弄明白中間值6D367076036E2239在作為密文,而不是IV傳遞至應(yīng)用程序時(shí)會(huì)被如何解密。在這里只要使用與破解過(guò)程相同的技巧就行了,我們把它作為密文傳遞給應(yīng)用程序,并從全部為NULL的IV開(kāi)始進(jìn)行暴力破解:
Request: http://sampleapp/home.jsp?UID=00000000000000006D367076036E2239
一旦我們通過(guò)暴力破解得到中間值之后,IV便可以用來(lái)生成我們想要的任意值。新的IV可以被放在前一個(gè)示例的前面,這樣便可以得到一個(gè)符合我們要求的,包含兩個(gè)數(shù)據(jù)塊的密文了。這個(gè)過(guò)程可以不斷重復(fù),這樣便能生成任意長(zhǎng)度的數(shù)據(jù)了。
使用PadBuster加密任意的值
(譯注:這段內(nèi)容為PadBuster的使用指南,在此略過(guò),如果您對(duì)這部分內(nèi)容感興趣可以閱讀原文《Automated Padding Oracle Attacks with PadBuster》。)