Migrate native DNS services to Dnsruby data format

Dnsruby provides advanced options like DNSSEC in its data format
and is a current and well supported library.
The infrastructure services - resolver, server, etc, were designed
for a standalone configuration, and carry entirely too much weight
and redundancy to implement for this context. Instead of porting
over their native resolver, update the Net::DNS subclassed Rex
Resolver to use Dnsruby data formats and method calls.
Update the Msf namespace infrastructure mixins and native server
module with new method calls and workarounds for some instance
variables having only readers without writers. Implement the Rex
ServerManager to start and stop the DNS service adding relevant
alias methods to the Rex::Proto::DNS::Server class.

Rex services are designed to be modular and lightweight, as well
as implement the sockets, threads, and other low-level interfaces.
Dnsruby's operations classes implement their own threading and
socket semantics, and do not fit with the modular mixin workflow
used throughout Framework. So while the updated resolver can be
seen as adding rubber to the tire fire, converting to dnsruby's
native classes for resolvers, servers, and caches, would be more
like adding oxy acetylene and heavy metals.

Testing:
  Internal tests for resolution of different record types locally
and over pivot sessions.
MS-2855/keylogger-mettle-extension
RageLtMan 2018-01-12 05:00:00 -05:00
parent f76adf6a62
commit c65c03722c
6 changed files with 176 additions and 76 deletions

View File

@ -47,8 +47,8 @@ module Client
], Exploit::Remote::DNS::Client ], Exploit::Remote::DNS::Client
) )
register_autofilter_ports([ 53 ]) register_autofilter_ports([ 53 ]) if respond_to?(:register_autofilter_ports)
register_autofilter_services(%W{ dns }) register_autofilter_services(%W{ dns }) if respond_to?(:register_autofilter_services)
end end
@ -58,7 +58,7 @@ module Client
# @param domain [String] Domain for which to request a record # @param domain [String] Domain for which to request a record
# @param type [String] Type of record to request for domain # @param type [String] Type of record to request for domain
# #
# @return [Net::DNS::RR] DNS response # @return [Dnsruby::RR] DNS response
def query(domain = datastore['DOMAIN'], type = 'A') def query(domain = datastore['DOMAIN'], type = 'A')
client.query(domain, type) client.query(domain, type)
end end
@ -110,7 +110,7 @@ module Client
if datastore['NS'].blank? if datastore['NS'].blank?
resp_soa = client.query(target, "SOA") resp_soa = client.query(target, "SOA")
if (resp_soa) if (resp_soa)
(resp_soa.answer.select { |i| i.class == Net::DNS::RR::SOA}).each do |rr| (resp_soa.answer.select { |i| i.is_a?(Dnsruby::RR::SOA)}).each do |rr|
resp_1_soa = client.search(rr.mname) resp_1_soa = client.search(rr.mname)
if (resp_1_soa and resp_1_soa.answer[0]) if (resp_1_soa and resp_1_soa.answer[0])
set_nameserver(resp_1_soa.answer.map(&:address).compact.map(&:to_s)) set_nameserver(resp_1_soa.answer.map(&:address).compact.map(&:to_s))
@ -139,7 +139,7 @@ module Client
if response.answer.length != 0 if response.answer.length != 0
vprint_status("This domain has wildcards enabled!!") vprint_status("This domain has wildcards enabled!!")
response.answer.each do |rr| response.answer.each do |rr|
print_status("Wildcard IP for #{rendsub}.#{target} is: #{rr.address.to_s}") if rr.class != Net::DNS::RR::CNAME print_status("Wildcard IP for #{rendsub}.#{target} is: #{rr.address.to_s}") if rr.class != Dnsruby::RR::CNAME
addr = rr.address.to_s addr = rr.address.to_s
end end
end end

View File

