Node.js作为常见的Web开发语言之一,Xcheck也针对该语言打造了对应的扫描引擎:JsCheck。
同样基于污点传播模型,支持以下常见漏洞类型:
目前JsCheck支持以下2个框架:Koa、Express,其他框架有需要可以方便添加。
JsCheck为了能够精准的做污点传播,对Node.js的特性进行了精确的适配,比如:this关键字,变量声明提升等。
Node.js里的this根据所处的位置不同(普通函数,箭头函数),调用方式不同(直接调用,赋值给一个对象的属性再调用,当做构造函数调用),有着不同的指向含义。
1)当在文件的最上层的时候,this指向的当前文件的exports
对象:
this.a = 1111
console.log(exports)
输出:
{a: 1111}
2)当this在一个普通函数里,this指向的是global
对象:
function foo(){
this.a = 222
}
foo()
console.log(global.a)
输出:
222
3)当this在一个构造函数里,this指向的是该构造函数生成的对象:
function Foo(){
this.a = 333;
}
var fn = new Foo();
console.log(fn.a);
console.log(global.a);
输出:
333
undefined
4)当this在箭头函数里,this指向的对象就会根据this定义时所处的为上下文而定:
class A {
b(){
let c = ()=>{
this.a = 9
}
c()
}
}
a = new A()
a.b()
console.log(a.a)
exports.s = 200
a.b = ()=>{
tmp = this;
console.log(tmp)
}
a.b()
输出:
9
{s: 200}
在Node.js里,如果一个变量不使用var,const,let
修饰,那么默认它是一个全局变量:
function test() {
a = 100 // 这里声明a为全局变量
console.log('1: ', a)
}
test()
console.log('2: ', global.a)
输出:
1: 100
2: 100
如果使用了var声明变量,则会出现变量声明提升:
function test() {
// 由于变量声明提升,这里仅仅是对局部变量赋值
a = 0
console.log('1: ', a)
if (true) {
var a // 声明一个局部变量a
a = 1
}
console.log('3: ', a)
}
test()
console.log(global.a)
输出:
1: 0
3: 1
undefined
不但变量的声明会提升,函数的声明也会提升:
var getName = function(){
console.log(2);
}
function getName (){
console.log(1);
}
getName();
等价于:
var getName; //变量声明提升
function getName(){ //函数声明提升到顶部
console.log(1);
}
getName = function(){ //变量赋值依然保留在原来的位置
console.log(2);
}
getName(); // 最终输出:2
在处理误报的时候,最不好处理的就是用户自定义的过滤逻辑。因为,这里的写法形式很多,难以提取出固定的模式,其中有两个关键点: 1.识别出一段代码是过滤逻辑 2.判定此段过滤逻辑是完备的 针对这两个点,目前从业界来看,都没有完美的解决办法,Xcheck在这里做了一些尝试,对一些情况能够做出准确的判定。 比如,通过字符串直接比对的情况:
function isGoodCmd() {
var cmd = taint()
if(!("safecmd1" == cmd || "safecmd2" == cmd)) {
return
}
eval(cmd)
}
isGoodCmd()
检测不满足条件后抛出异常的情况:
function isGoodCmd() {
var cmd = taint()
if(!("safecmd1" == cmd)) {
throw new SQLException()
}
eval(cmd)
}
isGoodCmd()
对污点数据进行常量化替换(清洗):
function isGoodCmd() {
var cmd = taint()
if(!("safecmd1" == cmd)) {
cmd = "help"
}
eval(cmd)
}
isGoodCmd()
过滤逻辑叠加污点对象传播时:
function isGoodCmd() {
var cmd = taint()
var safe_cmd
if("safecmd1" == cmd) {
safe_cmd = cmd
}
// 有风险
eval(cmd)
// 无风险
eval(safe_cmd)
}
isGoodCmd()
判定对象和风险函数执行的对象不是同一个的情况:
function isGoodCmd() {
var cmd1 = taint()
var cmd2 = taint()
if("safecmd1" == cmd1) {
eval(cmd2)
}
}
isGoodCmd()
常见目录穿越漏洞的过滤逻辑:
function safePath(filePath) {
if (!filePath.startsWith("/media/file") || filePath.indexOf("./") != -1) {
return ""
} else {
return filePath
}
}
function isGoodCmd() {
var cmd1 = taint()
eval(safePath(cmd1))
}
isGoodCmd()
从实测效果来看,JsCheck的误报已经明显降低,但是后续还有优化提升空间。
目前,使用github上CodeQL的Node.js测试集来扫描,未做专门适配的情况下发现漏洞243个。
{'REDOS': 2, 'READ': 173, 'RCE': 7, 'XSS': 45, 'SQLI': 7, 'URL': 9}
查看详细的扫描报告,针对每个漏洞,各个污点传播节点有详细的展示:
想了解Xcheck更多信息或者代码安全审计相关技术欢迎关注xcheck公众号~
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。