Web狗要懂的Padding Oracle攻击

之前写CBC翻转攻击的时候就在想什么时候能遇到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选项:
    输出经过aes-256-cbc加密的flag
  • 2选项:
    提供你的IV和要加密的数据,返回加密后的密文
  • 3选项:
    提供你的IV和要解密的数据,返回解密明文,只返回解密成功是否

我们可以从源码获取到的信息有:

  • 加密flag所采用的IV为16个字符A
  • 不能获取到加密flag所用的密钥
  • 解密时IV与密文可控

背景知识:

  • 加密过程


    cbcEncrypt.png
  1. 首先将明文分成每X位一组,位数不足的是用特殊字符填充!!!!!!!
    X常见的为16位,也有32位
    这里要注意,CBC的填充规则(有PKCS5和PKCS7,区别这里使用的是PKCS7 图解如下)是缺少N位,就用 N 个 '\xN'填充,如缺少10位则用 10 个 '\x10'填充
  2. 然后生成初始向量IV(这里的初始向量如果未特定给出则随机生成)和密钥
  3. 将初始向量与第一组明文异或生成密文A
  4. 用密钥加密密文A 得到密文A_1
  5. 重复3 将密文A_1与第二组明文异或生成密文B
  6. 重复4 用密钥加密密文B_1
  7. 重复3-6 直到最后一组明文
  8. 将IV和加密后的密文拼接在一起,得到最终的密文(也可以不拼接)
  • 解密过程


    cbcDecrypt.png

    解密过程则是相反的

  1. 首先从最终的密文中提取出IV (IV为加密时指定的X位) //如果加密时没有加入IV则不用提取
  2. 将密文分组
  3. 使用密钥对第一组密文解密得到密文A,然后用IV进行异或得到第一组明文
  4. 使用密钥对第二组密文解密得到密文B,然后用A与B进行异或得到第二组明文
  5. 重复3-4 直到最后一组密文

攻击

与CBC翻转攻击不同的地方是 我们这里不知道解密之后的明文,只知道并可控IV和密文,对了 还有解密是否成功
解密是否成功这个点成为了padding oracle攻击至关重要的一点,
因为我们知道padding只能为:

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

那如果出现以下这种padding的时候会怎么样呢?
data 0x05 0x05
(正常来说这个padding应为data 0x05 0x05 0x05 0x05 0x05
那解密之后的检验就会出现错误,因为padding的位数和padding内容不一致
如果这个服务没有catch这个错误的话那么程序就会中途报错退出,表现为,如http服务的status code为500
那么这里就给了我们一个爆破的机会,假如在第一组解密中,我们输入解密的IV为16个0x00的话,解密过程就为:

00wrong.png

最后一位为0x3D,不符合padding规则
我们将IV的最后一位递增,然后提交,在0x00到0xFF中,只会有一个异或middle最后一位之后会得到0x01,也就是正确的padding,这时候服务正常解密(只是解密出来的结果不是原来的明文而已),则假设Plainttext为明文,middle为经过aes解密之后尚未和IV异或的值,IV[0]则为需要遍历爆破的十六进制,有
00true.png

//根据
middle[最后一位] ^ IV[最后一位] = 0x01
middle[最后一位] = IV[最后一位] ^ 0x01
//因为正常的解密步骤是middle异或加密时使用的old_IV,所以
Plainttext[最后一位] = middle[最后一位] ^ old_IV[最后一位]

到这里我们就能在不知道密钥的情况下爆破出最后一位的明文了
接下来我们要爆破倒数第二位,后两位正确的padding应该是
data 0x02 0x02
首先我们得先把最后一位调整成0x02,所以

IV[0] = middle[0] ^ 0x02
//那么解密的时候middle[0] ^ IV[0]就会始终等于0x02了

然后继续从0x00爆破到0xFF,得到正确的解密提示之后将爆破得到的值异或old_IV[倒数第二位]就是Plainttext[倒数第二位了]
以此类推.....

但是在解密第二组及其以后的组的时候有一个注意的地方,经过aes解密之后的middle要异或的不再是IV了,而是前一组密文!!


坑点:

  • 首先记得查看加密的初始IV是多少位,再根据这个位数将密文分组!按组爆破!
  • 其次是IV是每爆破出一位最好都要重新根据middle生成爆破位后面的位数 (之前就是这个点坑了我一个通宵。。。。)

解题脚本 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]

作者水平有限 如有错误请指出 Orz

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容