Files
beef/extensions/dns/rest/dns.rb
soh_cah_toa bca9eccdf0 Implemented GET ruleset, rule, and POST rule handlers.
Many filter checks were removed because the new DNS extension performs
validation before performing any database operation.

Modified message for InvalidParamError to be more modular.
2014-04-24 16:40:19 -04:00

231 lines
8.1 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 Extension
module Dns
# This class handles the routing of RESTful API requests that query BeEF's DNS server
class DnsRest < BeEF::Core::Router::Router
# Filters out bad requests before performing any routing
before do
config = BeEF::Core::Configuration.instance
# Require a valid API token from a valid IP address
halt 401 unless params[:token] == config.get('beef.api_token')
halt 403 unless BeEF::Core::Rest.permitted_source?(request.ip)
headers 'Content-Type' => 'application/json; charset=UTF-8',
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
'Expires' => '0'
end
# Returns the entire current DNS ruleset
get '/ruleset' do
begin
ruleset = BeEF::Extension::Dns::Server.instance.get_ruleset
count = ruleset.length
result = {}
result[:count] = count
result[:ruleset] = ruleset
result.to_json
rescue StandardError => e
print_error "Internal error while retrieving DNS ruleset (#{e.message})"
halt 500
end
end
# Returns a specific rule given its id
get '/rule/:id' do
begin
id = params[:id]
rule = BeEF::Extension::Dns::Server.instance.get_rule(id)
raise InvalidParamError, 'id' if rule.nil?
halt 404 if rule.empty?
rule.to_json
rescue InvalidParamError => e
print_error e.message
halt 400
rescue StandardError => e
print_error "Internal error while retrieving DNS rule with id #{id} (#{e.message})"
halt 500
end
end
# Adds a new DNS rule
post '/rule' do
begin
body = JSON.parse(request.body.read)
pattern = body['pattern']
resource = body['resource']
response = body['response']
valid_resources = ["A", "AAAA", "CNAME", "HINFO", "MINFO", "MX", "NS", "PTR", "SOA", "TXT", "WKS"]
# Validate required JSON keys
unless [pattern, resource, response].include?(nil)
if response.is_a?(Array)
raise InvalidJsonError, 'Empty "response" key passed to endpoint /api/dns/rule' if response.empty?
else
raise InvalidJsonError, 'Non-array "response" key passed to endpoint /api/dns/rule'
end
raise InvalidJsonError, 'Wrong "resource" key passed to endpoint /api/dns/rule' unless valid_resources.include?(resource)
id = BeEF::Extension::Dns::Server.instance.add_rule(
:pattern => pattern,
:resource => eval("Resolv::DNS::Resource::IN::#{resource}"),
:response => response
)
result = {}
result['success'] = true
result['id'] = id
result.to_json
end
rescue InvalidJsonError => e
print_error e.message
halt 400
rescue StandardError => e
print_error "Internal error while adding DNS rule (#{e.message})"
halt 500
end
end
=begin
# Removes a rule given its id
delete '/rule/:id' do
begin
id = params[:id]
unless BeEF::Filters.alphanums_only?(id)
raise InvalidParamError, 'id'
end
result = {}
result['success'] = BeEF::Extension::Dns::Server.instance.remove_rule(id)
result.to_json
rescue InvalidParamError => e
print_error e.message
halt 400
rescue StandardError => e
print_error "Internal error while removing DNS rule with id #{id} (#{e.message})"
halt 500
end
end
private
# Generates a formatted string representation of the callback to invoke as a response.
#
# @param [String] type resource record type (e.g. A, CNAME, NS, etc.)
# @param [Array] rdata record data to include in response
#
# @return [String] string representation of response callback
def format_response(type, rdata)
src = 'proc { |t| t.respond!(%s) }'
args = case type
when 'A'
data = {:address => rdata[0]}
sprintf "'%<address>s'", data
when 'AAAA'
data = {:address => rdata[0]}
sprintf "'%<address>s'", data
when 'CNAME'
data = {:cname => rdata[0]}
sprintf "Resolv::DNS::Name.create('%<cname>s')", data
when 'HINFO'
data = {:cpu => rdata[0], :os => rdata[1]}
sprintf "'%<cpu>s', '%<os>s'", data
when 'MINFO'
data = {:rmailbx => rdata[0], :emailbx => rdata[1]}
sprintf "Resolv::DNS::Name.create('%<rmailbx>s'), " +
"Resolv::DNS::Name.create('%<emailbx>s')",
data
when 'MX'
data = {:preference => rdata[0], :exchange => rdata[1]}
sprintf "%<preference>d, Resolv::DNS::Name.create('%<exchange>s')", data
when 'NS'
data = {:nsdname => rdata[0]}
sprintf "Resolv::DNS::Name.create('%<nsdname>s')", data
when 'PTR'
data = {:ptrdname => rdata[0]}
sprintf "Resolv::DNS::Name.create('%<ptrdname>s')", data
when 'SOA'
data = {
:mname => rdata[0],
:rname => rdata[1],
:serial => rdata[2],
:refresh => rdata[3],
:retry => rdata[4],
:expire => rdata[5],
:minimum => rdata[6]
}
sprintf "Resolv::DNS::Name.create('%<mname>s'), " +
"Resolv::DNS::Name.create('%<rname>s'), " +
'%<serial>d, ' +
'%<refresh>d, ' +
'%<retry>d, ' +
'%<expire>d, ' +
'%<minimum>d',
data
when 'TXT'
data = {:txtdata => rdata[0]}
sprintf "'%<txtdata>s'", data
when 'WKS'
data = {
:address => rdata[0],
:protocol => rdata[1],
:bitmap => rdata[2]
}
sprintf "'%<address>s', %<protocol>d, %<bitmap>d", data
else
raise InvalidJsonError, 'Unknown "type" key passed to endpoint /api/dns/rule'
end
sprintf(src, args)
end
=end
# Raised when invalid JSON input is passed to an /api/dns handler.
class InvalidJsonError < StandardError
DEFAULT_MESSAGE = 'Invalid JSON input passed to /api/dns handler'
def initialize(message = nil)
super(message || DEFAULT_MESSAGE)
end
end
# Raised when an invalid named parameter is passed to an /api/dns handler.
class InvalidParamError < StandardError
DEFAULT_MESSAGE = 'Invalid parameter passed to /api/dns handler'
def initialize(message = nil)
str = "Invalid \"%s\" parameter passed to /api/dns handler"
message = sprintf str, message unless message.nil?
super(message)
end
end
end
end
end
end