以前一直没时间来好好研究下这两种攻击方式,虽然都是很老的点了= =!

0x01:Padding oracle

CBC加密模式为分组加密,初始时有初始向量,密钥,以及明文,明文与初始向量异或以后得到中间明文,然后其再和密钥进行加密将得到密文,得到的密文将作为下一个分组的初始向量,与下一个分组的明文进行异或得到的二组的中间明文,依次类推。

解密时根据也是分组解密,首先使用密钥解密密文,得到中间明文,然后将中间明文和初始向量异或以后得到明文,第一组的密文将和第二组的明文进行异或得到第二组的中间明文,然后再用密钥进行解密得到第二组的明文,依次类推

攻击流程:

明文填充:

分组密码Block Cipher需要在加载前确保每个每组的长度都是分组长度的整数倍。一般情况下,明文的最后一个分组很有可能会出现长度不足分组的长度:

这个时候,普遍的做法是在最后一个分组后填充一个固定的值,这个值的大小为填充的字节总数。即假如最后还差3个字符,则填充3个0×03

因为填充发生在最后一个分组,所以我们主要关注最后一个分组

这里有个条件是服务器会对我们显示padding error的异常,如果不回显那么肯定无法判断进行利用

比如在web应用中,如果Padding不正确,则应用程序很可能会返回500的错误(程序执行错误);如果Padding正确,但解密出来的内容不正确,则可能会返回200的自定义错误(这只是业务上的规定),所以,这种区别就可以成为一个二值逻辑的”注入点”。

攻击成立的两个重要假设前提:

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

我们的攻击流程实际上是不断地调整IV的值,以希望解密后,最后一个字节的值为正确的Padding Byte,因为padding正确时,这里padding正确是指最终解密并异或出来的明文最后一个字节在正确padding的范围内就是正确的,虽然最后明文不一定正确,但是padding是合法的,所以服务器才会返回200

此时若我们输入的初始向量为:

这时候最后一组密文经过密钥解密后再和我们输入的初始向量异或以后将得到

最后一位是0x3d,明显不满足padding的范围,所以肯定会返回500,那么此时假设padding为0x01,那么通过遍历初始向量最后一位将存在唯一一个初始向量值将于服务端解密得到的中间值异或以后得到0x01,直接遍历

IV值就可以得到该值,之后我们就可以利用以下的公式

因此可以求出明文第八个字节,之后我们需要继续求出其第七个字节的明文值,那么此时假设填充了两个字节,那么为0x02,0x02,此时我们需要更新最后一位要输入的IV值为中间值第八位异或上0x02(第八位中间值根据明文第八位异或上原来的IV值第八位即可得到),因为此时我们便利的后两位IV值,此时服务器期望得到是0x02

此时继续遍历第七位IV值,直到得到0x02,此时可以得到明文第七位,依次类推可以得到所有的明文。

0x02: CBC字节翻转攻击

对于解密过程而言,我们已经可以通过Padding Oracle攻击获取CBC模式加密的明文,此时我们可以通过CBC字节翻转攻击来实现伪造的明文,因为IV是我们可控的,我们可以控制IV,使其与中间明文异或后得到我们任意想要的明文

加入我们已知明文解密后为1dmin,我们想构造一个初始IV,使其解密成admin,因为有以下的逻辑:

而我们想要:

所以我们可以得到

而原来的中间明文可以通过以下方式得到,原来的明文第一位又是可以通过Padding Oracle攻击得到的

所以够在的IV第一位即为:

通过上面的式子,通过遍历明文,我们就可以让服务器端解密出我们想要的明文

CTF题目举例:

服务端校验身份标志为$id,所以可以利用padding oracle攻击去得到这个值的明文,得到这个值后,再利用cbc翻转攻击,将这个plain伪造成我们需要的admin

以实验吧的一道CBC翻转的题目为例:

源码:

