漏洞简介
- 漏洞名称:Node.js命令注入漏洞
- 影响范围:Systeminformation < 5.3.1
- CVSSv3评分:7.8
- 漏洞类型:命令注入
- 描述:systeminformation是用于获取各种系统信息的Node.JS模块,它包含多种轻量级功能,可以检索详细的硬件和系统相关信息,自发布至今,软件包下载次数近3400万。
环境搭建
1. 安装Nodejs环境
2. 部署Nodejs环境
git clone https://github.com/ForbiddenProgrammer/CVE-2021-21315-PoC
命令node index.js
挂载环境
攻击过程
浏览器打开localhost:8000
尝试poc
:
1 | /api/getServices?name[]=$(echo "hack" > test.txt) |
攻击效果
poc1
:生成test.txt
文件,内容为hack
。
poc2
:DNSLog查询有记录。
该漏洞允许攻击者无回显的进行命令执行。
漏洞原理
在网上没有找到任何有关于源码的漏洞触发点说明,所以自己尝试着去审计了一下源码,没怎么学过node.js
,如有错漏,请不吝指出。
根据源码,在index.js
中调用si.services()
函数,接收get传参name[]
内容;
在正常的使用情况下,是类似这样的代码段,传入字符串参数,探测部件的使用情况。
1 | const si = require('systeminformation'); |
它的运行结果是:
1 | [ |
si.services()
函数位于process.js
文件,内容如下;
对于传入的参数srv
,在函数util.sanitizeShellString()
处做过滤后赋值给const s
变量
跟进到util.js
文件,查看sanitizeShellString()
,可以看到,该函数对传入的字符串str
做了处理,将其中的一些特殊符号进行了过滤,旨在返回“干净的字符串”。
这里我根据它的过滤函数逻辑尝试写了一份js
文件,传入数组类型的name
给过滤函数去处理,内容为一个带特殊符号的字符串,运行结果得到的却是没有经过过滤的name
。
证实了这里对于传入的数组不能够正常进行字符串过滤处理。
然后在si.services()
函数的逻辑下,const s
继续被赋值给srvString
,由于是Linux
系统,故而进入到以下代码段:
通过exec
函数执行拼接了srvString
的命令,从而导致RCE
的效果。
漏洞修复
我比对了3.1.5版本的修改发现,3.1.5以前的版本缺少如下对传入参数类型的判断:
1 | return new Promise((resolve) => { |
修改后,在多处加入了对参数类型的判断以及对原型是否被污染的判断。旨在规定参数类型必须是字符串类型。
1 | return new Promise((resolve) => { |
Author: suyumen
Link: https://suyumen.github.io/2022/03/25/2022-03-25-CVE-2021-21315%E5%A4%8D%E7%8E%B0/
Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.