Skip to content

Commit 096d6ad

Browse files
committed
Land rapid7#3055, heapLib2 integration
2 parents a1aef92 + ee1209b commit 096d6ad

File tree

4 files changed

+293
-0
lines changed

4 files changed

+293
-0
lines changed

data/js/memory/heaplib2.js

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
//heapLib2 namespace
2+
function heapLib2() { }
3+
4+
//These are attributes that will not actually create a bstr
5+
//and directly use the back-end allocator, completely bypassing the cache
6+
var global_attrs = ["title", "lang", "class"];
7+
8+
heapLib2.ie = function(element, maxAlloc)
9+
{
10+
//128mb
11+
this.maxAlloc = 0x8000000;
12+
13+
//make sure that an HTML DOM element is passed
14+
if(!element.nodeType || element.nodeType != 1)
15+
throw "alloc.argument: element not valid";
16+
17+
this.element = element;
18+
19+
if(maxAlloc)
20+
this.maxAlloc = maxAlloc;
21+
22+
//empty the cache
23+
this.Oleaut32EmptyCache();
24+
this.Oleaut32FillCache();
25+
this.Oleaut32EmptyCache();
26+
27+
}
28+
29+
heapLib2.ie.prototype.newelement = function(element)
30+
{
31+
//make sure that an HTML DOM element is passed
32+
if(!element.nodeType || element.nodeType != 1)
33+
throw "alloc.argument: element not valid";
34+
35+
this.element = element;
36+
}
37+
38+
heapLib2.ie.prototype.alloc = function(attr_name, size, cache_ok)
39+
{
40+
if(typeof(cache_ok)==='undefined')
41+
cache_ok = false;
42+
else
43+
cache_ok = true;
44+
45+
//make sure the attribute name is a string
46+
if(typeof attr_name != "string")
47+
throw "alloc.argument: attr_name is not a string";
48+
49+
//make sure that the attribute name is not already present in the html element
50+
if(this.element.getAttribute(attr_name))
51+
throw "alloc.argument: element already contains attr_name: " + attr_name;
52+
53+
//ensure the size is a number
54+
if(typeof size != "number")
55+
throw "alloc.argument: size is not a number: " + size;
56+
57+
//make sure the size isn't one of the special values
58+
if(!cache_ok && (size == 0x20 || size == 0x40 || size == 0x100 || size == 0x8000))
59+
throw "alloc.argument: size cannot be flushed from cache: " + size;
60+
61+
if(size > this.maxAlloc)
62+
throw "alloc.argument: size cannot be greater than maxAlloc(" + this.maxAlloc + ") : " + size;
63+
64+
//the size must be at a 16-byte boundary this can be commented out but
65+
//the allocations will be rounded to the nearest 16-byte boundary
66+
if(size % 16 != 0)
67+
throw "alloc.argument: size be a multiple of 16: " + size;
68+
69+
//20-bytes will be added to the size
70+
//<4-byte size><data><2-byte null>
71+
size = ((size / 2) - 6);
72+
73+
//May have to change this due to allocation side effects
74+
var data = new Array(size).join(cache_ok ? "C" : "$");
75+
76+
var attr = document.createAttribute(attr_name);
77+
this.element.setAttributeNode(attr);
78+
this.element.setAttribute(attr_name, data);
79+
80+
}
81+
82+
//These items will allocate/free memory and should really
83+
//only be used once per element. You can use a new element
84+
//by calling the 'newelement' method above
85+
heapLib2.ie.prototype.alloc_nobstr = function(val)
86+
{
87+
//make sure the aval is a string
88+
if(typeof val != "string")
89+
throw "alloc.argument: val is not a string";
90+
91+
var size = (val.length * 2) + 6;
92+
93+
if(size > this.maxAlloc)
94+
throw "alloc_nobstr.val: string length cannot be greater than maxAlloc(" + this.maxAlloc + ") : " + size;
95+
96+
var i = 0;
97+
var set_gattr = 0;
98+
for(i = 0; i < global_attrs.length; i++)
99+
{
100+
curr_gattr = global_attrs[i];
101+
if(!this.element.getAttribute(curr_gattr))
102+
{
103+
this.element.setAttribute(curr_gattr, "");
104+
this.element.setAttribute(curr_gattr, val);
105+
set_gattr = 1;
106+
break;
107+
}
108+
}
109+
110+
if(set_gattr == 0)
111+
throw "alloc_nobstr: all global attributes are assigned, try a new element";
112+
}
113+
114+
//completely bypass the cache, useful for heap spraying (see heapLib2_test.html)
115+
heapLib2.ie.prototype.sprayalloc = function(attr_name, str)
116+
{
117+
//make sure the attribute name is a string
118+
if(typeof attr_name != "string")
119+
throw "alloc.argument: attr_name is not a string";
120+
121+
//make sure that the attribute name is not already present in the html element
122+
if(this.element.getAttribute(attr_name))
123+
throw "alloc.argument: element already contains attr_name: " + attr_name;
124+
125+
//ensure the size is a number
126+
if(typeof str != "string")
127+
throw "alloc.argument: str is not a string: " + typeof str;
128+
129+
var size = (str.length * 2) + 6;
130+
131+
//make sure the size isn't one of the special values
132+
if(size <= 0x8000)
133+
throw "alloc.argument: bigalloc must be greater than 0x8000: " + size;
134+
135+
if(size > this.maxAlloc)
136+
throw "alloc.argument: size cannot be greater than maxAlloc(" + this.maxAlloc + ") : " + size;
137+
138+
var attr = document.createAttribute(attr_name);
139+
this.element.setAttributeNode(attr);
140+
this.element.setAttribute(attr_name, str);
141+
}
142+
143+
heapLib2.ie.prototype.free = function(attr_name, skip_flush)
144+
{
145+
if(typeof(skip_flush)==='undefined')
146+
skip_flush = false;
147+
else
148+
skip_flush = true;
149+
150+
//make sure that an HTML DOM element is passed
151+
if(!this.element.nodeType || this.element.nodeType != 1)
152+
throw "alloc.argument: element not valid";
153+
154+
//make sure the attribute name is a string
155+
if(typeof attr_name != "string")
156+
throw "alloc.argument: attr_name is not a string";
157+
158+
//make sure that the attribute name is not already present in the html element
159+
if(!this.element.getAttribute(attr_name))
160+
throw "alloc.argument: element does not contain attribute: " + attr_name;
161+
162+
//make sure the cache is full so the chunk returns the general purpose heap
163+
if(!skip_flush)
164+
this.Oleaut32FillCache();
165+
166+
this.element.setAttribute(attr_name, null);
167+
168+
if(!skip_flush)
169+
this.Oleaut32EmptyCache()
170+
}
171+
172+
heapLib2.ie.prototype.Oleaut32FillCache = function()
173+
{
174+
for(var i = 0; i < 6; i++)
175+
{
176+
this.free("cache0x20"+i, true);
177+
this.free("cache0x40"+i, true);
178+
this.free("cache0x100"+i, true);
179+
this.free("cache0x8000"+i, true);
180+
}
181+
}
182+
183+
heapLib2.ie.prototype.Oleaut32EmptyCache = function()
184+
{
185+
for(var i = 0; i < 6; i++)
186+
{
187+
this.alloc("cache0x20"+i, 0x20, true);
188+
this.alloc("cache0x40"+i, 0x40, true);
189+
this.alloc("cache0x100"+i, 0x100, true);
190+
this.alloc("cache0x8000"+i, 0x8000, true);
191+
}
192+
}

