Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement native DNS for Msf Namespace #6611

Merged
merged 43 commits into from
Jan 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9f49903
Initial implementation of Rex::Proto::DNS
Feb 24, 2016
136cc96
Accessors, cache stop lock fix, and resp header
Feb 24, 2016
a8c3adf
Move recursion bit logic into the fwd lookup
Feb 24, 2016
e3c3728
Update Resolver's use of Rex Sockets
Feb 24, 2016
6e86ac6
Tweak Server and Resolver
Feb 24, 2016
b1b4355
Fixup Resolver socket creation slop
Feb 24, 2016
332862b
Server needs a resolver to perform fwd lookups
Feb 25, 2016
7b37062
Resolver - add accessors for comm and ctx
Feb 25, 2016
3afc5d2
Add running? check to Server
Feb 25, 2016
4467cef
Allow Server to start without caching
Feb 25, 2016
b5c89c4
Server::Cache.cache_record graceful failure
Feb 25, 2016
2f0003b
Implement native DNS for Msf Namespace
Feb 25, 2016
60ae0e5
Remove disclosure date from module
Feb 25, 2016
15f5854
Add sample Procs to native_server module
Feb 26, 2016
0e5ec4c
Fix processing of static hosts in Msf...Server
Feb 26, 2016
a9f1fce
Set resolver comm and ctx manually
Feb 28, 2016
8f9d987
Export common socket functionality from TcpServer
Feb 28, 2016
2679c26
Create and implement Rex::IO::GramServer mixin
Feb 28, 2016
2347c8d
Create basic packet manipulation modules
Feb 29, 2016
de0867a
Address wchen-r7's initial comments
Feb 29, 2016
3b7c195
Rex::Proto::DNS::Packet::Raw convenience methods
Feb 29, 2016
00611e9
Rex::Proto::DNS::Packet generate req/resp
Mar 1, 2016
820a07d
Update native dns server auxiliary module
Mar 1, 2016
570987a
Missing lines from Proto::DNS::Packet
Mar 1, 2016
e86ca56
add :closed? method to Meterpreter Channel
Mar 1, 2016
a555ee7
Fix typo in Rex DNS Server
Mar 1, 2016
d649629
Packet.valid_hostname? should be a class method
Mar 2, 2016
dafadb9
Address Egypt's GH comments
Mar 15, 2016
c442ad2
Update class name for CI
Mar 17, 2016
fd6da21
Update class name for CI, again
Mar 17, 2016
fec23cf
Remove setsockopt calls from DNS server
Mar 17, 2016
b60990c
Use a MockDnsClient object for request state
Mar 21, 2016
f24448c
Add :client accessor to Remote::DNS::Client
Jun 28, 2016
07dd59f
Import native DNS spoofing module and cleanup
Jul 11, 2016
deef4a9
Allow DNS::Server::Cache to find '*' names
Jul 12, 2016
c6c104d
Performance and entropy improvement
Jul 13, 2016
1a253f9
Finalize DNS spoofing module
Jul 14, 2016
e1e159f
DNS spoofer - capture BRE block
Jul 18, 2016
7cc7fe7
Address net-dns additional records empty array
Jun 25, 2017
f76adf6
Update Gemfile.lock with rex-socket bump
Jun 25, 2017
c65c037
Migrate native DNS services to Dnsruby data format
Jan 12, 2018
ee21865
Cleanup Msf server and add dnsruby to gemspec
Jan 13, 2018
de411e7
Msf DNS server - add :use_resolver? method
Jan 13, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ GEM
metasm
rex-core
rex-text
rex-socket (0.1.6)
rex-socket (0.1.7)
rex-core
rex-sslscan (0.1.4)
rex-socket
Expand Down
20 changes: 20 additions & 0 deletions lib/msf/core/exploit/dns.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: binary -*-
require 'msf/core'
require 'rex/proto/dns'


module Msf

###
#
# This namespace exposes methods for interacting with and providing services
#
###
module Exploit::Remote::DNS

end
end

