From 7b229a2a206d8dfdc40a46f2340adfdbcaf210ec Mon Sep 17 00:00:00 2001 From: soh_cah_toa Date: Thu, 24 Apr 2014 14:26:37 -0400 Subject: [PATCH] Added new #validate_pattern method. Uses BeEF::Filters to ensure that empty, null, and non-printable patterns are tossed out. Added new InvalidDnsPatternError exception class to handle these cases. Renamed #validate_response to #format_callback since the name is more appropriate. --- extensions/dns/model.rb | 355 +++++++++++++++++++++------------------- 1 file changed, 188 insertions(+), 167 deletions(-) diff --git a/extensions/dns/model.rb b/extensions/dns/model.rb index c66dad2d1..bd2610a4f 100644 --- a/extensions/dns/model.rb +++ b/extensions/dns/model.rb @@ -20,208 +20,229 @@ module BeEF property :response, Object, :required => true property :callback, Object, :required => true - # Hooks the model's "save" event. Generates a rule identifier and callback. + # Hooks the model's "save" event. Validates pattern/response and generates a rule identifier. before :save do |rule| - rule.callback = validate_response(rule.resource, rule.response) + begin + validate_pattern(rule.pattern) + rule.callback = format_callback(rule.resource, rule.response) + rescue InvalidDnsPatternError, InvalidDnsResourceError, InvalidDnsResponseError => e + print_error e.message + throw :halt + end + rule.id = BeEF::Core::Crypto.dns_rule_id end private + # Verifies that the given pattern is valid (i.e. non-empty, no null's or printable characters). + def validate_pattern(pattern) + raise InvalidDnsPatternError unless BeEF::Filters.is_non_empty_string?(pattern) && + !BeEF::Filters.has_null?(pattern) && + !BeEF::Filters.has_non_printable_char?(pattern) + end + # Strict validator which ensures that only an appropriate response is given. # # @param resource [Resolv::DNS::Resource::IN] resource record type # @param response [String, Symbol, Array] response to include in callback # # @return [String] string representation of callback that can safely be eval'd - def validate_response(resource, response) + def format_callback(resource, response) sym_regex = /^:?(NoError|FormErr|ServFail|NXDomain|NotImp|Refused|NotAuth)$/i - begin - src = if resource == Resolv::DNS::Resource::IN::A - if response.is_a?(String) && BeEF::Filters.is_valid_ip?(:ipv4, response) - sprintf "t.respond!('%s')", response - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - elsif response.is_a?(Array) - str1 = "t.respond!('%s');" - str2 = '' + src = if resource == Resolv::DNS::Resource::IN::A + if response.is_a?(String) && BeEF::Filters.is_valid_ip?(:ipv4, response) + sprintf "t.respond!('%s')", response + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym + elsif response.is_a?(Array) + str1 = "t.respond!('%s');" + str2 = '' - response.each do |r| - raise InvalidDnsResponseError, 'A' unless BeEF::Filters.is_valid_ip?(:ipv4, r) - str2 << sprintf(str1, r) - end - - str2 - else - raise InvalidDnsResponseError, 'A' + response.each do |r| + raise InvalidDnsResponseError, 'A' unless BeEF::Filters.is_valid_ip?(:ipv4, r) + str2 << sprintf(str1, r) end - elsif resource == Resolv::DNS::Resource::IN::AAAA - if response.is_a?(String) && BeEF::Filters.is_valid_ip?(:ipv6, response) - sprintf "t.respond!('%s')", response - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - elsif response.is_a?(Array) - str1 = "t.respond!('%s');" - str2 = '' - response.each do |r| - raise InvalidDnsResponseError, 'AAAA' unless BeEF::Filters.is_valid_ip?(:ipv6, r) - str2 << sprintf(str1, r) - end + str2 + else + raise InvalidDnsResponseError, 'A' + end + elsif resource == Resolv::DNS::Resource::IN::AAAA + if response.is_a?(String) && BeEF::Filters.is_valid_ip?(:ipv6, response) + sprintf "t.respond!('%s')", response + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym + elsif response.is_a?(Array) + str1 = "t.respond!('%s');" + str2 = '' - str2 - else - raise InvalidDnsResponseError, 'AAAA' + response.each do |r| + raise InvalidDnsResponseError, 'AAAA' unless BeEF::Filters.is_valid_ip?(:ipv6, r) + str2 << sprintf(str1, r) end - elsif resource == Resolv::DNS::Resource::IN::CNAME - if response.is_a?(String) && BeEF::Filters.is_valid_domain?(response) - sprintf "t.respond!(Resolv::DNS::Name.create('%s'))", response - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - else - raise InvalidDnsResponseError, 'CNAME' + + str2 + else + raise InvalidDnsResponseError, 'AAAA' + end + elsif resource == Resolv::DNS::Resource::IN::CNAME + if response.is_a?(String) && BeEF::Filters.is_valid_domain?(response) + sprintf "t.respond!(Resolv::DNS::Name.create('%s'))", response + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym + else + raise InvalidDnsResponseError, 'CNAME' + end + elsif resource == Resolv::DNS::Resource::IN::HINFO + if response.is_a?(Array) + response.each { |r| raise InvalidDnsResponseError, 'HINFO' unless r.is_a?(String) } + data = { :cpu => response[0], :os => response[1] } + sprintf "t.respond!('%s', '%s')", data + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym + else + raise InvalidDnsResponseError, 'HINFO' + end + elsif resource == Resolv::DNS::Resource::IN::MINFO + if response.is_a?(Array) + response.each { |r| raise InvalidDnsResponseError, 'MINFO' unless r.is_a?(String) && BeEF::Filters.is_valid_domain?(r) } + + data = { :rmailbx => response[0], :emailbx => response[1] } + + sprintf "t.respond!(Resolv::DNS::Name.create('%s'), " + + "Resolv::DNS::Name.create('%s'))", + data + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym + else + raise InvalidDnsResponseError, 'MINFO' + end + elsif resource == Resolv::DNS::Resource::IN::MX + if response[0].is_a?(Integer) && + BeEF::Filters.is_valid_domain?(response[1]) + + data = { :preference => response[0], :exchange => response[1] } + sprintf "t.respond!(%d, Resolv::DNS::Name.create('%s'))", data + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym + else + raise InvalidDnsResponseError, 'MX' + end + elsif resource == Resolv::DNS::Resource::IN::NS + if response.is_a?(String) && BeEF::Filters.is_valid_domain?(response) + sprintf "t.respond!(Resolv::DNS::Name.create('%s'))", response + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym + elsif response.is_a?(Array) + str1 = "t.respond!(Resolv::DNS::Name.create('%s'))" + str2 = '' + + response.each do |r| + raise InvalidDnsResponseError, 'NS' unless BeEF::Filters.is_valid_domain?(r) + str2 << sprintf(str1, r) end - elsif resource == Resolv::DNS::Resource::IN::HINFO - if response.is_a?(Array) - response.each { |r| raise InvalidDnsResponseError, 'HINFO' unless r.is_a?(String) } - data = { :cpu => response[0], :os => response[1] } - sprintf "t.respond!('%s', '%s')", data - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - else - raise InvalidDnsResponseError, 'HINFO' - end - elsif resource == Resolv::DNS::Resource::IN::MINFO - if response.is_a?(Array) - response.each { |r| raise InvalidDnsResponseError, 'MINFO' unless r.is_a?(String) && BeEF::Filters.is_valid_domain?(r) } - data = { :rmailbx => response[0], :emailbx => response[1] } + str2 + else + raise InvalidDnsResponseError, 'NS' + end + elsif resource == Resolv::DNS::Resource::IN::PTR + if response.is_a?(String) && BeEF::Filters.is_valid_domain?(response) + sprintf "t.respond!(Resolv::DNS::Name.create('%s'))", response + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym + else + raise InvalidDnsResponseError, 'PTR' + end + elsif resource == Resolv::DNS::Resource::IN::SOA + if response.is_a?(Array) + unless BeEF::Filters.is_valid_domain?(response[0]) && + BeEF::Filters.is_valid_domain?(response[1]) && + response[2].is_a?(Integer) && + response[3].is_a?(Integer) && + response[4].is_a?(Integer) && + response[5].is_a?(Integer) && + response[6].is_a?(Integer) - sprintf "t.respond!(Resolv::DNS::Name.create('%s'), " + - "Resolv::DNS::Name.create('%s'))", - data - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - else - raise InvalidDnsResponseError, 'MINFO' - end - elsif resource == Resolv::DNS::Resource::IN::MX - if response[0].is_a?(Integer) && - BeEF::Filters.is_valid_domain?(response[1]) - - data = { :preference => response[0], :exchange => response[1] } - sprintf "t.respond!(%d, Resolv::DNS::Name.create('%s'))", data - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - else - raise InvalidDnsResponseError, 'MX' - end - elsif resource == Resolv::DNS::Resource::IN::NS - if response.is_a?(String) && BeEF::Filters.is_valid_domain?(response) - sprintf "t.respond!(Resolv::DNS::Name.create('%s'))", response - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - elsif response.is_a?(Array) - str1 = "t.respond!(Resolv::DNS::Name.create('%s'))" - str2 = '' - - response.each do |r| - raise InvalidDnsResponseError, 'NS' unless BeEF::Filters.is_valid_domain?(r) - str2 << sprintf(str1, r) - end - - str2 - else - raise InvalidDnsResponseError, 'NS' - end - elsif resource == Resolv::DNS::Resource::IN::PTR - if response.is_a?(String) && BeEF::Filters.is_valid_domain?(response) - sprintf "t.respond!(Resolv::DNS::Name.create('%s'))", response - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - else - raise InvalidDnsResponseError, 'PTR' - end - elsif resource == Resolv::DNS::Resource::IN::SOA - if response.is_a?(Array) - unless BeEF::Filters.is_valid_domain?(response[0]) && - BeEF::Filters.is_valid_domain?(response[1]) && - response[2].is_a?(Integer) && - response[3].is_a?(Integer) && - response[4].is_a?(Integer) && - response[5].is_a?(Integer) && - response[6].is_a?(Integer) - - raise InvalidDnsResponseError, 'SOA' - end - - data = { - :mname => response[0], - :rname => response[1], - :serial => response[2], - :refresh => response[3], - :retry => response[4], - :expire => response[5], - :minimum => response[6] - } - - sprintf "t.respond!(Resolv::DNS::Name.create('%s'), " + - "Resolv::DNS::Name.create('%s'), " + - '%d, ' + - '%d, ' + - '%d, ' + - '%d, ' + - '%d)', - data - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - else raise InvalidDnsResponseError, 'SOA' end - elsif resource == Resolv::DNS::Resource::IN::TXT - if resource.is_a?(String) - sprintf "t.respond!('%s')", response - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - else - raise InvalidDnsResponseError, 'TXT' - end - elsif resource == Resolv::DNS::Resource::IN::WKS - if response.is_a?(Array) - unless BeEF::Filters.is_valid_ip?(resource[0]) && - resource[1].is_a?(Integer) && - resource[2].is_a?(Integer) - raise InvalidDnsResponseError, 'WKS' unless resource.is_a?(String) - end - data = { - :address => response[0], - :protocol => response[1], - :bitmap => response[2] - } + data = { + :mname => response[0], + :rname => response[1], + :serial => response[2], + :refresh => response[3], + :retry => response[4], + :expire => response[5], + :minimum => response[6] + } - sprintf "t.respond!('%
s', %d, %d)", data - elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex - sprintf "t.fail!(:%s)", response.to_sym - else - raise InvalidDnsResponseError, 'WKS' - end + sprintf "t.respond!(Resolv::DNS::Name.create('%s'), " + + "Resolv::DNS::Name.create('%s'), " + + '%d, ' + + '%d, ' + + '%d, ' + + '%d, ' + + '%d)', + data + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym else - raise UnknownDnsResourceError + raise InvalidDnsResponseError, 'SOA' end - rescue InvalidDnsResponseError, UnknownDnsResourceError => e - print_error e.message - throw :halt + elsif resource == Resolv::DNS::Resource::IN::TXT + if resource.is_a?(String) + sprintf "t.respond!('%s')", response + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym + else + raise InvalidDnsResponseError, 'TXT' + end + elsif resource == Resolv::DNS::Resource::IN::WKS + if response.is_a?(Array) + unless BeEF::Filters.is_valid_ip?(resource[0]) && + resource[1].is_a?(Integer) && + resource[2].is_a?(Integer) + raise InvalidDnsResponseError, 'WKS' unless resource.is_a?(String) + end + + data = { + :address => response[0], + :protocol => response[1], + :bitmap => response[2] + } + + sprintf "t.respond!('%
s', %d, %d)", data + elsif (response.is_a?(Symbol) && response.to_s =~ sym_regex) || response =~ sym_regex + sprintf "t.fail!(:%s)", response.to_sym + else + raise InvalidDnsResponseError, 'WKS' + end + else + raise UnknownDnsResourceError end src end + # Raised when an invalid pattern is given. + class InvalidDnsPatternError < StandardError + + DEFAULT_MESSAGE = 'Failed to add DNS rule with invalid pattern' + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + + end + # Raised when a response is not valid for the given DNS resource record. class InvalidDnsResponseError < StandardError def initialize(message = nil) - message = sprintf "Invalid response specified for %s resource record", message unless message.nil? + str = "Failed to add DNS rule with invalid response for %s resource record", message + message = sprintf str, message unless message.nil? super(message) end @@ -230,7 +251,7 @@ module BeEF # Raised when an unknown DNS resource record is given. class UnknownDnsResourceError < StandardError - DEFAULT_MESSAGE = 'Unknown resource record was given' + DEFAULT_MESSAGE = 'Failed to add DNS rule with unknown resource record' def initialize(message = nil) super(message || DEFAULT_MESSAGE)