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.
This commit is contained in:
@@ -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!('%<cpu>s', '%<os>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('%<rmailbx>s'), " +
|
||||
"Resolv::DNS::Name.create('%<emailbx>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!(%<preference>d, Resolv::DNS::Name.create('%<exchange>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!('%<cpu>s', '%<os>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('%<rmailbx>s'), " +
|
||||
"Resolv::DNS::Name.create('%<emailbx>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!(%<preference>d, Resolv::DNS::Name.create('%<exchange>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('%<mname>s'), " +
|
||||
"Resolv::DNS::Name.create('%<rname>s'), " +
|
||||
'%<serial>d, ' +
|
||||
'%<refresh>d, ' +
|
||||
'%<retry>d, ' +
|
||||
'%<expire>d, ' +
|
||||
'%<minimum>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!('%<address>s', %<protocol>d, %<bitmap>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('%<mname>s'), " +
|
||||
"Resolv::DNS::Name.create('%<rname>s'), " +
|
||||
'%<serial>d, ' +
|
||||
'%<refresh>d, ' +
|
||||
'%<retry>d, ' +
|
||||
'%<expire>d, ' +
|
||||
'%<minimum>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!('%<address>s', %<protocol>d, %<bitmap>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)
|
||||
|
||||
Reference in New Issue
Block a user