CBC字节翻转攻击

0x01 CBC翻转技术

  CBC模式的全称是密文分组链接模式(Cipher Block Chainning),之所以叫这个名字,是因为密文分组是像链条一样相互连接在一起的。

  在CBC模式中,首先将明文分组与前一个密文分组进行XOR运算,然后再进行加密,当加密第一个明文分组时,由于不存在“前一个密文分组”,因此需要事先准备一个长度为一个分组的比特序列来代替“前一个密文分组”,这个比特序列称为初始化向量(Initialization vector),通常缩写为IV,如果每次都使用相同的初始化向量(IV),当用同一密钥对同一明文进行加密时,所得到的密码一定是相同的,所以每次加密时都会随机产生一个不同的比特序列来作为初始化向量,避免这种情况产生。即:

1
2
3
4
5
6
7
8
首先将明文分组(常见的以16字节为一组),位数不足的使用特殊字符填充。
生成一个随机的初始化向量(IV)和一个密钥。
将IV和第一组明文异或。
用密钥对3中xor后产生的密文加密。
用4中产生的密文对第二组明文进行xor操作。
用密钥对5中产生的密文加密。
重复4-7,到最后一组明文。
将IV和加密后的密文拼接在一起,得到最终的密文。

加密公式:

  • Ciphertext-0 = Encrypt(Plaintext XOR IV) —只用于第一个组块

  • Ciphertext-N = Encrypt(Plaintext XOR Ciphertext-N-1) —用于第二及剩下的组块
      

1
2
3
4
从密文中提取出IV,然后将密文分组。
使用密钥对第一组的密文解密,然后和IV进行xor得到明文。
使用密钥对第二组密文解密,然后和2中的密文xor得到明文。
重复2-3,直到最后一组密文。

解密公式:

  • Plaintext-0 = Decrypt(Ciphertext) XOR IV—只用于第一个组块

  • laintext-N = Decrypt(Ciphertext) XOR Ciphertext-N-1—用于第二及剩下的组块

0x02 翻转攻击

  可以注意到Ciphertext-N-1用来产生下一块明文,这就是字节翻转攻击开始发挥作用的地方,如果我们改变Ciphertext-N-1中的一个字节,然后和下一块解密后的密文xor,就可以得到一个不同的明文,而这个明文是我们可以控制的。利用这一点,我们就欺骗服务端或者绕过过滤器。
  CBC比特反转攻击的目的是攻击者通过修改密文来操纵解密后的明文,攻击者会对初始化向量(IV)中的任意比特位进行反转(1变0,0变1),则明文分组(解密后得到的明文分组)中相应的比特也会被反转。比如一个叫admin的用户,登录,经过CBC模式加密后,token为”aaabbbccc999”,现在有一个攻击者,叫john,登录,经过CBC模式加密后,token为cccbbbccc1111,现在john将token改为”ffcbbbccc1111”,发现登录名变成了ohn,所以他知道token第一个位的ff转换成了,经过几轮测试,他发现如果将token改为“7bcbbbccc1111”,则登录名变成了’aohn’,最后他通过发送token为7bdc995465到服务器,发现自己已经变成了admin。

  根据解密流程,我们假设A为密文经过AES解密后的字串,B为前一组密文,C为明文:

1
2
3
A = Decrypt(Ciphertext)[0] = 6
B = (Ciphertext-N-1)[0] = 13
C = Plaintext[0] = 11

  那么经过解密过程可知:

1
C = A xor B

  那么

1
A xor B xor C = 0 (任何数与自己异或都为0)

  由于任何数与0异或都为自己本身,则

1
A xor B xor C xor 3 = 3

  那么此时我们可以这样来看

1
2
3
A = A = 6
B = B xor 3 = 13 xor 3 xor 11 = 5
C = C = 11

  现在我们修改密文对应的位让B = 5,那么当密文解密后,会发现,明文C会变成3,通过这种方法我们可以控制任何一位明文。

0x03 实例说明

  我们举例说明一下这个例子,这里有一个明文序列:

1
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的明文。在密文中改变的字节,只会影响到在下一明文当中,具有相同偏移量的字节。
  因此我们只需要改变在第一个密文块当中,偏移量是2的字节。在第2行我们得到了整个数据的密文,然后在第3行中,我们改变块1中偏移量为2的字节,最后我们再调用解密函数。

1
2
3
4
$v = "a:2:{s:4:"name";s:6:"sdsdsd";s:8:"greeting";s:20:"echo 'Hello sdsdsd!'";}";
$enc = @encrypt($v);
$enc[2] = chr(ord($enc[2]) ^ ord("6") ^ ord ("7"));
$b = @decrypt($enc);


  这样我们就把“s:6”当中的数字6转换成数字“7”,达到来了我们想要的目的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
define('MY_AES_KEY', "abcdef0123456789");
function aes($data, $encrypt) {
$aes = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = "1234567891234567";
mcrypt_generic_init($aes, MY_AES_KEY, $iv);
return $encrypt ? mcrypt_generic($aes,$data) : mdecrypt_generic($aes,$data);
}
define('MY_MAC_LEN', 40);
function encrypt($data) {
return aes($data, true);
}
function decrypt($data) {
$data = rtrim(aes($data, false), "\0");ss
return $data;
}
$v = "a:2:{s:4:\"name\";s:6:\"sdsdsd\";s:8:\"greeting\";s:20:\"echo 'Hello sdsdsd!'\";}";
echo "Plaintext before attack: $v\n";
$b = array();
$enc = array();
$enc = @encrypt($v);
$enc[2] = chr(ord($enc[2]) ^ ord("6") ^ ord ("7"));
$b = @decrypt($enc);
echo "Plaintext AFTER attack : $b\n";
?>

参考链接:
使用CBC比特反转攻击绕过加密的会话令牌
CBC字节反转攻击