又是一篇php的学习
打开环境后什么发现也没有,F12里面发现了提示
扒下来后简单审计一下
<!--
//1st
$query = $_SERVER['QUERY_STRING'];
if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 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 ~";
}
!-->
首先了解一个知识点:假如有url
结果:
$_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"] 当前正在执行脚本的文件名
简单来说$_SERVER['QUERY_STRING']是获取url后面的参数以及值的,然后存在了query这个变量里面。
substr_count() 函数计算子串在字符串中出现的次数。%5f是_的url编码
第一个判断语句要达成的条件是传入的变量里面不能有_或者它的编码形式。
第二个判断语句要达成的条件是需要传入一个b_u_p_t的值不能强等于字符23333并且要进行正则判断字符为23333(^不在括号内是限定字符的开头要为2,$则是限制了结尾必须为3,中间的字符也是确定的)
那么就要开始绕过了,首先要了解php解析的一些语法规则利用PHP的字符串解析特性Bypass - FreeBuf网络安全行业门户
简单来说就是php解析的时候会将某些字符转换为下划线(包括空格)
所以第一个if就很好绕过,只需要传入
- ?b%20u%20p%20t
- ?b u p t
- ?b.u.p.t
就可以了
而第二个if首先要注意是强等于,要比较变量的类型和值的,我开始做的时候就没注意到,一直想传一个23333a去绕过,但我去本地尝试的时候发现弱等于其实也不行的,弱等于只有在拿数字与字符串作比较,当传入的是字符串的时候可以绕过,简而言之如果是
if($_GET['a'] == 23333)
这样的话传入23333a就可以满足条件,但实际是
if($_GET['a'] == '23333')
这样的,弱等于也满足不了,因为他是字符串与字符串进行比较了,更何况这是强等于了。
接着看题,既然是强等于,又要求判断时候不能等于字符串23333,且又要正则判断的时候等于23333,就可以用%0a进行绕过,%0a是换行符的意思,在非多行模式下,$似乎会忽略在句尾的
%0a。参考这篇绕过
preg_match绕过总结 - MustaphaMond - 博客园 (cnblogs.com)那就可以构造payload:
- ?b%20u%20p%20t=23333%0a
- ?b u p t=23333%0a
- ?b.u.p.t=23333%0a
当变量以GET形式通过url传入后端的时候,会先进行一次解析,也就是说传入的%0a进入后端时变成了\n,要注意GET传入后的变量到达后端后是以""的形式包起来的,也就是说\n会被解析为换行符。然后进行比较后不相等,在进行正则匹配时\n也会被解析为换行符,还是相当于23333,则绕过了限制。
又提示我们flag在secrettw.php里面,于是又看到了这个
这个其实是js的一种加密方式jsfuck。想深入了解可以看看
(12条消息) JSfuck原理解析一——基础原理_直接开车的博客-CSDN博客_jsfuck
他还说了ip不对,可能会修改ip。因为是js,所以直接拿到控制台回车执行看结果就行了。
叫post传一个Merak上去 ,那就随便传一个值看看结果
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的作用
ini_set 是为一个配置选项设置值,open_basedir 将PHP所能打开的文件限制在指定的目录树中,包括文件本身。当程序要使用例如fopen()或file_get_contents()打开一个文件时,这个文件的位置将会被检查。当文件在指定的目录树之外,程序将拒绝打开。这里就是只允许你去包含根目录里面的存在的文件,不让你去包含自己的一句话木马之类的。
首先用if(isset($_POST['Merak']))函数检测是否存在参数名为Merak的POST数据,如果不存在则执行下面的语句,如果存在则执行if中的highlight_file函数高亮显示源码
我们已经通过POST提交Merak知道了源码,后面就不用再提交POST了,不然会被highlight_file函数截断,继续看下面的语句,中间的change函数暂时不管,是转换字符用的,后面会提到
后面的$ip = getIp();应该是使用了头部的takeip.php中的函数来获取客户端ip,再将获取到的ip赋值给变量$ip,因为getIp并不是内置函数,是自定义的。
然后有两个if,第一个要让获取的ip等于127.0.0.1,第二个也是让获取的ip等于127.0.0.1并且以GET方式传入一个变量,他的内容要等于todat is a happy day。
第一个条件$ip === '127.0.0.1',这个很容易满足,只要让get_ip获取到的值为127.0.0.1就行了,一般只有XFF和Client-ip或者REMOTE-ADDR这几种方法,这里只有CLIENT-IP行得通,我们可以用burpsuite来提交
第二个条件如果是以post的方式可以用php://input去绕过,这里是用GET传,可以用data://text/plain;base64,去绕过,这样也避免了前面不允许传其他文件的过滤。base64加密todat is a happy day后是dG9kYXQgaXMgYSBoYXBweSBkYXk=。所以需要通过get提交一个名为2333的参数,值为
data://text/plain;base64,dG9kYXQgaXMgYSBoYXBweSBkYXk=
最后就是要执行echo file_get_contents(change($_GET['file'])); 这个句子了,很明显只要让change过后的东西等于flag.php被读取后就可以echo出来了。
首先定义用法,然后将变量进行base64解码(这说明后面POST参数file的值必须先进行base64编码),然后通过一段for循环,(参数后面加上[]是把字符串当做数组看)这段for循环的作用是先依次将字符转换为ASCII码,再将ASCII码逐步
+$i*2
,$i
初始值为0,然后再转回字符,最后通过$re.相当于$re=$re.chr ( ord ($v[$i]) + $i*2 )把字符串重新拼接起来组成一个新的$re返回出去。
其中strlen函数作用是计算字符的数目,chr是把ASCII转成字符,ord是把字符转成ASCII数字
解密直接把中间+改成-就可以了,解密出来是这样的
再把它进行base64加密等于ZmpdYSZmXGI= 最后传入得到flag
也可以看到takeip.php里面是这样的
其实我本来还想用file=php://filter/read=convert.base64-encode/resource=./flag.php的方式去读取flag的,但是他用change解码后是这样的
有乱码的形式于是我就放弃了,想了想应该是有include包含的话没有回显更多会用这种方法,file_get_contents是读取文件内容的直接传入文件就好了。但理论上我觉得应该是可以的,他去把flag的内容以base64读取出来后echo出来,然后去解码应该也可以得到。