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/lib/rex/proto/dns/custom_nameserver_provider.rb
Views: 11704
require 'rex/proto/dns/upstream_resolver'12module Rex3module Proto4module DNS56##7# Provides a DNS resolver the ability to use different nameservers8# for different requests, based on the domain being queried.9##10module CustomNameserverProvider11CONFIG_KEY_BASE = 'framework/dns'12CONFIG_VERSION = Rex::Version.new('1.0')1314#15# A Comm implementation that always reports as dead, so should never16# be used. This is used to prevent DNS leaks of saved DNS rules that17# were attached to a specific channel.18##19class CommSink20include Msf::Session::Comm21def alive?22false23end2425def supports_udp?26# It won't be used anyway, so let's just say we support it27true28end2930def sid31'previous MSF session'32end33end3435def init36@upstream_rules = []3738resolvers = [UpstreamResolver.create_static]39if @config[:nameservers].empty?40# if no nameservers are specified, fallback to the system41resolvers << UpstreamResolver.create_system42else43# migrate the originally configured name servers44resolvers += @config[:nameservers].map(&:to_s)45@config[:nameservers].clear46end4748add_upstream_rule(resolvers)4950nil51end5253# Reinitialize the configuration to its original state.54def reinit55parse_config_file56parse_environment_variables5758self.static_hostnames.flush59self.static_hostnames.parse_hosts_file6061init6263cache.flush if respond_to?(:cache)6465nil66end6768# Check whether or not there is configuration data in Metasploit's configuration file which is persisted on disk.69def has_config?70config = Msf::Config.load71version = config.fetch(CONFIG_KEY_BASE, {}).fetch('configuration_version', nil)72if version.nil?73@logger.info 'DNS configuration can not be loaded because the version is missing'74return false75end7677their_version = Rex::Version.new(version)78if their_version > CONFIG_VERSION # if the config is newer, it's incompatible (we only guarantee backwards compat)79@logger.info "DNS configuration version #{their_version} can not be loaded because it is too new"80return false81end8283my_minimum_version = Rex::Version.new(CONFIG_VERSION.canonical_segments.first.to_s)84if their_version < my_minimum_version # can not be older than our major version85@logger.info "DNS configuration version #{their_version} can not be loaded because it is too old"86return false87end8889true90end9192#93# Save the custom settings to the MSF config file94#95def save_config96new_config = {97'configuration_version' => CONFIG_VERSION.to_s98}99Msf::Config.save(CONFIG_KEY_BASE => new_config)100101save_config_upstream_rules102save_config_static_hostnames103end104105#106# Load the custom settings from the MSF config file107#108def load_config109unless has_config?110raise ResolverError.new('There is no compatible configuration data to load')111end112113load_config_entries114load_config_static_hostnames115end116117# Add a custom nameserver entry to the custom provider.118#119# @param [Array<String>] resolvers The list of upstream resolvers that would be used for this custom rule.120# @param [Msf::Session::Comm] comm The communication channel to be used for these DNS requests.121# @param [String] wildcard The wildcard rule to match a DNS request against.122# @param [Integer] index The index at which to insert the rule, defaults to -1 to append it at the end.123def add_upstream_rule(resolvers, comm: nil, wildcard: '*', index: -1)124resolvers = [resolvers] if resolvers.is_a?(String) # coerce into an array of strings125126@upstream_rules.insert(index, UpstreamRule.new(127wildcard: wildcard,128resolvers: resolvers,129comm: comm130))131end132133#134# Remove upstream rules with the given indexes135# Ignore entries that are not found136# @param ids [Array<Integer>] The IDs to removed137# @return [Array<UpstreamRule>] The removed entries138def remove_ids(ids)139removed = []140ids.sort.reverse.each do |id|141upstream_rule = @upstream_rules.delete_at(id)142removed << upstream_rule if upstream_rule143end144145removed.reverse146end147148def flush149@upstream_rules.clear150end151152# The nameservers that match the given packet153# @param packet [Dnsruby::Message] The DNS packet to be sent154# @raise [ResolveError] If the packet contains multiple questions, which would end up sending to a different set of nameservers155# @return [Array<Array>] A list of nameservers, each with Rex::Socket options156#157def upstream_resolvers_for_packet(packet)158unless feature_set.enabled?(Msf::FeatureManager::DNS)159return super160end161# Leaky abstraction: a packet could have multiple question entries,162# and each of these could have different nameservers, or travel via163# different comm channels. We can't allow DNS leaks, so for now, we164# will throw an error here.165results_from_all_questions = []166packet.question.each do |question|167name = question.qname.to_s168upstream_rule = self.upstream_rules.find { |ur| ur.matches_name?(name) }169170if upstream_rule171upstream_resolvers = upstream_rule.resolvers172else173# Fall back to default nameservers174upstream_resolvers = super175end176results_from_all_questions << upstream_resolvers.uniq177end178results_from_all_questions.uniq!179if results_from_all_questions.size != 1180raise ResolverError.new('Inconsistent nameserver entries attempted to be sent in the one packet')181end182183results_from_all_questions[0]184end185186def self.extended(mod)187mod.init188end189190def set_framework(framework)191self.feature_set = framework.features192end193194def upstream_rules195@upstream_rules.dup196end197198private199200def load_config_entries201config = Msf::Config.load202203with_rules = []204config.fetch("#{CONFIG_KEY_BASE}/rules", {}).each do |_name, value|205wildcard, resolvers, uses_comm = value.split(';')206wildcard = '*' if wildcard.blank?207resolvers = resolvers.split(',')208uses_comm.downcase!209210raise Rex::Proto::DNS::Exceptions::ConfigError.new('DNS parsing failed: Comm must be true or false') unless ['true','false'].include?(uses_comm)211raise Rex::Proto::DNS::Exceptions::ConfigError.new('Invalid DNS config: Invalid upstream DNS resolver') unless resolvers.all? {|resolver| UpstreamRule.valid_resolver?(resolver) }212raise Rex::Proto::DNS::Exceptions::ConfigError.new('Invalid DNS config: Invalid rule') unless UpstreamRule.valid_wildcard?(wildcard)213214comm = uses_comm == 'true' ? CommSink.new : nil215with_rules << UpstreamRule.new(216wildcard: wildcard,217resolvers: resolvers,218comm: comm219)220end221222# Now that config has successfully read, update the global values223@upstream_rules = with_rules224end225226def load_config_static_hostnames227config = Msf::Config.load228229static_hostnames.flush230config.fetch("#{CONFIG_KEY_BASE}/static_hostnames", {}).each do |_name, value|231hostname, ip_addresses = value.split(';', 2)232ip_addresses.split(',').each do |ip_address|233next if ip_address.blank?234235unless Rex::Socket.is_ip_addr?(ip_address)236raise Rex::Proto::DNS::Exceptions::ConfigError.new('Invalid DNS config: Invalid IP address')237end238239static_hostnames.add(hostname, ip_address)240end241end242end243244def save_config_upstream_rules245new_config = {}246@upstream_rules.each_with_index do |entry, index|247val = [248entry.wildcard,249entry.resolvers.map do |resolver|250resolver.type == Rex::Proto::DNS::UpstreamResolver::Type::DNS_SERVER ? resolver.destination : resolver.type.to_s251end.join(','),252(!entry.comm.nil?).to_s253].join(';')254new_config["##{index}"] = val255end256Msf::Config.save("#{CONFIG_KEY_BASE}/rules" => new_config)257end258259def save_config_static_hostnames260new_config = {}261static_hostnames.each_with_index do |(hostname, addresses), index|262val = [263hostname,264(addresses.fetch(Dnsruby::Types::A, []) + addresses.fetch(Dnsruby::Types::AAAA, [])).join(',')265].join(';')266new_config["##{index}"] = val267end268Msf::Config.save("#{CONFIG_KEY_BASE}/static_hostnames" => new_config)269end270271attr_accessor :feature_set272end273end274end275end276277278