CBC加密原理及攻击
原理
基于分组加密
加密过程
Plaintext:明文,待加密的数据。
IV:用于随机化加密的比特块,保证即使对相同明文多次加密,也可以得到不同的密文,初始向量,用来与第一块的明文异或运算。
Key:被一些如AES的对称加密算法使用。
Ciphertext:密文,加密后的数据。
在这里重要的一点是,CBC工作于一个固定长度的比特组,将其称之为块。在本文中,我们将使用包含16字节的块。
• Ciphertext-0 = Encrypt(Plaintext XOR IV)—只用于第一个组块
• Ciphertext-N= Encrypt(Plaintext XOR Ciphertext-N-1)—用于第二及剩下的组块
XOR为异或运算 , N为第N块数据
---------------------------------------------------------------------------
上述的公式和图可以简单描述为:
1.将要CBC加密的数据分为N个块,每个块为16字节
2.随机找一个IV(初始向量),大小为每个块的大小(16字节),用于与第一个块进行异或运算
3.将异或运算的结果进行选定的加密方式进行加密
4.将得到的第一块密文与第二块明文进行异或运算
5.将异或运算的结果进行选定的加密方式进行加密
6.将得到的第二块密文与第三块明文进行异或运算
7.将异或运算的结果进行选定的加密方式进行加密
8.最后得到三块加密获得的密文
注意:正如你所见,前一块的密文用来产生后一块的密文。
----------------------------------------------------------------------
解密过程
Decryption Process
• Plaintext-0 = Decrypt(Ciphertext) XOR IV—只用于第一个组块
• Plaintext-N= Decrypt(Ciphertext) XOR Ciphertext-N-1—用于第二及剩下的组块
• -----------------------------------------------------------------------------------
上述的公式和图可以简单描述为:
1.将已经CBC加密的数据分为N个块,每个块为16字节
2.将第一块加密的数据用选定的加密方式解密
3.找到加密时的IV,用于与第一个解密后的块进行异或运算,得到第一块的明文
4.将第二块加密数据进行解密
5.用第一块加密的密文与第二块解密的块进行异或运算,得到第二块的明文
6.将第三块加密的数据进行解密
7.用第二块加密的数据与第三块解密的块进行异或运算,得到第二块的明文
通过上述的解密过程可以推算出:
如果要修改第二个块中的第n个字节的数据,只要修改第一块数据的密文中的第n个字节数据,这样第一个块的密文与第二个块解密得到的数据进行异或运算时就可以得到自己想要的数据,对第三个块及其以后的数据都不会产生影响(因为与下一块进行异或运算的都是加密的数据,第二块的加密数据没有进行改变),但是第一个块修改了加密数据,进行解密和与IV异或运算后会产生错误数据。
以此类推,如果要修改第三块中的数据,就修改第二块的加密信息,会产生错误数据的只有第二块数据,其他块的数据不会产生错误。
如果要修改第一块的数据的话,只能修改第一块密文,这需要知道IV是什么,过程:
1.将第一块的明文修改成想要的数据
2.将修改完的数据与IV进行异或运算,然后进行选定方式的加密,得到密文数据
3.将第一块的密文数据换成的到的密文数据
这样的话会出现错误的块是第二块,因为第一块的密文修改了,与第二块的已解密的数据进行异或运算时产生错误。
解决错误的方式是修改IV
-----------------------------------------------------------------------------
注意:Ciphertext-N-1(密文-N-1)是用来产生下一块明文;这就是字节翻转攻击开始发挥作用的地方。如果我们改变Ciphertext-N-1(密文-N-1)的一个字节,然后与下一个解密后的组块异或,我们就可以得到一个不同的明文了!You got it?别担心,下面我们将看到一个详细的例子。与此同时,下面的这张图也可以很好地说明这种攻击:
0x02 一个例子(CBC Blocks of 16 bytes)
比方说,我们有这样的明文序列:
a:2:{s:4:"name";s:6:"sdsdsd";s:8:"greeting";s:20:"echo 'Hello sdsdsd!'";}
我们的目标是将“s:6”当中的数字6转换成数字“7”。我们需要做的第一件事就是把明文分成16个字节的块:
• Block 1:a:2:{s:4:"name";
• Block 2:s:6:"sdsdsd";s:8
• Block 3::"greeting";s:20
• Block 4::"echo 'Hello sd
• Block 5:sdsd!'";}
因此,我们的目标字符位于块2,这意味着我们需要改变块1的密文来改变第二块的明文。
有一条经验法则是(注:结合上面的说明图可以得到),你在密文中改变的字节,只会影响到在下一明文当中,具有相同偏移量的字节。所以我们目标的偏移量是2:
• [0] = s
• [1]= :
• [2] =6
因此我们要改变在第一个密文块当中,偏移量是2的字节。正如你在下面的代码当中看到的,在第2行我们得到了整个数据的密文,然后在第3行中,我们改变块1中偏移量为2的字节,最后我们再调用解密函数。
1. $v = "a:2:{s:4:"name";s:6:"sdsdsd";s:8:"greeting";s:20:"echo 'Hello sdsdsd!'";}";
2. $enc = @encrypt($v);
3. $enc[2] = chr(ord($enc[2]) ^ ord("6") ^ ord ("7"));
4. $b = @decrypt($enc);
运行这段代码后,我们可以将数字6变为7:
但是我们在第3行中,是如何改变字节成为我们想要的值呢?
基于上述的解密过程,我们知道有,A = Decrypt(Ciphertext)与B = Ciphertext-N-1异或后最终得到C = 6。等价于:
C = A XOR B
所以,我们唯一不知道的值就是A(注:对于B,C来说)(block cipher decryption);借由XOR,我们可以很轻易地得到A的值:
A = B XOR C
最后,A XOR B XOR C等于0。有了这个公式,我们可以在XOR运算的末尾处设置我们自己的值,就像这样:
A XOR B XOR C XOR "7"会在块2的明文当中,偏移量为2的字节处得到7
--------------------------------------------------------------------
异或计算的公式:
1.A XOR B =C , B XOR C=A, A XOR C =B
2.A XOR B XOR C =0
3.0 XOR A = A
----------------------------------------------------------------
Demo
目前自己测试只能修改第二块和第一块数据
源码:
function login($info){
$iv = get_random_iv();
$plain = serialize($info);
$cipher = openssl_encrypt($plain, aes-128-cbc, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
$_SESSION['username'] = $info['username'];
setcookie("iv", base64_encode($iv));
setcookie("cipher", base64_encode($cipher));
}
function check_login(){
if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
$cipher = base64_decode($_COOKIE['cipher']);
$iv = base64_decode($_COOKIE["iv"]);
if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
$info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
$_SESSION['username'] = $info['username'];
}else{
die("ERROR!");
}
}
}
if(isset($_POST['username']) && isset($_POST['password'])){
$username = (string)$_POST['username'];
$password = (string)$_POST['password'];
if($username === 'admin'){
exit('<p>admin are not allowed to login</p>');
}else{
$info = array('username'=>$username,'password'=>$password);
login($info);
show_homepage();
}
else{
if(isset($_SESSION["username"])){
check_login();
show_homepage();
}
输入用户名admir密码1,抓取数据内容为:
Cookie:
PHPSESSID=iq1lrnq2fhp235ndd4ke2njl73;
iv=XyP2qyLI00SzmCP8t766mA%3d%3d;
cipher=8Rhxwqez6NPucPx4mKM4oytL0QCEM6YnRhjkPTjIVvDMp8HAF2%2f8JjiWG8oSLqwgdGs4EV018W7SU63K3bYV9w%3d%3d
过程: XOR异或 、CRYPT加密(未知密钥)、en-加密后
plain XOR iv ->after_plain CRYPT ->en-after_plain
补充:AES加密后的数据为16字节的整数倍,可直接进行异或运算
解题过程:
<?php
$enc=base64_decode("bIpgPK29vVQosJ+smzh0pOdq7QrP3H9CN0MBfynL1eKtILs/ayew1snTYbeYSIz8rQctkAUMORS76SWQHXwuKg==");
$enc[13] = chr(ord($enc[13]) ^ ord("k") ^ ord ("n"));
echo base64_encode($enc);
?>
<?php
$enc=base64_decode("4quudO++PAeVPQfcFJ0bbm1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjM6IjEyMyI7fQ==");
$iv=base64_decode("TrphJjWLH37sj6+EBqh28A==");
$cleartext = 'a:2:{s:8:"userna';
$newiv = '';
for ($i=0;$i<16;$i++){
$newiv=$newiv.chr(ord($iv[$i]) ^ ord($enc[$i]) ^ ord ($cleartext[$i]));
}
echo base64_encode($newiv);
?>
详解:
1. $enc = en-after_plain
2. 因为序列化的内容为
a:2:{s:8:"userna
me";s:5:"admik";
s:8:"password";s
:1:"1";}
所以修改块二上的第13位数据(从0开始),因此修改块一13位数据(AES加密)
3. 修改数据计算:
1.算法
ciphertext XOR plaintext = 加密前xor后的plain
加密前xor后的plain XOR plaintext =ciphertext
所以比如要修改enc[23]处的值时,使用的算法为enc[7]^"替换前"^"替换后"
注意错误:不能使用加密后直接异或,从过程来看就为错误
例如enc[23]^"H"就是错误的,因为加密后原本异或后位置的字符改变
2.关于改变后iv的获得
由于数据改变导致第一块数据异或时得到不能序列化的数据,所以需要根据网页返回的不能序列化的数据(也就是除iv修改完后块一的数据,base64解码后就是序列化数据)进行异或运算来的得到新的iv
与前面的1 运算相同
iv XOR 网页返回的数据 = 加密前xor后的plain(序列化错误的)
加密前xor后的plain XOR plain(正常序列的) = new_iv
所以得到iv算法为 iv ^ wrong_plain ^ plain
补充:由于加密解密得关系 所以不只是特定位置的字符错误,需要全部异或计算
posted on 2019-09-18 19:10 Hanamizuki花水木 阅读(7453) 评论(0) 编辑 收藏 举报