Web狗要懂的Padding Oracle攻擊

之前寫CBC翻轉(zhuǎn)攻擊的時(shí)候就在想什么時(shí)候能遇到Padding Oracle的題目hhhhh 想不到這么快就遇到了hhhhh


題目

題目ruby代碼如下:

#!/usr/bin/ruby -w
require 'openssl'
require 'base64'

def banner()
    puts ' ____________________________________________'
    puts '|                                            |'
    puts '| Welcome to our secure communication system |'
    puts '| Our system is secured by AES               |'    
    puts '| So...No key! No Message!                   |'
    puts '|____________________________________________|'
    puts ''
end

def option()
    puts '1. Get the secret message.'
    puts '2. Encrypt the message'
    puts '3. Decrypt the message.'
    puts 'Give your option:'
    STDOUT.flush
    op=gets
    return op.to_i
end

def init()
    file_key=File.new("./aeskey","r")
    $key=file_key.gets
    file_key.close()
end
def aes_encrypt(iv,data)
    cipher = OpenSSL::Cipher::AES.new(256, :CBC)
    cipher.encrypt
    cipher.key = $key
    cipher.iv  = iv
    cipher.update(data) << cipher.final
end

def aes_decrypt(iv,data)
    cipher = OpenSSL::Cipher::AES.new(256, :CBC)
    cipher.decrypt
    cipher.key = $key
    cipher.iv  = iv
    data = cipher.update(data) << cipher.final
end

def output_secret()
    file_secret=File.new("./flag","r")
    secret=file_secret.gets
    file_secret.close
    secret_enc=aes_encrypt("A"*16,secret)
    secret_enc_b64=Base64.encode64(secret_enc)
    puts secret_enc_b64 
end

init
banner
while true do
    begin
        op=option
        if op==1
            output_secret
        elsif op==2
            puts "IV:"
            STDOUT.flush
            iv=Base64.decode64(gets)
            puts "Data:"
            STDOUT.flush
            data=Base64.decode64(gets)
            data_enc=aes_encrypt iv,data
            puts Base64.encode64(data_enc)
            puts "Encrytion Done"    
            STDOUT.flush
        elsif op==3
            puts "IV:"
            STDOUT.flush
            iv=Base64.decode64(gets)
            puts "Data:"
            STDOUT.flush
            data=Base64.decode64(gets)
            data_dec=aes_decrypt iv,data
            puts data_dec
            puts "Decrpytion Done"
            STDOUT.flush
        else
            puts 'Wrong Option'
            STDOUT.flush
        end
    rescue Exception => e  
        puts e.message
        STDOUT.flush
        retry
    end
end

可以得出題目的基本信息:

  • 1選項(xiàng):
    輸出經(jīng)過aes-256-cbc加密的flag
  • 2選項(xiàng):
    提供你的IV和要加密的數(shù)據(jù)逢慌,返回加密后的密文
  • 3選項(xiàng):
    提供你的IV和要解密的數(shù)據(jù)茎芋,返回解密明文佣蓉,只返回解密成功是否

我們可以從源碼獲取到的信息有:

  • 加密flag所采用的IV為16個(gè)字符A
  • 不能獲取到加密flag所用的密鑰
  • 解密時(shí)IV與密文可控

背景知識(shí):

  • 加密過程


    cbcEncrypt.png
  1. 首先將明文分成每X位一組愁溜,位數(shù)不足的是用特殊字符填充!!!Lㄐ弧!K昃朋沮!
    X常見的為16位,也有32位
    這里要注意缀壤,CBC的填充規(guī)則(有PKCS5和PKCS7樊拓,區(qū)別這里使用的是PKCS7 圖解如下)是缺少N位,就用 N 個(gè) '\xN'填充塘慕,如缺少10位則用 10 個(gè) '\x10'填充
  2. 然后生成初始向量IV(這里的初始向量如果未特定給出則隨機(jī)生成)和密鑰
  3. 將初始向量與第一組明文異或生成密文A
  4. 用密鑰加密密文A 得到密文A_1
  5. 重復(fù)3 將密文A_1與第二組明文異或生成密文B
  6. 重復(fù)4 用密鑰加密密文B_1
  7. 重復(fù)3-6 直到最后一組明文
  8. 將IV和加密后的密文拼接在一起筋夏,得到最終的密文(也可以不拼接)
  • 解密過程


    cbcDecrypt.png

    解密過程則是相反的

  1. 首先從最終的密文中提取出IV (IV為加密時(shí)指定的X位) //如果加密時(shí)沒有加入IV則不用提取
  2. 將密文分組
  3. 使用密鑰對(duì)第一組密文解密得到密文A,然后用IV進(jìn)行異或得到第一組明文
  4. 使用密鑰對(duì)第二組密文解密得到密文B图呢,然后用A與B進(jìn)行異或得到第二組明文
  5. 重復(fù)3-4 直到最后一組密文

