(Fixes issue 386) Updated 'console' extension to incorporate MSFs Rex Library for an interactive shell

git-svn-id: https://beef.googlecode.com/svn/trunk@1221 b87d56ec-f9c0-11de-8c8a-61c5e9addfc9
This commit is contained in:
xntrik
2011-08-20 02:45:14 +00:00
parent c19873e12d
commit 92c422096e
14 changed files with 10692 additions and 3 deletions

15
beef
View File

@@ -101,5 +101,16 @@ BeEF::Extension::Console::Banners.print_network_interfaces_routes
# We dynamically get the list of all browser hook handler using the API and register them
BeEF::API.fire(BeEF::API::Server, 'pre_http_start', http_hook_server)
# starts the web server
http_hook_server.start
# We check now for whether we load the Console Shell or not
if config.get("beef.extension.console.shell.enable") == true
puts ""
begin
FileUtils.mkdir_p(File.expand_path(config.get("beef.extension.console.shell.historyfolder")))
BeEF::Extension::Console::Shell.new(BeEF::Extension::Console::Shell::DefaultPrompt,
BeEF::Extension::Console::Shell::DefaultPromptChar,{'config' => config, 'http_hook_server' => http_hook_server}).run
rescue Interrupt
end
else
# starts the web server
http_hook_server.start
end

View File

@@ -64,3 +64,6 @@ beef:
enable: true
metasploit:
enable: false
console:
shell:
enable: false

View File

@@ -33,6 +33,8 @@ require 'xmlrpc/client'
require 'erubis'
require 'openssl'
require 'term/ansicolor'
require 'rex'
require 'rex/ui'
# Include the filters
require 'core/filters'

View File

@@ -18,4 +18,8 @@ beef:
console:
enable: true
name: 'Console'
shell:
enable: true
historyfolder: '~/.beef/'
historyfile: 'history'

View File

@@ -60,3 +60,4 @@ end
require 'extensions/console/banners'
require 'extensions/console/commandline'
require 'extensions/console/shell'

View File

@@ -0,0 +1,37 @@
#
# Copyright 2011 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 Console
module CommandDispatcher
include Rex::Ui::Text::DispatcherShell::CommandDispatcher
def initialize(driver)
super
self.driver = driver
end
attr_accessor :driver
end
end end end
require 'extensions/console/lib/command_dispatcher/core'
require 'extensions/console/lib/command_dispatcher/target'
require 'extensions/console/lib/command_dispatcher/command'

View File

@@ -0,0 +1,169 @@
#
# Copyright 2011 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 Console
module CommandDispatcher
class Command
include BeEF::Extension::Console::CommandDispatcher
def initialize(driver)
super
end
def commands
{
"execute" => "Go! Execute the command module",
"param" => "Set parameters for this module",
"response" => "Get previous responses to this command module",
"cmdinfo" => "See information about this particular command module"
}
end
def name
"Command"
end
@@bare_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help." ])
def cmd_cmdinfo(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_cmdinfo_help
return false
end
}
print_line("Module name: " + driver.interface.cmd['Name'])
print_line("Module category: " + driver.interface.cmd['Category'])
print_line("Module description: " + driver.interface.cmd['Description'])
print_line("Module parameters:")
driver.interface.cmd['Data'].each{|data|
print_line(data['name'] + " => \"" + data['value'] + "\" # this is the " + data['ui_label'] + " parameter")
} if not driver.interface.cmd['Data'].nil?
end
def cmd_cmdinfo_help(*args)
print_status("Displays information about the current command module")
end
def cmd_param(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_param_help
return false
end
}
if (args[0] == nil || args[1] == nil)
cmd_param_help
return
else
p = ""
(1..args.length-1).each do |x|
p << args[x] << " "
end
p.chop!
driver.interface.setparam(args[0],p)
end
end
def cmd_param_help(*args)
print_status("Sets parameters for the current modules. Run \"cmdinfo\" to see the parameter values")
print_status(" Usage: param <paramname> <paramvalue>")
end
def cmd_execute(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_execute_help
return false
end
}
if driver.interface.executecommand == true
print_status("Command successfully queued")
else
print_status("Something went wrong")
end
end
def cmd_execute_help(*args)
print_status("Execute this module... go on!")
end
def cmd_response(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_response_help
return false
end
}
tbl = Rex::Ui::Text::Table.new(
'Columns' =>
[
'Id',
'Executed Time',
'Response Time'
])
if args[0] == nil
driver.interface.getcommandresponses.each do |resp|
indiresp = driver.interface.getindividualresponse(resp['object_id'])
respout = ""
if indiresp.nil? or indiresp[0].nil?
respout = "No response yet"
else
respout = Time.at(indiresp[0]['date'].to_i).to_s
end
tbl << [resp['object_id'].to_s, resp['creationdate'], respout]
end
puts "\n"
puts "List of responses for this command module:\n"
puts tbl.to_s + "\n"
else
output = driver.interface.getindividualresponse(args[0])
if output.nil?
print_line("Invalid response ID")
elsif output[0].nil?
print_line("No response yet from the hooked browser or perhaps an invalid response ID")
else
print_line("Results retrieved: " + Time.at(output[0]['date'].to_i).to_s)
print_line("")
print_line("Response:")
print_line(output[0]['data']['data'].to_s)
end
end
end
def cmd_response_help(*args)
print_status("List and review particular responses to this command")
print_status(" Usage: response (id)")
print_status(" If you omit id you'll see a list of all responses for the currently active command module")
end
end
end end end end

View File

@@ -0,0 +1,339 @@
#
# Copyright 2011 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 Console
module CommandDispatcher
class Core
include BeEF::Extension::Console::CommandDispatcher
def initialize(driver)
super
end
def commands
{
"?" => "Help menu",
"back" => "Move back from the current context",
"exit" => "Exit the console",
"help" => "Help menu",
"jobs" => "Print jobs",
"online" => "List online hooked browsers",
"offline" => "List previously hooked browsers",
"quit" => "Exit the console",
"review" => "Target a particular previously hooked (offline) hooked browser",
"show" => "Displays 'zombies' or 'browsers' or 'commands'. (For those who prefer the MSF way)",
"target" => "Target a particular online hooked browser",
}
end
def name
"Core"
end
def cmd_back(*args)
if (driver.current_dispatcher.name == 'Command')
driver.remove_dispatcher('Command')
driver.interface.clearcommand #TODO: TIDY THIS UP
driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.to_s+"] ")
elsif (driver.current_dispatcher.name == 'Target')
driver.remove_dispatcher('Target')
driver.interface.cleartarget
driver.update_prompt('')
elsif (driver.dispatcher_stack.size > 1 and
driver.current_dispatcher.name != 'Core')
driver.destack_dispatcher
driver.update_prompt('')
end
end
def cmd_back_help(*args)
print_status("Move back one step")
end
def cmd_exit
driver.stop
end
alias cmd_quit cmd_exit
@@jobs_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help." ],
"-l" => [ false, "List jobs." ],
"-k" => [ true, "Terminate the job." ])
def cmd_jobs(*args)
if (args[0] == nil)
cmd_jobs_list
print_line "Try: jobs -h"
return
end
@@jobs_opts.parse(args) {|opt, idx, val|
case opt
when "-k"
if (not driver.jobs.has_key?(val))
print_error("no such job")
else
#This is a special job, that has to be terminated different prior to cleanup
driver.http_hook_server.stop if driver.jobs[val].name == "http_hook_server"
print_line("Stopping job: #{val}...")
driver.jobs.stop_job(val)
end
when "-l"
cmd_jobs_list
when "-h"
cmd_jobs_help
return false
end
}
end
def cmd_jobs_help(*args)
print_line "Usage: jobs [options]"
print_line
print @@jobs_opts.usage()
end
def cmd_jobs_list
tbl = Rex::Ui::Text::Table.new(
'Columns' =>
[
'Id',
'Job Name'
])
driver.jobs.keys.each{|k|
tbl << [driver.jobs[k].jid.to_s, driver.jobs[k].name]
}
puts "\n"
puts tbl.to_s + "\n"
end
@@bare_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help." ])
def cmd_online(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_online_help
return false
end
}
tbl = Rex::Ui::Text::Table.new(
'Columns' =>
[
'Id',
'IP',
'OS'
])
BeEF::Core::Models::HookedBrowser.all(:lastseen.gte => (Time.new.to_i - 30)).each do |zombie|
tbl << [zombie.id,zombie.ip,beef_logo_to_os(BeEF::Extension::Initialization::Models::BrowserDetails.os_icon(zombie.session))]
end
puts "\n"
puts "Currently hooked browsers within BeEF"
puts "\n"
puts tbl.to_s + "\n"
end
def cmd_online_help(*args)
print_status("Show currently hooked browsers within BeEF")
end
def cmd_offline(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_offline_help
return false
end
}
tbl = Rex::Ui::Text::Table.new(
'Columns' =>
[
'Id',
'IP',
'OS'
])
BeEF::Core::Models::HookedBrowser.all(:lastseen.lt => (Time.new.to_i - 30)).each do |zombie|
tbl << [zombie.id,zombie.ip,beef_logo_to_os(BeEF::Extension::Initialization::Models::BrowserDetails.os_icon(zombie.session))]
end
puts "\n"
puts "Previously hooked browsers within BeEF"
puts "\n"
puts tbl.to_s + "\n"
end
def cmd_offline_help(*args)
print_status("Show previously hooked browsers")
end
def cmd_target(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_target_help
return false
end
}
if args[0] == nil
cmd_target_help
return
end
if not driver.interface.settarget(args[0]).nil?
if (driver.dispatcher_stack.size > 1 and
driver.current_dispatcher.name != 'Core')
driver.destack_dispatcher
driver.update_prompt('')
end
driver.enstack_dispatcher(Target)
driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.to_s+"] ")
end
end
def cmd_target_help(*args)
print_status("Target a particular online, hooked browser")
print_status(" Usage: target <id>")
end
def cmd_review(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_review_help
return false
end
}
if args[0] == nil
cmd_review_help
return
end
if not driver.interface.setofflinetarget(args[0]).nil?
if (driver.dispatcher_stack.size > 1 and
driver.current_dispatcher.name != 'Core')
driver.destack_dispatcher
driver.update_prompt('')
end
driver.enstack_dispatcher(Target)
driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.to_s+"] ")
end
end
def cmd_review_help(*args)
print_status("Review an offline, previously hooked browser")
print_status(" Usage: review <id>")
end
def cmd_show(*args)
args << "-h" if (args.length == 0)
args.each { |type|
case type
when '-h'
cmd_show_help
when 'zombies'
driver.run_single("online")
when 'browsers'
driver.run_single("online")
when 'online'
driver.run_single("online")
when 'offline'
driver.run_single("offline")
when 'commands'
if driver.dispatched_enstacked(Target)
driver.run_single("commands")
else
print_error("You aren't targeting a zombie yet")
end
when 'info'
if driver.dispatched_enstacked(Target)
driver.run_single("info")
else
print_error("You aren't targeting a zombie yet")
end
when 'cmdinfo'
if driver.dispatched_enstacked(Command)
driver.run_single("cmdinfo")
else
print_error("You haven't selected a command module yet")
end
else
print_error("Invalid parameter, try show -h for more information.")
end
}
end
def cmd_show_tabs(str, words)
return [] if words.length > 1
res = %w{zombies browsers online offline}
if driver.dispatched_enstacked(Target)
res.concat(%w{commands info})
end
if driver.dispatched_enstacked(Command)
res.concat(%w{cmdinfo})
end
return res
end
def cmd_show_help
global_opts = %w{zombies browsers}
print_status("Valid parameters for the \"show\" command are: #{global_opts.join(", ")}")
target_opts = %w{commands}
print_status("If you're targeting a module, you can also specify: #{target_opts.join(", ")}")
end
def beef_logo_to_os(logo)
case logo
when "mac.png"
hbos = "Mac OS X"
when "linux.png"
hbos = "Linux"
when "win.png"
hbos = "Microsoft Windows"
when "unknown.png"
hbos = "Unknown"
end
end
end
end end end end