@ -1,7 +1,7 @@
# -*- coding: binary -*- # -*- coding: binary -*-
require 'msf/core' require 'msf/core'
require 'rex/proto/dns' require 'rex/proto/dns'
require 'msf/core/exploit/dns/common'
module Msf module Msf
@ -12,7 +12,7 @@ module Msf
### ###
module Exploit::Remote::DNS module Exploit::Remote::DNS
module Server module Server
include Common include Exploit::Remote::DNS::Common
include Exploit::Remote::SocketServer include Exploit::Remote::SocketServer
# #
@ -110,7 +110,8 @@ module Server
begin begin
comm = _determine_server_comm comm = _determine_server_comm
self.service = Rex::Proto::DNS::Server.new( self.service = Rex::ServiceManager.start(
Rex::Proto::DNS::Server,
datastore['SRVHOST'], datastore['SRVHOST'],
datastore['SRVPORT'], datastore['SRVPORT'],
datastore['DnsServerUdp'], datastore['DnsServerUdp'],
@ -154,7 +155,7 @@ module Server
# Stops the server # Stops the server
# @param destroy [TrueClass,FalseClass] Dereference the server object # @param destroy [TrueClass,FalseClass] Dereference the server object
def stop_service(destroy = false) def stop_service(destroy = false)
self.service.stop unless self.service.nil? Rex::ServiceManager.stop_service(self.service) if self.service
if destroy if destroy
@dns_resolver = nil @dns_resolver = nil
self.service = nil self.service = nil

View File

@ -1,5 +1,8 @@
# -*- coding: binary -*-
require 'net/dns' require 'net/dns'
require 'resolv' require 'resolv'
require 'dnsruby'
module Rex module Rex
module Proto module Proto
@ -21,26 +24,26 @@ module Packet
# Reconstructs a packet with both standard DNS libraries # Reconstructs a packet with both standard DNS libraries
# Ensures that headers match the payload # Ensures that headers match the payload
# #
# @param packet [String, Net::DNS::Packet] Data to be validated # @param packet [String, Net::DNS::Packet, Dnsruby::Message] Data to be validated
# #
# @return [Net::DNS::Packet] # @return [Dnsruby::Message]
def self.validate(packet) def self.validate(packet)
self.encode_net(self.encode_res(self.encode_raw(packet))) self.encode_drb(self.encode_net(self.encode_res(packet)))
end end
# #
# Sets header values to match packet content # Sets header values to match packet content
# #
# @param packet [String] Net::DNS::Packet, Resolv::DNS::Message] # @param packet [String] Net::DNS::Packet, Resolv::DNS::Message, Dnsruby::Message]
# #
# @return [Net::DNS::Packet] # @return [Dnsruby::Message]
def self.recalc_headers(packet) def self.recalc_headers(packet)
packet = self.encode_net(packet) packet = self.encode_drb(packet)
{ {
:qdCount= => :question, :qdcount= => :question,
:anCount= => :answer, :ancount= => :answer,
:nsCount= => :authority, :nscount= => :authority,
:arCount= => :additional :arcount= => :additional
}.each do |header,body| }.each do |header,body|
packet.header.send(header,packet.send(body).count) packet.header.send(header,packet.send(body).count)
end end
@ -51,36 +54,48 @@ module Packet
# #
# Reads a packet into the Net::DNS::Packet format # Reads a packet into the Net::DNS::Packet format
# #
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message] Input data # @param data [String, Net::DNS::Packet, Resolv::DNS::Message, Dnsruby::Message] Input data
# #
# @return [Net::DNS::Packet] # @return [Net::DNS::Packet]
def self.encode_net(packet) def self.encode_net(packet)
return packet if packet.respond_to?(:data) return packet if packet.is_a?(Net::DNS::Packet)
Net::DNS::Packet.parse( Net::DNS::Packet.parse(
packet.respond_to?(:encode) ? packet.encode : packet self.encode_raw(packet)
) )
end end
# Reads a packet into the Resolv::DNS::Message format # Reads a packet into the Resolv::DNS::Message format
# #
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message] Input data # @param data [String, Net::DNS::Packet, Resolv::DNS::Message, Dnsruby::Message] Input data
# #
# @return [Resolv::DNS::Message] # @return [Resolv::DNS::Message]
def self.encode_res(packet) def self.encode_res(packet)
return packet if packet.respond_to?(:encode) return packet if packet.is_a?(Resolv::DNS::Message)
Resolv::DNS::Message.decode( Resolv::DNS::Message.decode(
packet.respond_to?(:data) ? packet.data : packet self.encode_raw(packet)
)
end
# Reads a packet into the Dnsruby::Message format
#
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message, Dnsruby::Message] Input data
#
# @return [Dnsruby::Message]
def self.encode_drb(packet)
return packet if packet.is_a?(Dnsruby::Message)
Dnsruby::Message.decode(
self.encode_raw(packet)
) )
end end
# Reads a packet into the raw String format # Reads a packet into the raw String format
# #
# @param data [String, Net::DNS::Packet, Resolv::DNS::Message] Input data # @param data [String, Net::DNS::Packet, Resolv::DNS::Message, Dnsruby::Message] Input data
# #
# @return [Resolv::DNS::Message] # @return [String]
def self.encode_raw(packet) def self.encode_raw(packet)
return packet unless packet.respond_to?(:encode) or packet.respond_to?(:data) return packet unless packet.respond_to?(:encode) or packet.respond_to?(:data)
packet.respond_to?(:data) ? packet.data : packet.encode (packet.respond_to?(:data) ? packet.data : packet.encode).force_encoding('binary')
end end
# #
@ -91,16 +106,16 @@ module Packet
# @param cls [Fixnum] Class of dns record to query # @param cls [Fixnum] Class of dns record to query
# @param recurse [Fixnum] Recursive query or not # @param recurse [Fixnum] Recursive query or not
# #
# @return [Net::DNS::Packet] request packet # @return [Dnsruby::Message] request packet
def self.generate_request(subject, type = Net::DNS::A, cls = Net::DNS::IN, recurse = 1) def self.generate_request(subject, type = Dnsruby::Types::A, cls = Dnsruby::Classes::IN, recurse = 1)
case subject case subject
when IPAddr when IPAddr
name = subject.reverse name = subject.reverse
type = Net::DNS::PTR type = Dnsruby::Types::PTR
when /\d/ # Contains a number, try to see if it's an IP or IPv6 address when /\d/ # Contains a number, try to see if it's an IP or IPv6 address
begin begin
name = IPAddr.new(subject).reverse name = IPAddr.new(subject).reverse
type = Net::DNS::PTR type = Dnsruby::Types::PTR
rescue ArgumentError rescue ArgumentError
name = subject if self.valid_hostname?(subject) name = subject if self.valid_hostname?(subject)
end end
@ -109,9 +124,9 @@ module Packet
end end
# Create the packet # Create the packet
packet = Net::DNS::Packet.new(name,type,cls) packet = Dnsruby::Message.new(name, type, cls)
if packet.query? if packet.header.opcode == Dnsruby::OpCode::Query
packet.header.recursive = recurse packet.header.recursive = recurse
end end
@ -128,26 +143,26 @@ module Packet
# @param authority [Array] Set of authority records to provide in the response # @param authority [Array] Set of authority records to provide in the response
# @param additional [Array] Set of additional records to provide in the response # @param additional [Array] Set of additional records to provide in the response
# #
# @return [Net::DNS::Packet] Response packet # @return [Dnsruby::Message] Response packet
def self.generate_response(request, answer = nil, authority = nil, additional = nil) def self.generate_response(request, answer = nil, authority = nil, additional = nil)
packet = self.encode_net(request) packet = self.encode_drb(request)
packet.answer = answer if answer packet.answer = answer if answer
packet.authority = authority if authority packet.authority = authority if authority
packet.additional = additional if additional packet.additional = additional if additional
packet = self.recalc_headers(packet) packet = self.recalc_headers(packet)
# Set error code for NXDomain or unset it if reprocessing a response # Set error code for NXDomain or unset it if reprocessing a response
if packet.header.anCount < 1 if packet.header.ancount < 1
packet.header.rCode = 3 packet.header.rcode = Dnsruby::RCode::NXDOMAIN
else else
if packet.header.response? and packet.header.rCode.code == 3 if packet.header.qr and packet.header.get_header_rcode.to_i == 3
packet.header.rCode = 0 packet.header.rcode = Dnsruby::RCode::NOERROR
end end
end end
# Set response bit last to allow reprocessing of responses # Set response bit last to allow reprocessing of responses
packet.header.qr = 1 packet.header.qr = true
# Set recursion available bit if recursion desired # Set recursion available bit if recursion desired
packet.header.ra = 1 if packet.header.recursive? packet.header.ra = true if packet.header.rd
return packet return packet
end end

