0x01 背景

​ 之前举行过的云资产的演练又开始了,目前拥有目标某设备的老版本前台RCE漏洞,但本次因某些原因不允许使用此漏洞,所以入口点需要重新找,准备找个新洞,记录nodejs审计过程如下

0x02 审计

1.前期过程

​ 首先我的想法是寻找那种简单的命令注入漏洞,因为之前的洞就蛮简单的,我做了如下操作,目的是定位调用了命令执行函数的路由文件

grep -rl "child_process" --include=\*.js .
image-20240306153951101

​ 然后首先看的是ping功能,很简单的定义路由,接参,然后传入命令执行函数中,但发现存在正则过滤,由于正则我都是现记现忘,重新看了下,写死了\d限制了其他字符,所以漏洞不存在

image-20240306154513368

​ 接着过了一遍筛选出来的所有存在命令注入函数的路由,没发现能用的,有的看着代码是存在漏洞但是当用在目标网站上是不存在漏洞的,推测是目标更新了版本进行修复漏洞(这里还没有去跟踪各个函数调用链深挖)

​ 随即又搜了下nodejs的文章知道了还可以寻找代码执行漏洞,也就是eval函数,eval参数可控时直接进行导入命令执行进行调用就行

http://localhost:3000/?abc=require('child_process').execSync('open -a Calculator.app', { encoding: 'utf8' })
image-20240306155641844

​ grep大法之故技重施,实际就有2个route文件存在eval,当然别的文件也是耗费精力看了但没东西,就不讲了,后续在反推eval函数代码执行的调用链中同时找到一个命令注入漏洞

grep -rl "eval" --include=\*.js .
image-20240306160716095

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

image-20240306173844318

2.2 getNodevalueBynodeType()

​ 此函数为当nodeType!=1时进入目标函数,payload参数名为regex

image-20240306173958751

​ 此函数需要使relationMap!=1进入目标函数,payload参数名为relationMap.srcRegex,是键的值

image-20240307102735880

​ 此函数需要满足3个条件进入目标函数,payload参数名为relationShips => relation

  • outPutFieldsArray.length > 1
  • 存在/usr/local/las/program/neo4j/structure.csv文件
  • fileSize != 0
image-20240307103029536

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函数处理后返回的键
image-20240307104547930
2.5.1 splitrelationsBystructure()

​ payload参数为relationShips,若要将payload最终保存到noStructure键中,需满足以下条件

  • srcNodeType != 1 or dstNodeType != 1

​ 并且通过这里的键调用,联动前面的payload传递过程,也得知了最终传入eval的参数为srcRegex,如果在下面这个函数中调用,就是relation.srcRegex,和srcNodeType这些是同级的,所以srcRegex和srcNodeType这些都是插入id对应的内容中的键名

image-20240307163104008
2.5.2 通过前端定位添加关系规则功能

​ 由于需要找到如何控制id查询结果,所以在网站上找到代码中对应的前端功能位置,此功能是生成关系规则图,然后在前端找到了对应的添加关系规则的功能,由于没保存具体图片,口述记录一下过程

​ 然后在尝试新增关系模型时,发现提交对应的参数输入框被注释了,当时梳理的还没有那么清晰还是什么其他原因导致没在http中改包,是使用burp将返回<!--注释符号注释和其他的一些返回包修改,让新增关系模型的功能重新在前端显示

image-20240307170246059

​ 获取到添加关系模型的路由和数据包后,定位到代码,确定无误,参数名都能对上,最后构造能够满足以上需要步入的目标函数的所有条件的参数值

image-20240307171949013

2.6 漏洞利用

最终利用过程分为四步

1.添加关系规则

image-20240307172913148

2.查询关系规则,获得ID

image-20240307173108052

3.调用ID,触发payload

image-20240307173330194

4.删除关系规则,否则会在数据库中留下很多数据

image-20240307173813160

5.目标不出网通过写入文件执行命令结果进行命令回显

image-20240308205846820

3.命令执行

3.1 generateToCsv()

​ 命令执行是在找代码执行跟踪调用链时发现的,这个函数就是在代码执行调用链那个execGetNostructureNodeAndLinks函数中调用的

​ 传入到这里的参数跟踪后会发现是和代码执行中传入的payload是差不多的,通过审计发现只是传入的参数名改为了srcLogfield,利用过程就不写了,就是添加关系时payload改下参数

image-20240307175732745

0x03 总结

​ 整体就是类似于sql的二次注入的代码执行&命令注入漏洞,先保存payload到数据库,在后面调用数据库数据时触发恶意代码。

​ 看文章说nodejs的代码还可以尝试找原型链污染进行RCE,以后有需要再学,最后吐槽下公司给我们调薪了,高兴吧?是往下调!入职几年,年收入比入职时还低了,还低了,抵了,了...🤡

​ 实际审计过程还有其他一些东西,大概如下

  • 拿到权限发现是低权限用户,通过其他发现可利用漏洞直接变成root权限
  • 请求必须携带对应目标host的Referer
  • 记不清了。。