攻擊

與CBC翻轉(zhuǎn)攻擊不同的地方是 我們這里不知道解密之后的明文条篷,只知道并可控IV和密文,對(duì)了 還有解密是否成功
解密是否成功這個(gè)點(diǎn)成為了padding oracle攻擊至關(guān)重要的一點(diǎn)蛤织,
因?yàn)槲覀冎纏adding只能為:

data 0x01 或
data 0x02 0x02 或
data 0x03 0x03 0x03 或
data 0x04 0x04 0x04 0x04 或
data 0x05 0x05 0x05 0x05 0x05 或
......

那如果出現(xiàn)以下這種padding的時(shí)候會(huì)怎么樣呢赴叹?
data 0x05 0x05
(正常來說這個(gè)padding應(yīng)為data 0x05 0x05 0x05 0x05 0x05
那解密之后的檢驗(yàn)就會(huì)出現(xiàn)錯(cuò)誤,因?yàn)閜adding的位數(shù)和padding內(nèi)容不一致
如果這個(gè)服務(wù)沒有catch這個(gè)錯(cuò)誤的話那么程序就會(huì)中途報(bào)錯(cuò)退出指蚜,表現(xiàn)為乞巧,如http服務(wù)的status code為500
那么這里就給了我們一個(gè)爆破的機(jī)會(huì),假如在第一組解密中摊鸡,我們輸入解密的IV為16個(gè)0x00的話绽媒,解密過程就為:

00wrong.png

最后一位為0x3D,不符合padding規(guī)則
我們將IV的最后一位遞增免猾,然后提交是辕,在0x00到0xFF中,只會(huì)有一個(gè)異或middle最后一位之后會(huì)得到0x01猎提,也就是正確的padding获三,這時(shí)候服務(wù)正常解密(只是解密出來的結(jié)果不是原來的明文而已),則假設(shè)Plainttext為明文,middle為經(jīng)過aes解密之后尚未和IV異或的值石窑,IV[0]則為需要遍歷爆破的十六進(jìn)制,有
00true.png

//根據(jù)
middle[最后一位] ^ IV[最后一位] = 0x01
middle[最后一位] = IV[最后一位] ^ 0x01
//因?yàn)檎5慕饷懿襟E是middle異或加密時(shí)使用的old_IV蚓炬,所以
Plainttext[最后一位] = middle[最后一位] ^ old_IV[最后一位]

到這里我們就能在不知道密鑰的情況下爆破出最后一位的明文了
接下來我們要爆破倒數(shù)第二位松逊,后兩位正確的padding應(yīng)該是
data 0x02 0x02
首先我們得先把最后一位調(diào)整成0x02,所以

IV[0] = middle[0] ^ 0x02
//那么解密的時(shí)候middle[0] ^ IV[0]就會(huì)始終等于0x02了

然后繼續(xù)從0x00爆破到0xFF肯夏,得到正確的解密提示之后將爆破得到的值異或old_IV[倒數(shù)第二位]就是Plainttext[倒數(shù)第二位了]
以此類推.....

但是在解密第二組及其以后的組的時(shí)候有一個(gè)注意的地方经宏,經(jīng)過aes解密之后的middle要異或的不再是IV了,而是前一組密文Q被鳌烁兰!


坑點(diǎn):

  • 首先記得查看加密的初始IV是多少位,再根據(jù)這個(gè)位數(shù)將密文分組徊都!按組爆破沪斟!
  • 其次是IV是每爆破出一位最好都要重新根據(jù)middle生成爆破位后面的位數(shù) (之前就是這個(gè)點(diǎn)坑了我一個(gè)通宵。暇矫。主之。。)

解題腳本 1:

from pwn import *
import base64 as b64

IV = ['\x00'] * 16
secret = 'nPQctp6AezY8BcGPjlYW8Pv+Fpo15LeatsVbj47jqgE='
secret1 = b64.b64decode(secret)[0:16]
secret2 = b64.b64decode(secret)[16:]
p = remote('10.188.2.20',10010)
middle = []
pt = ''

