在bugku关于CBC翻转攻击思路

Posted lxz-1263030049

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在bugku关于CBC翻转攻击思路相关的知识,希望对你有一定的参考价值。

今天在bugku遇到关于CBC翻转攻击的题目,总结了一下关于CBC翻转攻击的原理,以及关于这道题目的解题思路

CBC翻转攻击的主要目的:通过损坏密文字节来改变明文字节。(注:借助CBC内部的模式) 通过添加单引号等恶意字符来绕过过滤器,或通过将用户ID更改为admin来提升权限,或者更改应用程序所需的明文的任何其他后果。

加密过程:

 技术分享图片

 

上图CBC加密原理图

1:Plaintext:待加密的数据。

2:IV:用于随机化加密的比特块,保证即使对相同明文多次加密,也可以得到不同的密文。

3: Ciphertext:加密后的数据

4:Key:分组加密使用的密钥

这里重要的一点是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,就可以得到一个不同的明文,而这个明文是我们可以控制的。利用这一点,我们就欺骗服务端或者绕过过滤器

再解释一下:

根据解密方式我们可以知道,

A=ciphertext(N-1),B=plaintext(N)

 

,C为第N块待异或且经过解密的字符,C‘为我们经过翻转要得到的明文。

所以我们可以打得到关系:

A = B ^ C

C = A ^ B

A ^ B ^ C = 0

A ^ B ^ C ^ C = C

根据关系式可以得到

A = A ^ C ^ C

 

所以说我们只需要修改前一组密文所对应的本组明文相同位置的字符,即可得到想要的明文

 

下面就是关于Bugku的题目

http://118.89.219.210:49168/

这一题属于常规思路,可是不容易想到,我记得以前做过类似的题目,毕竟是萌新总会有很多知识点会忘记(QAQ)

使用备份文件脚本进行扫描就会得到:脚本代码:

运行之后就会得到:

 技术分享图片

 

打开链接发现是应该可以下载的文件并且文件是以.swp为后缀名

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

怎么恢复.swp:
可以使用
vim -r:命令来查看当前目录下的所有swp文件 
vi -r {your file name} :命令恢复文件
rm .{your file name}.swp:命令删除swp文件,不然每一次编辑时总是有这个提示。

 技术分享图片

 

就会得到html文档:

 技术分享图片

 

看到该题的代码:

<!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>

 

 

 技术分享图片

 

GO一下可以看到cookie中返回的iv和cipher

 技术分享图片

 

将输入序列化得s:2:{s:8:"username";s:5:"adcin";s:8:"password";s:3:"123";}

然后每16字节分组得

① a : 2 : { s : 8 : " u s e r n a

② m e " ; s : 5 : " a d c i n " ;

③ s : 8 : " p a s s w o r d " ; s

④ : 3 : " 1 2 3 " ; }

可见,如果我们想要将②中的c变成m就需要对①中的s进行改变

于是我们编写脚本:

# -*- coding: utf-8 -*-

import base64,urllib

 

def get_newCipher():

    cipher = ‘‘#输入所得cipher

    cipher = base64.b64decode(urllib.unquote(cipher))

    newCipher = cipher[0:x] + chr(ord(cipher[x])^ord(‘‘)^ord(‘‘)) + cipher[x+1:]#x为需要改变值所在的字节数,第二个ord中为输入值,第三个ord中为目标值

    print urllib.quote(base64.b64encode(newCipher))

 

def get_newIV():

    cipher = ‘‘#get_newCipher提交后所得的无法反序列化密文

    iv = ‘‘#所得iv

    #cipher = urllib.unquote(cipher)

    cipher = base64.b64decode(cipher)

    iv = base64.b64decode(urllib.unquote(iv))

    newIv = ‘‘

    right = ‘‘#被损坏前正确的明文

    for i in range(16):

        newIv += chr(ord(right[i])^ord(iv[i])^ord(cipher[i]))

    print urllib.quote(base64.b64encode(newIv))

 

if __name__ == __main__:

    #get_newCipher()

    #get_newIV()

 

 

 技术分享图片

 

发现新密文无法反序列化,这是因为,我们将c修改成m时破坏了①中的结构

于是我们将新得到的密文复制,通过base64解密过后的iv与新密文解密的明文与原始①中数据对应异或

 

技术分享图片

 


提交新的iv和刚才得到的cipher即可

 技术分享图片

 

以上是关于在bugku关于CBC翻转攻击思路的主要内容,如果未能解决你的问题,请参考以下文章

Padding Oracle攻击

BugKu CTF web25解题思路笔记

片段(Java) | 机试题+算法思路+考点+代码解析 2023

Heroku dynos 是不是容易受到 BEAST 的攻击? (TLS 1.0 CBC 漏洞利用)

web4——bugku

华为OD机试真题Python实现翻转单词顺序真题+解题思路+代码(2022&2023)