昨天晚上大佬在群里发了一道题,原题如下
< !-- please login as uid=1! -- >
< ?php
include ("AES.php");
highlight_file('index.php');
$v = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890auid=9;123123123123";
$b = array();
$enc = @encrypt($v);
$b = isset($_COOKIE[user])?@decrypt(base64_decode($_COOKIE[user])):$enc;
$uid = substr($b,strpos($b,"uid")+4,1);
echo 'uid:'.$uid.'< br/>';
if ($uid == 1){
echo $flag;
}
else {
echo "Hello Client!";
}
setcookie("user",base64_encode($enc));
?>
uid:�
Hello Client!
读了题目可以发现最后要求的是使得最后的 $b 里面包含 uid=1,在代码最后面可以发现将密文进行base64加密后存进了cookie,使用 EditThisCookie 读取cookie后发现并不是base64编码,有点懵,经大佬点醒之后突然发现这是自动进行了url encode,然后因为是密文分组,又根据上面所引入的 AES.php 推测可能是CBC字节翻转攻击。
引用一段维基解释:
1976年,IBM发明了密码分组链接(CBC,Cipher-block chaining)模式。在CBC模式中,每个平文块先与前一个密文块进行异或后,再进行加密。在这种方法中,每个密文块都依赖于它前面的所有平文块。同时,为了保证每条消息的唯一性,在第一个块中需要使用初始化向量。
若第一个块的下标为1,则CBC模式的加密过程为
$$ C_i = E_K(P_i \oplus C_{i-1}), C_0 = IV $$
而其解密过程则为
$$ P_i = D_K(C_i) \oplus C_{i-1}, C_0 = IV $$
CBC是最为常用的工作模式。它的主要缺点在于加密过程是串行的,无法被并行化,而且消息必须被填充到块大小的整数倍。解决后一个问题的一种方法是利用密文窃取。
注意在加密时,平文中的微小改变会导致其后的全部密文块发生改变,而在解密时,从两个邻接的密文块中即可得到一个平文块。因此,解密过程可以被并行化,而解密时,密文中一位的改变只会导致其对应的平文块完全改变和下一个平文块中对应位发生改变,不会影响到其它平文的内容。
重点是最后一段,密文中某一位的内容改变只会导致下一个块中对应位置的密文内容改变,那么只需要取对应的位置进行XOR操作即可。
1234567890abcdef
1234567890abcdef
1234567890abcdef
1234567890auid=9
;123123123123
首先将明文分割为 16bytes 的块,为了改变 uid=9 为 uid=1,就需要改变 uid=9 前一个块中的 f 的内容,然后设 A 为第四组的解密后的明文,B为第三组的密文,则 C 为 “1234567890auid=9”
C = A XOR B
然后再借由 XOR,可以解出 A 的值
A = B XOR C
然后解可以写出解密的函数
public function CBC(){
//URL解码
$str=urldecode("S9PsFp43k9VgyrggRHLbISjUAjwzSSPPajrF9Dzz0o%2FieSZbxwGjTJ5xhAZEi5tDBjvwsQtH0BynlLC0p0F0zOZMx25M6iekcLvX%2F%2FMNKSA%3D");
//BASE64 解码
$str=base64_decode($str);
//翻转字节
$str[47] = chr(ord($str[47])^ ord("9")^ ord ("1"));
return base64_encode($str);
}
得到如下
uid:1
flag{Small_Li_i5_stup1D!}
最后得到了flag,然而并没有什么卵用。