require 'msf/core/exploit/dns/common'
require 'msf/core/exploit/dns/client'
require 'msf/core/exploit/dns/server'
217 changes: 217 additions & 0 deletions lib/msf/core/exploit/dns/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# -*- coding: binary -*-
require 'msf/core'
require 'rex/proto/dns'


module Msf

###
#
# This module exposes methods for querying a remote DNS service
#
###
module Exploit::Remote::DNS
module Client

include Common
include Exploit::Remote::Udp
include Exploit::Remote::Tcp

#
# Initializes an exploit module that interacts with a DNS server.
#
def initialize(info = {})
super

deregister_options('RHOST')
register_options(
[
Opt::RPORT(53),
Opt::Proxies,
OptString.new('DOMAIN', [ false, "The target domain name"]),
OptString.new('NS', [ false, "Specify the nameservers to use for queries, space separated" ]),
OptString.new('SEARCHLIST', [ false, "DNS domain search list, comma separated"]),
OptInt.new('THREADS', [true, "Number of threads to use in threaded queries", 1])
], Exploit::Remote::DNS::Client
)

register_advanced_options(
[
OptString.new('DnsClientDefaultNS', [ false, "Specify the default to use for queries, space separated", '8.8.8.8 8.8.4.4' ]),
OptInt.new('DnsClientRetry', [ false, "Number of times to try to resolve a record if no response is received", 2]),
OptInt.new('DnsClientRetryInterval', [ false, "Number of seconds to wait before doing a retry", 2]),
OptBool.new('DnsClientReportARecords', [false, "Add hosts found via BRT and RVL to DB", true]),
OptBool.new('DnsClientRVLExistingOnly', [false, "Only perform lookups on hosts in DB", true]),
OptBool.new('DnsClientTcpDns', [false, "Run queries over TCP", false]),
OptPath.new('DnsClientResolvconf', [true, "Resolvconf formatted configuration file to use for Resolver", "/dev/null"])
], Exploit::Remote::DNS::Client
)

register_autofilter_ports([ 53 ]) if respond_to?(:register_autofilter_ports)
register_autofilter_services(%W{ dns }) if respond_to?(:register_autofilter_services)
end


#
# Convenience wrapper around Resolver's query method - send DNS request
#
# @param domain [String] Domain for which to request a record
# @param type [String] Type of record to request for domain
#
# @return [Dnsruby::RR] DNS response
def query(domain = datastore['DOMAIN'], type = 'A')
client.query(domain, type)
end

#
# Performs a set of asynchronous lookups for an array of domain,type pairs
#
# @param queries [Array] Set of domain,type pairs to pass into #query
# @param threadmax [Fixnum] Max number of running threads at a time
# @param block [Proc] Code block to execute with the query result
#
# @return [Array] Resulting set of responses or responses processed by passed blocks
def query_async(queries = [], threadmax = datastore['THREADS'], &block)
running = []
while !queries.empty?
domain, type = queries.shift
running << framework.threads.spawn("Module(#{self.refname})-#{domain} #{type}", false) do |qat|
if block
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there ever a reason to run async without a block? Maybe if you just want to populate the cache?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, lots of queries can be issued, and since the cache, server, and resolver components can all be shimmed to do "other things," its useful to have the async behavior in each tier.

block.call(query(domain,type))
else
query(domain,type)
end
end
while running.select(&:alive?).count >= threadmax
Rex::ThreadSafe.sleep(1)
end
end
return running.join
end

#
# Switch DNS forwarders in resolver with thread safety
#
# @param ns [Array, String] List of (or single) nameservers to use
def set_nameserver(ns = [])
if ns.respond_to?(:split)
ns = [ns]
end
@lock.synchronize do
@dns_resolver.nameserver = ns.flatten
end
end

#
# Switch nameservers to use explicit NS or SOA for target
#
# @param domain [String] Domain for which to find SOA
def switchdns(domain)
if datastore['NS'].blank?
resp_soa = client.query(target, "SOA")
if (resp_soa)
(resp_soa.answer.select { |i| i.is_a?(Dnsruby::RR::SOA)}).each do |rr|
resp_1_soa = client.search(rr.mname)
if (resp_1_soa and resp_1_soa.answer[0])
set_nameserver(resp_1_soa.answer.map(&:address).compact.map(&:to_s))
print_status("Set DNS Server to #{target} NS: #{client.nameserver.join(', ')}")
break
end
end
end
else
vprint_status("Using DNS Server: #{client.nameserver.join(', ')}")
client.nameserver = process_nameservers
end
end

