deef4a94fe
Allow retrieval of '*' from stored static entries for spoofing all domains to any IP using wildcard names. Replace the wildcard response with the name submitted to the search in the response. Fix improper checks in DNS::Packet for Resolv objects from decode to encode. Misc cleanup for records not responding to :address, convenience methods, and packet structure.
175 lines
4.7 KiB
Ruby
175 lines
4.7 KiB
Ruby
# -*- 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 Server
|
|
include Common
|
|
include Exploit::Remote::SocketServer
|
|
|
|
#
|
|
# Initializes an exploit module that serves DNS requests
|
|
#
|
|
def initialize(info = {})
|
|
super
|
|
|
|
register_options(
|
|
[
|
|
OptPort.new('SRVPORT', [true, 'The local port to listen on.', 53]),
|
|
OptString.new('STATIC_ENTRIES', [ false, "DNS domain search list (hosts file or space/semicolon separate entries)"]),
|
|
OptBool.new('DISABLE_RESOLVER', [ false, "Disable DNS request forwarding", false]),
|
|
OptBool.new('DISABLE_NS_CACHE', [ false, "Disable DNS response caching", false])
|
|
], Exploit::Remote::DNS::Server
|
|
)
|
|
|
|
register_advanced_options(
|
|
[
|
|
OptBool.new('DnsServerUdp', [true, "Serve UDP DNS requests", true]),
|
|
OptBool.new('DnsServerTcp', [true, "Serve TCP DNS requests", false])
|
|
], Exploit::Remote::DNS::Server
|
|
)
|
|
end
|
|
|
|
attr_accessor :service
|
|
|
|
#
|
|
# Process static entries
|
|
#
|
|
# @param entries [String] Filename or String containing static entries
|
|
# @param type [String] Type of record for which to add static entries
|
|
#
|
|
# @return [Array] List of static entries in the cache
|
|
def add_static_hosts(entries = datastore['STATIC_ENTRIES'], type = 'A')
|
|
return if entries.nil? or entries.empty?
|
|
if File.file?(File.expand_path(entries))
|
|
data = File.read(File.expand_path(entries)).split("\n")
|
|
else
|
|
data = entries.split(';')
|
|
end
|
|
data.each do |entry|
|
|
next if entry.gsub(/\s/,'').empty?
|
|
addr, names = entry.split(' ', 2)
|
|
names.split.each do |name|
|
|
name << '.' unless name[-1] == '.' or name == '*'
|
|
service.cache.add_static(name, addr, type)
|
|
end
|
|
end
|
|
service.cache.records.select {|r,e| e == 0}
|
|
end
|
|
|
|
#
|
|
# Flush all static entries
|
|
#
|
|
def flush_static_hosts
|
|
data.cache.records.select {|r,e| e == 0}.each do |flush|
|
|
data.cache.delete(flush)
|
|
end
|
|
end
|
|
|
|
#
|
|
# Flush cache entries
|
|
# @param static [TrueClass, FalseClass] flush static hosts
|
|
def flush_cache(static = false)
|
|
self.service.cache.stop(true)
|
|
flush_static_hosts if static
|
|
self.service.cache.start
|
|
end
|
|
|
|
#
|
|
# Handle incoming requests
|
|
# Override this method in modules to take flow control
|
|
#
|
|
def on_dispatch_request(cli, data)
|
|
service.default_dispatch_request(cli,data)
|
|
end
|
|
|
|
#
|
|
# Handle incoming requests
|
|
# Override this method in modules to take flow control
|
|
#
|
|
def on_send_response(cli, data)
|
|
cli.write(data)
|
|
end
|
|
|
|
#
|
|
# Starts the server
|
|
#
|
|
def start_service
|
|
options.validate(datastore) # This is a hack, DS values should not be Strings prior to this
|
|
if !datastore['DISABLE_RESOLVER'] and self.respond_to?(:setup_resolver)
|
|
setup_resolver
|
|
end
|
|
begin
|
|
|
|
comm = _determine_server_comm
|
|
self.service = Rex::Proto::DNS::Server.new(
|
|
datastore['SRVHOST'],
|
|
datastore['SRVPORT'],
|
|
datastore['DnsServerUdp'],
|
|
datastore['DnsServerTcp'],
|
|
(datastore['DISABLE_RESOLVER'] ? false : @dns_resolver),
|
|
comm,
|
|
{'Msf' => framework, 'MsfExploit' => self}
|
|
) if self.service.nil?
|
|
|
|
self.service.dispatch_request_proc = Proc.new do |cli, data|
|
|
on_dispatch_request(cli,data)
|
|
end
|
|
self.service.send_response_proc = Proc.new do |cli, data|
|
|
on_send_response(cli,data)
|
|
end
|
|
|
|
add_static_hosts
|
|
self.service.start(!datastore['DISABLE_NS_CACHE'])
|
|
|
|
rescue ::Errno::EACCES => e
|
|
if (srvport.to_i < 1024)
|
|
print_line(" ")
|
|
print_error("Could not start the DNS server: #{e}.")
|
|
print_error(
|
|
"This module is configured to use a privileged port (#{srvport}). " +
|
|
"On Unix systems, only the root user account is allowed to bind to privileged ports." +
|
|
"Please run the framework as root to use this module."
|
|
)
|
|
print_error(
|
|
"On Microsoft Windows systems, this error is returned when a process attempts to "+
|
|
"listen on a host/port combination that is already in use. For example, Windows XP "+
|
|
"will return this error if a process attempts to bind() over the system SMB/NetBIOS services."
|
|
)
|
|
print_line(" ")
|
|
end
|
|
raise e
|
|
end
|
|
end
|
|
|
|
#
|
|
# Stops the server
|
|
# @param destroy [TrueClass,FalseClass] Dereference the server object
|
|
def stop_service(destroy = false)
|
|
self.service.stop unless self.service.nil?
|
|
if destroy
|
|
@dns_resolver = nil
|
|
self.service = nil
|
|
end
|
|
end
|
|
|
|
#
|
|
# Resets the DNS server
|
|
#
|
|
def reset_service
|
|
stop_service(true)
|
|
start_service
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|