MENU

分组密码模式: CBC模式

什么是CBC模式

1976年,IBM发明了密码分组链接(CBC,Cipher-block chaining)模式。在CBC模式中,每个明文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有明文块。同时,为了保证每条消息的唯一性,在第一个块中需要使用初始化向量。

mark

mark

若第一个块的下标为1,则CBC模式的加密过程为
$$C_i = E_K(P_i ⊕ C_{i-1})$$
$$C_0 = IV$$
解密过程为
$$P_i = D_K(C_i) ⊕ C_{i-1}$$
$$C_0 = IV$$
这里还需要解释两个东西 初始化向量(IV) 还有一个 填充

初始化向量(IV)

初始化向量(IV,Initialization Vector)是许多工作模式中用于将加密随机化的一个位块,由此即使同样的明文被多次加密也会产生不同的密文,避免了较慢的重新产生密钥的过程。
初始化向量与密钥相比有不同的安全性需求,因此IV通常无须保密,然而在大多数情况中,不应当在使用同一密钥的情况下两次使用同一个IV。对于CBC和CFB,重用IV会导致泄露明文首个块的某些信息,亦包括两个不同消息中相同的前缀。对于OFB和CTR而言,重用IV会导致完全失去安全性。

填充

块密码只能对确定长度的数据块进行处理,而消息的长度通常是可变的。因此部分模式(即ECB和CBC)需要最后一块在加密前进行填充。CBC模式的做法是在最后一个分组后填充一个固定的值,这个值的大小为填充的字节总数。即假如最后还差3个字符,则填充0x03。
这种Padding原则遵循的是常见的PKCS#5标准

mark

针对CBC模式的攻击

这里提及两个攻击模式 CBC字节翻转攻击 和 Padding Oracle Attack

CBC字节翻转攻击

原理

CBC字节翻转攻击的精髓在于:通过损坏密文字节来改变明文字节。
通过观察上面CBC解密的例图 我们可以发现:改变前一块密文块会影响后一块密文块的解密结果,并且前后之间使用异或,这样的话我就可以修改前面的密文块来操控后面的解密结果。

mark

例子

测试代码如下

<?php
error_reporting(0);

define("key", "dfhehrhrh3429hrtg934");
define("method", "aes-128-cbc");

function getIV()
{
    $iv = '';
    for ($i = 0; $i < 16; $i++)
    {
        $iv .= chr(rand(1, 255));
    }
    return $iv;
}

function enc($data)
{
    $iv = getIV();
    $c = openssl_encrypt((string)$data, method, key, OPENSSL_RAW_DATA, $iv);
    return bin2hex($iv . $c);
}

function dec($data)
{
    $data = hex2bin($data);
    if ($iv = substr($data, 0, 16))
    {
        if ($c = substr($data, 16))
        {
            if ($m = openssl_decrypt((string)$c, method, key, OPENSSL_RAW_DATA, $iv))
            {
                return $m;
            }
        }
    }
}

if ($_GET['m']) 
{
    echo enc($_GET['m']) . "<br/>"; 
}
if ($_GET['c']) 
{
    echo dec($_GET['c']) . "<br/>"; 
}

测试数据:m=1234 目标:解密结果为1235

# -*- coding:utf-8 -*-
from ctfutils import *

#m = 1234
c = "eac88303c5f788c4988a71bbbec2fd6e6911740587a51db4335731d45977a45c"

strc = hex2str(c)
iv = strc[:16] #提取IV
c = strc[16:]

#IV ^ DC = 4 也就是说 IV ^ 4 = DC 我们的目的是 DC ^ IV' = 5 
#将上面的式子整合就能得到 IV' = 5 ^ DC = 5 ^ IV ^ 4
iv = list(iv)
iv[3] = chr(ord(iv[3]) ^ 4 ^ 5) 
iv = ''.join(iv)

print str2hex(iv + c)
#new c=eac88302c5f788c4988a71bbbec2fd6e6911740587a51db4335731d45977a45c

mark

mark

Padding Oracle Attack

原理

Padding Oracle Attack 其实就是针对填充方式的一种攻击手段 引用维基百科的话

In cryptography, a padding oracle attack is an attack which is performed using the padding of a cryptographic message. In cryptography, variable-length plaintext messages often have to be padded (expanded) to be compatible with the underlying cryptographic primitive. The attack relies on having a "padding oracle" who freely responds to queries about whether a message is correctly padded or not. Padding oracle attacks are mostly associated with CBC mode decryption used within block ciphers.

mark

Padding Oracle Attack最关键的就是填充。如果最后的Padding不正确(值和数量不一致),则解密程序往往会抛出异常(Padding Error)。而利用应用的错误回显,我们就可以判断出Paddig是否正确。在Padding Oracle Attack攻击中,攻击者输入的参数是IV+Cipher,我们要通过对IV的”穷举”来请求服务器端对我们指定的Cipher进行解密,并对返回的结果进行判断。

前提条件

攻击者能够获得密文(Ciphertext),以及附带在密文前面的IV(初始化向量)。
攻击者能够触发密文的解密过程,且能够知道密文的解密结果。

例子

测试代码需要稍微修改一下 主要是为了方便测试着想

<?php
error_reporting(0);

define("key", "dfhehrhrh3429hrtg934");
define("method", "aes-128-cbc");

function enc($data)
{
    $iv = "0123456789abcedf"; //为了方便直接写死IV的值
    $c = openssl_encrypt((string)$data, method, key, OPENSSL_RAW_DATA, $iv);
    return bin2hex($iv . $c);
}

function dec($data)
{
    $data = hex2bin($data);
    if ($iv = substr($data, 0, 16))
    {
        if ($c = substr($data, 16))
        {
            if ($m = openssl_decrypt((string)$c, method, key, OPENSSL_RAW_DATA, $iv))
            {
                return $m;
            }
            else
            {
                return "dec error";
            }
        }
    }
}

echo enc('1') . "<br/>";
if ($_GET['c']) 
{
    echo dec($_GET['c']) . "<br/>"; 
}

爆破脚本 总感觉有点问题

# -*- coding:utf-8 -*-
from ctfutils import *
import requests

url = "http://localhost:8080/test.php?c="

#m=1 
c = "30313233343536373839616263656466fba16d7d0c6e62d5d9b78fb9c5f07f5d"
iv = ''
mid = []
k = 15

while k != -1:
    for i in range(0, 256):
        data = '0' * (k + 1) * 2 + str(iv) + c[32:]
        data = list(hex2str(data))
        data[k] = chr(i)
        data = ''.join(data)

        re = requests.get(url + str2hex(data))
        if "dec error" not in re.content:
            mid.insert(0, i ^ (16 - k))
            iv = ''
            for j in mid:
                if  k == 1:
                    tmp = 15
                else:
                    tmp = 17 - k
                if len(hex(int(j) ^ tmp)[2:]) == 1:
                    iv += '0' + hex(int(j) ^ tmp)[2:]
                else:
                    iv += hex(int(j) ^ tmp)[2:]
            print mid, iv
            k = k - 1
            break
Tags: Crypto
Archives QR Code
QR Code for this page
Tipping QR Code
Leave a Comment