IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    代码审计学习之三个白帽Xor挑战

    admin发表于 2016-05-24 04:35:21
    love 0

    很难找到一个密码学的实例,就拿上一期的xor挑战来讲咯
    感谢基友@overlord和我一起努力俩天才干掉这个题
    先上代码吧

    ";
            }
        }
        }
    }
     
     
    if (!$_SESSION[\'uid\']) {
        echo "

    Decrypt me!: “.authcode(base64_encode($msg), \’ENCODE\’, $secret_key).”

    ";
    }else{
        echo $msg;
    }
     
    ?>
     
    

    0x01 构造cookie注入

    先忽略加密的部分,看看后面

    if ($_COOKIE[\'auth\']){
        list($user, $password) = explode("\t", authcode($_COOKIE[\'auth\'], \'DECODE\', $secret_key));
        if ($user !=\'\' && $password != \'\'){
        $sql = "select uid, username, password from users where username=\'$user\'";
        $result = mysql_query($sql);
        if ($result){
            $row = @mysql_fetch_array($result);
            if ($row[\'password\']===md5($password)){
                $_SESSION[\'uid\'] = $row[\'uid\'];
                echo "

    Welcome “.$row[\’username\’].”. My lord!

    ";
            }
        }
        }
    }

    user和pass是解密出来后用\t来分割得到的,然后进入sql查询
    查到的md5和输入的对比,过了以后输出用户名和msg
    很简单的注入 主要解密出来的是这样的字符串就可以了 最后这个\t可以不加

    \' union select 1,2,\'eccbc87e4b5ce2fe28308fd9f2a7baf3\'#\t3\t

    来看看效果

    33

    这样就可以通过验证了

    0x02 算法分析

    好的 现在进入正题 看看算法的逻辑
    由$secret_key产生32位$keya与32位$keyb

    加密的时候 $keyc是根据时间戳生成随机8位字符
    $cryptkey是$keya.md5($keya.$keyc)的sha256 //包含了$keyc也就是说每次请求都会变化
    新的$string是10个0加上md5($keyb.$string).$string
    俩者异或之后输出$keyc加上base64编码的密文

    解密的时候 $keyc取传入$string的前8位
    $cryptkey是$keya.md5($keya.$keyc)的sha256 //由于$keyc是获取的 所以和加密时的$cryptkey相同
    $string是传如$string的除了前8位的字符解base64编码 //也就是加密时的$result
    两者异或得到$result
    由于异或算法是对合的 通俗的讲 $string xor $cryptkey xor $cryptkey = $string
    所以解密得到的是加密时的$string 这一点非常重要

    可能看文字 有同学会有点懵逼了 我们来画张图(请原谅我不会用绘图工具)

    加密的

    1875823916

    解密的

    22

    不同的是 解密有一个验证

    if($operation == 'DECODE') {
            if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 32) == md5($keyb.substr($result, 42))) {
                return substr($result, 42);
            } else {
                return '';
            }
    } else {
            return $keyc.str_replace('=', '', base64_encode($result));
    }

    在前面我们已经知道这里的$result就是加密时的$string
    $string是一个这样的字符串 “0000000000”.md5($keyb.$string).$string
    所以第一个判断过了,第2个判断是要验证$result[10:42]==md5($keyb.$result[42:]) //分片写法
    满足的话返回$string

    好的 终于解释完代码了
    我们想把密文传入cookie,解密后进行注入
    我们不知道$secret_key,所以无法本地加密,而程序调用加密的地方只有一个
    就是程序每次会回显的authcode(base64_encode($msg), ‘ENCODE’, $secret_key)
    意思就是说 我们要通过这个密文 产生一个新的密文 这需要$cryptkey 这个加密秘钥

    0x03 xor还原密钥

    看看我们能得到什么
    首先,我们很容易得到$cryptkey的前10位,这是因为$string的前10位是0
    由此 我们也能得到$msg base64后的某10位,这是因为$cryptkey是循环使用的
    得到的10位为 FnIGlzIGlu base64的原理我就不细讲了 去掉前俩位 解出明文 is in
    目测是the flag is in the database

    好像并不能得到什么了,无聊一直刷密文,再看看题目 xor挑战
    应该是和xor有关

    灵光一闪 不会是把密文xor吧?
    马上再看看加密的图3622572339

    传入的$string不变 $key不变 那么产生的$string就是不变的
    把密文去掉前8位,解码后xor 将得到俩个$cryptkey的xor 有戏啊
    $cryptkey是sha256后的值 只有0~f 这16个字符
    我们获取一张异或表看看 0~f xor 0~f alp = [0~f]

    57815619

    验证了一下,没有哪一行是完全相同的,那不是能求出每一位的值了
    我们获取200条密文,把密文去掉前8位,解码后将第一条与后面的199条依次xor 将结果保存到数组num
    然后遍历一遍xor表 如果能找到num0就将xor表这一行读入 看看能匹配几个保存下来
    最后选取匹配最多的 那一行的值 就是sha256的值 语言总是苍白的 show code

    for i in xrange(10,42):
        num = []
        for n in xrange(1,len(dec_b64)):
            num += [ord(dec_b64[0][i])^ord(dec_b64[n][i])]
        most = [0,len(num)]
        tmp = 0
        for j in xrange(len(xor_tab)):
            if xor_tab[j] == num[0]:
                right = len(num)
                for k in xrange(16):
                    if xor_tab[k+(j/16)*16] in num:
                        right -= 1
                if most[0] < len(num) - right:
                    if most[1] > right:
                        most[0] = len(num) - right
                        most[1] = right
                        tmp = j/16
        sha += hex(tmp)[2:]
        md5 += chr(ord(dec_b64[0][i])^ord(hex(tmp)[2:]))

    就这样 我们得到了cryptkey 和md5($keyb.$string)
    也可以得到$msg 为 R29vZCBqb2IhIFRoZSBmbGFnIGlzIGluIHRoZSBkYXRhYmFzZS4=
    解码后是 Good job! The flag is in the database.

    0x04 hash扩展绕过验证

    得到了$cryptkey 我们已经能修改任意密文了
    不过解密的时候验证$result[10:42]==md5($keyb.$result[42:]) 才能返回$string
    $keyb是未知的,而md5($keyb.$result[42:])已经知道了 并且是$result[42:]可控的
    这样 就可以通过hash扩展攻击产生一个新的string “0**0”.md5($keyb.$msg.$data).$msg.$data

    网上已经有很多人分析过了 我就不再讲一遍了 附上一张图上的hash函数实现图
    英文还行的看这里
    没过4级的看这里

    57815619
    M是明文分组512位为一组 超过的448位的 需要填充一个新的组
    CV0 是初始向量 产生的CV1 是M0的md5结果 嗯 就补充这俩点

    注意这个脚本的CV位置 hash2个块后才覆盖产生的CV
    细心的同学可能会发现我这个py是实时获取的
    因为$secret_key 是他喵每小时会变的!!!

    【via@90专栏 乐清小俊杰】



沪ICP备19023445号-2号
友情链接