Files
beef/extensions/dns/model.rb
soh_cah_toa e1c27f4feb Removed support for TXT resource record.
Like the HINFO and MINFO RR's, TXT is vulnerable to RCE attacks and has
no purpose at the moment. TXT may be needed in the future (e.g. data
exfiltration) which is why it has been removed separately.
2014-04-28 20:34:56 -04:00

236 lines
9.4 KiB
Ruby

#
# Copyright (c) 2006-2014 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
module BeEF
module Core
module Models
module Dns
# Represents an individual DNS rule.
class Rule
include DataMapper::Resource
storage_names[:default] = 'extension_dns_rules'
property :id, String, :key => true
property :pattern, Object, :required => true
property :resource, Object, :required => true
property :response, Object, :required => true
property :callback, Object, :required => true
# Hooks the model's "save" event. Validates pattern/response and generates a rule identifier.
before :save do |rule|
begin
validate_pattern(rule.pattern)
rule.callback = format_callback(rule.resource, rule.response)
rescue InvalidDnsPatternError, UnknownDnsResourceError, 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 format_callback(resource, response)
sym_regex = /^:?(NoError|FormErr|ServFail|NXDomain|NotImp|Refused|NotAuth)$/i
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'
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, '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::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::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)
str = "Failed to add DNS rule with invalid response for %s resource record", message
message = sprintf str, message unless message.nil?
super(message)
end
end
# Raised when an unknown DNS resource record is given.
class UnknownDnsResourceError < StandardError
DEFAULT_MESSAGE = 'Failed to add DNS rule with unknown resource record'
def initialize(message = nil)
super(message || DEFAULT_MESSAGE)
end
end
end
end
end
end
end