1.首先Cookie伪造
我们可以看到,游客不允许访问,想到admin伪造,所以,下一步就是伪造Cookie,我这里使用插件来进行修改,也可以F12进去网络,然后cookie查看以及修改。
经过URL解码,我们可以看到,这里role需要更改,后面的passnum猜测也需要绕过,怎么绕过呢,所以这里想到.bak文件,试了下,可以直接下载。
<?php
require("header.php");
include_once("config/config.php");
if(!isset($_COOKIE['authe'])){
//secret_is_'hash.??????'
$autharr=array(
'role'=>'guest',
'passnum'=>'????????'
);
$auth= json_encode($autharr);
ob_start();
setcookie('authe', $auth);
ob_end_clean();
$_SESSION['isguest']=true;
}else{
$temp=$_COOKIE['authe'];
$data=json_decode($temp);
$num=$data->passnum;
if(json_last_error() != JSON_ERROR_NONE){
echo "json error";
exit();
}
if($num!=="????????"){
for ($i=0; $i < 7; $i++) {
//secret num is random generated that you can't guess, only admin can enter this site.
if(!($num[$i]==$secretnum[$i]))
{
echo "random secret num error";
exit();
}
}
if($data->role==='admin'){
$_SESSION['isguest']=false;
}
}
}
$page="";
if (isset($_GET['page']))
{
$page=strtolower($_GET['page']);
$page=str_replace("#", "", $page);
$page=str_replace("'", "", $page);
if(strpos($page,"config")!==false)
exit();
if(strpos($page,"phar")!==false||strpos($page,"zip")!==false||strpos($page,"data")!==false)
exit();
$page=$_GET['page'].".php";
}
else
$page="main.php";
if(!isset($_SESSION['isguest'])||$_SESSION['isguest']===true)
{
echo "游客(guest)不允许访问更多功能";
exit();
}
include($page);
?>
打开源码,我们审计一下,发现关键部分其实是passnum参数的判断,也就是如下
if(!isset($_SESSION['isguest'])||$_SESSION['isguest']===true)
{
echo "游客(guest)不允许访问更多功能";
exit();
}
其实我们让$_SESSION['isguest']为False就行了,这样就不会进入if判断里面,这里有个提示,isset()函数在非空情况下会返回False,而这里我们设置$_SESSION['isguest']为False,所以isset返回的指为True,前面再非一下,就变成了False,后面也是False,不会进入if判断。那么,怎样让$_SESSION['isguest']为False呢?往下看,发现又有个if判断,
if($num!=="????????"){
for ($i=0; $i < 7; $i++) {
//secret num is random generated that you can't guess, only admin can enter this site.
if(!($num[$i]==$secretnum[$i]))
{
echo "random secret num error";
exit();
}
}
这里如果我们给$num设置值,就会进入for循环,所以考虑一下,这里是7层循环,并且secretnum未知,我们需要绕过,这里而且是==,思考使用php弱类型进行绕过,这里的知识点如下:
使用true来绕过==,即true弱等于任何非0数值,我们就可以使用来绕过,但是这里不能传字符串,是我们需要将true隔开,字符串不能实现,所以这里需要传入数组来实现,即传入[true,true,true,true,true,true,true] ,所以现在我们知道怎么做了,上手,我们进行修改
这个是我们修改后的,这里其实有疑问,我这里使用了8个true,其实只有7次循环,但我用7个true是没办法进入的,而源码里面显示的?也有8个,但只有7次循环,有待解决,后面蚁剑连接上去了之后才发现源码其实是循环了8次,和bak还不一样,如下图,然后url编码后给入并保存,再刷新页面
然后我们进入到admin的页面
这里有个文件上传的页面,考虑直接传马链接上去, 这里我试了下传图片,上传上去但无法访问,如下:
后续看源码才知道,这个是时间戳,这里肯定有过滤方式,无奈只有查看源码,可以看到路径其实在文件包含,直接伪协议读, 使用php://filter
而且我们在上传的时候发现了upload
这里后面没有跟php,是因为index.php源码有自动添加,如下:
page=upload,所以这里不用我们添加后缀。
伪协议我们得到源码,
base64解码一下,得到:
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<?php
include_once("config/config.php");
if(!isset($_SESSION['isguest'])||$_SESSION['isguest']===true)
{
echo "游客(guest)不允许访问更多功能";
exit();
}
$error=$_FILES['pic']['error'];
$tmpName=$_FILES['pic']['tmp_name'];
$name=$_FILES['pic']['name'];
$size=$_FILES['pic']['size'];
$type=$_FILES['pic']['type'];
try{
if($name!=="")
{
$name1=substr($name,-4);
if(is_uploaded_file($tmpName)){
$time=time();
$file=md5($name);
$rootpath='uploads/'.$file.$name1;
if(!move_uploaded_file($tmpName,$rootpath)){
echo "<script language='JavaScript'>alert('文件移动失败!');window.location='index.php?page=submit'</script>";
exit;
}
else{
if($name1===".php"){
file_put_contents($rootpath,preg_replace("/<\?/","",file_get_contents($rootpath)));
}
}
}
echo "图片ID:".$time;
}
}
catch(Exception $e)
{
echo "ERROR";
}
//
?>
</html>
然后我们看$name是我们的上传的文件名,下面对php做了过滤
这里是把< ? 给过滤了,所以双写绕过,直接写码连接
然后源码给出了查看路径,并且对文件名做了md5加密,所以我们需要访问md5加密后的,
这里需要注意,我们后面需要加上php,是因为源码里面进行了拼接 ,前面的$file是我们md5加密后的整个文件名(b.php),后面是$name1,也就是文件类型,即后缀,所以要加上php
然后我们可以访问一下
然后上蚁jian,直接连
在上层目录即发现flag