<?php
define("SECRET_KEY", '***********');
define("METHOD", "aes-128-cbc");
error_reporting(0);
include('conn.php');
function sqliCheck($str){
if(preg_match("/\\\|,|-|#|=|~|union|like|procedure/i",$str)){
return 1;
}
return 0;
}
function get_random_iv(){
$random_iv='';
for($i=0;$i<16;$i++){
$random_iv.=chr(rand(1,255));
}
return $random_iv;
}
function login($info){
$iv = get_random_iv();
$plain = serialize($info);
$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
setcookie("iv", base64_encode($iv));
setcookie("cipher", base64_encode($cipher));
}
function show_homepage(){
global $link;
if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
$cipher = base64_decode($_COOKIE['cipher']);
$iv = base64_decode($_COOKIE["iv"]);
if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
$info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
$sql="select * from users limit ".$info['id'].",0";
$result=mysqli_query($link,$sql); if(mysqli_num_rows($result)>0 or die(mysqli_error($link))){
$rows=mysqli_fetch_array($result);
echo '<h1><center>Hello!'.$rows['username'].'</center></h1>';
}
else{
echo '<h1><center>Hello!</center></h1>';
}
}else{
die("ERROR!");
}
}
}
if(isset($_POST['id'])){
$id = (string)$_POST['id'];
if(sqliCheck($id))
die("<h1 style='color:red'><center>sql inject detected!</center></h1>");
$info = array('id'=>$id);
login($info);
echo '<h1><center>Hello!</center></h1>';
}else{
if(isset($_COOKIE["iv"])&&isset($_COOKIE['cipher'])){
show_homepage();
}else{
echo '<body class="login-body" style="margin:0 auto">
<div id="wrapper" style="margin:0 auto;width:800px;">
<form name="login-form" class="login-form" action="" method="post">
<div class="header">
<h1>Login Form</h1>
<span>input id to login</span>
</div>
<div class="content">
<input name="id" type="text" class="input id" value="id" onfocus="this.value=\'\'" />
</div>
<div class="footer">
<p><input type="submit" name="submit" value="Login" class="button" /></p>
</div>
</form>
</div>
</body>';
}
}

首先将$_POST中的id参数传入login函数进行加密,iv是随机的16进制字符串,明文是序列化后的$info变量,其中$info变量是包含$id参数的数组,

然后使用CBC模式对其进行加密,然后将IV值和密文base64编码以后返回给客户端,如果没有post过去id参数,将调用show_homepage函数,此时将密文进行解密并反序列化后传递给$info,这里要通过sql注入查出数据才行,但是$id处有waf,所以必须通过构造IV来使解密出的明文中出现注入payload来拼接进sql语句,正常的id参数查不出数据是因为那里是limit id,0,零条数据,所以只需要最后的payload为1#注释掉即可

所以可以首先post一个id=10参数

将得到

iv=PxuF5ruZTSyyT%2FgbLaLtAQ%3D%3D
cipher=j3UwMobjznjdxF6BMMDEcMMROOqlCBWzCt6I5Wewru8%3D

此时就可以针对此回显的IV值进行攻击,来构造新的IV值,首先我们要构造出进入加密函数的明文:

$id = (string)$_POST['id'];
$info = array('id'=>$id);
function login($info){
$iv = get_random_iv();
$plain = serialize($info);
$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
setcookie("iv", base64_encode($iv));
setcookie("cipher", base64_encode($cipher));
}

所以我们可以构造出id的序列化数据:

a:1:{s:2:"id";s:2:"10";}

因为IV为16个字节,因此我们将明文以16字节为一组进行分组

a:1:{s:2:"id";s:  //第一组
2:"10";} //第二组

我们需要更改的是第二组的第五位字符,即将0替换成#,所以首先应该更改第一组密文的对应位,将得到新的密文

可以利用公式1:

然后将得到的密文和之前的原始IV一起发送,此时因为更改了第一组密文的一位,将导致第一组密文解密后无法反序列化,因为此时解密出来的明文也随之发生了变化,因此我们需要更改IV来使其不变,所以遍历第一组

密文,然后根据公示1来进行构造,其中明文就是服务器端解密返回并输出的

根据自己的理解写的exp:

