var getContextsCompletion = function (cmd) { var completion = []; _.each(wsrc.get().contexts, function (v, k) { completion.push('$_.' + cmd + '("' + k + '")'); }); web_repl.rli.complete(completion); };
process.on('exit', function () { if (web_repl.rli._hardClosed) { var rc = wsrc.get(); } else { var rc = wsrc.saveContext('_previous', $_); } rc.history = web_repl.rli.history; wsrc.write(rc, cookies); });
function WebShell(stream) { function httpSuccess(status) { return 200 <= status && status < 300; } function httpRedirection(status) { return 300 <= status && status < 400; } function httpClientError(status) { return 400 <= status && status < 500; } function httpServerError(status) { return 500 <= status && status < 600; } function parseURL(urlStr, protocolHelp) { var u = url.parse(urlStr); if (protocolHelp && !u.protocol) { u = url.parse('http://'+urlStr); } u.port = u.port || (u.protocol === 'https:' ? 443 : 80); u.pathname = u.pathname || '/'; return u; } oldParseREPLKeyword = repl.REPLServer.prototype.parseREPLKeyword; wsrc.loadContext('_previous', $_); var getContextsCompletion = function (cmd) { var completion = []; _.each(wsrc.get().contexts, function (v, k) { completion.push('$_.' + cmd + '("' + k + '")'); }); web_repl.rli.complete(completion); }; var getObjectCompletion = function (cmd, obj) { var completion = []; _.each(obj, function (v, k) { var completer = cmd + '.' + k; if (_.isFunction(obj[k])) { completer += '('; } completion.push(completer); }); web_repl.rli.complete(completion); }; web_repl = new repl.REPLServer("webshell> ", stream); process.on('exit', function () { if (web_repl.rli._hardClosed) { var rc = wsrc.get(); } else { var rc = wsrc.saveContext('_previous', $_); } rc.history = web_repl.rli.history; wsrc.write(rc, cookies); }); web_repl.rli.history = wsrc.get().history; // trap tab key: web_repl.stream.on('data', function (chunk) { if (web_repl.rli.cursor != web_repl.rli.line.length) { // cursor is not at the end of the line return true; } if (chunk != String.fromCharCode(9)) { // not the tab key return true; } var split = web_repl.rli.line.split(' '); if (_.include(verbs, split[0])) { return web_repl.rli.completeHistory(); } else if (web_repl.rli.line.substring(0, '$_.loadContext('.length) == '$_.loadContext(') { getContextsCompletion('loadContext'); } else if (web_repl.rli.line.substring(0, '$_.delContext('.length) == '$_.delContext(') { getContextsCompletion('delContext'); } else if (web_repl.rli.line.substring(0, '$_.'.length) == '$_.') { var pieces = web_repl.rli.line.split('.'); // discard last piece: pieces.pop(); switch (pieces.length) { case 1: // "$_" getObjectCompletion('$_', $_); break; case 2: // "$_.something" if ($_[pieces[1]]) { getObjectCompletion(pieces.join('.'), $_[pieces[1]]); } break; case 3: // "$_.something.somethingelse" if ($_[pieces[1]][pieces[2]]) { getObjectCompletion(pieces.join('.'), $_[pieces[1]][pieces[2]]); } break; case 4: // "$_.something.somethingelse.other" if ($_[pieces[1]][pieces[2]][pieces[3]]) { getObjectCompletion(pieces.join('.'), $_[pieces[1]][pieces[2]][pieces[3]]); } break; default: // too deep; } } else if (web_repl.rli.line.substring(0, '$_.toolbox.'.length) == '$_.toolbox.') { getObjectCompletion('$_.toolbox', $_.toolbox); } else if (web_repl.rli.line.substring(0, 3) == '$_.') { getObjectCompletion('$_', $_); } return true; }); var ctx = web_repl.context; repl.REPLServer.prototype.parseREPLKeyword = this.parseREPLKeyword; formatStatus = function(code, url) { var msg = "HTTP " + code + " " + stylize(url, 'white'); if (httpSuccess(code)) { console.log(stylize(msg, 'green')); } else if (httpRedirection(code)) { console.log(stylize(msg, 'yellow')); } else if (httpClientError(code) || httpServerError(code)) { console.log(stylize(msg, 'red')); } }; normalizeName = function(name) { return _.map(name.split('-'), function(s) { return s[0].toUpperCase() + s.slice(1, s.length); }).join('-'); }; printHeader = function(value, name) { sys.puts(normalizeName(name) + ": " + value); }; ctx.$_ = $_; doRedirect = function() { var location = $_.headers.location; if (location) { var locationUrl = parseURL(location, false); if (!locationUrl.protocol) { var prevUrl = parseURL($_.previousUrl); // a relative URL, auto-populate with previous URL's info locationUrl.protocol = prevUrl.protocol; locationUrl.hostname = prevUrl.hostname; if (prevUrl.auth) { locationUrl.auth = prevUrl.auth; } if (prevUrl.port) { locationUrl.port = prevUrl.port; } location = url.format(locationUrl); } doHttpReq($_.previousVerb, location); } else { sys.puts(stylize("No previous request!", 'red')); } }; ctx.$_.follow = doRedirect; ctx.$_.saveContext = function (name) { wsrc.saveContext(name, $_); }; ctx.$_.loadContext = function (name) { wsrc.loadContext(name, $_); }; ctx.$_.delContext = function (name) { wsrc.delContext(name, $_); }; function base64Encode(str) { return (new Buffer(str, 'ascii')).toString('base64'); } function makeHeaders(url) { var hostHeader = url.hostname; if (url.protocol === 'https:' && url.port !== 443) { hostHeader += ":" + url.port; } else if (url.protocol === 'http:' && url.port !== 80) { hostHeader += ":" + url.port; } var headers = { 'Host': hostHeader, 'User-Agent': 'Webshell/' + webshellVersion + ' node.js/' + process.version, 'Accept': 'application/json, */*' }; if (url.auth) { headers['Authorization'] = 'Basic ' + base64Encode(url.auth); } if ($_.useCookies) { var cookie = cookies.headerFor(url); if (cookie) { headers['Cookie'] = cookie; } } return headers; } function ResultHolder(verb, url) { this.verb = verb; this.url = url; this.inspectStr = verb + " " + url; } ResultHolder.prototype = { inspect: function() { var str = this.inspectStr; this.inspectStr = "[Pending]"; return str; } }; _.define(ResultHolder.prototype, 'finalize', function() { _.define(this, 'inspect', null); }); doHttpReq = function(verb, urlStr, cb) { web_repl.suppressPrompt++; result = new ResultHolder(verb, urlStr); var u = parseURL(urlStr); var client = http.createClient(u.port, u.hostname, u.protocol === 'https:'); var jsonHeaders = ['application/json', 'text/x-json']; $_.previousVerb = verb; $_.previousUrl = urlStr; var content = null; var headers = makeHeaders(u); // merge in $_.requestHeaders if (typeof $_.requestHeaders != 'object') { // something's not right here, so reset requestHeaders // note: setting $_.requestHeaders to {} is also a good way for users // to reset things like the Accept header $_.requestHeaders = {}; } _.each($_.requestHeaders, function(v, k) { if (k.toLowerCase() != 'host') { // host is provided by makeHeaders() headers[k] = v; } }); switch (verb) { case 'POST': if (typeof $_.requestData == "object") { content = querystring.stringify($_.requestData); headers['Content-type'] = 'application/x-www-form-urlencoded'; } else { if (!headers['Content-type']) { headers['Content-type'] = 'application/x-www-form-urlencoded'; } content = $_.requestData; } break; case 'PUT': try { content = fs.readFileSync($_.requestData); } catch (e) { sys.puts(stylize("Set $_.requestData to the filename to PUT", 'red')); web_repl.displayPrompt(true); return false; } if (!headers['Content-type']) { headers['Content-type'] = 'application/octet-stream'; } break; } var path = u.pathname; if (u.search) { path += u.search; } var request = client.request(verb, path, headers); if (content) { headers['Content-length'] = content.length; request.write(content); } $_.requestHeaders = headers; request.end(); request.on('response', function (response) { if ($_.printResponse) { formatStatus(response.statusCode, u.href); } ctx.$_.status = response.statusCode; if ($_.printHeaders) { _.each(response.headers, printHeader); } ctx.$_.headers = response.headers; if ($_.useCookies) { $_.cookies.update(u.hostname, response.headers['set-cookie']); } response.setEncoding('utf8'); var body = ""; response.on('data', function (chunk) { body += chunk; }); response.on('end', function() { $_.raw = body; $_.document = $_.json = null; if (httpSuccess(response.statusCode)) { if ($_.headers['content-type'] && _.include(jsonHeaders, $_.headers['content-type'].split('; ')[0])) { $_.json = JSON.parse(body); } } _.extend(result, {raw: $_.raw, headers: $_.headers, statusCode: $_.status, json: $_.json}); result.finalize(); if (cb) { cb($_); } web_repl.displayPrompt(true); }); }); return result; }; _.each(verbs, function (v) { $_[v.toLowerCase()] = function(url, cb) { return doHttpReq(v, url, cb); }; }); }