Files
beef/extensions/dns/ruby/rubydns.rb
soh_cah_toa 9d4ea6c224 Fixed issue mentioned in FIXME comment in RubyDNS::Server#match.
Changed 'block.class.name' to just 'block' in case/when clause.
2013-07-22 22:42:27 -04:00

243 lines
6.4 KiB
Ruby

#
# Copyright (c) 2006-2013 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
# This module is a modified version of RubyDNS built to be compatible with BeEF.
# For the most part, it will behave exactly the same except where otherwise noted.
#
# Additional features include database support, BeEF logger, assignment of unique
# identifiers to rules, rule removal, and more.
#
# The core functionality of BeEF's DNS server is implemented here, whereas
# BeEF::Extension::Dns::Server is simply a small wrapper around it.
#
# @see http://rubydoc.info/gems/rubydns/frames
module RubyDNS
# Behaves exactly the same, except without any logger output
def self.run_server(options = {}, &block)
server = RubyDNS::Server.new(&block)
options[:listen] ||= [[:udp, '0.0.0.0', 53], [:tcp, '0.0.0.0', 53]]
EventMachine.run do
server.fire(:setup)
options[:listen].each do |spec|
if spec[0] == :udp
EventMachine.open_datagram_socket(spec[1], spec[2], UDPHandler, server)
elsif spec[0] == :tcp
EventMachine.start_server(spec[1], spec[2], TCPHandler, server)
end
end
server.load_rules
server.fire(:start)
end
server.fire(:stop)
end
class Server
class Rule
attr_accessor :id
# Now uses an 'id' parameter to uniquely identify rules
def initialize(id, pattern, callback)
@id = id
@pattern = pattern
@callback = callback
end
end
# New method that loads all rules from the database at server startup
def load_rules
BeEF::Core::Models::Dns::Rule.each do |rule|
id = rule.id
pattern = [rule.pattern, rule.type]
block = eval rule.block
regex = pattern[0]
pattern[0] = Regexp.new(regex) if regex =~ /^\(\?-mix:/
@rules << Rule.new(id, pattern, block)
end
end
# Now includes BeEF database support and checks for already present rules
def match(*pattern, block)
id = ''
catch :match do
begin
# Sourcify block (already a string only for RESTful API calls)
block_src = case block
when String then block
when Proc then block.to_source
end
# Break out and return id if rule is already present
BeEF::Core::Models::Dns::Rule.each do |rule|
if pattern[0] == rule.pattern &&
pattern[1] == rule.type &&
block_src == rule.block
id = rule.id
throw :match
end
end
id = generate_id
case block
when String
@rules << Rule.new(id, pattern, eval(block_src))
when Proc
@rules << Rule.new(id, pattern, block)
end
BeEF::Core::Models::Dns::Rule.create(
:id => id,
:pattern => pattern[0].to_s,
:type => pattern[1],
:block => block_src
)
rescue Sourcify::CannotHandleCreatedOnTheFlyProcError,
Sourcify::CannotParseEvalCodeError,
Sourcify::MultipleMatchingProcsPerLineError,
Sourcify::NoMatchingProcError,
Sourcify::ParserInternalError
@logger.error "Failed to sourcify block for DNS rule '#{id}'"
raise
end
end
id
end
# New method that removes a rule given its id and returns boolean result
def remove_rule(id)
@rules.delete_if { |rule| rule.id == id }
rule = BeEF::Core::Models::Dns::Rule.get(id)
rule != nil ? rule.destroy : false
end
# New method that returns a hash representing the given rule
def get_rule(id)
result = {}
begin
rule = BeEF::Core::Models::Dns::Rule.get!(id)
result[:id] = rule.id
result[:pattern] = rule.pattern
result[:type] = rule.type.to_s.split('::')[-1]
result[:response] = parse_response(rule.block)
rescue DataMapper::ObjectNotFoundError => e
@logger.error(e.message)
end
result
end
# New method that returns the entire DNS ruleset as an AoH
def get_ruleset
result = []
BeEF::Core::Models::Dns::Rule.each do |rule|
element = {}
element[:id] = rule.id
element[:pattern] = rule.pattern
element[:type] = rule.type.to_s.split('::')[-1]
element[:response] = parse_response(rule.block)
result << element
end
result
end
# New method that removes the entire DNS ruleset
def remove_ruleset
@rules = []
BeEF::Core::Models::Dns::Rule.destroy
end
private
# New method that generates a unique id for a rule
def generate_id
begin
id = BeEF::Core::Crypto.secure_token.byteslice(0..6)
# Make sure id isn't already in use
BeEF::Core::Models::Dns::Rule.each { |rule| throw StandardError if id == rule.id }
rescue StandardError
retry
end
id
end
# New method that parses response callback and returns RDATA as an array
def parse_response(block)
# Extract response arguments into an array
methods = '(respond|failure)'
args = /(?<=\.#{methods}!\().*(?=\))/.match(block).to_s.split(/,\s*/)
result = []
# Determine whether each argument is a domain name, integer, or IP address
args.each do |elem|
arg = nil
if /Name\.create\((.*)\)/.match(elem)
arg = $1
elsif /:(NoError|FormErr|ServFail|NXDomain|NotImp|Refused|NotAuth)/.match(elem)
arg = $1.upcase
else
int_test = elem.to_i
arg = (int_test != 0 ? int_test : elem)
end
arg.gsub!(/['"]/, '') unless arg.is_a?(Integer)
result << arg
end
result
end
end
class Transaction
# Behaves exactly the same, except using debug logger instead of info
def respond!(*data)
options = data.last.kind_of?(Hash) ? data.pop : {}
resource_class = options[:resource_class] || @resource_class
if resource_class == nil
raise ArgumentError, "Could not instantiate resource #{resource_class}!"
end
@server.logger.debug("Resource class: #{resource_class.inspect}")
resource = resource_class.new(*data)
@server.logger.debug("Resource: #{resource.inspect}")
append!(resource, options)
end
end
end