1. 程式人生 > >實驗吧 簡單的登入題 之[ CBC翻轉攻擊 ] 詳解

實驗吧 簡單的登入題 之[ CBC翻轉攻擊 ] 詳解

解題連結: http://ctf5.shiyanbar.com/web/jiandan/index.php
1)Burpsuite抓包
在這裡插入圖片描述
2)根據Tips,修改GET請求為test.php獲得原始碼。
在這裡插入圖片描述

3)程式碼審計
這個是實驗吧的原始碼:

      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>'; } }

4) 以下是題目:http://118.89.219.210:49168/index.php 的原始碼
因為正在學習CBC翻轉攻擊,查閱了csu_vc的文章
所以暫時先按他的文章的原始碼來分析CBC翻轉的題目。
實驗吧的題目以後更新。

    <!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>';
        }
    }
    ?>

5)可以簡單地畫一個流程圖:
在這裡插入圖片描述

解題思路:
1)在於考察aes-128-cbc演算法的“CBC翻轉攻擊“漏洞

2)由原始碼我們可知:
2.1)Flag只有Session = admin可以檢視
2.2)我們不可以通過POST傳參的方式Set Session = admin
2.3)我們可以通過CBC演算法的翻轉攻擊漏洞 Set Session = admin

3)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,直到最後一組密文。

假設我們修改了第一個密文塊的第 4 個位元組,那麼根據 CBC 模式的解密,它將影響下一個密文塊的解密結果,如圖示紅部分。

上述的影響過程中,只涉及到異或運算,這裡異或運算的特性就十足重要了!

4)異或的概念:

1)異或的運算方法是一個二進位制運算: 1^1=0 0^0=0 1^0=1 0^1=1 兩者相等為0,不等為1.
這樣我們發現交換兩個整數的值時可以不用第三個引數。 如a=11,b=9.以下是二進位制 a=a^ b=1011^1001=0010; b=b^
a=1001^0010=1011; a=a^ b=0010^1011=1001; 這樣一來a=9,b=11了。

2)異或是可逆的。
已知A=B^ C
可以得到結論B=A^ C
並且也能得到A^ C^B= B ^ B=0

上述結論拿到這裡的 CBC 翻轉攻擊假設中看

設第一塊密文中的第4個位元組設為變數A,在第二塊密文的第4個位元組設為變數B,第二塊的明文中第4個位元組設為變數C
因為A ^ B=C,根據結論有B=A^C
如果人為令A變數值改變為A^ C,那麼原本的A^B將變成A ^ C ^ B=B ^ B=0,第二個產生的明文的第4個位元組將變為0
如果認為將A改變為A ^ C^ X(這裡X是任意字元),那麼參與運算後,A ^ B將等於A ^ C ^ X ^ B=B ^ X ^ B=X,
第二個產生的明文的第4個位元組將變為X字元,這樣,第二塊密文塊解密的結果就可控了!

1、根據CBC演算法的原理,此題中:
Plaintext1:s:2:{s:8:“u serna
Plaintext2:me”;s:5:“z dmin”;
Plaintext3:s:8:“password”;s
Plaintext4::3:“12345”;}XXX

2、不妨設
①②③④代表Plaintext1、2、3、4
(1)(2)(3)(4)分別代表Ciphertext1、2、3、4

3、CBC加密前:
plaintext = ①②③④
4、CBC加密後:
Ciphertext = (1)(2)(3)(4)

CBC加密:
iv ⊕① =(1)
(1)⊕② =(2)
(2)⊕③ =(3)
(3)⊕④ =(4)

CBC解密
(1)⊕iv = ①
(1)⊕(2)=②
(2)⊕(3)=③
(3)⊕(4)=④

5)攻擊流程:
1、修改能夠得到的密文的第一塊(前16個位元組),使得第二塊密文塊結果的字元中zdmin能變成admin
cipher[9] = u

 xor_cipher = cipher[0:9] +  chr(ord(cipher[9]) ^ ord('z') ^ ord('a')) + cipher[10:]  

2、由於操作1導致第一塊密文塊解密後得到錯誤結果,修正IV初始化向量,將第一塊的明文結果還原成a:2:{s:8:"userna

通俗點說就是:
我們需要更改(1),使得(1)⊕(2)後,得到②中的zdmin變成admin
因為(1)被更改,所以(1)⊕iv得到的①會被更改,導致無法反序列化。
所以我們需要適當更改iv使得(1)⊕iv 仍然等於①。

Realfirstblk = 'a:2:{s:8:"userna'#被損壞前正確的明文
for i in range(16):
    newIv += chr(ord(iv[i])^ord(cipher[i])^ord(Realfirstblk[i])) #這一步相當於把原來iv中不匹配的部分修改過來

iv ⊕ ① =(1)
(1)⊕(2)= ②

令iv =A ,①=B , (1)=C
(1)⊕(2)=② 等價於A ^ B =C
可控點(A^C ^X) ^B = X
等價於iv ^ (1) ^ RealFirstBlock = RealFirstBlock
iv = oldiv
(1) = cipher[0:16]
所以newIv[i] = chr(ord(iv[i])^ ord(cipher[i])^ord(Realfirstblk[i]))

6)攻擊流程1的Exp

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

7)攻擊流程2的Exp

#!/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 = ''
Realfirstblk = 'a:2:{s:8:"userna'#被損壞前正確的明文
for i in range(16):
    newIv += chr(ord(iv[i])^ord(cipher[i])^ord(Realfirstblk[i])) #這一步相當於把原來iv中不匹配的部分修改過來
print "oldIv: " + iv
print "newIv:" + urllib.quote(base64.b64encode(newIv))

最終得到計算得到的 cipher 和 iv:

[email protected]:~/Desktop# python 1.py 
[*]原始iv和cipher
iv_raw:  %2F8iEm4jh%2BjbgVGwlQ31ycg%3D%3D
cipher_raw:  8WdhbPxjZy9xYAgoCeghiOUQu0ri1Y3dv7cX44MbvOfIC6zZxCbR%2FPFpeMatL5qIgT%2BYA66tIdCBpxtWsWxV9Q%3D%3D
[*]對cipher解碼,進行反轉
反轉後的cipher:8WdhbPxjZy9xewgoCeghiOUQu0ri1Y3dv7cX44MbvOfIC6zZxCbR/PFpeMatL5qIgT%2BYA66tIdCBpxtWsWxV9Q%3D%3D