View File

@ -1,3 +1,5 @@
# -*- coding: binary -*-
require 'net/dns/resolver' require 'net/dns/resolver'
module Rex module Rex
@ -6,6 +8,7 @@ module DNS
## ##
# Provides Rex::Sockets compatible version of Net::DNS::Resolver # Provides Rex::Sockets compatible version of Net::DNS::Resolver
# Modified to work with Dnsruby::Messages, their resolvers are too heavy
## ##
class Resolver < Net::DNS::Resolver class Resolver < Net::DNS::Resolver
@ -112,24 +115,25 @@ module DNS
# @param argument # @param argument
# @param type [Fixnum] Type of record to look up # @param type [Fixnum] Type of record to look up
# @param cls [Fixnum] Class of question to look up # @param cls [Fixnum] Class of question to look up
def send(argument,type=Net::DNS::A,cls=Net::DNS::IN) def send(argument, type = Dnsruby::Types::A, cls = Dnsruby::Classes::IN)
if @config[:nameservers].size == 0 if @config[:nameservers].size == 0
raise ResolverError, "No nameservers specified!" raise ResolverError, "No nameservers specified!"
end end
method = self.use_tcp? ? :send_tcp : :send_udp method = self.use_tcp? ? :send_tcp : :send_udp
if argument.kind_of? Net::DNS::Packet case argument
when Dnsruby::Message
packet = argument packet = argument
elsif argument.kind_of? Resolv::DNS::Message when Net::DNS::Packet, Resolv::DNS::Message
packet = Net::DNS::Packet.parse(argument.encode) packet = Rex::Proto::DNS::Packet.encode_drb(argument)
else else
packet = make_query_packet(argument,type,cls) packet = make_query_packet(argument,type,cls)
end end
# Store packet_data for performance improvements, # Store packet_data for performance improvements,
# so methods don't keep on calling Packet#data # so methods don't keep on calling Packet#encode
packet_data = packet.data packet_data = packet.encode
packet_size = packet_data.size packet_size = packet_data.size
# Choose whether use TCP, UDP # Choose whether use TCP, UDP
@ -146,7 +150,7 @@ module DNS
end end
end end
if type == Net::DNS::AXFR if type == Dnsruby::Types::AXFR
@logger.warn "AXFR query, switching to TCP" unless method == :send_tcp @logger.warn "AXFR query, switching to TCP" unless method == :send_tcp
method = :send_tcp method = :send_tcp
end end
@ -160,9 +164,10 @@ module DNS
end end
@logger.info "Received #{ans[0].size} bytes from #{ans[1][2]+":"+ans[1][1].to_s}" @logger.info "Received #{ans[0].size} bytes from #{ans[1][2]+":"+ans[1][1].to_s}"
response = Net::DNS::Packet.parse(ans[0],ans[1]) # response = Net::DNS::Packet.parse(ans[0],ans[1])
response = Dnsruby::Message.decode(ans[0])
if response.header.truncated? and not ignore_truncated? if response.header.tc and not ignore_truncated?
@logger.warn "Packet truncated, retrying using TCP" @logger.warn "Packet truncated, retrying using TCP"
self.use_tcp = true self.use_tcp = true
begin begin
@ -215,7 +220,7 @@ module DNS
got_something = false got_something = false
loop do loop do
buffer = "" buffer = ""
ans = socket.recv(Net::DNS::INT16SZ) ans = socket.recv(2)
if ans.size == 0 if ans.size == 0
if got_something if got_something
break #Proper exit from loop break #Proper exit from loop
@ -305,8 +310,69 @@ module DNS
end end
return ans return ans
end end
#
# Perform search using the configured searchlist and resolvers
#
# @param name
# @param type [Fixnum] Type of record to look up
# @param cls [Fixnum] Class of question to look up
#
# @return ans [Dnsruby::Message] DNS Response
def search(name, type = Dnsruby::Types::A, cls = Dnsruby::Classes::IN)
return query(name,type,cls) if name.class == IPAddr
# If the name contains at least one dot then try it as is first.
if name.include? "."
@logger.debug "Search(#{name},#{Dnsruby::Types.new(type)},#{Dnsruby::Classes.new(cls)})"
ans = query(name,type,cls)
return ans if ans.header.ancount > 0
end end
# If the name doesn't end in a dot then apply the search list.
if name !~ /\.$/ and @config[:dns_search]
@config[:searchlist].each do |domain|
newname = name + "." + domain
@logger.debug "Search(#{newname},#{Dnsruby::Types.new(type)},#{Dnsruby::Classes.new(cls)})"
ans = query(newname,type,cls)
return ans if ans.header.ancount > 0
end
end
# Finally, if the name has no dots then try it as is.
@logger.debug "Search(#{name},#{Dnsruby::Types.new(type)},#{Dnsruby::Classes.new(cls)})"
return query(name+".",type,cls)
end
end
#
# Perform query with default domain validation
#
# @param name
# @param type [Fixnum] Type of record to look up
# @param cls [Fixnum] Class of question to look up
#
# @return ans [Dnsruby::Message] DNS Response
def query(name, type = Dnsruby::Types::A, cls = Dnsruby::Classes::IN)
return send(name,type,cls) if name.class == IPAddr
# If the name doesn't contain any dots then append the default domain.
if name !~ /\./ and name !~ /:/ and @config[:defname]
name += "." + @config[:domain]
end
@logger.debug "Query(#{name},#{Dnsruby::Types.new(type)},#{Dnsruby::Classes.new(cls)})"
return send(name,type,cls)
end
end end
end end
end end