lib/msf/core/exploit/http/server.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,13 @@ def heaplib(custom_js = '', opts = {})
719719
Rex::Exploitation::HeapLib.new(custom_js, opts).to_s
720720
end
721721

722+
#
723+
# Returns the heaplib2 javascript
724+
#
725+
def js_heaplib2(custom_js = '', opts = {})
726+
@cache_heaplib2 ||= Rex::Exploitation::Js::Memory.heaplib2(custom_js, opts={})
727+
end
728+
722729
def js_base64
723730
@cache_base64 ||= Rex::Exploitation::Js::Utils.base64
724731
end

lib/rex/exploitation/js/memory.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ def self.mstime_malloc
2424
}).obfuscate
2525
end
2626

27+
def self.heaplib2(custom_js='', opts={})
28+
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "memory", "heaplib2.js"))
29+
30+
unless custom_js.blank?
31+
js << custom_js
32+
end
33+
34+
js = ::Rex::Exploitation::JSObfu.new js
35+
js.obfuscate
36+
return js
37+
end
38+
2739
def self.property_spray
2840
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "memory", "property_spray.js"))
2941

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class Metasploit3 < Msf::Auxiliary
9+
Rank = NormalRanking
10+
11+
include Msf::Exploit::Remote::HttpServer::HTML
12+
13+
def initialize(info={})
14+
super(update_info(info,
15+
'Name' => "heaplib2 test",
16+
'Description' => %q{
17+
This tests heaplib2
18+
},
19+
'License' => MSF_LICENSE,
20+
'Author' => [ 'sinn3r' ],
21+
'References' =>
22+
[
23+
[ 'URL', 'http://metasploit.com' ]
24+
],
25+
'Platform' => 'win',
26+
'Targets' =>
27+
[
28+
[ 'Automatic', {} ]
29+
],
30+
'Privileged' => false,
31+
'DisclosureDate' => "Mar 1 2014",
32+
'DefaultTarget' => 0))
33+
end
34+
35+
36+
def on_request_uri(cli, request)
37+
spray = %Q|
38+
function log(msg) {
39+
console.log("[*] " + msg);
40+
Math.atan2(0x0101, msg);
41+
}
42+
43+
log("Creating element div");
44+
var element = document.createElement("div");
45+
46+
log("heapLib2");
47+
var heaplib = new heapLib2.ie(element, 0x80000);
48+
49+
log("Creating spray");
50+
var spray = unescape("%u4141%u4141");
51+
while (spray.length < 0x20000) { spray += spray };
52+
53+
log("spraying...");
54+
for (var i=0; i<0x400; i++) {
55+
heaplib.sprayalloc("userspray"+i, spray);
56+
}
57+
58+
alert("free is about to happen");
59+
60+
log("freeing...");
61+
for (var i=0; i<0x400; i++) {
62+
heaplib.free("userspray"+i);
63+
}
64+
|
65+
66+
html = %Q|
67+
<html>
68+
<script>
69+
#{js_heaplib2(spray)}
70+
</script>
71+
</html>
72+
|
73+
74+
print_status("Sending html")
75+
send_response(cli, html, {'Content-Type'=>'text/html'})
76+
end
77+
78+
def run
79+
exploit
80+
end
81+
82+
end

0 commit comments

Comments
 (0)