title: 分组密码CBC加密缺陷

date: 2017-05-15 10:04:47

tags: ["密码学"]

关于密码学的种种漏洞以及利用网上也有不少,但是比较零散,有关介绍比较局限,导致一些东西晦涩难懂不易理解,这里是一个有关于CBC分组加密的一个讲解

CBC加密模式

首先上图

这里文字描述不如看图直观,还是大致描述一下,CBC模式的加密方式是通过一个初始向量(IV)先和明文分组第一组异或后使用秘钥K加密,作为第一组密文,同时又与后一分组的明文异或后进行加密产生下一组密文,依次重复。

其解密和加密是对称的,密文先解密,再异或。

关于这个初始向量IV的完整性要比其保密性更为重要。在CBC模式下,最好是每发一个消息,都改变IV,比如将其值加一。

这里说说有关于CBC的错误传播,有利于之后字节翻转攻击的理解

其特点如下:

  1. 明文有一组中有错,会使以后的密文组都受影响,但经解密后的恢复结果,除原有误的一组外,其后各组明文都正确地恢复。
  2. 解密时,有一组秘钥错误,该错误会影响下一分组相同位置的解密
  3. 若在传送过程中,某组密文组出错时,则该组恢复的明文和下一组恢复数据出错。再后面的组将不会受中错误比特的影响。

字节翻转攻击

概述

有关于这里的攻击,没有下面那种方式刺激,但是效果也还是可以

这里最大的效果就是:在不知道Key(秘钥)的情况下篡改明文

通过上面的错误传播,我们可以想到,解密时通过修改前一个密文分组可以影响后一个的解密后的明文分组

这里修改可以将前一个密文中的任意比特进行修改(0,1进行翻转)

详解

这里举个例子:

明文是"lee-jayy:12345678$,ohh he is very rich!"

这里使用DES算法,秘钥key = "leejleej" 初始向量iv='thisisiv'

加密结果为:04f2e7d245cec18f6c1769c66f0e30ccc30a378c58597b15409a0a8c296df6a66a10679b55ddbabb

第一步将消息分组,其des算法的分组长度是64bit,一个字符是8bit故8个字符一组,如下

第一组:lee-jayy

第二组::1234567

第三组:8$,ohh h

第四组: eis ver

第五组: y rich!

这里我想把第二组的第2个字符“1”改为“9”,这里该如何操作?

由上面的特点我们知道,修改第n分组的秘文,其第n+1分组的明文会被窜改,这里我们待修改的字符在第二分组,我们可以修改第一分组的密文来控制第二分组。

为了方便我把密文也分组一下:

04f2e7d245cec18f

6c1769c66f0e30cc

c30a378c58597b15

409a0a8c296df6a6

6a10679b55ddbabb

由于这里需要修改的字符“1”位于第二分组的第二个字符,所以我们只需修改第一分组相同的位置的密文

,一个字符是16bit两个十六进制位,故我们需要修改第一分组密文的“f2”,这里改如何改呢?

在改之前我们先了解一个基本知识,有关于异或:

异或的规则是相同为0,不同为1

于是有1 xor 1 =0,1 xor 0 =1,0 xor 0 = 0

可以看出这么一个规则,A xor B = C <=> A xor C = B

这时候可以回看一下上面的解密的图

我们知道的东西有:

1、第二组des算法加密后要和第一组的密文异或得到第二组的密文

2、字符1的ascii码16进制是31

3、字符1经过cbc后的密文要和f2进行异或

这里我们并不晓得其秘钥key,这里我们假设第二组des算法加密后16进制是ABCDEFGHIGKLMNOP,故字符1经过DES加密的结果是AB

第一步、我们根据上面的算法知道 AB xor f2 = 31,这里04是字符1的密文,我们需要解出AB,只需要f2 xor 31既可得出1经过des算法加密后是C3

第二步、我们利用上一步计算出的DES对字符1加密的结果35异或待修改的字符“9”(其ascii码为39)。

C3 xor 39 = FA

第三步、修改密文中第一分组开始的f2为FA,修改后的密文是:

04FAe7d245cec18f6c1769c66f0e30ccc30a378c58597b15409a0a8c296df6a66a10679b55ddbabb