#
# Detect if target has wildcards enabled for a record type
#
# @param target [String] Domain to test
# @param type [String] Record type to test
#
# @return [String] Address which is returned for wildcard requests
def wildcard(domain, type = "A")
addr = false
rendsub = rand(10000).to_s
response = query("#{rendsub}.#{target}", type)
if response.answer.length != 0
vprint_status("This domain has wildcards enabled!!")
response.answer.each do |rr|
print_status("Wildcard IP for #{rendsub}.#{target} is: #{rr.address.to_s}") if rr.class != Dnsruby::RR::CNAME
addr = rr.address.to_s
end
end
return addr
end

#
# Create and configure Resolver object
#
def setup_resolver
options.validate(datastore) # This is a hack, DS values should not be Strings prior to this
config = {
:config_file => datastore['DnsClientResolvconf'],
:nameservers => process_nameservers,
:port => datastore['RPORT'],
:retry_number => datastore['DnsClientRetry'].to_i,
:retry_interval => datastore['DnsClientRetryInterval'].to_i,
:use_tcp => datastore['DnsClientTcpDns'],
:context => {'Msf' => framework, 'MsfExploit' => self}
}
if datastore['SEARCHLIST']
if datastore['SEARCHLIST'].split(',').all? do |search|
search.match(MATCH_HOSTNAME)
end
config[:search_list] = datastore['SEARCHLIST'].split(',')
else
raise 'Domain search list must consist of valid domains'
end
end
if datastore['CHOST']
config[:source_address] = IPAddr.new(datastore['CHOST'].to_s)
end
if datastore['CPORT']
config[:source_port] = datastore['CPORT'] unless datastore['CPORT'] == 0
end
if datastore['Proxies']
vprint_status("Using DNS/TCP resolution for proxy config")
config[:use_tcp] = true
config[:proxies] = datastore['Proxies']
end
@dns_resolver_lock = Mutex.new unless @dns_resolver_lock
@dns_resolver = Rex::Proto::DNS::Resolver.new(config)
end

#
# Convenience method for DNS resolver as client
# Executes setup_resolver if none exists
#
def client
setup_resolver unless @dns_resolver
@dns_resolver
end

#
# Sets the resolver's nameservers
# Uses explicitly defined NS option if set
# Uses RHOSTS if not explicitly defined
def process_nameservers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this whole method is more complicated than it needs to be. How about this?

def process_nameservers
  if datastore['NS'].blank?
    nameservers = datastore['DnsClientDefaultNS'].split(/\s|,/)
  else
    nameservers = datastore['NS'].split(/\s|,/)
  end

  invalid = nameservers.select { |ns| !Rex::Socket.dotted_ip?(ns) } 
  if !invalid.empty?
    raise "Nameservers must be IP addresses. The following were invalid: #{invalid.join(", ")}"
  end

  nameservers
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use this method

if datastore['NS'].blank?
nameservers = datastore['DnsClientDefaultNS'].split(/\s|,/)
else
nameservers = datastore['NS'].split(/\s|,/)
end

invalid = nameservers.select { |ns| !Rex::Socket.dotted_ip?(ns) }
if !invalid.empty?
raise "Nameservers must be IP addresses. The following were invalid: #{invalid.join(", ")}"
end

nameservers
end

end
end
end
22 changes: 22 additions & 0 deletions lib/msf/core/exploit/dns/common.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: binary -*-
require 'msf/core'
require 'rex/proto/dns'


module Msf

###
#
# This module exposes methods for querying a remote DNS service
#
###
module Exploit::Remote::DNS
module Common

MATCH_HOSTNAME = Rex::Proto::DNS::Constants::MATCH_HOSTNAME

Packet = Rex::Proto::DNS::Packet

end
end
end
Loading