Implemented #validate_response method in Dns::Rule.
Perhaps the ugliest part of the DNS extension, it is also the most crucial. This method ensures that a given resource and response are appropriate for each other. It must also prevent RCE vulns since the input is eval'd later on. However, HINFO, MINFO, and especially TXT validation is not strict enough. These three need to be reviewed scrupulously since a 100% anti-RCE solution may prove to be difficult.
This commit is contained in:
@@ -34,7 +34,233 @@ module BeEF
|
||||
#
|
||||
# @return [String] string representation of callback that can safely be eval'd
|
||||
def validate_response(resource, response)
|
||||
"t.respond!('1.1.1.1')"
|
||||
domain_regex = /^[0-9a-z-]+(\.[0-9a-z-]+)*(\.[a-z]{2,})$/i
|
||||
sym_regex = /^:?(NoError|FormErr|ServFail|NXDomain|NotImp|Refused|NotAuth)$/i
|
||||
|
||||
ipv4_regex = /^((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}
|
||||
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])$/x
|
||||
|
||||
ipv6_regex = /^(([0-9a-f]{1,4}:){7,7}[0-9a-f]{1,4}|
|
||||
([0-9a-f]{1,4}:){1,7}:|
|
||||
([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|
|
||||
([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}|
|
||||
([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}|
|
||||
([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}|
|
||||
([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}|
|
||||
[0-9a-f]{1,4}:((:[0-9a-f]{1,4}){1,6})|
|
||||
:((:[0-9a-f]{1,4}){1,7}|:)|
|
||||
fe80:(:[0-9a-f]{0,4}){0,4}%[0-9a-z]{1,}|
|
||||
::(ffff(:0{1,4}){0,1}:){0,1}
|
||||
((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}
|
||||
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|
|
||||
([0-9a-f]{1,4}:){1,4}:
|
||||
((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}
|
||||
(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/ix
|
||||
|
||||
begin
|
||||
src = if resource == Resolv::DNS::Resource::IN::A
|
||||
if response.is_a?(String) && response =~ ipv4_regex
|
||||
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 r =~ ipv4_regex
|
||||
str2 << sprintf(str1, r)
|
||||
end
|
||||
|
||||
str2
|
||||
else
|
||||
raise InvalidDnsResponseError, 'A'
|
||||
end
|
||||
elsif resource == Resolv::DNS::Resource::IN::AAAA
|
||||
if response.is_a?(String) && response =~ ipv6_regex
|
||||
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 r =~ ipv6_regex
|
||||
str2 << sprintf(str1, r)
|
||||
end
|
||||
|
||||
str2
|
||||
else
|
||||
raise InvalidDnsResponseError, 'AAAA'
|
||||
end
|
||||
elsif resource == Resolv::DNS::Resource::IN::CNAME
|
||||
if response.is_a?(String) && response =~ domain_regex
|
||||
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) && r =~ domain_regex }
|
||||
|
||||
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) &&
|
||||
response[1].is_a?(String) &&
|
||||
response[1] =~ domain_regex
|
||||
|
||||
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) && response =~ domain_regex
|
||||
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 r =~ ipv4_regex
|
||||
str2 << sprintf(str1, r)
|
||||
end
|
||||
|
||||
str2
|
||||
else
|
||||
raise InvalidDnsResponseError, 'NS'
|
||||
end
|
||||
elsif resource == Resolv::DNS::Resource::IN::PTR
|
||||
if response.is_a?(String) && response =~ domain_regex
|
||||
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 response[0].is_a?(String) &&
|
||||
response[0] =~ domain_regex &&
|
||||
response[1].is_a?(String) &&
|
||||
response[1] =~ domain_regex &&
|
||||
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 resource[0].is_a?(String) &&
|
||||
resource[0] =~ ipv4_regex &&
|
||||
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
|
||||
rescue InvalidDnsResponseError, UnknownDnsResourceError => e
|
||||
print_error e.message
|
||||
throw :halt
|
||||
end
|
||||
|
||||
src
|
||||
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?
|
||||
super(message)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Raised when an unknown DNS resource record is given.
|
||||
class UnknownDnsResourceError < StandardError
|
||||
|
||||
DEFAULT_MESSAGE = 'Unknown resource record was given'
|
||||
|
||||
def initialize(message = nil)
|
||||
super(message || DEFAULT_MESSAGE)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user