#coding:utf-8
import base64
import urllib
iv="MhmPZk6p8ZbW0MipFeIwlA%3D%3D"
iv=urllib.unquote(iv)
iv=base64.b64decode(iv)
offset = 4
ciper="gcrKiWF6uRNNuYRjM%2FJPYHGoPTPZ1OpOajvZ6UfVMvY%3D"
ciper = urllib.unquote(ciper)
ciper = base64.b64decode(ciper)
block_1 = 'a:1:{s:2:"id";s:'
block_2 = '2:"10";}'
new_block_offet_4 = chr(ord(ciper[4]) ^ ord(block_2[4]) ^ ord("#"))
new_ciper = ciper[:4]+new_block_offet_4+ciper[5:]
new_ciper = urllib.quote(base64.b64encode(new_ciper))
print new_ciper plain="qvttg/CIu9gp3DGoR+mCETI6IjEjIjt9"
plain=base64.b64decode(plain)
new_iv = ""
for i in range(16):
new_iv = new_iv + chr(ord(plain[i]) ^ ord(block_1[i]) ^ ord(iv[i]))
new_iv= urllib.quote(base64.b64encode(new_iv))
print new_iv

最后放一个LCTF2017上一个Padding oracle+CBC 字节翻转的脚本,上面有自己的注解:

# -*- coding: utf-8 -*-
import requests
import base64
url = 'http://127.0.0.1/cbc.php'
N = 16 def inject_token(token):
header = {"Cookie": "PHPSESSID=" + phpsession + ";token=" + token}
result = requests.post(url, headers = header)
return result def xor(a, b):
return "".join([chr(ord(a[i]) ^ ord(b[i % len(b)])) for i in xrange(len(a))]) def pad(string, N):
l = len(string)
if l != N:
return string + chr(N - l) * (N - l) def padding_oracle(N):
get = ""
for i in xrange(1, N+1):
for j in xrange(0, 256):
padding = xor(get, chr(i) * (i-1))#此时更新padding的值,更新要发送的对应位置的明文位所对应的IV值
c = chr(0) * (16-i) + chr(j) + padding #chr(j)就是每次要新尝试的填充值
result = inject_token(base64.b64encode(c))
if "Error!" not in result.content: #如果没有padding错误,padding错误将返回"Error"
get = chr(j ^ i) + get #包含所有中间明文,每次得到一位得到中间明文,此时得到的IV值(即c)和中间明文异或以后满足padding,两个值异或后再和原来的iv进行异或即可得到
#对应位置明文
break
return get while 1:
session = requests.get(url).headers['Set-Cookie'].split(',')
phpsession = session[0].split(";")[0][10:]
print phpsession
token = session[1][6:].replace("%3D", '=').replace("%2F", '/').replace("%2B", '+').decode('base64')
middle1 = padding_oracle(N)
print "\n"
if(len(middle1) + 1 == 16):
for i in xrange(0, 256):
#因为后十五位都可以通过Padding Oracle Attack正常的解出,
#但是在解第一位时按逻辑应该解出全为padding的plaintext(在这个环境下也就是16个0x10),即解密的结果为NULL
middle = chr(i) + middle1
print "token:" + token
print "middle:" + middle
plaintext = xor(middle,token)
print "plaintext:" + plaintext
des = pad('admin', N)
tmp = ""
print des.encode("base64")
for i in xrange(16):
tmp += chr(ord(token[i]) ^ ord(plaintext[i]) ^ ord(des[i]))
print tmp.encode('base64')
result = inject_token(base64.b64encode(tmp))
if "You are admin!" in result.content:
print result.content
print "success"
exit()

参考:

https://skysec.top/2017/12/13/padding-oracle%E5%92%8Ccbc%E7%BF%BB%E8%BD%AC%E6%94%BB%E5%87%BB/

http://www.freebuf.com/articles/web/15504.html

												

