攻击和捍卫JavaScript Sandboxes

一个沙箱的图片与里面的代码的

攻击沙箱

当我找到了Safari Unicode问题早些时候我开始看目前的JavaScript Sandboxes,发现了一些使用代理的不同不同。代理是一个特殊对象,允许您拦截目标对象上的操作,例如get / set / function调用操作。代理应该是创建沙箱的理想候选者,因为您可以使用它来拦截对象上的任何属性访问,但它永远不会简单;您需要克服一些问题。

一个众所周知的javascript开发开发程序在a中创建了一个简单的JavaScript Sandbox要旨几年前。它有一些缺陷。首先,因为根本没有语法检查,你可以注入一个结束的卷曲支架,以突破“with”语句并注入您喜欢的任何代码:

Sandboxjs(“} eval('警报(1337)'); {”);

没有保护覆盖覆盖原型,因此您可以修改索引(如索引)的关键函数,使它们返回允许列表。Sandbox的第一次执行将修改索引,第二个执行允许您使用任何代码:

Sandboxjs(“[]。构造函数.prototype.indexof = x => 1”);
沙盒子(“eval('top.alert(1337)')”);

它没有保护函数构造函数,也可以启用它的攻击Angularjs沙箱很多次。这将允许您使用基元来获取函数构造函数并执行您喜欢的任何代码:

