CBC字节翻转攻击
上文我们初步了解了CBC
加密原理以及模拟实现,但是根据原理,我们可以控制第一段密文来控制第二段明文的形成,这就是所谓的CBC
字节翻转攻击。当然实现该攻击是要具备一定条件的,例如我们需要获取到iv
向量,密文段,以及修改后的第一段错误明文。
举个例子,阿强爱上了阿珍,他将信息:
i love you
通过编码后,将iv
和密文cipher
发送给阿珍,但是在传输过程中被隔壁老王截取到了,老王心想能拆散一对是一对,将把信息篡改成:
i hate you
成功将他们的爱情扼杀在摇篮中!?
攻击原理
让我们先回顾一下解密流程:
字节翻转攻击的利用点是利用上一段密文来控制本段明文的生成,这是因为:
plain[2] = decrypt(cipher[2]) ⊕ cipher[1]
由于我们的数据是分段的,即有:
for i from 1 to 16:
plain[2][i] = decrypt(cipher[2][i]) ⊕ cipher[1][i]
如果我们希望plain[2][i]
是某一个特定的字符,如'a'
,那么我们可以从cipher[1][i]
入手,为了达成该目的,我们先来看推导过程:
假设我们希望plain[2][1]
将来解密以后是'a'
,设改变后的cipher[1][1]
的值为x
,则有:
'a' = decrypt(cipher[2][1]) ⊕ x
'a' ⊕ x = decrypt(cipher[2][1]) = y
而未改变之前
Plain[2][1] = decrypt(cipher[2][1]) ⊕ cipher[1][1]
即
Plain[2][1] ⊕ cipher[1][1] = decrypt(cipher[2][1]) = y
注意到,我们第二段密文我们并没有改变,即第 2 行的 y 和 第 6 行的 y 是一致的,将第 2 行和第 6 行合并,有
'a' ⊕ x = Plain[2][1] ⊕ cipher[1][1]
即
x = Plain[2][1] ⊕ cipher[1][1] ⊕ 'a'
上述的 cipher[1][1] 指的是最初的
修复iv
由于我们修改了密文的第一段,因此经过解码后,再和原始的iv
进行异或,那么得到的明文就不再是原来的第一段明文了,因此我们要修改iv
,使得其生成最终生成的第一段明文和原来的一致(当然,要是你想第一段也修改,那就另当别论)。
老规矩,先上推导过程:
依题意
plain_erro = iv_old ⊕ decrypt(cipher[1]_new)
即
plain_erro ⊕ iv_old = decrypt(cipher[1]_new)
令
iv_new ⊕ decrypt(cipher[1]_new) = plain
上式子中 plain 是原来的明文,式子可以进一步变形,即
iv_new = plain ⊕ decrypt(cipher[1]_new)
将第 4 行和第 8 行合并,有
iv_new = plain ⊕ plain_erro ⊕ iv_old
由此可见,为了使得第一段明文能够被解密方解密,我们需要获得我们修改第一段密文后,得到的错误明文段。
模拟复现
CBC加密复现基本上和上文的一样,只不过为了使得加密后的字符串可见,这里加了base64
编码。而且为了方便演示,这里将加密和解密分开到两个脚本中:
<?php
// cbc_encode.php
// 密钥
define("SECRET_KEY", 23);
// 产生长度为 16字节 的随机 iv 向量,一个字符在计算机内部使用8比特表示,即一个字节
function get_random_init_vector() {
$random_iv = '';
for ($i = 0;$i < 16;$i++) {
$random_iv .= chr(rand(1, 128));
}
return $random_iv;
}
// 加密运算
function encode($data){
$tmp = "";
// 和密钥异或
for ($i = 0;$i < strlen($data);$i++) {
$tmp .= chr(ord($data[$i]) ^ SECRET_KEY);
}
return $tmp;
}
// CBC 加密
function cbc_encode($plain, $len){
$iv = get_random_init_vector();
// 分段
$plain_tmp = array();
for($i = 0;$i < $len;$i++){
$plain_tmp[$i] = substr($plain, $i * 16, 16);
}
$cipher = "";
// 对于每一段明文都要进行加密
$last_cipher = $iv;
for($i = 0;$i < $len;$i++){
// 上一段密文先和本明文做异或运算
$str_tmp = "";
for ($j = 0; $j < 16; $j++){
$str_tmp .= chr(ord($last_cipher[$j]) ^ ord($plain_tmp[$i][$j]));
}
// 再进行加密
$cipher_i = encode($str_tmp);
$cipher .= $cipher_i;
$last_cipher = $cipher_i;
}
return array(
'iv' => base64_encode($iv),
'cipher' => base64_encode($cipher)
);
}
// $mydata = "I love you.............";
$mydata = $argv[1];
$info = cbc_encode($mydata, 3);
echo "数据是:\n";
echo $mydata . "\n";
echo "加密后:" . "\n";
var_dump($info);
?>
<?php
// cbc_decode.php
// 密钥
define("SECRET_KEY", 23);
// 解密运算
function decode($data){
$tmp = "";
// 和密钥异或
for ($i = 0;$i < strlen($data);$i++) {
$tmp .= chr(ord($data[$i]) ^ SECRET_KEY);
}
return $tmp;
}
// CBC 解密
function cbc_decode($cipher, $iv, $len){
$cipher = base64_decode($cipher);
$iv = base64_decode($iv);
// 分段
$cipher_tmp = array();
for($i = 0;$i < $len;$i++){
$cipher_tmp[$i] = substr($cipher, $i * 16, 16);
}
$plain = "";
// 对于每一段密文都要进行解密
$last_cipher = $iv;
for($i = 0;$i < $len;$i++){
// 先进行解密
$plain_i_tmp = decode($cipher_tmp[$i]);
// 再和上一段密文做异或运算
$str_tmp = "";
for ($j = 0; $j < 16; $j++){
$str_tmp .= chr(ord($last_cipher[$j]) ^ ord($plain_i_tmp[$j]));
}
$plain .= $str_tmp;
$last_cipher = $cipher_tmp[$i];
}
if (substr($plain, 0, 16) !== "I "){
die("消息被篡改!" . base64_encode($plain) . "是错误的!");
}
return array(
'plain' => $plain
);
}
$info = array();
$info['iv'] = $argv[1];
$info['cipher'] = $argv[2];
$data = cbc_decode($info['cipher'], $info['iv'], 3);
var_dump($data);
?>
阿强给阿珍发了加密信息:
如果阿珍能够正常接受,解密后:
下面进行攻击模拟:
假设隔壁老王获悉了原始的明文、初始向量和密文
老王想把消息的第二段改为hate
,他略加思考,写下了下面的篡改脚本
<?php
// hack_data.php
$origin_iv = base64_decode("bFQYFlR5BVdfHChTUGspWA==");
$origin_cipher = base64_decode("MmMvIWNOMmBoKx9kZ1web0kbTlNUeQVXXxwoU1BrKVh+dTYxbUA8bmYlEWppUhBh");
$hack_msg = "hate ";
$plain = "I love you.............";
for($i = 0; $i < 16; $i++){
// x = Plain[2][1] ⊕ cipher[1][1] ⊕ 'a'
$origin_cipher[$i] = chr(ord($plain[$i + 16]) ^ ord($origin_cipher[$i]) ^ ord($hack_msg[$i]));
}
$new_cipher = base64_encode($origin_cipher);
echo $new_cipher;
?>
他将信息篡改好,然后发给阿珍,阿珍解析出来后,暗号对不上(第一段明文不是I
),并将错误信息发出来:
可以看得到,老王成功地将第二段明文修改好了,下一步就是将iv
修改好。
老外略加思索,立刻写出来恢复iv
的代码:
<?php
// hack_iv.php
$origin_iv = base64_decode("bFQYFlR5BVdfHChTUGspWA==");
$plain = "I love you.............";
$plain_erro = base64_decode("TS4iICAgICAgICAgICAgIGhhdGUgICAgICAgICAgICAgeW91Li4uLi4uLi4uLi4u");
for ($i = 0; $i < 16; $i++){
// iv_new = plain ⊕ plain_erro ⊕ iv_old
$origin_iv[$i] = chr(ord($plain[$i]) ^ ord($plain_erro[$i]) ^ ord($origin_iv[$i]));
}
echo base64_encode($origin_iv);
?>
阿珍立即收到错误的消息,老王成功棒打鸳鸯!?
从这个例子中,我们学会了喜欢一个人的时候,要大胆地面对面说出来,不要整这些花里胡哨的的暗示。?
本文由「黄阿信」创作,创作不易,请多支持。
如果您觉得本文写得不错,那就点一下「赞赏」请我喝杯咖啡~
商业转载请联系作者获得授权,非商业转载请附上原文出处及本链接。
关注公众号,获取最新动态!