View File

@ -39,7 +39,7 @@ class Server
) )
end.keys.map do |record| end.keys.map do |record|
if search.to_s.match(MATCH_HOSTNAME) and record.name == '*' if search.to_s.match(MATCH_HOSTNAME) and record.name == '*'
record = Net::DNS::RR.new(:name => search, :address => record.address) record = Dnsruby::RR.create(name: name, type: type, address: address)
else else
record record
end end
@ -49,10 +49,10 @@ class Server
# #
# Add record to cache, only when "running" # Add record to cache, only when "running"
# #
# @param record [Net::DNS::RR] Record to cache # @param record [Dnsruby::RR] Record to cache
def cache_record(record) def cache_record(record)
return unless @monitor_thread return unless @monitor_thread
if record.class.ancestors.include?(Net::DNS::RR) and if record.is_a?(Dnsruby::RR) and
(!record.respond_to?(:address) or Rex::Socket.is_ip_addr?(record.address.to_s)) and (!record.respond_to?(:address) or Rex::Socket.is_ip_addr?(record.address.to_s)) and
record.name.to_s.match(MATCH_HOSTNAME) record.name.to_s.match(MATCH_HOSTNAME)
add(record, Time.now.to_i + record.ttl) add(record, Time.now.to_i + record.ttl)
@ -73,7 +73,7 @@ class Server
find(name, type).each do |found| find(name, type).each do |found|
delete(found) delete(found)
end if replace end if replace
add(Net::DNS::RR.new(:name => name, :address => address),0) add(Dnsruby::RR.create(name: name, type: type, address: address),0)
else else
raise "Invalid parameters for static entry - #{name}, #{address}, #{type}" raise "Invalid parameters for static entry - #{name}, #{address}, #{type}"
end end
@ -120,7 +120,7 @@ class Server
# #
# Add a record to the cache with thread safety # Add a record to the cache with thread safety
# #
# @param record [Net::DNS::RR] Record to add # @param record [Dnsruby::RR] Record to add
# @param expire [Fixnum] Time in seconds when record becomes stale # @param expire [Fixnum] Time in seconds when record becomes stale
def add(record, expire = 0) def add(record, expire = 0)
self.lock.synchronize do self.lock.synchronize do
@ -131,7 +131,7 @@ class Server
# #
# Delete a record from the cache with thread safety # Delete a record from the cache with thread safety
# #
# @param record [Net::DNS::RR] Record to delete # @param record [Dnsruby::RR] Record to delete
def delete(record) def delete(record)
self.lock.synchronize do self.lock.synchronize do
self.records.delete(record) self.records.delete(record)
@ -285,11 +285,12 @@ class Server
# @param cli [Rex::Socket::Tcp, Rex::Socket::Udp] Client sending the request # @param cli [Rex::Socket::Tcp, Rex::Socket::Udp] Client sending the request
# @param data [String] raw DNS request data # @param data [String] raw DNS request data
def default_dispatch_request(cli,data) def default_dispatch_request(cli,data)
req = Packet.encode_net(data) return if data.strip.empty?
req = Packet.encode_drb(data)
forward = req.dup forward = req.dup
# Find cached items, remove request from forwarded packet # Find cached items, remove request from forwarded packet
req.question.each do |ques| req.question.each do |ques|
cached = self.cache.find(ques.qName, ques.qType.to_s) cached = self.cache.find(ques.qname, ques.qtype.to_s)
if cached.empty? if cached.empty?
next next
else else
@ -304,17 +305,32 @@ class Server
forwarded.answer.each do |ans| forwarded.answer.each do |ans|
self.cache.cache_record(ans) self.cache.cache_record(ans)
end end
req.header.ra = 1 # Set recursion bit req.header.ra = true # Set recursion bit
end end
# Finalize answers in response # Finalize answers in response
# Check for empty response prior to sending # Check for empty response prior to sending
if req.answer.size < 1 if req.answer.size < 1
req.header.rCode = 3 req.header.rCode = Dnsruby::RCode::NOERROR
end end
req.header.qr = 1 # Set response bit req.header.qr = true # Set response bit
send_response(cli, validate_packet(req).data) send_response(cli, validate_packet(req).data)
end end
#
# Returns the hardcore alias for the DNS service
#
def self.hardcore_alias(*args)
"#{(args[0] || '')}#{(args[1] || '')}"
end
#
# DNS server.
#
def alias
"DNS Server"
end
protected protected
# #
# This method monitors the listener socket for new connections and calls # This method monitors the listener socket for new connections and calls

