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

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

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

0x01 构造cookie注入

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

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

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

不同的是 解密有一个验证

在前面我们已经知道这里的$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

就这样 我们得到了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专栏 乐清小俊杰