可以对比一下输出:

修改前:lee-jayy:12345678$,ohh he is very rich!

修改后:4糉L柖292345678$,ohh he is very rich!

看出对应的1的确变为了9

利用同样方法我把1234567修改为了9999999,秘文为04FAECD848C2CE816c1769c66f0e30ccc30a378c58597b15409a0a8c296df6a66a10679b55ddbabb

结果:d&φΤ5ς:99999998$,ohh he is very rich!

加密脚笨如下,有兴趣可以自己试一试:

<?php

function jiami_DES($input = "",$key = "leejleej",$iv='thisisiv')
{
$td = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($td, $key, $iv); $encrypted_data = mcrypt_generic($td, $input); mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return bin2hex($encrypted_data);
} function jiemi_DES($input = "",$key = "leejleej",$iv='thisisiv')
{
$td = mcrypt_module_open(MCRYPT_DES, '', MCRYPT_MODE_CBC, '');
mcrypt_generic_init($td, $key, $iv);
$mdecrypted_data = mdecrypt_generic($td,hex2bin($input));//$encrypted_data);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $mdecrypted_data;
} echo jiami_DES('lee-jayy:12345678$,ohh he is very rich!');
echo '<br />';
echo jiemi_DES($_GET['key']);
?>

CTF实例

from twisted.internet import reactor, protocol
from Crypto.Cipher import AES
import os
import random
from secret import KEY,KEYSIZE,IV,FLAG PORT = 6666 def pad(instr, length):
if(length == None):
print "Supply a length to pad to"
elif(len(instr) % length == 0):
print "No Padding Needed"
return instr
else:
return instr + '\x04' * (length - (len(instr) % length )) def encrypt_block(key, plaintext):
encobj = AES.new(key, AES.MODE_ECB)
return encobj.encrypt(plaintext).encode('hex') def decrypt_block(key, ctxt):
decobj = AES.new(key, AES.MODE_ECB)
return decobj.decrypt(ctxt).encode('hex') def xor_block(first,second):
if(len(first) != len(second)):
print "Blocks need to be the same length!"
return -1 first = list(first)
second = list(second)
for i in range(0,len(first)):
first[i] = chr(ord(first[i]) ^ ord(second[i]))
return ''.join(first) def encrypt_cbc(key,IV, plaintext):
if(len(plaintext) % len(key) != 0): #加密文本的长度必须能整除Key,否则在后面加x40
plaintext = pad(plaintext,len(key))
blocks = [plaintext[x:x+len(key)] for x in range(0,len(plaintext),len(key))]
for i in range(0,len(blocks)):
if (i == 0):
ctxt = xor_block(blocks[i],IV)
ctxt = encrypt_block(key,ctxt)
else:
tmp = xor_block(blocks[i],ctxt[-1 * (len(key) * 2):].decode('hex'))
ctxt = ctxt + encrypt_block(key,tmp)
return ctxt def decrypt_cbc(key,IV,ctxt):
ctxt = ctxt.decode('hex')
if(len(ctxt) % len(key) != 0):
print "Invalid Key."
return -1
blocks = [ctxt[x:x+len(key)] for x in range(0,len(ctxt),len(key))]
for i in range(0,len(blocks)):
#print blocks[0].encode('hex')
if (i == 0):
ptxt = decrypt_block(key,blocks[i])
ptxt = xor_block(ptxt.decode('hex'),IV)
#print ptxt.encode('hex')
else:
tmp = decrypt_block(key,blocks[i])
tmp = xor_block(tmp.decode('hex'),blocks[i-1])
ptxt = ptxt + tmp
return ptxt def mkprofile(email):
if((";" in email)):
return -1
prefix = "comment1=wowsuch%20CBC;userdata="
suffix = ";coment2=%20suchsafe%20very%20encryptwowww" ptxt = prefix + email + suffix #连接字符串
print ptxt
return encrypt_cbc(KEY,IV,ptxt) def parse_profile(data):
print "DATA:"
print data
ptxt = decrypt_cbc(KEY,IV,data.encode('hex'))
ptxt = ptxt.replace("\x04","")
print ptxt
if ";admin=true" in ptxt:
return 1
return 0 class MyServer(protocol.Protocol):
def dataReceived(self,data):
if(len(data) > 512):
self.transport.write("Data too long.\n")
self.transport.loseConnection()
return if(data.startswith("mkprof:")):
data = data[7:]
resp = mkprofile(data)
if (resp == -1):
self.transport.write("No Cheating!\n")
else:
self.transport.write(resp + '\n')
elif(data.startswith("parse:")):
self.transport.write("Parsing Profile...")
data = data[6:].decode('hex')
if (len(data) % KEYSIZE != 0):
self.transport.write("Invalid Ciphertext <length>\n")
self.transport.loseConnection()
return if(parse_profile(data) == 1):
self.transport.write("Congratulations!\nThe FLAG is: ")
self.transport.write(FLAG)
self.transport.loseConnection() else:
self.transport.write("You are a normal user.\n") else:
self.transport.write("Syntax Error")
self.transport.loseConnection() class MyServerFactory(protocol.Factory):
protocol = MyServer factory = MyServerFactory()
reactor.listenTCP(PORT, factory)
reactor.run()