Sandboxjs(“''。Sub.Constructor('eval(警报(1337))')')()”);

我也发现了另一个沙箱使用几年前制作的“与”声明,但这具有与上面的类似问题。没有语法检查,这样你就可以通过破坏“with”声明来再次解决:

测试('}} eval(“警报(1337)”); {+函数(){');

使用发电机也可以绕过沙箱。创建生成器功能时,它有其自己的构造函数与函数构造函数不同,JavaScript无法修改或删除此构造函数,因此您可以使用生成器绕过此沙箱:

测试('(函数*(){})。构造函数(“eval('aergn(1337)')”)“)()。下一个()');

沙箱防御

我们想知道是否有可能创建一个更有弹性的基于代理的沙箱。首先,我们创建了一个checksyntax函数,只需使用函数构造函数来生成代码的函数而不执行它。这可以防止攻击,例如“与”声明中断:

功能checksyntax(代码){
函数(代码);
}

然后我们冻结所有全局原型,如字符串原型。这可以防止它们被修改并停止攻击,例如前面提到的索引攻击:

if(!object.isfrozen(string.prototype)){
function.prototype.constructor = null;
object.freeze(String.Prototype);
object.freeze(number.prototype);
object.freeze(array.prototype);
object.freeze(symbol.prototype);
object.freeze(Math.Prototype);
object.freeze(function.prototype);
object.freeze(regexp.prototype);
}

之后,我们创建了两个代理:authentAllproxy,它处于外部“with”语句和处理程序,返回true以捕获每个属性,并且返回未定义或全局,如“窗口”。然后我们在另一个内部代理与语句中,此代理是我们的全局对象,并包含所有允许的属性。我们这样做的原因是允许操作员(例如“在”运算符中的运算符(在“全局对象(内部代理)上工作,并捕获与外部代理的允许列表不匹配的任何其他属性:

var输出=函数('proxy','catchallproxy',`
与(catchallproxy){
与(代理){
返回(函数(){
$ {code};
})();
}
}
`)(代理,Catchallproxy);

允许列表对象用于指定您希望沙箱允许的对象/功能。我使用了__ $的后缀,以确保只允许这些属性:

var allowlist = {
__proto__:null,
控制台__ $:控制台,
警报__ $:function(){
警报(“沙盒警报:”+参数[0]);
},
字符串__ $:string,
号码__ $:数字,
数组__ $:数组,
符号__ $:符号,
数学__ $:math,
regexp __ $:regexp,
对象__ $:对象,
eval __ $:函数(代码){
返回nicescript.run(“返回”+代码);
}
};

JavaScript Sandboxes通常发生的另一个问题是泄漏窗口。我们遇到过过去与angularjs.使用__lookupgetter__功能。即使您有一个代理拦截每个可能的属性,您仍然可以使用函数来获取对窗口对象的引用。您可以执行此操作,因为默认情况下,在未创建对象的函数中使用“此”时,JavaScript将返回窗口对象。例如:

函数x(){返回这个;}
x()。警报(1)

可以通过使用“使用严格”指令来防止这一点。在此模式中,JavaScript不会退回“窗口”对象,而是将使用未定义。这插来了安全漏洞:

var输出=函数('proxy','catchallproxy',`
与(catchallproxy){
与(代理){
返回(函数(){
“使用strict”;
$ {code};
})();
}
}
`)(代理,Catchallproxy);

剩余两个问题:生成器函数和动态导入语句。不幸的是,无法阻止调用动态导入语句。这是因为它不是用户可定义的函数,因此您无法覆盖它或拦截它。我们提出的解决方案是通过Regex寻找导入:

if(/ \ bimport \ s *(?:[(] | \ / [*] | \ / \ / | <! - )/。测试(代码)){
抛出新的错误(“动态导入被阻止”);
}

显然不是理想的,但有计划防止这种情况用字符串调用所以直到那时我们留下了这个补丁。然后我们有函数发生器问题,似乎无法覆盖函数生成器构造函数,没有全局函数generator函数。但是,我们可以覆盖函数发生器方法并防止它们被调用。这显然禁用生成器,但确实可以阻止代码被执行:

Object.getPrototypeof(函数*(){})。构造函数.prototype.prototype.return = null;
object.getPrototypeof(函数*(){})。构造函数.prototype.prototype.throw = null;
object.getPrototypeof(函数*(){})。构造函数.prototype.prototype.next = null;
object.freeze((函数*(){})。构造函数.Prototype.Prototype);

我们希望您喜欢这篇文章,很高兴在JavaScript中看JavaScript Sandboxing,因为您可以了解更多有关该语言的信息,并查看可能的最有可能的限制。我们很乐意看到一种方法来定制动态导入语句,不仅适用于沙箱,而且没有动态分析,因为目前没有办法在任何JavaScript引擎中执行此操作。我们知道JavaScript Sandboxing非常困难,我们希望我们的沙箱被打破,有些人甚至叫做它傻瓜的差事。然而,如果我们想提高安全性,我们觉得这是从攻击和防御角度看的。如果你想玩这个沙箱,甚至打破它会很棒,请在下面试试:

漂亮的剧本沙盒演示

如果您想贡献,则源代码也可在Github上获得:

良好的脚本源代码

更新...

最棒的MichałBentkowski.使用AYSNC函数/生成器旁路良好的脚本。谢天谢地,他提供了一个非常酷的补丁这将防止异步构造函数攻击,但也将允许使用生成器。第一次攻击使用了异步功能和发电机。我通过将null分配给函数构造函数来保护我免于构造函数攻击,但异步函数具有自己的函数构造函数。以下是攻击:

(async函数(){})。构造函数('警报(1)')();
(async函数*(){})。构造函数('警报(2)')()。下一个();

因此,第一个向量是异步函数并使用异步构造函数。第二个是异步生成器功能。Sandbox防止生成器方法下一个(),抛出(),返回()被调用。但是,因为这是一个异步生成器,它也得到了绕过沙箱的不同功能构造函数。

他再次打破了沙箱,但这一次有动态进口。我必须用正则表达式修补动态导入。正则表达式查找导入命令后跟零个或多个空格,然后是“(”(“,”/ *“,”<! - “或”//“,但我忘记了 - >这是一个有效的单行注释在JavaScript中。利润是:

进口
- >
('数据:文本/ javascript,警报(1)')

Antony Garand.发现我忘了冻结巨头。我现在修补了这个。谢谢Terjanq.使用Regexp的regexp发现拼写错误,这将导致能够修改Regexp对象。

你能打破它吗?

返回所有文章

相关的研究

每日SWIG的推荐故事beplay2018官网