Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
beefproject
GitHub Repository: beefproject/beef
Path: blob/master/extensions/requester/rest/requester.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 Extension
8
module Requester
9
# This class handles the routing of RESTful API requests for the requester
10
class RequesterRest < BeEF::Core::Router::Router
11
# Filters out bad requests before performing any routing
12
before do
13
config = BeEF::Core::Configuration.instance
14
15
# Require a valid API token from a valid IP address
16
halt 401 unless params[:token] == config.get('beef.api_token')
17
halt 403 unless BeEF::Core::Rest.permitted_source?(request.ip)
18
19
H = BeEF::Core::Models::Http
20
HB = BeEF::Core::Models::HookedBrowser
21
22
headers 'Content-Type' => 'application/json; charset=UTF-8',
23
'Pragma' => 'no-cache',
24
'Cache-Control' => 'no-cache',
25
'Expires' => '0'
26
end
27
28
# Returns a request by ID
29
get '/request/:id' do
30
id = params[:id]
31
raise InvalidParamError, 'id' unless BeEF::Filters.nums_only?(id)
32
33
requests = H.find(id)
34
halt 404 if requests.nil?
35
36
result = {}
37
result[:count] = requests.length
38
result[:requests] = []
39
requests.each do |request|
40
result[:requests] << request2hash(request)
41
end
42
43
result.to_json
44
rescue InvalidParamError => e
45
print_error e.message
46
halt 400
47
rescue StandardError => e
48
print_error "Internal error while retrieving request with id #{id} (#{e.message})"
49
halt 500
50
end
51
52
# Returns all requestes given a specific hooked browser id
53
get '/requests/:id' do
54
id = params[:id]
55
raise InvalidParamError, 'id' unless BeEF::Filters.is_valid_hook_session_id?(id)
56
57
requests = H.where(hooked_browser_id: id)
58
halt 404 if requests.nil?
59
60
result = {}
61
result[:count] = requests.length
62
result[:requests] = []
63
requests.each do |request|
64
result[:requests] << request2hash(request)
65
end
66
67
result.to_json
68
rescue InvalidParamError => e
69
print_error e.message
70
halt 400
71
rescue StandardError => e
72
print_error "Internal error while retrieving request list for hooked browser with id #{id} (#{e.message})"
73
halt 500
74
end
75
76
# Return a response by ID
77
get '/response/:id' do
78
# super debugging
79
80
error = {}
81
82
error[:code] = 0
83
84
id = params[:id]
85
raise InvalidParamError, 'id' unless BeEF::Filters.nums_only?(id)
86
87
error[:code] = 1
88
89
responses = H.find(id) || nil
90
error[:code] = 2
91
halt 404 if responses.nil?
92
error[:code] = 3
93
result = {}
94
result[:success] = 'true'
95
error[:code] = 4
96
97
result[:result] = response2hash(responses)
98
error[:code] = 5
99
100
result.to_json
101
rescue InvalidParamError => e
102
print_error e.message
103
halt 400
104
rescue StandardError => e
105
print_error "Internal error while retrieving response with id #{id} (#{e.message})"
106
107
error[:id] = id
108
error[:message] = e.message
109
error.to_json
110
# halt 500
111
end
112
113
# Deletes a specific response given its id
114
delete '/response/:id' do
115
id = params[:id]
116
raise InvalidParamError, 'id' unless BeEF::Filters.nums_only?(id)
117
118
responses = H.find(id) || nil
119
halt 404 if responses.nil?
120
121
result = {}
122
result['success'] = H.delete(id)
123
result.to_json
124
rescue InvalidParamError => e
125
print_error e.message
126
halt 400
127
rescue StandardError => e
128
print_error "Internal error while removing response with id #{id} (#{e.message})"
129
halt 500
130
end
131
132
# Send a new HTTP request to the hooked browser
133
post '/send/:id' do
134
id = params[:id]
135
proto = params[:proto].to_s || 'http'
136
raw_request = params['raw_request'].to_s
137
138
zombie = HB.where(session: id).first || nil
139
halt 404 if zombie.nil?
140
141
# @TODO: move most of this to the model
142
143
raise InvalidParamError, 'raw_request' if raw_request == ''
144
145
raise InvalidParamError, 'raw_request: Invalid request URL scheme' if proto !~ /\Ahttps?\z/
146
147
req_parts = raw_request.split(/ |\n/)
148
149
verb = req_parts[0]
150
raise InvalidParamError, 'raw_request: Only HEAD, GET, POST, OPTIONS, PUT or DELETE requests are supported' unless BeEF::Filters.is_valid_verb?(verb)
151
152
uri = req_parts[1]
153
raise InvalidParamError, 'raw_request: Invalid URI' unless BeEF::Filters.is_valid_url?(uri)
154
155
version = req_parts[2]
156
raise InvalidParamError, 'raw_request: Invalid HTTP version' unless BeEF::Filters.is_valid_http_version?(version)
157
158
host_str = req_parts[3]
159
raise InvalidParamError, 'raw_request: Invalid HTTP version' unless BeEF::Filters.is_valid_host_str?(host_str)
160
161
# Validate target hsot
162
host = req_parts[4]
163
host_parts = host.split(/:/)
164
host_name = host_parts[0]
165
host_port = host_parts[1] || nil
166
167
raise InvalidParamError, 'raw_request: Invalid HTTP HostName' unless BeEF::Filters.is_valid_hostname?(host_name)
168
169
host_port = host_parts[1] || nil
170
if host_port.nil? || !BeEF::Filters.nums_only?(host_port)
171
host_port = proto.eql?('https') ? 443 : 80
172
end
173
174
# Save the new HTTP request
175
http = H.new(
176
hooked_browser_id: zombie.session,
177
request: raw_request,
178
method: verb,
179
proto: proto,
180
domain: host_name,
181
port: host_port,
182
path: uri,
183
request_date: Time.now,
184
allow_cross_origin: 'true'
185
)
186
187
print_debug "added new http request for #{zombie.session}"
188
print_debug http.to_json
189
190
if verb.eql?('POST') || verb.eql?('PUT')
191
req_parts.each_with_index do |value, index|
192
http.content_length = req_parts[index + 1] if value.match(/^Content-Length/i)
193
end
194
end
195
196
http.save
197
198
result = request2hash(http)
199
print_debug "[Requester] Sending HTTP request through zombie [ip: #{zombie.ip}] : #{result}"
200
201
# result.to_json
202
rescue InvalidParamError => e
203
print_error e.message
204
halt 400
205
rescue StandardError => e
206
print_error "Internal error while removing network host with id #{id} (#{e.message})"
207
halt 500
208
end
209
210
# Convert a request object to Hash
211
def request2hash(http)
212
{
213
id: http.id,
214
proto: http.proto,
215
domain: http.domain,
216
port: http.port,
217
path: http.path,
218
has_ran: http.has_ran,
219
method: http.method,
220
request_date: http.request_date,
221
response_date: http.response_date,
222
response_status_code: http.response_status_code,
223
response_status_text: http.response_status_text,
224
response_port_status: http.response_port_status
225
}
226
end
227
228
# Convert a response object to Hash
229
def response2hash(http)
230
response_data = ''
231
232
unless http.response_data.nil?
233
if (http.response_data.length > (1024 * 100)) # more than 100K
234
response_data = http.response_data[0..(1024 * 100)]
235
response_data += "\n<---------- Response Data Truncated---------->"
236
else
237
response_data = http.response_data
238
end
239
end
240
241
response_headers = ''
242
response_headers = http.response_headers unless http.response_headers.nil?
243
244
{
245
id: http.id,
246
request: http.request.force_encoding('UTF-8'),
247
response: response_data.force_encoding('UTF-8'),
248
response_headers: response_headers.force_encoding('UTF-8'),
249
proto: http.proto.force_encoding('UTF-8'),
250
domain: http.domain.force_encoding('UTF-8'),
251
port: http.port.force_encoding('UTF-8'),
252
path: http.path.force_encoding('UTF-8'),
253
date: http.request_date,
254
has_ran: http.has_ran.force_encoding('UTF-8')
255
}
256
end
257
258
# Raised when invalid JSON input is passed to an /api/requester handler.
259
class InvalidJsonError < StandardError
260
DEFAULT_MESSAGE = 'Invalid JSON input passed to /api/requester handler'.freeze
261
262
def initialize(message = nil)
263
super(message || DEFAULT_MESSAGE)
264
end
265
end
266
267
# Raised when an invalid named parameter is passed to an /api/requester handler.
268
class InvalidParamError < StandardError
269
DEFAULT_MESSAGE = 'Invalid parameter passed to /api/requester handler'.freeze
270
271
def initialize(message = nil)
272
str = 'Invalid "%s" parameter passed to /api/requester handler'
273
message = format str, message unless message.nil?
274
super(message)
275
end
276
end
277
end
278
end
279
end
280
end
281
282