for x in xrange(0,16):
    for y in xrange(0,256):
        p.recvuntil("Give your option:\n")
        p.sendline('3')
        p.recvuntil("IV:\n")
        p.sendline(b64.b64encode(''.join(IV))) #send your IV
        p.recvuntil("Data:\n")
        p.sendline(b64.b64encode(secret1)) #send your Data
        # p.sendline(b64.b64encode(secret2)) #send your Data
        res = p.recvuntil("\n")
        # print res
        if 'bad decrypt' in res:
            IV[15-x] = chr(y)
        elif 'Decrpytion Done' in res:
            print IV
            IV[15-x] = chr(ord(IV[15-x]) ^ (x + 1)) #to get the correct middle, just like ---> IV[0] ^ 0x01 = middle[0]
            middle.append(ord(IV[15-x])) #store the correct middle
            print middle
            pt += chr(ord(IV[15-x]) ^ ord('A')) #first plaint text
            # pt += chr(ord(IV[15-x]) ^ ord(secret1[15-x])) #second plaint text
            for z in xrange(0,x + 1):
                IV[15-z] = chr(middle[z] ^ (x + 2)) #generate the next new IV
            break
        else:
            print res
            exit()
        if y == 255:
            print '[!] Something wrong'
            print x + 1
            exit()

print '[!] Final IV : '
print IV
print '[!] Get middle : ', middle
print '[!] PlaintText is : ' + pt[::-1]

解題腳本 2:

from pwn import *
import base64 as b64

secret = 'nPQctp6AezY8BcGPjlYW8Pv+Fpo15LeatsVbj47jqgE='
secret1 = b64.b64decode(secret)[0:16]
secret2 = b64.b64decode(secret)[16:]
p = remote('10.188.2.20',10010)

middle = []
padding = ''

for x in xrange(1,17):
    for y in xrange(0,256):
        IV = "\x00" * (16-x) + chr(y) + padding

        p.recvuntil("Give your option:\n")
        p.sendline("3")

        p.recvuntil("IV:\n")
        p.sendline(b64.b64encode(IV))

        p.recvuntil("Data:\n")
        p.sendline(b64.b64encode(secret2))

        res = p.recvuntil("\n")
        if 'Decrpytion Done' in res:
            middle.append(y ^ x) #calculate and store the correct middle
            print middle
            padding = ''
            for z in middle:
                padding = chr((x+1) ^ z) + padding #generate the next new IV tail
            break

flag = ""
for x,y in zip(middle,secret1[::-1]):
    # flag += chr(x ^ ord('A')) #for secret1
    flag += chr(x ^ ord(y)) #for secret2
print flag[::-1]

作者水平有限 如有錯(cuò)誤請(qǐng)指出 Orz

參考文章 :
http://blog.zhaojie.me/2010/10/padding-oracle-attack-in-detail.html
http://www.reibang.com/p/1851f778e579
http://www.reibang.com/p/9b4d3565de87

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末李根,一起剝皮案震驚了整個(gè)濱河市槽奕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌房轿,老刑警劉巖粤攒,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異囱持,居然都是意外死亡夯接,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門纷妆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钻蹬,“玉大人,你說我怎么就攤上這事凭需∥是罚” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵粒蜈,是天一觀的道長顺献。 經(jīng)常有香客問我,道長枯怖,這世上最難降的妖魔是什么注整? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上肿轨,老公的妹妹穿的比我還像新娘寿冕。我一直安慰自己,他們只是感情好椒袍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布驼唱。 她就那樣靜靜地躺著,像睡著了一般驹暑。 火紅的嫁衣襯著肌膚如雪玫恳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天优俘,我揣著相機(jī)與錄音京办,去河邊找鬼。 笑死帆焕,一個(gè)胖子當(dāng)著我的面吹牛惭婿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播叶雹,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼审孽,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了浑娜?” 一聲冷哼從身側(cè)響起佑力,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筋遭,沒想到半個(gè)月后打颤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漓滔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年编饺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片响驴。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡透且,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出豁鲤,到底是詐尸還是另有隱情秽誊,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布琳骡,位于F島的核電站锅论,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏楣号。R本人自食惡果不足惜最易,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一怒坯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧藻懒,春花似錦剔猿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至员寇,卻和暖如春弄慰,著一層夾襖步出監(jiān)牢的瞬間第美,已是汗流浹背蝶锋。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留什往,地道東北人扳缕。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像别威,于是被迫代替她去往敵國和親躯舔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容