Lucene search
K

SMTP NTLM Domain Extraction

🗓️ 20 Nov 2014 16:02:21Reported by Rich Whitcroft <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 20 Views

Extract Windows domain name from SMTP NTLM challeng

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

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::Smtp
  include Msf::Auxiliary::Report
  include Msf::Auxiliary::Scanner

  def initialize
    super(
      'Name'        => 'SMTP NTLM Domain Extraction',
      'Description' => 'Extract the Windows domain name from an SMTP NTLM challenge.',
      'References'  => [ ['URL', 'https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smtpntlm/a048c79f-7597-401b-bcb4-521d682de765' ] ],
      'Author'      => [ 'Rich Whitcroft <rwhitcroft[at]digitalboundary.net>' ],
      'License'     => MSF_LICENSE
    )

    register_options(
      [
        Opt::RPORT(25),
        OptString.new('EHLO_DOMAIN', [ true, 'The domain to send with the EHLO command', 'localhost' ]),
      ])

    deregister_options('MAILTO', 'MAILFROM')
  end

  def run_host(ip)
    begin
      domain = nil
      connect

      unless banner
        vprint_error("#{rhost}:#{rport} No banner received, aborting...")
        return
      end

      vprint_status("#{rhost}:#{rport} Connected: #{banner.strip.inspect}")

      # Report the last line of the banner as services information (typically the interesting one)
      report_service(host: rhost, port: rport, name: 'smtp', proto: 'tcp', info: banner.strip.split("\n").last)

      # Send a EHLO and parse the extensions returned
      sock.puts("EHLO " + datastore['EHLO_DOMAIN'] + "\r\n")

      # Find all NTLM references in the EHLO response
      exts = sock.get_once.to_s.split(/\n/).grep(/NTLM/)
      if exts.length == 0
        vprint_error("#{rhost}:#{rport} No NTLM extensions found")
        return
      end

      exts.each do |ext|

        # Extract the reply minus the first 4 chars (response code + dash)
        e = ext[4..-1].chomp

        # Try the usual AUTH NTLM approach if possible, otherwise echo the extension back to server
        if e =~ /AUTH.*NTLM/
          sock.puts("AUTH NTLM\r\n")
          vprint_status("#{rhost}:#{rport} Sending AUTH NTLM")
        else
          sock.puts(e + "\r\n")
          vprint_status("#{rhost}:#{rport} Sending #{e}")
        end

        # We expect a "334" code to go ahead with NTLM auth
        reply = sock.get_once.to_s
        if reply !~ /^334\s+/m
          vprint_status("#{rhost}:#{rport} Expected a 334 response, received #{reply.strip.inspect} aborting...")
          break
        else
          # Send the NTLM AUTH blob to tell the server we're ready to auth
          blob = "TlRMTVNTUAABAAAAt4II4gAAAAAAAAAAAAAAAAAAAAAFAs4OAAAADw=="
          sock.puts(blob + "\r\n")

          # Capture the challenge sent by server
          challenge = sock.get_once.to_s.split(/\s+/).last

          if challenge.length == 0
            vprint_status("#{rhost}:#{rport} Empty challenge response, aborting...")
            break
          end

          begin
            # Extract the domain out of the NTLM response
            ntlm_reply = Rex::Proto::NTLM::Message.parse(Rex::Text.decode_base64(challenge))
            if ! ntlm_reply && ntlm_reply.has_key?(:target_name)
              vprint_status("#{rhost}:#{rport} Invalid challenge response, aborting...")
              break
            end

            # TODO: Extract the server name from :target_info as well
            domain = ntlm_reply[:target_name].value.to_s.gsub(/\x00/, '')
            if domain.to_s.length == 0
              vprint_status("#{rhost}:#{rport} Invalid target name in challenge response, aborting...")
              break
            end

            print_good("#{rhost}:#{rport} Domain: #{domain}")
            report_note(host: rhost, port: rport, proto: 'tcp', type: 'smtp.ntlm_auth_info', data: { domain: domain })
            break

          rescue ::Rex::ArgumentError
            vprint_status("#{rhost}:#{rport} Invalid challenge response message, aborting...")
            break
          end
        end
      end

      if ! domain
        vprint_error("#{rhost}:#{rport} No NTLM domain found")
      end

    rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error
      # Ignore common networking and response timeout errors
    ensure
      disconnect
    end
  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

16 Feb 2022 23:22Current
7.3High risk
Vulners AI Score7.3
20