SAS CTF 2025 Quals

Drift Chat

此题目考点为 GO 语言代码审计,如果在GO代码中,一个判断语句并未返回结果,那么会继续向下执行。

首先通过查看service.go代码获取路由。

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
package service

import (
"context"
"editor/internal/repository/chat"
"editor/internal/repository/user"
"log/slog"

"github.com/gin-gonic/gin"
red "github.com/redis/go-redis/v9"
)

const tokenCookie = "token"

type Service struct {
eng *gin.Engine
red red.UniversalClient
chat chat.Repository
user user.Repository
}

func Init(ctx context.Context, chat chat.Repository, user user.Repository, r red.UniversalClient) (Service, error) {
return Service{
eng: gin.Default(),
chat: chat,
user: user,
red: r,
}, nil
}

func (s *Service) Run() {
s.routes()
s.eng.Run(":3134")
}

func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:37755")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")

if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}

c.Next()
}
}

func ginBodyLogMiddleware(c *gin.Context) {
for _, err := range c.Errors {
if err == nil {
slog.Error("nil error")
continue
}
slog.Error("error", "err", err.Error())
}
}

func (s *Service) routes() {
eng := gin.Default()
eng.Use(CORSMiddleware())
eng.Use(ginBodyLogMiddleware)

routes := eng.Group("/api")

routes.POST("/register", s.Register)
routes.POST("/login", s.Login)
routes.POST("/logout", s.Logout)

routes.POST("/chat/create", s.Create)
routes.GET("/chat/list", s.List)
routes.POST("/chat/get_drafts", s.GetDrafts)
routes.POST("/chat/get", s.GetChat)

routes.POST("/send_message", s.SendMessage)
routes.POST("/set_draft", s.SetDraft)

s.eng = eng
}

并且在给的源码中给出了init.sql

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


CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL,
password TEXT NOT NULL,

unique (id)
);

CREATE TABLE IF NOT EXISTS chats (
name TEXT PRIMARY KEY,
allowed_users TEXT[] NOT NULL
);

CREATE TABLE IF NOT EXISTS messages (
id SERIAL PRIMARY KEY,
chat_name TEXT REFERENCES chats(name) ON DELETE CASCADE,
author TEXT NOT NULL,
content TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_messages_chat_name ON messages(chat_name);

INSERT INTO users (username, password) VALUES ('kek', substring(md5(random()::text) from 0 for 16));
INSERT INTO users (username, password) VALUES ('admin', substring(md5(random()::text) from 0 for 16));
INSERT INTO chats (name, allowed_users) VALUES ('best chat eva', '{"kek", "admin"}');
INSERT INTO messages (chat_name, author, content) VALUES ('best chat eva', 'admin', 'SAS{FLAG}');


从数据库中发现,在messages表中有flag字段。

get_chat.go中发现能够获取到数据库内容的代码片段。

1
2
3
4
5
6
messages, err := s.chat.GetMessages(ctx, req.Chat)
if err != nil {
c.AbortWithStatus(500)
c.Error(err)
return
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// GetMessages ...
func (r *chatRepository) GetMessages(ctx context.Context, chatName string) ([]Message, error) {
rows, err := r.qe.Query(ctx, "SELECT author, content FROM messages WHERE chat_name = $1 order by id ASC limit 100", chatName)
if err != nil {
return nil, fmt.Errorf("retrieve messages: %s", err)
}
defer rows.Close()

var messages []Message
for rows.Next() {
var msg Message
if err := rows.Scan(&msg.Author, &msg.Content); err != nil {
return nil, fmt.Errorf("scan message: %w", err)
}
messages = append(messages, msg)
}

if err = rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating over messages: %w", err)
}

return messages, nil
}

但是在此之前还有一个身份验证。

1
2
3
4
ok, _ := s.check_is_allowed(ctx, username, req.Chat)
if !ok {
c.AbortWithStatus(403)
}

这里虽然检测了用户是否允许访问chat房间,但是并未执行return,所以即使403,也会继续向下执行到getMessages

那么这里逻辑漏洞就很明显了,我们只要去访问best chat eva这个chat房间,无论是什么用户,我们都可以执行到getMessages获取到数据库内容。

首先,任意注册

image-20250530091337508

那么登录进去后,抓包访问/api/get_chat

根据代码需要的参数进行传参

image-20250530091746201

image-20250530092445364


SAS CTF 2025 Quals
https://r3bir7hcx.github.io/2025/05/30/SAS-CTF-2025-Quals/
Author
CXCX
Posted on
May 30, 2025
Licensed under