View File

@ -45,35 +45,37 @@ class MetasploitModule < Msf::Auxiliary
# Creates Proc to handle incoming requests # Creates Proc to handle incoming requests
# #
def on_dispatch_request(cli,data) def on_dispatch_request(cli,data)
req = Packet.encode_net(data) return if data.strip.empty?
req = Packet.encode_drb(data)
peer = "#{cli.peerhost}:#{cli.peerport}" peer = "#{cli.peerhost}:#{cli.peerport}"
asked = req.question.map(&:qName).join(', ') asked = req.question.map(&:qname).map(&:to_s).join(', ')
vprint_status("Received request for #{asked} from #{peer}") vprint_status("Received request for #{asked} from #{peer}")
answered = [] answered = []
# Find cached items, remove request from forwarded packet # Find cached items, remove request from forwarded packet
req.question.each do |ques| req.question.each do |ques|
cached = service.cache.find(ques.qName, ques.qType.to_s) cached = service.cache.find(ques.qname, ques.qtype.to_s)
if cached.empty? if cached.empty?
next next
else else
req.answer = (req.answer + cached).uniq req.instance_variable_set(:@answer, (req.answer + cached).uniq)
answered << ques answered << ques
cached.map do |hit| cached.map do |hit|
if hit.respond_to?(:address) if hit.respond_to?(:address)
hit.name + ':' + hit.address.to_s + ' ' + hit.type hit.name.to_s + ':' + hit.address.to_s + ' ' + hit.type.to_s
else else
hit.name + ' ' + hit.type hit.name.to_s + ' ' + hit.type.to_s
end end
end.each {|h| vprint_status("Cache hit for #{h}")} end.each {|h| vprint_status("Cache hit for #{h}")}
end end
end unless service.cache.nil? end unless service.cache.nil?
# Forward remaining requests, cache responses # Forward remaining requests, cache responses
if answered.count < req.question.count and service.fwd_res if answered.count < req.question.count and service.fwd_res
if !req.header.recursive? if !req.header.rd
vprint_status("Recursion forbidden in query for #{req.question.first.name} from #{peer}") vprint_status("Recursion forbidden in query for #{req.question.first.name} from #{peer}")
else else
forward = req.dup forward = req.dup
forward.question = req.question - answered # forward.question = req.question - answered
forward.instance_variable_set(:@question, req.question - answered)
forwarded = service.fwd_res.send(Packet.validate(forward)) forwarded = service.fwd_res.send(Packet.validate(forward))
forwarded.answer.each do |ans| forwarded.answer.each do |ans|
rstring = ans.respond_to?(:address) ? "#{ans.name}:#{ans.address}" : ans.name rstring = ans.respond_to?(:address) ? "#{ans.name}:#{ans.address}" : ans.name
@ -81,20 +83,20 @@ class MetasploitModule < Msf::Auxiliary
service.cache.cache_record(ans) service.cache.cache_record(ans)
end unless service.cache.nil? end unless service.cache.nil?
# Merge the answers and use the upstream response # Merge the answers and use the upstream response
forwarded.answer = (req.answer + forwarded.answer).uniq forward.instance_variable_set(:@question, (req.answer + forwarded.answer).uniq)
req = forwarded req = forwarded
end end
end end
service.send_response(cli, Packet.validate(Packet.generate_response(req)).data) service.send_response(cli, Packet.validate(Packet.generate_response(req)).encode)
end end
# #
# Creates Proc to handle outbound responses # Creates Proc to handle outbound responses
# #
def on_send_response(cli,data) def on_send_response(cli,data)
res = Packet.encode_net(data) res = Packet.encode_drb(data)
peer = "#{cli.peerhost}:#{cli.peerport}" peer = "#{cli.peerhost}:#{cli.peerport}"
asked = res.question.map(&:qName).join(', ') asked = res.question.map(&:qname).map(&:to_s).join(', ')
vprint_status("Sending response for #{asked} to #{peer}") vprint_status("Sending response for #{asked} to #{peer}")
cli.write(data) cli.write(data)
end end