View File

@@ -0,0 +1,182 @@
#
# Copyright 2011 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 Console
module CommandDispatcher
class Target
include BeEF::Extension::Console::CommandDispatcher
@@commands = []
def initialize(driver)
super
begin
driver.interface.getcommands.each { |folder|
folder['children'].each { |command|
@@commands << folder['text'] + "/" + command['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_")
}
}
rescue
return
end
end
def commands
{
"commands" => "List available commands against this particular target",
"info" => "Info about the target",
"select" => "Prepare the command module for execution against this target"
}
end
def name
"Target"
end
@@bare_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help." ])
def cmd_commands(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_commands_help
return false
end
}
tbl = Rex::Ui::Text::Table.new(
'Columns' =>
[
'Id',
'Command',
'Status',
'Execute Count'
])
driver.interface.getcommands.each { |folder|
folder['children'].each { |command|
tbl << [command['id'].to_s,
folder['text'] + "/" + command['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_"),
command['status'],
driver.interface.getcommandresponses(command['id']).length] #TODO
}
}
puts "\n"
puts "List command modules for this target\n"
puts tbl.to_s + "\n"
end
def cmd_commands_help(*args)
print_status("List command modules for this target")
end
def cmd_info(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_info_help
return false
end
}
tbl = Rex::Ui::Text::Table.new(
'Columns' =>
[
'Param',
'Value'
])
driver.interface.select_zombie_summary['results'].each { |x|
x['data'].each { |k,v|
tbl << [k,v]
}
}
puts "\nHooked Browser Info:\n"
puts tbl.to_s + "\n"
end
def cmd_info_help(*args)
print_status("Display initialisation information about the hooked browser.")
end
def cmd_select(*args)
@@bare_opts.parse(args) {|opt, idx, val|
case opt
when "-h"
cmd_select_help
return false
end
}
if args[0] == nil
cmd_select_help
return false
end
modid = nil
if args[0] =~ /[0-9]+/
modid = args[0]
else
driver.interface.getcommands.each { |x|
x['children'].each { |y|
if args[0].chomp == x['text']+"/"+y['text'].gsub(/[-\(\)]/,"").gsub(/\W+/,"_")
modid = y['id']
end
}
}
end
if modid.nil?
print_status("Could not find command module")
return false
end
driver.interface.setcommand(modid)
driver.enstack_dispatcher(Command) if driver.dispatched_enstacked(Command) == false
driver.update_prompt("(%bld%red"+driver.interface.targetip+"%clr) ["+driver.interface.targetid.to_s+"] / "+driver.interface.cmd['Name']+" ")
end
def cmd_select_help(*args)
print_status("Select a command module to use against the current target")
print_status(" Usage: module <id> OR <modulename>")
end
def cmd_select_tabs(str,words)
return if words.length > 1
if @@commands == ""
#nothing prepopulated?
else
return @@commands
end
end
end
end end end end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,549 @@
# readline.rb -- GNU Readline module
# Copyright (C) 1997-2001 Shugo Maed
#
# Ruby translation by Park Heesob phasis@gmail.com
=begin
Copyright (c) 2009, Park Heesob
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Park Heesob nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=end
module Readline
require 'extensions/console/lib/rbreadline'
include RbReadline
@completion_proc = nil
@completion_case_fold = false
#
# A sneaky way to prevent the real Readline from loading after us
#
$LOADED_FEATURES.unshift("readline.rb")
# Begins an interactive terminal process using +prompt+ as the command
# prompt that users see when they type commands. The method returns the
# line entered whenever a carriage return is encountered.
#
# If an +add_history+ argument is provided, commands entered by users are
# stored in a history buffer that can be recalled for later use.
#
# Note that this method depends on $stdin and $stdout both being open.
# Because this is meant as an interactive console interface, they should
# generally not be redirected.
#
# Example:
#
# loop{ Readline.readline('> ') }
#
def readline(prompt, add_history=nil)
if $stdin.closed?
raise IOError, "stdin closed"
end
status = 0
begin
RbReadline.rl_instream = $stdin
RbReadline.rl_outstream = $stdout
buff = RbReadline.readline(prompt)
rescue ::Interrupt
raise $!
rescue Exception => e
buff = nil
RbReadline.rl_cleanup_after_signal()
RbReadline.rl_deprep_terminal()
$stderr.puts "[-] RbReadline Error: #{e.class} #{e} #{e.backtrace}"
retry
end
if add_history && buff
RbReadline.add_history(buff)
end
return buff ? buff.dup : nil
end
# Sets the input stream (an IO object) for readline interaction. The
# default is <tt>$stdin</tt>.
#
def self.input=(input)
RbReadline.rl_instream = input
end
# Sets the output stream (an IO object) for readline interaction. The
# default is <tt>$stdout</tt>.
#
def self.output=(output)
RbReadline.rl_outstream = output
end
# Sets the auto-completion procedure (i.e. tab auto-complete).
#
# The +proc+ argument is typically a Proc object. It must respond to
# <tt>.call</tt>, take a single String argument and return an Array of
# candidates for completion.
#
# Example:
#
# list = ['search', 'next', 'clear']
# Readline.completion_proc = proc{ |s| list.grep( /^#{Regexp.escape(s)}/) }
#
def self.completion_proc=(proc)
unless defined? proc.call
raise ArgumentError,"argument must respond to `call'"
end
@completion_proc = proc
end
# Returns the current auto-completion procedure.
#
def self.completion_proc()
@completion_proc
end
# Sets whether or not the completion proc should ignore case sensitivity.
# The default is false, i.e. completion procs are case sensitive.
#
def self.completion_case_fold=(bool)
@completion_case_fold = bool
end
# Returns whether or not the completion proc is case sensitive. The
# default is false, i.e. completion procs are case sensitive.
#
def self.completion_case_fold()
@completion_case_fold
end
def self.readline_attempted_completion_function(text,start,_end)
proc = @completion_proc
return nil if proc.nil?
RbReadline.rl_attempted_completion_over = true
# Remove leading spaces
text.gsub!(/^\s+/, '')
case_fold = @completion_case_fold
ary = proc.call(text)
if ary.class != Array
ary = Array(ary)
else
ary.compact!
ary.uniq!
end
ary.delete('')
matches = ary.length
return nil if (matches == 0)
if(matches == 1)
ary[0] = ary[0].strip + " "
end
result = Array.new(matches+2)
for i in 0 ... matches
result[i+1] = ary[i].dup
end
result[matches+1] = nil
if(matches==1)
result[0] = result[1].dup
else
i = 1
low = 100000
while (i < matches)
if (case_fold)
si = 0
while ((c1 = result[i][si,1].downcase) &&
(c2 = result[i + 1][si,1].downcase))
break if (c1 != c2)
si += 1
end
else
si = 0
while ((c1 = result[i][si,1]) &&
(c2 = result[i + 1][si,1]))
break if (c1 != c2)
si += 1
end
end
if (low > si)
low = si
end
i+=1
end
result[0] = result[1][0,low]
end
result
end
# Sets vi editing mode.
#
def self.vi_editing_mode()
RbReadline.rl_vi_editing_mode(1,0)
nil
end
# Sets emacs editing mode
#
def self.emacs_editing_mode()
RbReadline.rl_emacs_editing_mode(1,0)
nil
end
# Sets the character that is automatically appended after the
# Readline.completion_proc method is called.
#
# If +char+ is nil or empty, then a null character is used.
#
def self.completion_append_character=(char)
if char.nil?
RbReadline.rl_completion_append_character = ?\0
elsif char.length==0
RbReadline.rl_completion_append_character = ?\0
else
RbReadline.rl_completion_append_character = char[0]
end
end
# Returns the character that is automatically appended after the
# Readline.completion_proc method is called.
#
def self.completion_append_character()
if RbReadline.rl_completion_append_character == ?\0
nil
end
return RbReadline.rl_completion_append_character
end
# Sets the character string that signal a break between words for the
# completion proc.
#
def self.basic_word_break_characters=(str)
RbReadline.rl_basic_word_break_characters = str.dup
end
# Returns the character string that signal a break between words for the
# completion proc. The default is " \t\n\"\\'`@$><=|&{(".
#
def self.basic_word_break_characters()
if RbReadline.rl_basic_word_break_characters.nil?
nil
else
RbReadline.rl_basic_word_break_characters.dup
end
end
# Sets the character string that signal the start or end of a word for
# the completion proc.
#
def self.completer_word_break_characters=(str)
RbReadline.rl_completer_word_break_characters = str.dup
end
# Returns the character string that signal the start or end of a word for
# the completion proc.
#
def self.completer_word_break_characters()
if RbReadline.rl_completer_word_break_characters.nil?
nil
else
RbReadline.rl_completer_word_break_characters.dup
end
end
# Sets the list of quote characters that can cause a word break.
#
def self.basic_quote_characters=(str)
RbReadline.rl_basic_quote_characters = str.dup
end
# Returns the list of quote characters that can cause a word break.
# The default is "'\"" (single and double quote characters).
#
def self.basic_quote_characters()
if RbReadline.rl_basic_quote_characters.nil?
nil
else
RbReadline.rl_basic_quote_characters.dup
end
end
# Sets the list of characters that can be used to quote a substring of
# the line, i.e. a group of characters within quotes.
#
def self.completer_quote_characters=(str)
RbReadline.rl_completer_quote_characters = str.dup
end
# Returns the list of characters that can be used to quote a substring
# of the line, i.e. a group of characters inside quotes.
#
def self.completer_quote_characters()
if RbReadline.rl_completer_quote_characters.nil?
nil
else
RbReadline.rl_completer_quote_characters.dup
end
end
# Sets the character string of one or more characters that indicate quotes
# for the filename completion of user input.
#
def self.filename_quote_characters=(str)
RbReadline.rl_filename_quote_characters = str.dup
end
# Returns the character string used to indicate quotes for the filename
# completion of user input.
#
def self.filename_quote_characters()
if RbReadline.rl_filename_quote_characters.nil?
nil
else
RbReadline.rl_filename_quote_characters.dup
end
end
# The History class encapsulates a history of all commands entered by
# users at the prompt, providing an interface for inspection and retrieval
# of all commands.
class History
extend Enumerable
# The History class, stringified in all caps.
#--
# Why?
#
def self.to_s
"HISTORY"
end
# Returns the command that was entered at the specified +index+
# in the history buffer.
#
# Raises an IndexError if the entry is nil.
#
def self.[](index)
if index < 0
index += RbReadline.history_length
end
entry = RbReadline.history_get(RbReadline.history_base+index)
if entry.nil?
raise IndexError,"invalid index"
end
entry.line.dup
end
# Sets the command +str+ at the given index in the history buffer.
#
# You can only replace an existing entry. Attempting to create a new
# entry will result in an IndexError.
#
def self.[]=(index,str)
if index<0
index += RbReadline.history_length
end
entry = RbReadline.replace_history_entry(index,str,nil)
if entry.nil?
raise IndexError,"invalid index"
end
str
end
# Synonym for Readline.add_history.
#
def self.<<(str)
RbReadline.add_history(str)
end
# Pushes a list of +args+ onto the history buffer.
#
def self.push(*args)
args.each do |str|
RbReadline.add_history(str)
end
end
# Internal function that removes the item at +index+ from the history
# buffer, performing necessary duplication in the process.
#--
# TODO: mark private?
#
def self.rb_remove_history(index)
entry = RbReadline.remove_history(index)
if (entry)
val = entry.line.dup
entry = nil
return val
end
nil
end
# Removes and returns the last element from the history buffer.
#
def self.pop()
if RbReadline.history_length>0
rb_remove_history(RbReadline.history_length-1)
else
nil
end
end
# Removes and returns the first element from the history buffer.
#
def self.shift()
if RbReadline.history_length>0
rb_remove_history(0)
else
nil
end
end
# Iterates over each entry in the history buffer.
#
def self.each()
for i in 0 ... RbReadline.history_length
entry = RbReadline.history_get(RbReadline.history_base + i)
break if entry.nil?
yield entry.line.dup
end
self
end
# Returns the length of the history buffer.
#
def self.length()
RbReadline.history_length
end
# Synonym for Readline.length.
#
def self.size()
RbReadline.history_length
end
# Returns a bolean value indicating whether or not the history buffer
# is empty.
#
def self.empty?()
RbReadline.history_length == 0
end
# Deletes an entry from the histoyr buffer at the specified +index+.
#
def self.delete_at(index)
if index < 0
i += RbReadline.history_length
end
if index < 0 || index > RbReadline.history_length - 1
raise IndexError, "invalid index"
end
rb_remove_history(index)
end
end
HISTORY = History
# The Fcomp class provided to encapsulate typical filename completion
# procedure. You will not typically use this directly, but will instead
# use the Readline::FILENAME_COMPLETION_PROC.
#
class Fcomp
def self.call(str)
matches = RbReadline.rl_completion_matches(str,
:rl_filename_completion_function)
if (matches)
result = []
i = 0
while(matches[i])
result << matches[i].dup
matches[i] = nil
i += 1
end
matches = nil
if (result.length >= 2)
result.shift
end
else
result = nil
end
return result
end
end
FILENAME_COMPLETION_PROC = Fcomp
# The Ucomp class provided to encapsulate typical filename completion
# procedure. You will not typically use this directly, but will instead
# use the Readline::USERNAME_COMPLETION_PROC.
#
# Note that this feature currently only works on Unix systems since it
# ultimately uses the Etc module to iterate over a list of users.
#
class Ucomp
def self.call(str)
matches = RbReadline.rl_completion_matches(str,
:rl_username_completion_function)
if (matches)
result = []
i = 0
while(matches[i])
result << matches[i].dup
matches[i] = nil
i += 1
end
matches = nil
if (result.length >= 2)
result.shift
end
else
result = nil
end
return result
end
end
USERNAME_COMPLETION_PROC = Ucomp
RbReadline.rl_readline_name = "Ruby"
RbReadline.using_history()
VERSION = RbReadline.rl_library_version
module_function :readline
RbReadline.rl_attempted_completion_function = :readline_attempted_completion_function
end

View File

@@ -0,0 +1,576 @@
#
# Copyright 2011 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 Console
class ShellInterface
BD = BeEF::Extension::Initialization::Models::BrowserDetails
def initialize(config)
self.config = config
self.cmd = {}
end
def settarget(id)
begin
self.targetsession = BeEF::Core::Models::HookedBrowser.first(:id => id).session
self.targetip = BeEF::Core::Models::HookedBrowser.first(:id => id).ip
self.targetid = id
rescue
return nil
end
end
def setofflinetarget(id)
begin
self.targetsession = BeEF::Core::Models::HookedBrowser.first(:id => id).session
self.targetip = "(OFFLINE) " + BeEF::Core::Models::HookedBrowser.first(:id => id).ip
self.targetid = id
rescue
return nil
end
end
def cleartarget
self.targetsession = nil
self.targetip = nil
self.targetid = nil
self.cmd = {}
end
# This is a *modified* replica of select_command_modules_tree from extensions/admin_ui/controllers/modules/modules.rb
def getcommands
return if self.targetid.nil?
tree = []
BeEF::Modules.get_categories.each { |c|
tree.push({
'text' => c,
'cls' => 'folder',
'children' => []
})
}
BeEF::Modules.get_enabled.each{|k, mod|
update_command_module_tree(tree, mod['category'], get_command_module_status(k), mod['name'],mod['db']['id'])
}
# if dynamic modules are found in the DB, then we don't have yaml config for them
# and loading must proceed in a different way.
dynamic_modules = BeEF::Core::Models::CommandModule.all(:path.like => "Dynamic/")
if(dynamic_modules != nil)
all_modules = BeEF::Core::Models::CommandModule.all(:order => [:id.asc])
all_modules.each{|dyn_mod|
next if !dyn_mod.path.split('/').first.match(/^Dynamic/)
dyn_mod_name = dyn_mod.path.split('/').last
dyn_mod_category = nil
if(dyn_mod_name == "Msf")
dyn_mod_category = "Metasploit"
else
# future dynamic modules...
end
#print_debug ("Loading Dynamic command module: category [#{dyn_mod_category}] - name [#{dyn_mod.name.to_s}]")
command_mod = BeEF::Modules::Commands.const_get(dyn_mod_name.capitalize).new
command_mod.session_id = hook_session_id
command_mod.update_info(dyn_mod.id)
command_mod_name = command_mod.info['Name'].downcase
update_command_module_tree(tree, dyn_mod_category, "Verified Unknown", command_mod_name,dyn_mod.id)
}
end
# sort the parent array nodes
tree.sort! {|a,b| a['text'] <=> b['text']}
# sort the children nodes by status
tree.each {|x| x['children'] =
x['children'].sort_by {|a| a['status']}
}
# 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
tree
end
def setcommand(id)
key = BeEF::Module.get_key_by_database_id(id.to_i)
self.cmd['id'] = id
self.cmd['Name'] = self.config.get("beef.module.#{key}.name")
self.cmd['Description'] = self.config.get("beef.module.#{key}.description")
self.cmd['Category'] = self.config.get("beef.module.#{key}.category")
self.cmd['Data'] = BeEF::Module.get_options(key)
end
def clearcommand
self.cmd = {}
end
def setparam(param,value)
self.cmd['Data'].each do |data|
if data['name'] == param
data['value'] = value
return
end
end
end
def getcommandresponses(cmdid = self.cmd['id'])
commands = []
i = 0
BeEF::Core::Models::Command.all(:command_module_id => cmdid, :hooked_browser_id => self.targetid).each do |command|
commands.push({
'id' => i,
'object_id' => command.id,
'creationdate' => Time.at(command.creationdate.to_i).strftime("%Y-%m-%d %H:%M").to_s,
'label' => command.label
})
i+=1
end
commands
end
def getindividualresponse(cmdid)
results = []
begin
BeEF::Core::Models::Result.all(:command_id => cmdid).each { |result|
results.push({'date' => result.date, 'data' => JSON.parse(result.data)})
}
rescue
return nil
end
results
end
def executecommand
definition = {}
options = {}
options.store("zombie_session", self.targetsession.to_s)
options.store("command_module_id", self.cmd['id'])
if not self.cmd['Data'].nil?
self.cmd['Data'].each do |key|
options.store("txt_"+key['name'].to_s,key['value'])
end
end
options.keys.each {|param|
definition[param[4..-1]] = options[param]
oc = BeEF::Core::Models::OptionCache.first_or_create(:name => param[4..-1])
oc.value = options[param]
oc.save
}
mod_key = BeEF::Module.get_key_by_database_id(self.cmd['id'])
# Hack to rework the old option system into the new option system
def2 = []
definition.each{|k,v|
def2.push({'name' => k, 'value' => v})
}
# End hack
if BeEF::Module.execute(mod_key, self.targetsession.to_s, def2) == true
return true
else
return false
end
#Old method
#begin
# BeEF::Core::Models::Command.new( :data => definition.to_json,
# :hooked_browser_id => self.targetid,
# :command_module_id => self.cmd['id'],
# :creationdate => Time.new.to_i
# ).save
#rescue
# return false
#end
#return true
end
def update_command_module_tree(tree, cmd_category, cmd_status, cmd_name, cmd_id)
# construct leaf node for the command module tree
leaf_node = {
'text' => cmd_name,
'leaf' => true,
'status' => cmd_status,
'id' => cmd_id
}
# 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
}
end
def get_command_module_status(mod)
hook_session_id = self.targetsession
if hook_session_id == nil
return "Verified Unknown"
end
case 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')]})
when BeEF::Core::Constants::CommandModule::VERIFIED_NOT_WORKING
return "Verfied Not Working"
when BeEF::Core::Constants::CommandModule::VERIFIED_USER_NOTIFY
return "Verified User Notify"
when BeEF::Core::Constants::CommandModule::VERIFIED_WORKING
return "Verified Working"
when BeEF::Core::Constants::CommandModule::VERIFIED_UNKNOWN
return "Verified Unknown"
else
return "Verified Unknown"
end
end
#Yoinked from the UI panel - we really need to centralise all this stuff and encapsulate it away??
def select_zombie_summary
return if self.targetsession.nil?
# init the summary grid
summary_grid_hash = {
'success' => 'true',
'results' => []
}
# set and add the return values for the page title
page_title = BD.get(self.targetsession, 'PageTitle')
if not page_title.nil?
encoded_page_title = CGI.escapeHTML(page_title)
encoded_page_hash = { 'Page Title' => encoded_page_title }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_page_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the return values for the host name
host_name = BD.get(self.targetsession, 'HostName')
if not host_name.nil?
encoded_host_name = CGI.escapeHTML(host_name)
encoded_host_name_hash = { 'Hostname/IP' => encoded_host_name }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_host_name_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the return values for the os name
os_name = BD.get(self.targetsession, 'OsName')
if not os_name.nil?
encoded_os_name = CGI.escapeHTML(os_name)
encoded_os_name_hash = { 'OS Name' => encoded_os_name }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_os_name_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the return values for the browser name
browser_name = BD.get(self.targetsession, 'BrowserName')
if not browser_name.nil?
friendly_browser_name = BeEF::Core::Constants::Browsers.friendly_name(browser_name)
browser_name_hash = { 'Browser Name' => friendly_browser_name }
browser_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => browser_name_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(browser_name_row) # add the row
end
# set and add the return values for the browser version
browser_version = BD.get(self.targetsession, 'BrowserVersion')
if not browser_version.nil?
encoded_browser_version = CGI.escapeHTML(browser_version)
browser_version_hash = { 'Browser Version' => encoded_browser_version }
browser_version_row = {
'category' => 'Browser Hook Initialisation',
'data' => browser_version_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(browser_version_row) # add the row
end
# set and add the return values for the browser ua string
browser_uastring = BD.get(self.targetsession, 'BrowserReportedName')
if not browser_uastring.nil?
browser_uastring_hash = { 'Browser UA String' => browser_uastring }
browser_uastring_row = {
'category' => 'Browser Hook Initialisation',
'data' => browser_uastring_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(browser_uastring_row) # add the row
end
# set and add the list of cookies
cookies = BD.get(self.targetsession, 'Cookies')
if not cookies.nil? and not cookies.empty?
encoded_cookies = CGI.escapeHTML(cookies)
encoded_cookies_hash = { 'Cookies' => encoded_cookies }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_cookies_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the list of plugins installed in the browser
browser_plugins = BD.get(self.targetsession, 'BrowserPlugins')
if not browser_plugins.nil? and not browser_plugins.empty?
encoded_browser_plugins = CGI.escapeHTML(browser_plugins)
encoded_browser_plugins_hash = { 'Browser Plugins' => encoded_browser_plugins }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_browser_plugins_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the internal ip address
internal_ip = BD.get(self.targetsession, 'InternalIP')
if not internal_ip.nil?
encoded_internal_ip = CGI.escapeHTML(internal_ip)
encoded_internal_ip_hash = { 'Internal IP' => encoded_internal_ip }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_internal_ip_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the internal hostname
internal_hostname = BD.get(self.targetsession, 'InternalHostname')
if not internal_hostname.nil?
encoded_internal_hostname = CGI.escapeHTML(internal_hostname)
encoded_internal_hostname_hash = { 'Internal Hostname' => encoded_internal_hostname }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_internal_hostname_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the zombie screen size and color depth
screen_params = BD.get(self.targetsession, 'ScreenParams')
if not screen_params.nil?
screen_params_hash = JSON.parse(screen_params.gsub(/\"\=\>/, '":')) # tidy up the string for JSON
width = screen_params_hash['width']
#raise WEBrick::HTTPStatus::BadRequest, "width is wrong type" if not width.is_a?(Fixnum)
height = screen_params_hash['height']
#raise WEBrick::HTTPStatus::BadRequest, "height is wrong type" if not height.is_a?(Fixnum)
colordepth = screen_params_hash['colordepth']
#raise WEBrick::HTTPStatus::BadRequest, "colordepth is wrong type" if not colordepth.is_a?(Fixnum)
# construct the string to be displayed in the details tab
encoded_screen_params = CGI.escapeHTML("Width: "+width.to_s + ", Height: " + height.to_s + ", Colour Depth: " + colordepth.to_s)
encoded_screen_params_hash = { 'Screen Params' => encoded_screen_params }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_screen_params_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the zombie browser window size
window_size = BD.get(self.targetsession, 'WindowSize')
if not window_size.nil?
window_size_hash = JSON.parse(window_size.gsub(/\"\=\>/, '":')) # tidy up the string for JSON
width = window_size_hash['width']
#raise WEBrick::HTTPStatus::BadRequest, "width is wrong type" if not width.is_a?(Fixnum)
height = window_size_hash['height']
#raise WEBrick::HTTPStatus::BadRequest, "height is wrong type" if not height.is_a?(Fixnum)
# construct the string to be displayed in the details tab
encoded_window_size = CGI.escapeHTML("Width: "+width.to_s + ", Height: " + height.to_s)
encoded_window_size_hash = { 'Window Size' => encoded_window_size }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_window_size_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the yes|no value for JavaEnabled
java_enabled = BD.get(self.targetsession, 'JavaEnabled')
if not java_enabled.nil?
encoded_java_enabled = CGI.escapeHTML(java_enabled)
encoded_java_enabled_hash = { 'Java Enabled' => encoded_java_enabled }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_java_enabled_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the yes|no value for VBScriptEnabled
vbscript_enabled = BD.get(self.targetsession, 'VBScriptEnabled')
if not vbscript_enabled.nil?
encoded_vbscript_enabled = CGI.escapeHTML(vbscript_enabled)
encoded_vbscript_enabled_hash = { 'VBScript Enabled' => encoded_vbscript_enabled }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_vbscript_enabled_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the yes|no value for HasFlash
has_flash = BD.get(self.targetsession, 'HasFlash')
if not has_flash.nil?
encoded_has_flash = CGI.escapeHTML(has_flash)
encoded_has_flash_hash = { 'Has Flash' => encoded_has_flash }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_has_flash_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the yes|no value for HasGoogleGears
has_googlegears = BD.get(self.targetsession, 'HasGoogleGears')
if not has_googlegears.nil?
encoded_has_googlegears = CGI.escapeHTML(has_googlegears)
encoded_has_googlegears_hash = { 'Has GoogleGears' => encoded_has_googlegears }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_has_googlegears_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the return values for hasSessionCookies
has_session_cookies = BD.get(self.targetsession, 'hasSessionCookies')
if not has_session_cookies.nil?
encoded_has_session_cookies = CGI.escapeHTML(has_session_cookies)
encoded_has_session_cookies_hash = { 'Session Cookies' => encoded_has_session_cookies }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_has_session_cookies_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
# set and add the return values for hasPersistentCookies
has_persistent_cookies = BD.get(self.targetsession, 'hasPersistentCookies')
if not has_persistent_cookies.nil?
encoded_has_persistent_cookies = CGI.escapeHTML(has_persistent_cookies)
encoded_has_persistent_cookies_hash = { 'Persistent Cookies' => encoded_has_persistent_cookies }
page_name_row = {
'category' => 'Browser Hook Initialisation',
'data' => encoded_has_persistent_cookies_hash,
'from' => 'Initialisation'
}
summary_grid_hash['results'].push(page_name_row) # add the row
end
summary_grid_hash
end
attr_reader :targetsession
attr_reader :targetid
attr_reader :targetip
attr_reader :cmd
protected
attr_writer :targetsession
attr_writer :targetid
attr_writer :targetip
attr_writer :cmd
attr_accessor :config
end
end end end

View File

@@ -0,0 +1,78 @@
#
# Copyright 2011 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 Console
class Shell
DefaultPrompt = "%undBeEF%clr"
DefaultPromptChar = "%clr>"
include Rex::Ui::Text::DispatcherShell
def initialize(prompt = DefaultPrompt, prompt_char = DefaultPromptChar, opts = {})
require 'extensions/console/lib/readline_compatible'
require 'extensions/console/lib/command_dispatcher'
require 'extensions/console/lib/shellinterface'
self.http_hook_server = opts['http_hook_server']
self.config = opts['config']
self.jobs = Rex::JobContainer.new
self.interface = BeEF::Extension::Console::ShellInterface.new(self.config)
super(prompt, prompt_char, File.expand_path(self.config.get("beef.extension.console.shell.historyfolder").to_s + self.config.get("beef.extension.console.shell.historyfile").to_s))
input = Rex::Ui::Text::Input::Stdio.new
output = Rex::Ui::Text::Output::Stdio.new
init_ui(input,output)
enstack_dispatcher(CommandDispatcher::Core)
#To prevent http_hook_server from blocking, we kick it off as a background job here.
self.jobs.start_bg_job(
"http_hook_server",
self,
Proc.new { |ctx_| self.http_hook_server.start }
)
end
def stop
super
end
#New method to determine if a particular command dispatcher it already .. enstacked .. gooood
def dispatched_enstacked(dispatcher)
inst = dispatcher.new(self)
self.dispatcher_stack.each { |disp|
if (disp.name == inst.name)
return true
end
}
return false
end
attr_accessor :http_hook_server
attr_accessor :config
attr_accessor :jobs
attr_accessor :interface
end
end end end

View File

@@ -34,7 +34,7 @@ puts "\nPlease make sure you have installed SQLite before proceeding. For instr
# array of required gems - add to as needed (specify a version if needed eg "gem_name, =x.x.x")
$gems_required = ["ansi", "term-ansicolor", "dm-core", "json", "data_objects", "do_sqlite3", "sqlite3", "dm-sqlite-adapter",
"parseconfig", "erubis", "dm-migrations"]
"parseconfig", "erubis", "dm-migrations", "librex"]
# array of missing non-version specific gems installed
$gems_missing = Array.new