wp:

from pwn import *
sh = remote('133.130.52.128',6666)
target = ";admin=true"
email = '0000000000000000000000'
prefix = "comment1=wowsuch%20CBC;userdata="
suffix = ";coment2=%20suchsafe%20very%20encryptwowww"
ptxt = prefix + email + suffix sh.send('mkprof:' + email)
s = sh.recv(1024)[0:len(ptxt)*2]
s = list(s.decode('hex'))
for i in range(len(target)):
s[32+i] = p8( u8(s[32+i]) ^ u8(target[i]) ^ u8(ptxt[48+i]) )
s = ''.join(s).encode('hex')
sh.send('parse:' + s)
print sh.recv(1024)

Padding Oracle Attack

概述

有关于这种方式的攻击,实在是巧妙!

首先说一下使用条件,以及可以达到的效果。

使用条件:

一、我们可以修改iv(在我的理解其实这里如果数据分组大于1组其实不需要iv也可以进行攻击)

二、我们知道密文

三、我们可以利用服务端进行解密

效果:获取明文!(如果分组大于1组,没有iv的情况这里可以获取到部分明文)

详解

首先介绍一下对于分组加密过程中数据填充常用的方式(PKCS#5)

这种方式其实就是缺少多少字节就补充多少字节,其补充的数值就是填充的字节的数量比如数据差2字节,我们就补充两个0x02即可,就是差几个补几个几,如下图。

下面我们回顾一下关于cbc模式的解密方式

这里对每一分组进行一下细分,

可以看出末尾的数值为四个0×04,即为填充,这里如果填充的数值不对或者没有进行填充,解密过程往往不会进行,同时程序会报异常。

有了这个特性,我们就有了利用点,在这一点有些类似于在SQL注入中的盲注的思想。

下面具体分析一下:

在sql注入中,盲注我们通常是只有两种状态,truefalse

这里又是如何区分的呢?这里先具体看看具体原理分析

之前说到的使用条件是,我们可以修改iv、知道密文同时我们可以利用服务端进行解密

这里构造一个例子,如果服务端如果给用户设置了这样的cookie:key=6d367076036e2239|f851d6cc68fc9537

我们推测出‘|’之前的是iv,其后面的是加密结果,这个时候如果这里我将iv设置为0000000000000000

即为key=0000000000000000|f851d6cc68fc9537,发现这个时候页面出错了

看一下这样子的解密过程

这里之前提到的有关填充,可以知道不管消息多长,最后肯定是有填充的,其中填充数值范围和分组的大小有关,这里如果是DES算法8字节一组的话那么最后一位的范围一定是在0x01~0x08之间的,这里0x3D不是这个范围故出错。

这时候使程序不出错只有最后一位是0x01才可以

我们开始修正iv,发现当iv为000000000000003C的时候程序没有报错了,这个时候分析一下解密过程

这个这个时候0x3D异或0x3C为0x01,程序以为这里的填充位为一位,之前的全是数据,没毛病,开始解密

我们在这里0x3c可以穷举得出,中间值0x3d可以利用0x01异或0x3c计算出,同时我们还知道一个原始的iv=6d367076036e2239。

由于明文 = 中间值 xor IV,我们列两个式子

0x01 = 中间值 xor 0x3c

待求明文 = 中间值 xor 0x39

这里一个二元一次方程,不难解出待求明文。

这时候继续向前一位进行攻击,这时候需要后两位为0x02

第八位已经计算出,这里只需要重复之前的方法计算第七位即可

最开始看到这里会觉得有些疑问,为什么我们不能直接多位一起计算,这里可能会出现这么一个情况,假设最后一位是一个合理的填充(假设八字节分组0x01~0x08)第七位是个错误的值时候,就会出现非预期的错误,比如中间值的后两位为0x00和0x02,我们构造的初始向量的后两位为0x02和0x00,这时候程序并不会报错。

以此类推我们便可以获取到明文。

如果这里我们并不知道初始iv,在这里我们依然可以进行攻击,只不过第一组的数据无法获取到

仔细回顾一下上述的加密方式,我们的初始iv只是加密第一组数据中用得到,加密第二组数据的时候其实就是用的第一组的密文充当iv的角色继续加密,我们这里只需要不断修改分组上一分组的密文即可得到下一组的明文。

实例

有关的例子:

<?php
$type = "aes-128-cbc"; //加密类型,即分组大小为16
$P = "aaaaaaa"; //明文
$Key = "aAbBcCdDeE"; //加密要用到的Key
$IV = "thisivthisivthis"; //初始化向量,因为有一个异或的过程,所以它的大小和分组大小要一样
$C = openssl_encrypt($P, $type, $Key, OPENSSL_RAW_DATA, $IV);
//满足padding oracle attack前提条件1
print "iv: ".bin2hex($IV)."<br>";
print "c: ".bin2hex($C)."<br>"; //可能存在不可显示的字符,加个base64的编码 if(isset($_GET['s']) && isset($_GET['iv'])){
$s = hex2bin($_GET['s']);
$iv = hex2bin($_GET['iv']);
if(($n = openssl_decrypt($s, $type, $Key, OPENSSL_RAW_DATA, $iv)) !== false){ //解密失败会返回false
//bit flipping attack
echo $n;
if($n === "admin"){
print "well done!";
}
}else{
//满足padding oracle attack前提条件2
die("Fail!");
}
}
?>

