记CVE-2020-5840发现过程

 

前言

本文记录了我在学习了Offensive-Security 的AWAE课程后,用所学的知识练手,发现Hashbrown CMS 漏洞的过程。

 

Hashbrown CMS 介绍

Hashbrown CMS 是一款用nodejs 编写的无头CMS。所谓无头CMS其实是指不提供前端网页论坛等等的展示部分,只是提供后端的媒体,网页内容等等的存储和相应的API供内容管理员使用。这款CMS的介绍里面提到
By storing your website’s content separately from the site itself, you are not only making it hard for attackers to bring down your site, you are also rendering the effort completely pointless. There is simply no database on your website from which to steal information and hold ransom
在它的设计里面,用户,项目,媒体等等基本都赋予随机的ID,希望用这种方式保证安全性。

 

漏洞发现过程

原本是希望通过搜索nodejs 的危险函数看有没可以利用的点。在grep ‘exec(‘ 的过程中发现了如下的输出

grep 'exec(' * -r

Server/Service/MediaService.js: await HashBrown.Service.AppService.exec(‘convert ‘ + cachedPath + ‘ -resize ‘ + width + (height ? ‘x’ + height : ‘’) + ‘> ‘ + cachedPath);

打开源码查看可见这是一个函数, 传入project,media,width,height 参数。235,236行做了目录的拼接,247行建立目录。于是我在248行加了一行debug 命令来打印变量帮助理解。253行看起来是read文件内容,如果cache 里面没有这个文件就在261行复制一份到cache的目录。最后如果满足264行if(width && media.isImage() && !media.isSvg()) 的条件执行265行bash命令,有可能可以在cachePath 变量注入bash 命令触发命令执行。

接下来查看哪里调用了这个函数

通过grep 发现Server/Controller/MediaController.js中有调用。

root@xxxxxx:~/hashbrown-cms/hashbrown-cms-1.3.1/src# grep getCachedMedia * -r
Server/Controller/MediaController.js: let data = await HashBrown.Service.MediaService.getCachedMedia(req.project, media, parseInt(req.query.width), parseInt(req.query.height));

正常情况下req.project和media是随机生成的ID。比下图,如果我们上传一个新的图片,结合之前添加的console log 输出,可见如下标红的几个ID。

media.id: ba25d3f728f346ed
project id: 6e8d287e9be2fb46
cache id: ba25d3f728f346ed

经过分析发现这款CMS在处理上传文件的时候, 真实的文件存放在media.path 比如/root/hashbrown-cms/hashbrown-cms-1.3.1/media/ba25d3f728f346ed/1.png, 同时cache一份放到cache 的目录用随机的project id 和cache id 生成目录和文件名,去掉文件后缀。这样来保证安全性。其中project id 和 cache id 是在http 请求中可控的, 如下图,是在请求的url中包含了project id 和cache id。

那如果我们在project id 或者cache id 中包含我们的恶意bash 命令不是就有可能触发命令注入了么? 一个简单的测试发现不是这么简单。如下图我插入了&id 做测试,结果报错404 not fond。

查看相关代码发现对于提交的这些id,原本是要建立相应的目录的,如图

那如果我们传入的参数程序找不到相应的目录或者文件,就要报404. 到这里看起来命令执行是个dead end。但是如果只要找到相应的目录或者文件即可那我们是不是可以把directory traversal 的技术放到这里来呢?想到这里做了一番尝试发现这个思路可以

比如设置url末段为../../../../tmp 程序会把系统 tmp目录里面的第一个文件复制到/root/hashbrown-cms/tmp。响应的请求和console 日志如下

Console 日志:

media.id: ../../../../tmp media.path: /root/hashbrown-cms/hashbrown-cms-1.3.1/../../../tmp/a.txt cachedPath: /root/hashbrown-cms/tmp

这里算是一个小进步但是之前的命令注入的目标是无法完成的,即便写成../&id../,在传递给这个函数之前程序也会把&id去掉。那是不是我们可以用同样的方法去看有没有别的函数可以利用。很快就找到了给media rename 的功能。

正常的request 请求为http://10.200.159.166:8080/api/6e8d287e9be2fb46/live/media/rename/ba25d3f728f346ed?name=2.png。 那如果我们对name 进行赋值在有权限的情况下就可以对任意系统文件进行覆盖,加上该CMS原本就不对上传的文件内容做检察,那简直就是任意文件上传+任意系统文件修改。如下图这是一个非常严重的漏洞了。发散一下思维的话我们可以上传passwd 和shadow 文件进行对系统文件覆盖。或者上传ssh 的秘钥用ssh 登录, 还可以在对原本的hashbrown cms 的js文件进行修改,加入恶意代码再覆盖原始文件, 这样再下次运行时候拿到系统的反弹shell。经过试验正事这些思路都可行,这里就不在展示了。如下图放置了一个图片文件到系统的tmp目录。

经过上报给该CMS 的开发, 他致谢后很快就说修复了这问题发布了1.3.2 版本。他采用了Path.basename 函数在有多个/的时候只选取最后一个/以后的部分。但是我发现这其实并不是一个完全的fix。可以用delete media 的功能设置url末段为”/..”,这样一来回将hashbrown cms 所在的目录内容完全删除掉。再次沟通后在1.3.4版本真正修复次问题。申请CVE获得号码CVE-2020-5840。

本文内容到此结束,恳请大佬们指教。

(完)