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
parent
f76adf6a62
commit
c65c03722c
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue