昨天做一個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的密文。