-
Notifications
You must be signed in to change notification settings - Fork 13.8k
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
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
136cc96
Accessors, cache stop lock fix, and resp header
a8c3adf
Move recursion bit logic into the fwd lookup
e3c3728
Update Resolver's use of Rex Sockets
6e86ac6
Tweak Server and Resolver
b1b4355
Fixup Resolver socket creation slop
332862b
Server needs a resolver to perform fwd lookups
7b37062
Resolver - add accessors for comm and ctx
3afc5d2
Add running? check to Server
4467cef
Allow Server to start without caching
b5c89c4
Server::Cache.cache_record graceful failure
2f0003b
Implement native DNS for Msf Namespace
60ae0e5
Remove disclosure date from module
15f5854
Add sample Procs to native_server module
0e5ec4c
Fix processing of static hosts in Msf...Server
a9f1fce
Set resolver comm and ctx manually
8f9d987
Export common socket functionality from TcpServer
2679c26
Create and implement Rex::IO::GramServer mixin
2347c8d
Create basic packet manipulation modules
de0867a
Address wchen-r7's initial comments
3b7c195
Rex::Proto::DNS::Packet::Raw convenience methods
00611e9
Rex::Proto::DNS::Packet generate req/resp
820a07d
Update native dns server auxiliary module
570987a
Missing lines from Proto::DNS::Packet
e86ca56
add :closed? method to Meterpreter Channel
a555ee7
Fix typo in Rex DNS Server
d649629
Packet.valid_hostname? should be a class method
dafadb9
Address Egypt's GH comments
c442ad2
Update class name for CI
fd6da21
Update class name for CI, again
fec23cf
Remove setsockopt calls from DNS server
b60990c
Use a MockDnsClient object for request state
f24448c
Add :client accessor to Remote::DNS::Client
07dd59f
Import native DNS spoofing module and cleanup
deef4a9
Allow DNS::Server::Cache to find '*' names
c6c104d
Performance and entropy improvement
1a253f9
Finalize DNS spoofing module
e1e159f
DNS spoofer - capture BRE block
7cc7fe7
Address net-dns additional records empty array
f76adf6
Update Gemfile.lock with rex-socket bump
c65c037
Migrate native DNS services to Dnsruby data format
ee21865
Cleanup Msf server and add dnsruby to gemspec
de411e7
Msf DNS server - add :use_resolver? method
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.