Padding Oracle 和 CBC字节翻转攻击学习的更多相关文章

  1. CBC字节翻转攻击

    iscc2018线上赛开始两周多了,学到了很多,写几篇文章总结一下遇到的知识点,做一个归纳,方便以后查找. web300-----CBC字节翻转攻击 cbc是AES加密的cbc模式 即密码分组链模式: ...

  2. CBC 字节反转攻击

    一.CBC 简介 现代密码体制 现代密码中的加密体制一般分为对称加密体制(Symmetric Key Encryption)和非对称加密体制(Asymmetric Key Encryption).对称 ...

  3. 实验吧之【简单的登录题(】CBC字节反转攻击)

    开始刷ctf题吧  慢慢来. 实验吧---简单的登录题 题目地址:http://ctf5.shiyanbar.com/web/jiandan/index.php 随便提交一个id,看到后台set了两个 ...

  4. 关于AES-CBC模式字节翻转攻击(python3)

    # coding:utf-8 from Crypto.Cipher import AES import base64 def encrypt(iv, plaintext): if len(plaint ...

  5. session安全&&CBC字符反转攻击&&hash拓展攻击

    session安全 p神写的: 在传统PHP开发中,$_SESSION变量的内容默认会被保存在服务端的一个文件中,通过一个叫"PHPSESSID"的Cookie来区分用户.这类se ...

  6. Padding Oracle攻击

    最近在复现LCTF2017的一道题目,里面有一个padding oracle攻击,也算是CBC翻转攻击,这个攻击主要针对CBC加密模式的 网上有关这个攻击的博客文章很多,但是其中有一些细节可能是个人的 ...

  7. padding Oracle attack(填充Oracle攻击)

    最近学习到一种老式的漏洞,一种基于填充字节的漏洞.就想记录下来,早在2010年的blackhat大会上,就介绍了padding Oracle漏洞,并公布了ASP.NET存在该漏洞.2011年又被评选为 ...

  8. 于bugku中游荡意外得到关于CBC翻转攻击思路

    个人简介:渣渣一枚,萌新一个,会划水,会喊六六今天在bugku遇到关于CBC翻转攻击的题目,总结了一下关于CBC翻转攻击的原理,以及关于这道题目的解题思路个人博客:https://www.cnblog ...

  9. Padding Oracle Attack的一些细节与实现

    Padding Oracle Attack还是颇具威力的,ASP.NET的Padding Oracle Attack被Pwnie评为2010年最佳服务端漏洞之一.还是看 Juliano Rizzo a ...

随机推荐

  1. NSIS逻辑函数头文件介绍

    !include "LogicLib.nsh"使用 NSIS 的宏来提供各种逻辑基本语句,不需要预先添加函数. 基本语句 If|Unless..{ElseIf|ElseUnless ...

  2. linux mint ubuntu 安装virtualbox

    安装虚拟机:virtualbox 1.打开终端而且切换到root帐号,然后输入安装命令: apt-get install virtualbox 2.安装推荐的软件包:(必须安装这个包.不然看不到应用程 ...

  3. cpu 100%怎样定位

    先用top定位最耗cpu的java进程 例如: 12430工具:top或者 htop(高级)方法:top -c 显示进程运行详细列表键入 P (大写P),按照cpu进行排序 然后用top -p 124 ...

  4. 记一次被自己DDOS攻击

    服务器报警初步分析进一步分析最终分析总结 TOC 服务器报警 7月24号下午5点半开始,突然服务器报警,检查监控,发现CPU异常100%. 该服务器正常情况下CPU使用率在40%已经算高了,另外负载经 ...

  5. window, linux, mac 比较文件和文件夹的区别

    windows 端 winmerge beyondcompare Mac 和 linux  端 Meld kdiff3 diff command 更多可参考:https://alternativeto ...

  6. java web中 8080端口号被占用的问题处理,终于明白了 Address already in use: JVM_Bind(端口冲突)

    1.错误描述 2011-7-20 11:05:18 org.apache.catalina.core.StandardServer await严重: StandardServer.await: cre ...

  7. Hadoop2.7.3集群安装scala-2.12.8 和spark2.7

    Apache Spark™是用于大规模数据处理的统一分析引擎. 从右侧最后一条新闻看,Spark也用于AI人工智能 spark是一个实现快速通用的集群计算平台.它是由加州大学伯克利分校AMP实验室 开 ...

  8. JavaWeb【JSTL】

    根据JSTL标签所提供的功能,可以将其分为5个类别. 核心标签 格式化标签 SQL 标签 XML 标签 JSTL 函数 使用方式 1.下载包 地址:http://archive.apache.org/ ...

  9. Delphi GetCommModemStatus函数

  10. 下载好的vue项目如何在自己电脑环境上运行,步骤!!

    本文链接:https://blog.csdn.net/qq_39309900/article/details/84837659首先第一步,需要安装node.js 下载地址:https://nodejs ...