Files
beef/extensions/proxy/handlers/zombie/handler.rb

124 lines
5.4 KiB
Ruby

#
# Copyright 2011 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 Proxy
module Handlers
module Zombie
class Handler
attr_reader :guard
@response = nil
H = BeEF::Core::Models::Http
# This function will forward requests to the target and
# the browser will perform the request. Then the results
# will be sent back.
def forward_request(hooked_browser_id, req, res)
# validate that the raw request is correct and can be used
req_parts = req.to_s.split(/ |\n/) # break up the request
verb = req_parts[0]
raise 'Only HEAD, GET, POST, OPTIONS, PUT or DELETE requests are supported' if not BeEF::Filters.is_valid_verb?(verb) #check verb
# antisnatchor: is_valid_url supposes that the uri is relative, while here we're passing an absolute one
#uri = req_parts[1]
#raise 'Invalid URI' if not BeEF::Filters.is_valid_url?(uri) #check uri
version = req_parts[2]
raise 'Invalid HTTP version' if not BeEF::Filters.is_valid_http_version?(version) # check http version - HTTP/1.0
# antisnatchor: the following checks are wrong. the req_parts array can always contains elements at different postions.
# for example proxying Opera, the req_parts[3] is the User-Agent header...
# host_str = req_parts[3]
# raise 'Invalid HTTP host header' if not BeEF::Filters.is_valid_host_str?(host_str) # check host string - Host:
# host = req_parts[4]
# host_parts = host.split(/:/)
# hostname = host_parts[0]
# raise 'Invalid hostname' if not BeEF::Filters.is_valid_hostname?(hostname) #check the target hostname
# hostport = host_parts[1] || nil
# if !hostport.nil?
# raise 'Invalid hostport' if not BeEF::Filters.nums_only?(hostport) #check the target hostport
# end
# Saves the new HTTP request to the db for processing by browser.
# IDs are created and incremented automatically by DataMapper.
http = H.new(
:request => req,
:method => req.request_method.to_s,
:domain => req.host,
:port => req.port,
:path => req.path.to_s,
:request_date => Time.now,
:hooked_browser_id => hooked_browser_id
)
http.save
# Starts a new thread scoped to this Handler instance, in order to minimize performance degradation
# while waiting for the HTTP response to be stored in the db.
print_info("[PROXY] Thread started in order to process request ##{http.id} to [#{req.path.to_s}] on domain [#{req.host}:#{req.port}]")
@response_thread = Thread.new do
while H.first(:id => http.id).has_ran != "complete"
sleep 0.5
end
@response = H.first(:id => http.id)
end
@response_thread.join
print_info("[PROXY] Response for request ##{http.id} to [#{req.path.to_s}] on domain [#{req.host}:#{req.port}] correctly processed")
res.body = @response['response_data']
# set the original response status code
res.status = @response['response_status_code']
headers = @response['response_headers']
#print_debug("====== original HTTP response headers =======\n#{headers}")
# The following is needed to forward back some of the original HTTP response headers obtained via XHR calls.
# Original XHR response headers are stored in extension_requester_http table (response_headers column),
# but we are forwarding back only some of them (Server, X-.. - like X-Powered-By -, Content-Type, ... ).
# Some of the original response headers need to be removed, like encoding and cache related: for example
# about encoding, the original response headers says that the content-length is 1000 as the response is gzipped,
# but the final content-length forwarded back by the proxy is clearly bigger. Date header follows the same way.
headers_hash = Hash.new
if(res.status != -1 && res.status != 0)
headers.each_line do |line|
# stripping the Encoding, Cache and other headers
if line.split(': ')[0] != "Content-Encoding" &&
line.split(': ')[0] != "Content-Length" &&
line.split(': ')[0] != "Keep-Alive" &&
line.split(': ')[0] != "Cache-Control" &&
line.split(': ')[0] != "Vary" &&
line.split(': ')[0] != "Pragma" &&
line.split(': ')[0] != "Connection" &&
line.split(': ')[0] != "Expires" &&
line.split(': ')[0] != "Accept-Ranges" &&
line.split(': ')[0] != "Date"
headers_hash[line.split(': ')[0]] = line.split(': ')[1].gsub!(/[\n]+/,"")
end
end
# note: override_headers is a (new) method of WebRick::HTTPResponse (the BeEF patch one: core\ruby\patches\webrick\httpresponse.rb)
res.override_headers(headers_hash)
end
res
end
end
end
end
end
end
end