@@ -56,19 +56,17 @@ ZombieTab_Requester = function(zombie) {
|
|||||||
********************************************/
|
********************************************/
|
||||||
var history_panel_store = new Ext.ux.data.PagingJsonStore({
|
var history_panel_store = new Ext.ux.data.PagingJsonStore({
|
||||||
storeId: 'requester-history-store-zombie-'+zombie.session,
|
storeId: 'requester-history-store-zombie-'+zombie.session,
|
||||||
url: '<%= @base_path %>/requester/history.json',
|
proxy: new Ext.data.HttpProxy({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/api/requester/requests/' + zombie.session + '?token=' + beefwui.get_rest_token(),
|
||||||
|
}),
|
||||||
remoteSort: false,
|
remoteSort: false,
|
||||||
autoDestroy: true,
|
autoDestroy: true,
|
||||||
autoLoad: false,
|
autoLoad: false,
|
||||||
root: 'history',
|
root: 'requests',
|
||||||
|
|
||||||
fields: ['proto', 'domain', 'port', 'method', 'request_date', 'response_date','id', 'has_ran', 'path','response_status_code', 'response_status_text', 'response_port_status'],
|
fields: ['proto', 'domain', 'port', 'method', 'request_date', 'response_date','id', 'has_ran', 'path','response_status_code', 'response_status_text', 'response_port_status'],
|
||||||
sortInfo: {field: 'request_date', direction: 'DESC'},
|
sortInfo: {field: 'request_date', direction: 'DESC'},
|
||||||
|
|
||||||
baseParams: {
|
|
||||||
nonce: Ext.get("nonce").dom.value,
|
|
||||||
zombie_session: zombie.session
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var req_pagesize = 30;
|
var req_pagesize = 30;
|
||||||
@@ -183,10 +181,9 @@ ZombieTab_Requester = function(zombie) {
|
|||||||
title: 'History',
|
title: 'History',
|
||||||
items:[history_panel_grid],
|
items:[history_panel_grid],
|
||||||
layout: 'fit',
|
layout: 'fit',
|
||||||
|
|
||||||
listeners: {
|
listeners: {
|
||||||
activate: function(history_panel) {
|
activate: function(history_panel) {
|
||||||
history_panel.items.items[0].store.reload({params:{url:'<%= @base_path %>/requester/history.json'}});
|
history_panel.items.items[0].store.reload({params: {nonce: Ext.get("nonce").dom.value}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -207,7 +204,7 @@ ZombieTab_Requester = function(zombie) {
|
|||||||
var form = new Ext.FormPanel({
|
var form = new Ext.FormPanel({
|
||||||
title: 'Forge Raw HTTP Request',
|
title: 'Forge Raw HTTP Request',
|
||||||
id: 'requester-request-form-zombie'+zombie.session,
|
id: 'requester-request-form-zombie'+zombie.session,
|
||||||
url: '<%= @base_path %>/requester/send',
|
url: '/api/requester/send/' + zombie.session + '?token=' + beefwui.get_rest_token(),
|
||||||
hideLabels : true,
|
hideLabels : true,
|
||||||
border: false,
|
border: false,
|
||||||
padding: '3px 5px 0 5px',
|
padding: '3px 5px 0 5px',
|
||||||
@@ -238,13 +235,12 @@ ZombieTab_Requester = function(zombie) {
|
|||||||
var use_ssl = Ext.getCmp('requester-forge-requests-ssl').getValue();
|
var use_ssl = Ext.getCmp('requester-forge-requests-ssl').getValue();
|
||||||
if (use_ssl) var proto = 'https'; else var proto = 'http';
|
if (use_ssl) var proto = 'https'; else var proto = 'http';
|
||||||
var form = Ext.getCmp('requester-request-form-zombie'+zombie.session).getForm();
|
var form = Ext.getCmp('requester-request-form-zombie'+zombie.session).getForm();
|
||||||
|
|
||||||
bar.update_sending('Sending request to ' + zombie.ip + '...');
|
bar.update_sending('Sending request to ' + zombie.ip + '...');
|
||||||
|
|
||||||
form.submit({
|
form.submit({
|
||||||
params: {
|
params: {
|
||||||
nonce: Ext.get("nonce").dom.value,//insert the nonce with the form
|
raw_request: Ext.getCmp('raw-request-zombie-'+zombie.session).getValue(),
|
||||||
zombie_session: zombie.session,
|
|
||||||
proto: proto
|
proto: proto
|
||||||
},
|
},
|
||||||
success: function() {
|
success: function() {
|
||||||
@@ -277,14 +273,10 @@ ZombieTab_Requester = function(zombie) {
|
|||||||
function deleteResponse(request, zombie, bar) {
|
function deleteResponse(request, zombie, bar) {
|
||||||
|
|
||||||
Ext.Ajax.request({
|
Ext.Ajax.request({
|
||||||
url: '<%= @base_path %>/requester/delete',
|
url: '/api/requester/response/' + request.id + '?token=' + beefwui.get_rest_token(),
|
||||||
|
method: 'DELETE',
|
||||||
loadMask: true,
|
loadMask: true,
|
||||||
|
|
||||||
params: {
|
|
||||||
nonce: Ext.get("nonce").dom.value,
|
|
||||||
http_id: request.id
|
|
||||||
},
|
|
||||||
|
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
var xhr = Ext.decode(response.responseText);
|
var xhr = Ext.decode(response.responseText);
|
||||||
if (xhr['success'] == 'true') {
|
if (xhr['success'] == 'true') {
|
||||||
@@ -310,14 +302,8 @@ ZombieTab_Requester = function(zombie) {
|
|||||||
bar.update_sending('Getting response...');
|
bar.update_sending('Getting response...');
|
||||||
|
|
||||||
Ext.Ajax.request({
|
Ext.Ajax.request({
|
||||||
url: '<%= @base_path %>/requester/response.json',
|
url: '/api/requester/response/' + request.id + '?token=' + beefwui.get_rest_token(),
|
||||||
loadMask: true,
|
loadMask: true,
|
||||||
|
|
||||||
params: {
|
|
||||||
nonce: Ext.get("nonce").dom.value,
|
|
||||||
http_id: request.id
|
|
||||||
},
|
|
||||||
|
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
var xhr = Ext.decode(response.responseText);
|
var xhr = Ext.decode(response.responseText);
|
||||||
|
|
||||||
|
|||||||
@@ -192,13 +192,18 @@ module BeEF
|
|||||||
|
|
||||||
def get_tunneling_proxy
|
def get_tunneling_proxy
|
||||||
proxy_browser = HB.first(:is_proxy => true)
|
proxy_browser = HB.first(:is_proxy => true)
|
||||||
if (proxy_browser != nil)
|
unless proxy_browser.nil?
|
||||||
proxy_browser_id = proxy_browser.id.to_s
|
return proxy_browser.session.to_s
|
||||||
else
|
|
||||||
proxy_browser_id = 1
|
|
||||||
print_debug "[PROXY] Proxy browser not set. Defaulting to browser id #1"
|
|
||||||
end
|
end
|
||||||
proxy_browser_id
|
|
||||||
|
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
|
end
|
||||||
|
|||||||
@@ -6,30 +6,23 @@
|
|||||||
module BeEF
|
module BeEF
|
||||||
module Extension
|
module Extension
|
||||||
module Requester
|
module Requester
|
||||||
|
|
||||||
module RegisterHttpHandler
|
module RegisterHttpHandler
|
||||||
|
|
||||||
BeEF::API::Registrar.instance.register(BeEF::Extension::Requester::RegisterHttpHandler, BeEF::API::Server, 'mount_handler')
|
BeEF::API::Registrar.instance.register(BeEF::Extension::Requester::RegisterHttpHandler, BeEF::API::Server, 'mount_handler')
|
||||||
|
|
||||||
# We register the http handler for the requester.
|
|
||||||
# This http handler will retrieve the http responses for all requests
|
|
||||||
def self.mount_handler(beef_server)
|
def self.mount_handler(beef_server)
|
||||||
beef_server.mount('/requester', BeEF::Extension::Requester::Handler)
|
beef_server.mount('/requester', BeEF::Extension::Requester::Handler)
|
||||||
|
beef_server.mount('/api/requester', BeEF::Extension::Requester::RequesterRest.new)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module RegisterPreHookCallback
|
module RegisterPreHookCallback
|
||||||
|
|
||||||
BeEF::API::Registrar.instance.register(BeEF::Extension::Requester::RegisterPreHookCallback, BeEF::API::Server::Hook, 'pre_hook_send')
|
BeEF::API::Registrar.instance.register(BeEF::Extension::Requester::RegisterPreHookCallback, BeEF::API::Server::Hook, 'pre_hook_send')
|
||||||
|
|
||||||
def self.pre_hook_send(hooked_browser, body, params, request, response)
|
def self.pre_hook_send(hooked_browser, body, params, request, response)
|
||||||
dhook = BeEF::Extension::Requester::API::Hook.new
|
dhook = BeEF::Extension::Requester::API::Hook.new
|
||||||
dhook.requester_run(hooked_browser, body)
|
dhook.requester_run(hooked_browser, body)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ module BeEF
|
|||||||
@body = body
|
@body = body
|
||||||
# Generate all the requests and output them to the hooked browser
|
# Generate all the requests and output them to the hooked browser
|
||||||
output = []
|
output = []
|
||||||
BeEF::Core::Models::Http.all(:hooked_browser_id => hb.id, :has_ran => "waiting").each { |h|
|
BeEF::Core::Models::Http.all(:hooked_browser_id => hb.session, :has_ran => "waiting").each { |h|
|
||||||
output << self.requester_parse_db_request(h)
|
output << self.requester_parse_db_request(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,210 +0,0 @@
|
|||||||
#
|
|
||||||
# Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
|
|
||||||
# Browser Exploitation Framework (BeEF) - http://beefproject.com
|
|
||||||
# See the file 'doc/COPYING' for copying permission
|
|
||||||
#
|
|
||||||
module BeEF
|
|
||||||
module Extension
|
|
||||||
module AdminUI
|
|
||||||
module Controllers
|
|
||||||
|
|
||||||
#
|
|
||||||
# HTTP Controller for the Requester component of BeEF.
|
|
||||||
#
|
|
||||||
class Requester < BeEF::Extension::AdminUI::HttpController
|
|
||||||
|
|
||||||
# Variable representing the Http DB model.
|
|
||||||
H = BeEF::Core::Models::Http
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
super({
|
|
||||||
'paths' => {
|
|
||||||
'/send' => method(:send_request),
|
|
||||||
'/delete' => method(:delete_zombie_response),
|
|
||||||
'/history.json' => method(:get_zombie_history),
|
|
||||||
'/response.json' => method(:get_zombie_response)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
def err_msg(error)
|
|
||||||
print_error "[REQUESTER] #{error}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Send a new http request to the hooked browser.
|
|
||||||
def send_request
|
|
||||||
# validate that the hooked browser's session has been sent
|
|
||||||
zombie_session = @params['zombie_session'] || nil
|
|
||||||
(self.err_msg "Invalid session id";return @body = '{success : false}') if not BeEF::Filters.is_valid_hook_session_id?(zombie_session)
|
|
||||||
|
|
||||||
# validate that the hooked browser exists in the db
|
|
||||||
zombie = Z.first(:session => zombie_session) || nil
|
|
||||||
(self.err_msg "Invalid hooked browser session";return @body = '{success : false}') if zombie.nil?
|
|
||||||
|
|
||||||
# validate that the raw request has been sent
|
|
||||||
raw_request = @params['raw_request'] || nil
|
|
||||||
(self.err_msg "raw_request is nil";return @body = '{success : false}') if raw_request.nil?
|
|
||||||
(self.err_msg "raw_request contains non-printable chars";return @body = '{success : false}') if not BeEF::Filters.has_non_printable_char?(raw_request)
|
|
||||||
|
|
||||||
# validate nonce
|
|
||||||
nonce = @params['nonce'] || nil
|
|
||||||
(self.err_msg "nonce is nil";return @body = '{success : false}') if nonce.nil?
|
|
||||||
(self.err_msg "nonce incorrect";return @body = '{success : false}') if @session.get_nonce != nonce
|
|
||||||
|
|
||||||
# validate that the raw request is correct and can be used
|
|
||||||
req_parts = raw_request.split(/ |\n/) # break up the request
|
|
||||||
|
|
||||||
verb = req_parts[0]
|
|
||||||
self.err_msg 'Only HEAD, GET, POST, OPTIONS, PUT or DELETE requests are supported' if not BeEF::Filters.is_valid_verb?(verb) #check verb
|
|
||||||
|
|
||||||
uri = req_parts[1]
|
|
||||||
(self.err_msg 'Invalid URI';return @body = '{success : false}') if not BeEF::Filters.is_valid_url?(uri) #check uri
|
|
||||||
|
|
||||||
version = req_parts[2]
|
|
||||||
(self.err_msg 'Invalid HTTP version';return @body = '{success : false}') if not BeEF::Filters.is_valid_http_version?(version) # check http version - HTTP/1.0 or HTTP/1.1
|
|
||||||
|
|
||||||
host_str = req_parts[3]
|
|
||||||
(self.err_msg 'Invalid HTTP Host Header';return @body = '{success : false}') 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]
|
|
||||||
(self.err_msg 'Invalid HTTP HostName';return @body = '{success : false}') if not BeEF::Filters.is_valid_hostname?(hostname) #check the target hostname
|
|
||||||
|
|
||||||
hostport = host_parts[1] || nil
|
|
||||||
if !hostport.nil?
|
|
||||||
(self.err_msg 'Invalid HTTP HostPort';return @body = '{success : false}') if not BeEF::Filters.nums_only?(hostport) #check the target hostport
|
|
||||||
end
|
|
||||||
|
|
||||||
proto = @params['proto'] || 'http'
|
|
||||||
if proto !~ /\Ahttps?\z/
|
|
||||||
(self.err_msg 'Invalid request protocol';return @body = '{success : false}')
|
|
||||||
end
|
|
||||||
|
|
||||||
# Saves the new HTTP request.
|
|
||||||
http = H.new(
|
|
||||||
:request => raw_request,
|
|
||||||
:method => verb,
|
|
||||||
:proto => proto,
|
|
||||||
:domain => hostname,
|
|
||||||
:port => hostport,
|
|
||||||
:path => uri,
|
|
||||||
:request_date => Time.now,
|
|
||||||
:hooked_browser_id => zombie.id,
|
|
||||||
:allow_cross_domain => "true",
|
|
||||||
)
|
|
||||||
|
|
||||||
if verb.eql? 'POST'
|
|
||||||
req_parts.each_with_index do |value, index|
|
|
||||||
if value.match(/^Content-Length/)
|
|
||||||
http.content_length = req_parts[index+1]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
http.save
|
|
||||||
|
|
||||||
@body = '{success : true}'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a JSON object containing the history of requests sent to the zombie.
|
|
||||||
def get_zombie_history
|
|
||||||
# validate nonce
|
|
||||||
nonce = @params['nonce'] || nil
|
|
||||||
(self.err_msg "nonce is nil";return @body = '{success : false}') if nonce.nil?
|
|
||||||
(self.err_msg "nonce incorrect";return @body = '{success : false}') if @session.get_nonce != nonce
|
|
||||||
|
|
||||||
# validate that the hooked browser's session has been sent
|
|
||||||
zombie_session = @params['zombie_session'] || nil
|
|
||||||
(self.err_msg "Zombie session is nil";return @body = '{success : false}') if zombie_session.nil?
|
|
||||||
|
|
||||||
# validate that the hooked browser exists in the db
|
|
||||||
zombie = Z.first(:session => zombie_session) || nil
|
|
||||||
(self.err_msg "Invalid hooked browser session";return @body = '{success : false}') if zombie.nil?
|
|
||||||
|
|
||||||
history = []
|
|
||||||
H.all(:hooked_browser_id => zombie.id).each{|http|
|
|
||||||
history << {
|
|
||||||
'id' => http.id,
|
|
||||||
'proto' => http.proto,
|
|
||||||
'domain' => http.domain,
|
|
||||||
'port' => http.port,
|
|
||||||
'path' => http.path,
|
|
||||||
'has_ran' => http.has_ran,
|
|
||||||
'method' => http.method,
|
|
||||||
'request_date' => http.request_date,
|
|
||||||
'response_date' => http.response_date,
|
|
||||||
'response_status_code' => http.response_status_code,
|
|
||||||
'response_status_text' => http.response_status_text,
|
|
||||||
'response_port_status' => http.response_port_status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@body = {'success' => 'true', 'history' => history}.to_json
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns a JSON objecting containing the response of a request.
|
|
||||||
def get_zombie_response
|
|
||||||
# validate nonce
|
|
||||||
nonce = @params['nonce'] || nil
|
|
||||||
(self.err_msg "nonce is nil";return @body = '{success : false}') if nonce.nil?
|
|
||||||
(self.err_msg "nonce incorrect";return @body = '{success : false}') if @session.get_nonce != nonce
|
|
||||||
|
|
||||||
# validate the http id
|
|
||||||
http_id = @params['http_id'] || nil
|
|
||||||
(self.err_msg "http_id is nil";return @body = '{success : false}') if http_id.nil?
|
|
||||||
|
|
||||||
# validate that the http object exist in the dabatase
|
|
||||||
http_db = H.first(:id => http_id) || nil
|
|
||||||
(self.err_msg "http object could not be found in the database";return @body = '{success : false}') if http_db.nil?
|
|
||||||
|
|
||||||
if http_db.response_data.length > (1024 * 100) #more thank 100K
|
|
||||||
response_data = http_db.response_data[0..(1024*100)]
|
|
||||||
response_data += "\n<---------- Response Data Truncated---------->"
|
|
||||||
else
|
|
||||||
response_data = http_db.response_data
|
|
||||||
end
|
|
||||||
|
|
||||||
res = {
|
|
||||||
'id' => http_db.id,
|
|
||||||
'request' => http_db.request.force_encoding('UTF-8'),
|
|
||||||
'response' => response_data.force_encoding('UTF-8'),
|
|
||||||
'response_headers' => http_db.response_headers.force_encoding('UTF-8'),
|
|
||||||
'proto' => http_db.proto.force_encoding('UTF-8'),
|
|
||||||
'domain' => http_db.domain.force_encoding('UTF-8'),
|
|
||||||
'port' => http_db.port.force_encoding('UTF-8'),
|
|
||||||
'path' => http_db.path.force_encoding('UTF-8'),
|
|
||||||
'date' => http_db.request_date,
|
|
||||||
'has_ran' => http_db.has_ran.force_encoding('UTF-8')
|
|
||||||
}
|
|
||||||
|
|
||||||
@body = {'success' => 'true', 'result' => res}.to_json
|
|
||||||
end
|
|
||||||
|
|
||||||
# Deletes a response from the requester history
|
|
||||||
def delete_zombie_response
|
|
||||||
# validate nonce
|
|
||||||
nonce = @params['nonce'] || nil
|
|
||||||
(self.err_msg "nonce is nil";return @body = '{success : false}') if nonce.nil?
|
|
||||||
(self.err_msg "nonce incorrect";return @body = '{success : false}') if @session.get_nonce != nonce
|
|
||||||
|
|
||||||
# validate the http id
|
|
||||||
http_id = @params['http_id'] || nil
|
|
||||||
(self.err_msg "http_id is nil";return @body = '{success : false}') if http_id.nil?
|
|
||||||
|
|
||||||
# validate that the http object exist in the dabatase
|
|
||||||
http_db = H.first(:id => http_id) || nil
|
|
||||||
(self.err_msg "http object could not be found in the database";return @body = '{success : false}') if http_db.nil?
|
|
||||||
|
|
||||||
# delete response
|
|
||||||
http_db.destroy
|
|
||||||
|
|
||||||
@body = {'success' => 'true'}.to_json
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -15,3 +15,4 @@ require 'extensions/requester/models/http'
|
|||||||
require 'extensions/requester/api/hook'
|
require 'extensions/requester/api/hook'
|
||||||
require 'extensions/requester/handler'
|
require 'extensions/requester/handler'
|
||||||
require 'extensions/requester/api'
|
require 'extensions/requester/api'
|
||||||
|
require 'extensions/requester/rest/requester'
|
||||||
|
|||||||
@@ -17,26 +17,39 @@ module BeEF
|
|||||||
|
|
||||||
def initialize(data)
|
def initialize(data)
|
||||||
@data = data
|
@data = data
|
||||||
setup()
|
setup
|
||||||
end
|
end
|
||||||
|
|
||||||
def setup()
|
def setup
|
||||||
|
|
||||||
# validates the hook token
|
# validates the hook token
|
||||||
beef_hook = @data['beefhook'] || nil
|
beef_hook = @data['beefhook'] || nil
|
||||||
(print_error "beefhook is null";return) if beef_hook.nil?
|
if beef_hook.nil?
|
||||||
|
print_error "beefhook is null"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
# validates the request id
|
# validates the request id
|
||||||
request_id = @data['cid'] || nil
|
request_id = @data['cid'].to_s
|
||||||
(print_error "Original request id (command id) is null";return) if request_id.nil?
|
if request_id == ''
|
||||||
|
print_error "Original request id (command id) is null"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if !BeEF::Filters::nums_only?(request_id)
|
||||||
|
print_error "Original request id (command id) is invalid"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
# validates that a hooked browser with the beef_hook token exists in the db
|
# validates that a hooked browser with the beef_hook token exists in the db
|
||||||
zombie_db = Z.first(:session => beef_hook) || nil
|
zombie_db = Z.first(:session => beef_hook) || nil
|
||||||
(print_error "Invalid beefhook id: the hooked browser cannot be found in the database";return) if zombie_db.nil?
|
(print_error "Invalid beefhook id: the hooked browser cannot be found in the database";return) if zombie_db.nil?
|
||||||
|
|
||||||
# validates that we have such a http request saved in the db
|
# validates that we have such a http request saved in the db
|
||||||
http_db = H.first(:id => request_id.to_i, :hooked_browser_id => zombie_db.id) || nil
|
http_db = H.first(:id => request_id.to_i, :hooked_browser_id => zombie_db.session) || nil
|
||||||
(print_error "Invalid http_db: no such request found in the database";return) if http_db.nil?
|
if http_db.nil?
|
||||||
|
print_error "Invalid http_db: no such request found in the database"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
# validates that the http request has not been run before
|
# validates that the http request has not been run before
|
||||||
(print_error "This http request has been saved before";return) if http_db.has_ran.eql? "complete"
|
(print_error "This http request has been saved before";return) if http_db.has_ran.eql? "complete"
|
||||||
@@ -59,6 +72,7 @@ module BeEF
|
|||||||
if http_db.response_headers =~ /Content-Type: image/
|
if http_db.response_headers =~ /Content-Type: image/
|
||||||
http_db.response_data = http_db.response_data.unpack('a*')
|
http_db.response_data = http_db.response_data.unpack('a*')
|
||||||
end
|
end
|
||||||
|
|
||||||
http_db.save
|
http_db.save
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -69,8 +69,16 @@ module Models
|
|||||||
# The date at which the http request has been saved.
|
# The date at which the http request has been saved.
|
||||||
property :request_date, DateTime, :lazy => false
|
property :request_date, DateTime, :lazy => false
|
||||||
|
|
||||||
|
#
|
||||||
|
# Removes a request/response from the data store
|
||||||
|
#
|
||||||
|
def self.delete(id)
|
||||||
|
(print_error "Failed to remove response. Invalid response ID."; return) if id.to_s !~ /\A\d+\z/
|
||||||
|
r = BeEF::Core::Models::Http.get(id.to_i)
|
||||||
|
(print_error "Failed to remove response [id: #{id}]. Response does not exist."; return) if r.nil?
|
||||||
|
r.destroy
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,34 +13,263 @@ module BeEF
|
|||||||
# Filters out bad requests before performing any routing
|
# Filters out bad requests before performing any routing
|
||||||
before do
|
before do
|
||||||
config = BeEF::Core::Configuration.instance
|
config = BeEF::Core::Configuration.instance
|
||||||
@hb = BeEF::Core::Models::HookedBrowser
|
|
||||||
|
|
||||||
# Require a valid API token from a valid IP address
|
# Require a valid API token from a valid IP address
|
||||||
halt 401 unless params[:token] == config.get('beef.api_token')
|
halt 401 unless params[:token] == config.get('beef.api_token')
|
||||||
halt 403 unless BeEF::Core::Rest.permitted_source?(request.ip)
|
halt 403 unless BeEF::Core::Rest.permitted_source?(request.ip)
|
||||||
|
|
||||||
|
H = BeEF::Core::Models::Http
|
||||||
|
HB = BeEF::Core::Models::HookedBrowser
|
||||||
|
|
||||||
headers 'Content-Type' => 'application/json; charset=UTF-8',
|
headers 'Content-Type' => 'application/json; charset=UTF-8',
|
||||||
'Pragma' => 'no-cache',
|
'Pragma' => 'no-cache',
|
||||||
'Cache-Control' => 'no-cache',
|
'Cache-Control' => 'no-cache',
|
||||||
'Expires' => '0'
|
'Expires' => '0'
|
||||||
end
|
end
|
||||||
|
|
||||||
# @TODO: Move methods from the requester controller here
|
# Returns a request by ID
|
||||||
|
get '/request/:id' do
|
||||||
|
begin
|
||||||
|
id = params[:id]
|
||||||
|
raise InvalidParamError, 'id' unless BeEF::Filters::nums_only?(id)
|
||||||
|
|
||||||
|
requests = H.all(:id => id)
|
||||||
|
halt 404 if requests.nil?
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result[:count] = requests.length
|
||||||
|
result[:requests] = []
|
||||||
|
requests.each do |request|
|
||||||
|
result[:requests] << request2hash(request)
|
||||||
|
end
|
||||||
|
|
||||||
|
result.to_json
|
||||||
|
rescue InvalidParamError => e
|
||||||
|
print_error e.message
|
||||||
|
halt 400
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while retrieving request with id #{id} (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns all requestes given a specific hooked browser id
|
||||||
|
get '/requests/:id' do
|
||||||
|
begin
|
||||||
|
id = params[:id]
|
||||||
|
raise InvalidParamError, 'id' unless BeEF::Filters.is_valid_hook_session_id?(id)
|
||||||
|
|
||||||
|
requests = H.all(:hooked_browser_id => id)
|
||||||
|
halt 404 if requests.nil?
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result[:count] = requests.length
|
||||||
|
result[:requests] = []
|
||||||
|
requests.each do |request|
|
||||||
|
result[:requests] << request2hash(request)
|
||||||
|
end
|
||||||
|
|
||||||
|
result.to_json
|
||||||
|
rescue InvalidParamError => e
|
||||||
|
print_error e.message
|
||||||
|
halt 400
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while retrieving request list for hooked browser with id #{id} (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a response by ID
|
||||||
|
get '/response/:id' do
|
||||||
|
begin
|
||||||
|
id = params[:id]
|
||||||
|
raise InvalidParamError, 'id' unless BeEF::Filters::nums_only?(id)
|
||||||
|
|
||||||
|
responses = H.first(:id => id) || nil
|
||||||
|
halt 404 if responses.nil?
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result[:success] = 'true'
|
||||||
|
result[:result] = response2hash(responses)
|
||||||
|
|
||||||
|
result.to_json
|
||||||
|
rescue InvalidParamError => e
|
||||||
|
print_error e.message
|
||||||
|
halt 400
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while retrieving response with id #{id} (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Deletes a specific response given its id
|
||||||
|
delete '/response/:id' do
|
||||||
|
begin
|
||||||
|
id = params[:id]
|
||||||
|
raise InvalidParamError, 'id' unless BeEF::Filters::nums_only?(id)
|
||||||
|
|
||||||
|
responses = H.first(:id => id) || nil
|
||||||
|
halt 404 if responses.nil?
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
result['success'] = H.delete(id)
|
||||||
|
result.to_json
|
||||||
|
rescue InvalidParamError => e
|
||||||
|
print_error e.message
|
||||||
|
halt 400
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while removing response with id #{id} (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Send a new HTTP request to the hooked browser
|
||||||
|
post '/send/:id' do
|
||||||
|
begin
|
||||||
|
id = params[:id]
|
||||||
|
proto = params[:proto].to_s || 'http'
|
||||||
|
raw_request = params['raw_request'].to_s
|
||||||
|
|
||||||
|
zombie = HB.first(:session => id) || nil
|
||||||
|
halt 404 if zombie.nil?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# @TODO: move most of this to the model
|
||||||
|
|
||||||
|
if raw_request == ''
|
||||||
|
raise InvalidParamError, 'raw_request'
|
||||||
|
end
|
||||||
|
|
||||||
|
if proto !~ /\Ahttps?\z/
|
||||||
|
raise InvalidParamError, 'raw_request: Invalid request URL scheme'
|
||||||
|
end
|
||||||
|
|
||||||
|
req_parts = raw_request.split(/ |\n/)
|
||||||
|
|
||||||
|
verb = req_parts[0]
|
||||||
|
if not BeEF::Filters.is_valid_verb?(verb)
|
||||||
|
raise InvalidParamError, 'raw_request: Only HEAD, GET, POST, OPTIONS, PUT or DELETE requests are supported'
|
||||||
|
end
|
||||||
|
|
||||||
|
uri = req_parts[1]
|
||||||
|
if not BeEF::Filters.is_valid_url?(uri)
|
||||||
|
raise InvalidParamError, 'raw_request: Invalid URI'
|
||||||
|
end
|
||||||
|
|
||||||
|
version = req_parts[2]
|
||||||
|
if not BeEF::Filters.is_valid_http_version?(version)
|
||||||
|
raise InvalidParamError, 'raw_request: Invalid HTTP version'
|
||||||
|
end
|
||||||
|
|
||||||
|
host_str = req_parts[3]
|
||||||
|
if not BeEF::Filters.is_valid_host_str?(host_str)
|
||||||
|
raise InvalidParamError, 'raw_request: Invalid HTTP version'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Validate target hsot
|
||||||
|
host = req_parts[4]
|
||||||
|
host_parts = host.split(/:/)
|
||||||
|
host_name = host_parts[0]
|
||||||
|
host_port = host_parts[1] || nil
|
||||||
|
|
||||||
|
unless BeEF::Filters.is_valid_hostname?(host_name)
|
||||||
|
raise InvalidParamError, 'raw_request: Invalid HTTP HostName'
|
||||||
|
end
|
||||||
|
|
||||||
|
host_port = host_parts[1] || nil
|
||||||
|
if host_port.nil? || !BeEF::Filters::nums_only?(host_port)
|
||||||
|
host_port = proto.eql?('https') ? 443 : 80
|
||||||
|
end
|
||||||
|
|
||||||
|
# Save the new HTTP request
|
||||||
|
http = H.new(
|
||||||
|
:hooked_browser_id => zombie.session,
|
||||||
|
:request => raw_request,
|
||||||
|
:method => verb,
|
||||||
|
:proto => proto,
|
||||||
|
:domain => host_name,
|
||||||
|
:port => host_port,
|
||||||
|
:path => uri,
|
||||||
|
:request_date => Time.now,
|
||||||
|
:allow_cross_domain => "true",
|
||||||
|
)
|
||||||
|
|
||||||
|
if verb.eql?('POST') || verb.eql?('PUT')
|
||||||
|
req_parts.each_with_index do |value, index|
|
||||||
|
if value.match(/^Content-Length/i)
|
||||||
|
http.content_length = req_parts[index+1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
http.save
|
||||||
|
|
||||||
|
result = request2hash(http)
|
||||||
|
print_debug "[Requester] Sending HTTP request through zombie [ip: #{zombie.ip}] : #{result}"
|
||||||
|
|
||||||
|
#result.to_json
|
||||||
|
rescue InvalidParamError => e
|
||||||
|
print_error e.message
|
||||||
|
halt 400
|
||||||
|
rescue StandardError => e
|
||||||
|
print_error "Internal error while removing network host with id #{id} (#{e.message})"
|
||||||
|
halt 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert a request object to Hash
|
||||||
|
def request2hash(http)
|
||||||
|
{
|
||||||
|
:id => http.id,
|
||||||
|
:proto => http.proto,
|
||||||
|
:domain => http.domain,
|
||||||
|
:port => http.port,
|
||||||
|
:path => http.path,
|
||||||
|
:has_ran => http.has_ran,
|
||||||
|
:method => http.method,
|
||||||
|
:request_date => http.request_date,
|
||||||
|
:response_date => http.response_date,
|
||||||
|
:response_status_code => http.response_status_code,
|
||||||
|
:response_status_text => http.response_status_text,
|
||||||
|
:response_port_status => http.response_port_status
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert a response object to Hash
|
||||||
|
def response2hash(http)
|
||||||
|
if http.response_data.length > (1024 * 100) # more thank 100K
|
||||||
|
response_data = http.response_data[0..(1024*100)]
|
||||||
|
response_data += "\n<---------- Response Data Truncated---------->"
|
||||||
|
else
|
||||||
|
response_data = http.response_data
|
||||||
|
end
|
||||||
|
|
||||||
|
{
|
||||||
|
:id => http.id,
|
||||||
|
:request => http.request.force_encoding('UTF-8'),
|
||||||
|
:response => response_data.force_encoding('UTF-8'),
|
||||||
|
:response_headers => http.response_headers.force_encoding('UTF-8'),
|
||||||
|
:proto => http.proto.force_encoding('UTF-8'),
|
||||||
|
:domain => http.domain.force_encoding('UTF-8'),
|
||||||
|
:port => http.port.force_encoding('UTF-8'),
|
||||||
|
:path => http.path.force_encoding('UTF-8'),
|
||||||
|
:date => http.request_date,
|
||||||
|
:has_ran => http.has_ran.force_encoding('UTF-8')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
# Raised when invalid JSON input is passed to an /api/requester handler.
|
# Raised when invalid JSON input is passed to an /api/requester handler.
|
||||||
class InvalidJsonError < StandardError
|
class InvalidJsonError < StandardError
|
||||||
|
|
||||||
DEFAULT_MESSAGE = 'Invalid JSON input passed to /api/requester handler'
|
DEFAULT_MESSAGE = 'Invalid JSON input passed to /api/requester handler'
|
||||||
|
|
||||||
def initialize(message = nil)
|
def initialize(message = nil)
|
||||||
super(message || DEFAULT_MESSAGE)
|
super(message || DEFAULT_MESSAGE)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Raised when an invalid named parameter is passed to an /api/requester handler.
|
# Raised when an invalid named parameter is passed to an /api/requester handler.
|
||||||
class InvalidParamError < StandardError
|
class InvalidParamError < StandardError
|
||||||
|
|
||||||
DEFAULT_MESSAGE = 'Invalid parameter passed to /api/requester handler'
|
DEFAULT_MESSAGE = 'Invalid parameter passed to /api/requester handler'
|
||||||
|
|
||||||
def initialize(message = nil)
|
def initialize(message = nil)
|
||||||
@@ -48,11 +277,8 @@ module BeEF
|
|||||||
message = sprintf str, message unless message.nil?
|
message = sprintf str, message unless message.nil?
|
||||||
super(message)
|
super(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user