要么改变世界,要么适应世界

CBC字节翻转攻击

2022-07-10 17:24:08
88
目录

上文我们初步了解了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);
?>

阿强给阿珍发了加密信息:

image-20220711211953054

如果阿珍能够正常接受,解密后:

image-20220711212222566

下面进行攻击模拟:

假设隔壁老王获悉了原始的明文、初始向量和密文

老王想把消息的第二段改为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 ),并将错误信息发出来:

image-20220711144007390

可以看得到,老王成功地将第二段明文修改好了,下一步就是将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);
?>

image-20220711224840348

阿珍立即收到错误的消息,老王成功棒打鸳鸯!🙃


从这个例子中,我们学会了喜欢一个人的时候,要大胆地面对面说出来,不要整这些花里胡哨的的暗示。😁

历史评论
开始评论