diff --git a/extensions/dns/model.rb b/extensions/dns/model.rb index f4b9aef98..87d734a16 100644 --- a/extensions/dns/model.rb +++ b/extensions/dns/model.rb @@ -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!('%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) && r =~ domain_regex } + + 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) && + response[1].is_a?(String) && + response[1] =~ domain_regex + + 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) && 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('%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 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!('%
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 + 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