diff --git a/extensions/dns/dns.rb b/extensions/dns/dns.rb index 0896846b5..51ae3a256 100644 --- a/extensions/dns/dns.rb +++ b/extensions/dns/dns.rb @@ -25,7 +25,6 @@ module DNS def initialize @lock = Mutex.new @server = nil - @next_id = 0 end # Starts the main DNS server run-loop. @@ -73,9 +72,7 @@ module DNS # @see http://rubydoc.info/gems/rubydns/RubyDNS/Transaction def add_rule(pattern, type, &block) @lock.synchronize do - @next_id += 1 - @server.match(@next_id, pattern, type, block) - @next_id + return @server.match(pattern, type, block) end end @@ -86,7 +83,6 @@ module DNS def remove_rule(id) @lock.synchronize do @server.remove_rule(id) - @next_id -= 1 end end diff --git a/extensions/dns/model.rb b/extensions/dns/model.rb index 84ac3555a..7afea0c24 100644 --- a/extensions/dns/model.rb +++ b/extensions/dns/model.rb @@ -14,10 +14,10 @@ module DNS storage_names[:default] = 'extension_dns_rules' - property :id, Serial # Unique identifier - property :pattern, Object # Query pattern - property :type, Object # Resource type - property :block, Text # Associated callback + property :id, String, :key => true # Unique identifier + property :pattern, Object # Query pattern + property :type, Object # Resource type + property :block, Text # Associated callback end diff --git a/extensions/dns/rest/dns.rb b/extensions/dns/rest/dns.rb index a329f466b..a98cd62c9 100644 --- a/extensions/dns/rest/dns.rb +++ b/extensions/dns/rest/dns.rb @@ -33,12 +33,65 @@ module DNS # Returns a specific rule given its id get '/rule/:id' do - id = params[:id] + begin + id = params[:id] - halt 401 unless BeEF::Filters.nums_only?(id) + unless BeEF::Filters.alphanums_only?(id) + raise StandardError, 'Invalid id passed to endpoint /api/dns/rule/:id' + end - result = BeEF::Extension::DNS::DNS.instance.get_rule(id) - result.to_json + result = BeEF::Extension::DNS::DNS.instance.get_rule(id) + result.to_json + rescue StandardError => e + print_error e.message + halt 400 + end + end + + # Adds a new DNS rule + post '/rule' do + begin + body = JSON.parse(request.body.read) + + pattern = body['pattern'] + type = eval body['type'] + block = body['block'] + + # Validate required JSON keys + unless [pattern, type, block].include?(nil) + # Determine whether 'pattern' is a String or Regexp + begin + pattern_test = eval pattern + pattern = pattern_test if pattern_test.class == Regexp + rescue => e; end + + if type.superclass != Resolv::DNS::Resource + raise StandardError, 'Invalid resource type given in "type" key' + end + + unless BeEF::Filters.is_non_empty_string?(block) + raise StandardError, 'Invalid code block given in "block" key' + end + + id = '' + + # Bypass #add_rule so that 'block' can be passed as a String + BeEF::Extension::DNS::DNS.instance.instance_eval do + id = @server.match(pattern, type, block) + end + + result = {} + result['success'] = true + result['id'] = id + result.to_json + end + rescue StandardError => e + print_error e.message + halt 400 + rescue Exception => e + print_error "Invalid JSON input passed to endpoint /api/dns/rule" + halt 400 + end end end diff --git a/extensions/dns/ruby/logger.rb b/extensions/dns/ruby/logger.rb index 142f09a99..3e88cbfa0 100644 --- a/extensions/dns/ruby/logger.rb +++ b/extensions/dns/ruby/logger.rb @@ -4,7 +4,7 @@ # See the file 'doc/COPYING' for copying permission # -# Overrives the logger used by RubyDNS to use BeEF's print_info() and friends +# Overrives the logger used by RubyDNS to use BeEF's {#print_info} and friends. class Logger def debug(msg) diff --git a/extensions/dns/ruby/rubydns.rb b/extensions/dns/ruby/rubydns.rb index 3d8de48b1..d88996936 100644 --- a/extensions/dns/ruby/rubydns.rb +++ b/extensions/dns/ruby/rubydns.rb @@ -3,6 +3,17 @@ # 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::DNS 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 @@ -33,6 +44,7 @@ module RubyDNS class Rule + # XXX Can this be removed? attr_accessor :id # Now uses an 'id' parameter to uniquely identify rules @@ -44,22 +56,56 @@ module RubyDNS end - # Now uses an 'id' parameter to uniquely identify rules - def match(id, *pattern, block) + # Now includes BeEF database support and checks for already present rules + def match(*pattern, block) + id = '' + catch :match do - # Check if rule is already present - BeEF::Core::Models::DNS::Rule.each { |rule| throw :match if rule.id == id } + begin + # Sourcify block (already a string only for RESTful API calls) + block_src = case block.class.name + when String then block + when Proc then block.to_source + end - @rules << Rule.new(id, pattern, block) + # 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 - # Add new rule to database - BeEF::Core::Models::DNS::Rule.create( - :id => id, - :pattern => pattern[0], - :type => pattern[1], - :block => block.to_source - ) + id = rule.id + throw :match + end + end + + id = generate_id + + case block.class.name + 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], + :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 @@ -120,6 +166,22 @@ module RubyDNS result 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 + end class Transaction