脚本:

import requests
import base64
url = 'http://192.168.248.1/test/demo.php'
N = 16
l = [0] * N
iv = '74686973697674686973697674686973'.decode('hex')
tmp_iv = ''
out = [0] * N
s = ''
for i in range(1, N+1):
for c in range(0,256):
l[N-i] = c
tmp_iv = ''
for m in l:
tmp_iv += chr(m)
print tmp_iv.encode('hex')
payload = "?s=e211ffa0baa91627a5827f3867a0cff1&iv=" + tmp_iv.encode('hex')
#print payload
data = requests.get(url+payload).content
if 'Fail!' not in data:
out[N-i] = c ^ i
for y in range(i):
l[N-y-1] = out[N-y-1] ^ (i+1)
break
for i in range(N):
out[i] = out[i] ^ ord(iv[i])
for c in out:
s += chr(c)
print s

python有一个关于此方式利用的库,项目地址

参考

http://k1n9.me/2017/03/16/attack-in-cbc/

分组密码CBC加密缺陷的更多相关文章

  1. AES加密解密 助手类 CBC加密模式

    "; string result1 = AESHelper.AesEncrypt(str); string result2 = AESHelper.AesDecrypt(result1); ...

  2. CBC加密原理及攻击

    原理基于分组加密加密过程 Plaintext:明文,待加密的数据.IV:用于随机化加密的比特块,保证即使对相同明文多次加密,也可以得到不同的密文,初始向量,用来与第一块的明文异或运算.Key:被一些如 ...

  3. 在java项目中使用AES256 CBC加密

    首先要注意一点,默认的JDK是不支持256位加密的,需要到Oracle官网下载加密增强文件(Java Cryptography Extension (JCE) Unlimited Strength J ...

  4. Aes CBC加密

    <?php namespace app\components; use yii; class Aes { /** * This was AES-128 / CBC / PKCS5Padding ...

  5. JAVA AES CBC 加密 解密

    AES 256 , KEY 的长度为 32字节(32*8=256bit). AES 128 , KEY 的长度为 16字节(16*8=128bit) CBC 模式需要IV, IV的值是固定写死,还是当 ...

  6. 【java工具】AES CBC加密

    一.定义 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先 ...

  7. 各加密模式的演示(ECB,CBC)

    对于较长的明文进行加密需要进行分块加密,但是直接加密(ecb)不容易隐藏模式,用OpenCV写了个程序论证了一下 ECB 优点就是简单,可以并行计算,不会迭代误差 缺点就是隐藏不了模式 CBC 需要初 ...

  8. aes 128、192、256位,cbc、cfb、ecb、ofb、pcbc加密解密

    AES加解密总共有以下这些 算法/模式/填充 字节加密后数据长度 不满16字节加密后长度 AES/CBC/NoPadding 16 不支持 AES/CBC/PKCS5Padding 32 16 AES ...

  9. C++调用openssl实现DES加密解密cbc模式 zeropadding填充方式 pkcs5padding填充方式 pkcs7padding填充方式

    ============================================== des   cbc  加密 zeropadding填充方式 ======================= ...

随机推荐

  1. 用VSCode插件来一键填满Github的绿色格子吧-AutoCommit

    autoCommit 一个用于Git自动commit的VSCode插件,它可以用来补充之前忘记提交commit,帮助你把首页的绿色格子填满. 使用效果 使用本插件来控制commit次数. 如下图,你甚 ...

  2. Windows系统下批处理快速创建WIFI

    为什么要用cmd这种古老的东西创建wifi呢,电脑管家.360安全卫士都有这种插件,一键开启关闭,多方便啊! 开始用的也是电脑管家的免费wifi插件,但是我越来越不能忍它极慢的启动关闭过程,每一次看着 ...

  3. 【GeneXus】开发移动APP时,如何使用Canvas进行布局?

    当我们开发移动端APP的时候,经常遇到一种布局方式,那就是层级的布局,比如:一张照片我想在照片的上面显示它的名称,但不影响我照片展示的布局大小,也就是这个名称是浮在照片上的,如图: 如果要实现这样的布 ...

  4. TensorFlow——MNIST手写数据集

    MNIST数据集介绍 MNIST数据集中包含了各种各样的手写数字图片,数据集的官网是:http://yann.lecun.com/exdb/mnist/index.html,我们可以从这里下载数据集. ...

  5. 【一起学源码-微服务】Hystrix 源码二:Hystrix核心流程:Hystix非降级逻辑流程梳理

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一讲我们讲了配置了feign.hystrix.enabl ...

  6. C# 中 ConfigureAwait 相关答疑FAQ

    C# 中 ConfigureAwait 相关答疑FAQ 在前段时间经常看到园子里有一些文章讨论到 ConfigureAwait,刚好今天在微软官方博客看到了 Stephen Toub 前不久的一篇答疑 ...

  7. 使用Robot Framework框架远程操作UNIX系统

    bot Framework是一个强大的自动化测试框架,依靠社区力量编写的Test Library为它提供了非常强的扩展性.下面我将介绍的就是如何使用第三方提供的扩展测试库(Test Library)来 ...

  8. C语言进阶——全局变量

    全局变量 ·定义在函数外面的变量是全局变量 ·全局变量具有全局的生存期和作用域 ·它们与任何函数都无关 ·在任何函数内部都可以使用它们 全局变量初始化 ·没有做初始化的全局变量会得到0值 ·指针会得到 ...

  9. Photoshop 2020安装教程

    首先来看看新增功能 [Photoshop 2020新增功能] 1.ipad和云文档 2.预设改进 3.新对象选择工具 4.转换行为一致 5.改进的属性面板 6.智能对到图层 7.增强的转换变形. 需要 ...

  10. 对于Makefile的基本使用

    上课不听讲的后果就是课下疯狂补知识了 原文来自https://www.cnblogs.com/chenguanfu/p/4415072.html 在Windows下,只需要简单的点击以下make,re ...