终于补题补到了这个题目!这个也是非常经典的问题了,但是一直没有搞明白,之前鸡哥的讲解已经非常清楚了,忍不住还是想佩服一下4ction。下面还是大概讲一下这个Padding oracle attack到底是个啥,这里采用白帽子上的讲解方式就很好了!
由于CBC是块密码工作模式, 所以要求明文长度必须是块长度的整数倍.
对于不满足的数据, 会进行数据填充到满足整数倍. 即 padding,格式如下
** ** ** ** ** ** ** 01
** ** ** ** ** ** 02 02
** ** ** ** ** 03 03 03
** ** ** ** 04 04 04 04
** ** ** 05 05 05 05 05
** ** 06 06 06 06 06 06
** 07 07 07 07 07 07 07
08 08 08 08 08 08 08 08
当然这里是64位为一块的情况,其他的情况自行分析
在Padding Oracle Attack攻击中,攻击者输入的参数是IV+Cipher,我们要通过对IV的”穷举”来请求服务器端对我们指定的Cipher进行解密,并对返回的结果进行判断。
和SQL注入中的Blind Inject思想类似。我觉得Padding Oracle Attack也是利用了这个二值逻辑的推理原理,或者说这是一种”边信道攻击(Side channel attack)”
二者缺一不可
1.可以控制密文 或者 IV
2.如果解密后不满足 padding 服务端会报错.
然后这里需要搞清楚到底是怎样猜解,具体比较复杂,不多讲了。总之就是控制IV在解密的过程中寻找成功解密的IV值,并且根据填充的内容确定中间值。至于我们需要的明文只要中间值和原有的IV抑或一下就有了(本人就坑在这几个变量的关系上)
题目来自于第三届上海大学生网络安全大赛中的is_aes_secure密码题目,题目中明显就是了利用此漏洞,给定一个rb,贴一下代码如下
#!/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
puts secret
file_secret.close
secret_enc=aes_encrypt("A"*16,secret)
secret_enc_b64=Base64.encode64(secret_enc)
puts secret_enc_b64
STDOUT.flush
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 "233"
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
代码的逻辑非常清楚,然后我们需要进行一些设置将服务挂起来
安装socat,这个可以看我前面的介绍
socat安装使用指南
还需要配置几个文件
1.创建flag文件输入flag
flag{flag_is_here_but_you_cannont_see}
2.创建aeskey输入密码
因为这个我们在做题中不可得,填入任意的32字节长的字符串
然后我们挂载起来服务即可
之后就是做题了,首先我们输入1得到经过加密的flag内容,然后大概分析一下b64解密后长度为48,需要将之分为3组,然后就比较简单了,直接上程序,我写的程序可能比较乱,但是思路尽可能写清楚
ps:这里注意owntools的使用,这里在接收东西的时候用recvuntil远远比recv要稳定的多!!!之前做不出来就是因为这个!!!长见识了!!!
# coding:utf-8
from pwn import *
using_words='' #从0-255的字符
flag = '' #最终的flag
temp_flag='' #获取的每一块的flag值
def make_using_words():
global using_words
for i in range(0,255):
using_words+=chr(i)
def make_new(ans,sigal,pos): #这个函数生成爆破的IV向量,我们尤其需要注意ans值,和下面的的return值理清关系
ret=''
for i in range(pos): #添加前导零
ret+="0"
ret+=sigal
for i in range(len(ans)):
ret+=chr(ord(ans[i])^(16-pos)) #加入要填充nbyte,保证后n-1byte是0xn
return ret
def decode_sigal_word(conn,iv,block,ans,pos): #爆破一个byte
global using_words
for sigal in using_words:
padding = make_new(ans,sigal,pos) #NO.16-pos word
conn.send('3\n')
conn.recvuntil('IV:\n') #注意用recvuntil比较稳定
conn.send(base64.b64encode(padding)+"\n") #注意用\n才会进入下一步
conn.recvuntil('Data:\n')
conn.send(base64.b64encode(block)+"\n")
content = conn.recv()
#print content
if "Decrpytion Done" in content: #我们在文章中描述的二叉值的来源,Decrpytion Done说明解密成功了
global temp_flag
temp_flag+= chr(ord(sigal)^(16-pos)^ord(iv[pos])) #抑或原来的IV,得到直接是flag的值
return chr(ord(sigal)^(16-pos)) #注意这里不需要抑或原来的IV,后面只要num^(16-pos)^(15-pos),自己想清楚!!!
def decode_words(conn,iv,block): #爆破一整串
global temp_flag
temp_flag = ''
ans=''
for i in range(15,-1,-1): #从尾向头爆破
ans+=decode_sigal_word(conn,iv,block,ans[::-1],i)
#print ans
#return ans
if __name__=='__main__':
decode_total = 'zCGS96d+kbkerCze6RFPRz+0+0Lg6NzXbdhmLgXtliGJyJuoW4aAQT18u6AGkmFR' #我们按下1得到的密文
IV = "QUFBQUFBQUFBQUFBQUFBQQ==" #"A"×16的base64加密值
make_using_words()
decode = base64.b64decode(IV)+base64.b64decode(decode_total) #链接起来
conn = remote('127.0.0.1',3333)
print conn.recvuntil('option:\n')
global flag
global temp_flag
for i in range(1,4): #按照位置分片,前一个为IV值后一个为密文
iv = decode[i*16-16:i*16]
decode_block = decode[i*16:i*16+16]
decode_words(conn,iv,decode_block)
flag+=temp_flag[::-1]
print flag
学习到了,开心啊,再膜4ction师傅