0x01 背景
之前举行过的云资产的演练又开始了,目前拥有目标某设备的老版本前台RCE漏洞,但本次因某些原因不允许使用此漏洞,所以入口点需要重新找,准备找个新洞,记录nodejs审计过程如下
0x02 审计
1.前期过程
首先我的想法是寻找那种简单的命令注入漏洞,因为之前的洞就蛮简单的,我做了如下操作,目的是定位调用了命令执行函数的路由文件
grep -rl "child_process" --include=\*.js .
然后首先看的是ping功能,很简单的定义路由,接参,然后传入命令执行函数中,但发现存在正则过滤,由于正则我都是现记现忘,重新看了下,写死了\d限制了其他字符,所以漏洞不存在
接着过了一遍筛选出来的所有存在命令注入函数的路由,没发现能用的,有的看着代码是存在漏洞但是当用在目标网站上是不存在漏洞的,推测是目标更新了版本进行修复漏洞(这里还没有去跟踪各个函数调用链深挖)
随即又搜了下nodejs的文章知道了还可以寻找代码执行漏洞,也就是eval函数,eval参数可控时直接进行导入命令执行进行调用就行
http://localhost:3000/?abc=require('child_process').execSync('open -a Calculator.app', { encoding: 'utf8' })
grep大法之故技重施,实际就有2个route文件存在eval,当然别的文件也是耗费精力看了但没东西,就不讲了,后续在反推eval函数代码执行的调用链中同时找到一个命令注入漏洞
grep -rl "eval" --include=\*.js .
2.代码执行
然后就是发现的漏洞从eval函数反推调用链路及传参的伤脑细胞过程,调用链中较为重要的函数调用过程如下,函数里嵌套的其他一些函数就不写了
+-----------------------------------------------------------------+
| router.post('/generatorRuleMap', function (req, res) |
+-----------------------------------------------------------------+
|
V
+-----------------------------------------------------------------+
| execGetNostructureNodeAndLinks(NodeSet, startTime, ... , cb) |
+-----------------------------------------------------------------+
|
V
+-----------------------------------------------------------------+
| getRelationNodeandLinks(resultSet, relationMap, ... , cb) |
+-----------------------------------------------------------------+
|
V
+-----------------------------------------------------------------+
| getNodevalueBynodeType(row, nodeType, regex, index, cb) |
+-----------------------------------------------------------------+
|
V
+-----------------------------------------------------------------+
| getRegexPattern(srcMessage, pattern, index) |
+-----------------------------------------------------------------+
2.1 getRegexPattern()
此为最终调用eval的函数,没有限制,直接将接收的参数传入eval函数,payload参数名为pattern
2.2 getNodevalueBynodeType()
此函数为当nodeType!=1时进入目标函数,payload参数名为regex
2.3 getRelationNodeandLinks()
此函数需要使relationMap!=1进入目标函数,payload参数名为relationMap.srcRegex,是键的值
2.4 execGetNostructureNodeAndLinks()
此函数需要满足3个条件进入目标函数,payload参数名为relationShips => relation
- outPutFieldsArray.length > 1
- 存在/usr/local/las/program/neo4j/structure.csv文件
- fileSize != 0
2.5 router.post
此函数需要满足5个条件才能进入目标函数
- 能够根据_id查出结果docs
- docrelation == 0
- 服务器为linux
- indexArray.length > 0
- 获得相应的splitrelationsBystructure()函数返回
payload参数名依次变化如下,并且可知我们控制的参数是用来控制查询的id,而不是直接传入exp,所以还需要找到id对应的值是插入到数据库的过程
req.body._id //获得请求参数
↓
s_id //对请求参数进行了过滤,防止出现sql注入
↓
docs.relationships //查询结果的键
↓
relations //查询结果的键
↓
resultRelation.noStructure //经splitrelationsBystructure函数处理后返回的键
↓
noStructureRelations //经splitrelationsBystructure函数处理后返回的键
2.5.1 splitrelationsBystructure()
payload参数为relationShips,若要将payload最终保存到noStructure键中,需满足以下条件
- srcNodeType != 1 or dstNodeType != 1
并且通过这里的键调用,联动前面的payload传递过程,也得知了最终传入eval的参数为srcRegex,如果在下面这个函数中调用,就是relation.srcRegex,和srcNodeType这些是同级的,所以srcRegex和srcNodeType这些都是插入id对应的内容中的键名
2.5.2 通过前端定位添加关系规则功能
由于需要找到如何控制id查询结果,所以在网站上找到代码中对应的前端功能位置,此功能是生成关系规则图,然后在前端找到了对应的添加关系规则的功能,由于没保存具体图片,口述记录一下过程
然后在尝试新增关系模型时,发现提交对应的参数输入框被注释了,当时梳理的还没有那么清晰还是什么其他原因导致没在http中改包,是使用burp将返回<!--注释符号注释和其他的一些返回包修改,让新增关系模型的功能重新在前端显示
获取到添加关系模型的路由和数据包后,定位到代码,确定无误,参数名都能对上,最后构造能够满足以上需要步入的目标函数的所有条件的参数值
2.6 漏洞利用
最终利用过程分为四步
1.添加关系规则
2.查询关系规则,获得ID
3.调用ID,触发payload
4.删除关系规则,否则会在数据库中留下很多数据
5.目标不出网通过写入文件执行命令结果进行命令回显
3.命令执行
3.1 generateToCsv()
命令执行是在找代码执行跟踪调用链时发现的,这个函数就是在代码执行调用链那个execGetNostructureNodeAndLinks函数中调用的
传入到这里的参数跟踪后会发现是和代码执行中传入的payload是差不多的,通过审计发现只是传入的参数名改为了srcLogfield,利用过程就不写了,就是添加关系时payload改下参数
0x03 总结
整体就是类似于sql的二次注入的代码执行&命令注入漏洞,先保存payload到数据库,在后面调用数据库数据时触发恶意代码。
看文章说nodejs的代码还可以尝试找原型链污染进行RCE,以后有需要再学,最后吐槽下公司给我们调薪了,高兴吧?是往下调!入职几年,年收入比入职时还低了,还低了,抵了,了...🤡
实际审计过程还有其他一些东西,大概如下
- 拿到权限发现是低权限用户,通过其他发现可利用漏洞直接变成root权限
- 请求必须携带对应目标host的Referer
- 记不清了。。