CBC字节翻转攻击

题目

题目原地址见:https://nazo.io/1007/aes.php

题目给了一个PHP文件,并使用highlight_file方法将其源码显示出来(知道你们不会去点链接,我直接贴出来算了。。。):
(注:我解的时候还没有Hint….)

<?php 

// Super Secure Data Encryption Service! 

// 2018-10-7 12:00 Hint: Try CBC Byte Flipping Attack 

$data = $_GET['d'] ?? NULL; 
define('KEY', md5((require 'key.php').$_SERVER['REMOTE_ADDR'])); 
define('METHOD', 'AES-256-CBC'); 

if ($data) { 
    if (!isset($_GET['dec'])) { 
        if (shouldShowFlag($data)) die('not supported.'); 
        echo '<pre>'.bin2hex(encrypt($data)).'</pre>'; 
    } else { 
        $text = decrypt(hex2bin($data)); 
        echo '<pre>'.(htmlspecialchars($text) ?: bin2hex($text)).'</pre>'; 
        if (shouldShowFlag($text)) echo require_once 'congratulations.php'; 
    } 
} else { 
    highlight_file(__FILE__); 
} 

function shouldShowFlag($data) { 
    return strpos($data, 'besb66.com') !== false; 
} 

function encrypt($text) { 
    $text = $text.str_repeat(chr(0), 16 - strlen($text) % 16); 
    $ciphertext = openssl_encrypt($text, METHOD, KEY, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv = openssl_random_pseudo_bytes(16)); 
    return $iv.$ciphertext; 
} 

function decrypt($data) { 
    if (strlen($data) < 32) return 'error.'; 
    $iv = substr($data, 0, 16); 
    $ciphertext = substr($data, 16); 
    $text = openssl_decrypt($ciphertext, METHOD, KEY, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv); 
    return $text; 
}

解谜思路

从源码得知,本题通过d与dec这两个GET参数来作为输入,并且引用了key.php、congratulations.php两个PHP文件。

先来做一些简单的输入,检查一下这个所谓的Super Secure Data Encryption到底是怎么样的,而从结果可见加密应该是按照标准来的,可以正常加解密,没什么坑。。。

然后来看两个引用的php文件,分别用浏览器去访问它们看看。。。然而什么都没有返回。。。(其实应该是PHP根据请求者返回不同的信息,对解题者返回空罢了)

于是只能静下心来看代码。。。
我们发现key.php返回的字符串后面是跟了一串$_SERVER[‘REMOTE_ADDR’]后再进行MD5 HASH的,这个参数的大致意义是返回你在服务器眼里的IP地址(一般情况下就是你的外网IP)。
于是可以判断这个key应当没有解的意义。(因为每个人IP都不同,尽管它对每个人来讲都是固定不变的)

那么我们来看这个congratulations.php,如何让这个网页返回它的内容?可以看出,这个加/解密算法进行解密处理后的值为besb66.com时会返回它。
但是我们很遗憾地发现,我们不能用刚刚测试时候那样的方法,直接去逛一圈就得到答案,因为加密时有一个”如果输入值恰为besb66.com时报错”的判断。
而因为加密的KEY不可知,故我们也不能把源码拉到本地环境,把那个判断删掉跑一遍加密,再去服务器上跑一遍解密拿答案。

于是去百度AES-256-CBC加密算法。。。我们知道,加密后值的前16位是IV,即程序中的$iv
关于IV的解释,我的理解是类似钥匙或者暗号之类的,这里有一个比较系统的解释:
IV:用于随机化加密的比特块,保证即使对相同明文多次加密,也可以得到不同的密文。

CBC密文中,整个结构是:
[IV][密文1][密文2]......(其中每一段为16位)

然后这里有一张很关键的图(关于CBC解密过程的):

假设这个用Key对加密字符串str进行解密后的结果是f(str) (注意这个f(str)并不是密文,它只是一个过渡数据),那么我们可以得出:
f(str) xor IV = PlainText(即明文)

然后我们有一个定律,即:若 A xor B = C,那么 A xor C = B, C xor B = A

则有:f(str) = IV xor PlainText

好我们现在设一个可以被这个PHP文件成功加密的明文,如besb65.com,加密后得到(2位十六进制表示一个字符,所以说这个IV是16位没毛病):

又因为KEY固定,算法固定,故我们可以确认上述公式中的f(str)是一个固定值,那么:
f(str) = IV xor "besb65.com"
IV' = f(str) xor "besb66.com"

则有:IV' = IV xor "besb65.com" xor "besb66.com"

(这里有一个观点:f(str)这个过渡数据最终被解读成什么样,其实是由其本身与IV共同决定的,既然本身这个加密数据难改,那么改IV照样可以更改得到的明文内容)

那么,写一段程序来计算前面提到的公式。。。(注意:CBC在文本位数不够时会扩充0,扩充0的方法可参考题目)
(程序部分略…)

于是我们发现,得出的结果其实跟原加密字符串的头部十分相近(甚至只差一两个字符),于是将IV更改后再次提交给服务器进行解密,得出结果:

完毕~

题目进阶

题目地址:https://nazo.io/1007/aes2.php

原理不多赘述(上面说的够多了)
因为此题中的MD5刚好是16位,故其实密文被完整、平均、独立地分成了三段:
[IV][MD5][Cipher]

首先取出[MD5][Cipher]利用aes.php进行形如前一题的操作(反正Key一样),获得新的[MD5*](密文)。
[MD5]部分替换成新的之后提交一次,获得此时的MD5值明文(wrong hash后面会跟此时的MD5解密结果)。
同样地,利用获取到的MD5明文,取出[IV][MD5*]部分再进行形如前一题的操作(目标MD5值可以使用PHP现场算出来),获得[IV*]
[IV*][MD5*][Cipher]进行拼接后提交,结束。
相关代码:

<?php

$cipher = hex2bin("7b2f056b746233d4fa4fe5cf41f1ff6c095b35842abe71b74303dbeabe29a12c8458e50fec520eaecebcffd90770e988"); //besb65.com加密值
$iv = substr($cipher, 0, 16);
$md5_cipher = substr($cipher, 16, 16);
$text_cipher = bin2hex(substr($cipher, 32, 16));

$text = "besb66.com";
$textnow = "besb65.com";
$md5 = md5($text, true);
$md5now = hex2bin("11cf1434ed34929b12b2bb27057981ed"); //第一次提交后根据报错返回更改此值

$text = $text.str_repeat(chr(0), 16 - strlen($text) % 16);
$textnow = $textnow.str_repeat(chr(0), 16 - strlen($textnow) % 16);

$md5_cipher ^= $text ^ $textnow;
echo "First Output:\n";
output();
$iv ^= $md5 ^ $md5now;
echo "Second Output:\n";
output();

function output() {
    global $iv, $md5_cipher, $text_cipher;
    echo bin2hex($iv);
    echo bin2hex($md5_cipher);
    echo $text_cipher;
    echo "\n";
}

结果:

未经允许不得转载:初音未来Alpha » CBC字节翻转攻击

赞 (0)

评论 1

  • 昵称 (必填)
  • 邮箱 (选填)
  1. 匿名dalao回复