这道题名副其实,果真是套娃,一层一层把我头都绕晕了
原题地址:https://merak-ctf.site/challenges#套娃
从题目已经看出他的套路了,打开题目地址一看,标准的开场没有什么意外
右键查看源码,发现有一段注释
可以观察出来又是一道if套娃语句,需要一层一层解
同样先将这段代码格式化
代码语言:javascript复制//1st
$query = $_SERVER['QUERY_STRING'];
if( substr_count($query, '_') !== 0 || substr_count($query, '_') != 0 ){
die('Y0u are So cutE!');
}
if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
echo "you are going to the next ~";
}
首先将$_SERVER['QUERY_STRING']
的值赋给变量$query
关于$_SERVER['QUERY_STRING']
获取的值:
1,http://localhost/aaa/ (打开aaa中的index.php) 结果:
$_SERVER['QUERY_STRING'] = ""; $_SERVER['REQUEST_URI'] = "/aaa/"; $_SERVER['SCRIPT_NAME'] = "/aaa/index.php"; $_SERVER['PHP_SELF'] = "/aaa/index.php";
2,http://localhost/aaa/?p=222 (附带查询)
结果: $_SERVER['QUERY_STRING'] = "p=222"; $_SERVER['REQUEST_URI'] = "/aaa/?p=222"; $_SERVER['SCRIPT_NAME'] = "/aaa/index.php"; $_SERVER['PHP_SELF'] = "/aaa/index.php";
3,http://localhost/aaa/index.php?p=222&q=333
结果: $_SERVER['QUERY_STRING'] = "p=222&q=333"; $_SERVER['REQUEST_URI'] = "/aaa/index.php?p=222&q=333"; $_SERVER['SCRIPT_NAME'] = "/aaa/index.php"; $_SERVER['PHP_SELF'] = "/aaa/index.php";
由实例可知:
$_SERVER["QUERY_STRING"] 获取查询 语句,实例中可知,获取的是?后面的值 $_SERVER["REQUEST_URI"] 获取 http://localhost 后面的值,包括/ $_SERVER["SCRIPT_NAME"] 获取当前脚本的路径,如:index.php $_SERVER["PHP_SELF"] 当前正在执行脚本的文件名
此段原文地址:https://www.cnblogs.com/mitang/p/3874291.html 感谢大佬的测试 然后我们再来看第一层判断条件为
代码语言:javascript复制if( substr_count($query, '_') !== 0 || substr_count($query, '_') != 0 )
这段代码表示需要满足"或"语句 而其中的substr_count函数是用于计算子串在字符串中出现的次数 再看看第二层判断条件
代码语言:javascript复制if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t']))
我们需要同时满足两个条件,其中preg_match函数用于匹配正则表达式,这里需要通过get提交参数b_u_p_t,但是参照第一层提到的substr_count函数,所以get参数不能带下划线,这时我们可以用点来代替下划线,同时用 来进行过滤正则表达式的条件,最终get传递的参数为?b.u.p.t=23333
根据提示进入secrettw.php,仍然是标准套路,提示需要本地才能访问
右键查看源码,发现一大堆注释,乍一看像乱码,实际上ctf里的注释大家也都知道是怎么回事,肯定不是毫无意义的,百度一查,发现是一种叫jother的编码,直接复制粘贴到浏览器控制台即可解码
执行后弹出了一段alert信息
根据提示用hackbar通过POST提交一个Merak参数
返回了一段代码,应该是secrettw.php的部分源码高亮
源码如下
代码语言:javascript复制Flag is here~But how to get it? <?php
error_reporting(0);
include 'takeip.php';
ini_set('open_basedir','.');
include 'flag.php';
if(isset($_POST['Merak'])){
highlight_file(__FILE__);
die();
}
function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i ){
$re .= chr ( ord ($v[$i]) $i*2 );
}
return $re;
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission! Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>
可以简单的分析出secrettw.php的作用 首先用if(isset($_POST['Merak']))函数检测是否存在参数名为Merak的POST数据,如果不存在则执行下面的语句,如果存在则执行if中的highlight_file函数高亮显示源码
我们已经通过POST提交Merak知道了源码,后面就不用再提交POST了,不然会被highlight_file函数截断,继续看下面的语句,中间的change函数暂时不管,是转换字符用的,后面会提到 后面的$ip = getIp();应该是使用了头部的takeip.php中的函数来获取客户端ip,再将获取到的ip赋值给变量$ip 如果满足$ip!='127.0.0.1'则执行该if内的语句,但是这段语句没什么用,所以我们不用管,第二个if内的语句才是我们需要执行的 第二个if的判断条件为
代码语言:javascript复制if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' )
也就是说需要满足两个条件
第一个条件$ip === '127.0.0.1',这个很容易满足,只要让get_ip获取到的值为127.0.0.1就行了,一般只有XFF和Client-ip这两种方法,我们可以用burpsuite来提交
第二个条件file_get_contents($_GET['2333']) === 'todat is a happy day'
首先通过file_get_content函数将整个数据读入一个字符串中,但是后面的值使用的单引号,并且中间使用===来判断全等,所以,经过到百度上各种CTF技巧的查找,发现这里可以使用data:// 来进行转换,具体用法可以参考:https://www.php.cn/manual/view/285.html
格式为data://text/plain;base64,
将todat is a happy day进行base64编码得到dG9kYXQgaXMgYSBoYXBweSBkYXk=,所以需要通过get提交一个名为2333的参数,值为data://text/plain;base64,dG9kYXQgaXMgYSBoYXBweSBkYXk=
第二个if内的语句
代码语言:javascript复制echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file']));
还用到了一个名为file的get参数,用于返回文件内容,我们需要知道flag.php的内容,所以这里需要file_get_content的文件是flag.php
但是这里要注意file_get_content函数不是直接使用的$_GET['file']
的值,而是用到了上面说到的change函数来转换,我们来看一下change函数的作用
function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i ){
$re .= chr ( ord ($v[$i]) $i*2 );
}
return $re;
}
首先定义用法,然后将变量进行base64解码(这说明后面POST参数file的值必须先进行base64编码),然后通过一段for循环,这段for循环的作用是先将字符转换为ASCII码,再将ASCII码逐步 i*2,i初始值为0,然后再转回字符其中strlen函数作用是计算字符的数目,chr是把ASCII转成字符,ord是把字符转成ASCII数字经过对照ASCII码表和计算,我们需要传递到file参数的值为“fj]a&fb(flag.php经过change函数转换为fj]a&fb)”的base64值,也就是ZmpdYSZmXGI=
顺带一提,takeip.php经过change函数变换,我们需要提交的值为“t_g_af"bXp^”,不过我们用不上,有兴趣的可以自己试一试
所以,我们最终提交的两个get参数为
注意别忘了将ip改为127.0.0.1,这里get_ip用到的方法为Client-ip
burpsuite改包放行后返回页面,右键查看源码
得到flag:MRCTF{c323e009-6f72-410a-9dff-96686b411977}