昨天做一个CTF,遇到了一个自加密的题目,听师傅们说都是原题了,奈何之前没怎么做过,于是昨天花了下时间好好看了一下,顺便学习下。
首先由url格式和源码,容易猜测是文件包含,利用这个读取到index文件源码:
<?php /** * Created by PhpStorm. * Date: 2015/11/16 * Time: 1:31 */ header('content-type:text/html;charset=utf-8'); if(! isset($_GET['jpg'])) header('Refresh:0;url=./index.php?jpg=hei.jpg'); $file = $_GET['jpg']; echo '<title>file:'.$file.'</title>'; $file = preg_replace("/[^a-zA-Z0-9.]+/","", $file); $file = str_replace("config","_", $file); $txt = base64_encode(file_get_contents($file)); echo "<img src='data:image/gif;base64,".$txt."'></img>"; /* * Can you find the flag file? * */ ?>
简单看下,发现是用_替换了config。做到这里还没思路,这里突破点是在.idea文件夹,.idea文件夹是很多工具编写代码时生成的文件夹,如pytharm、phpstorm等均可以生成,本地新建个项目,就会发现里面的内容,大致如下:
然后发现.idea/workspace.xml文件里可能有敏感信息泄露,访问得到
然后利用之前的文件读取,读取该文件源码,这里_换成config即可,得到源码如下:
<?php /** * Created by PhpStorm. * Date: 2015/11/16 * Time: 1:31 */ error_reporting(E_ALL || ~E_NOTICE); include('config.php'); function random($length, $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz') { $hash = ''; $max = strlen($chars) - 1; for($i = 0; $i < $length; $i++) { $hash .= $chars[mt_rand(0, $max)]; } return $hash; } function encrypt($txt,$key){ for($i=0;$i<strlen($txt);$i++){ $tmp .= chr(ord($txt[$i])+10); } $txt = $tmp; $rnd=random(4); $key=md5($rnd.$key); $s=0; for($i=0;$i<strlen($txt);$i++){ if($s == 32) $s = 0; $ttmp .= $txt[$i] ^ $key[++$s]; } return base64_encode($rnd.$ttmp); } function decrypt($txt,$key){ $txt=base64_decode($txt); $rnd = substr($txt,0,4); $txt = substr($txt,4); $key=md5($rnd.$key); $s=0; for($i=0;$i<strlen($txt);$i++){ if($s == 32) $s = 0; $tmp .= $txt[$i]^$key[++$s]; } for($i=0;$i<strlen($tmp);$i++){ $tmp1 .= chr(ord($tmp[$i])-10); } return $tmp1; } $username = decrypt($_COOKIE['user'],$key); if ($username == 'system'){ echo $flag; }else{ setcookie('user',encrypt('guest',$key)); echo "â®(â¯â½â°)â"; } ?>
这里要想获取flag,就必须要使得$_COOKIE['user']的值解密出来等于system,但我们并不知道加密的$key值,而我们是知道guest的密文的,这里是参考的http://bbs.heetian.com/forum.php?mod=viewthread&tid=28,代码如下:
function ss($txt,$m){ for($i=0;$i<strlen($m);$i++){ $tmp .= chr(ord($m[$i])+10); } $m=$tmp; $tmp=''; $txt=base64_decode($txt); $rnd = substr($txt,0,4); $txt = substr($txt,4); for($i=0;$i<strlen($txt);$i++){ $key .= $txt[$i] ^ $m[$i]; } $s='0123456789abcdef'; $txt1='system'; for($i=0;$i<strlen($txt1);$i++){ $tmp .= chr(ord($txt1[$i])+10); } $txt1=$tmp; $tmp=''; for($i=0;$i<16;$i++){ $tmp = $key.$s[$i]; for($ii=0;$ii<strlen($txt1);$ii++){ $txt2 .= $txt1[$ii] ^ $tmp[$ii]; } file_put_contents('1.txt',base64_encode($rnd.$txt2)."\r\n",FILE_APPEND); $txt2=''; } } ss('eldUVRdOWRhI','guest'); // 设置获取的guest用户的cookie值。
我们先简单看看代码:
加密函数:首先是对明文每个字符ascii加10,然后获取一个4位随机字符串,然后利用该字符串和$key生成下面要进行异或的MD5值,这里异或次数是和加密明文的长度相关的,然后返回base64加密字符串。
解密函数:首先base64解密,然后截取出4位随机字符串,$key生成算法一样,然后同样根据密文长度进行异或,然后对结果字符串每个字符减10.
这里我们首先要知道的是,xor运算,这是可逆的,测试效果如下:
可以看到,只有在密文和密钥异或时得到结果少一位,这是因为密钥和明文长度不一致,php在异或时会自动转换成相同长度,如下:
回到这个题,他根据长度进行异或,由于根据guest获取的key进行异或出来的只有5位,知道他是和MD5值异或的,解密时的rnd又是和加密一样的,因此生成的md5也是一样的,加上system是6位,所以再暴力猜测一遍第6位的值,即可得到解密后为system的密文。