沙箱逃逸XSS
前段时间,在某程序中发现了一个有趣的功能,在前端实现了一个沙箱,可以执行一些js代码
翻找定位到 eval5 这个开源库,抽出其中的知识点,设计成题目分享一下
https://github.com/bplok20010/eval5
官网也有沙箱例子,屏蔽了window,document等对象
https://bplok20010.github.io/eval5/examples/sandbox.html
原站点代码过于奔放,XSS过于简单,以此设计了两个沙箱,real world风格,直接引用了最新版本
代码在 https://github.com/ttttmr/sandbox
ps: 可以简单diff出来我的改动
在线环境
简单修改版
https://sandbox.demo.xlab.app/sandbox2.html
增加CSP:script-src 'self' 'wasm-unsafe-eval'; object-src 'self'
https://sandbox.demo.xlab.app/sandbox3.html
writeup
sandbox2
黑盒可以简单试试得知可以使用Object
联想到nodejs沙箱逃逸,通过访问原型链中的构造函数,poc非常简单
1 | Object.prototype.constructor.constructor("return eval")()("alert(1)") |
推荐阅读 NodeJS VM和VM2沙箱逃逸
sandbox3
由于加了非常严格的CSP,sandbox2的poc会被拦截
script-src 'self' 'wasm-unsafe-eval'; object-src 'self'
想要直接绕过这个CSP是不太可能的
仔细研究sandbox2 poc,Object是在沙箱外
除了调用原型链中的函数,还可以实现原型链污染,利用原型链污染也能实现XSS,推荐阅读huli大佬的文章,这里不展开讲了
基於 JS 原型鏈的攻擊手法:Prototype Pollution
还有一些常见库可以作为Gadgets
Prototype Pollution and useful Script Gadgets
不过这个沙箱中似乎不太好利用
- 没有可以用于XSS的Sink,比如
innerHTML
- 也没有可以用的依赖库作为
gadget
,比如jQuery
但在sandbox.js
中存在一个特殊的变量访问
1 | function main() { |
code
这个变量没有在js中声明,而是在html中定义,也就是页面中的代码输入框
1 | <textarea id="code" class="code"> </textarea> |
js中可以直接访问code变量访问在DOM的的元素
这种技术也被用于Dom Clobbering,推荐阅读Zeddy大佬的文章
不幸的消息是,这种访问方式会经过原型链
通过污染code变量,可以实现修改代码执行内容
1 | Object.prototype.code={value:"2+2"} |
现在1+1=4了
但这有什么用呢,代码输入本来就是可控的
再看原型链中我们还能做什么
Object.prototype.__defineGetter__()
__defineGetter__
方法可以将一个函数绑定在当前对象的指定属性上,当那个属性的值被读取时,你所绑定的函数就会被调用。
当code被读取时,this
其实指向的是window
,将this
保存下来试试
1 | Object.prototype.__defineGetter__("code",function(){ |
总结一下,这个方案需要执行两次,一次注入原型链污染,第二次触发污染并窃取window
1 | Object.prototype.__defineGetter__("code", function () { |
暂时还没有研究出来只需要执行一次的方案