Updates for the web interface
git-svn-id: file:///home/svn/framework3/trunk@4287 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
180dbb09e0
commit
4bd5580784
|
@ -2,13 +2,29 @@
|
|||
# Description: The AJAX console controller of msfweb v.3. Handles commands,
|
||||
# operations and sessions carried over the web interface.
|
||||
class ConsoleController < ApplicationController
|
||||
def process_cmd
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Show the working shell and related facilities.
|
||||
#
|
||||
def idex
|
||||
end
|
||||
|
||||
#
|
||||
# Show the working shell and related facilities.
|
||||
#
|
||||
def index
|
||||
|
||||
cid = params[:id]
|
||||
|
||||
if (not (cid and $msfweb.consoles[cid]))
|
||||
cid = $msfweb.create_console
|
||||
redirect_to :id => cid
|
||||
return
|
||||
end
|
||||
|
||||
@cid = params[:id]
|
||||
@console = $msfweb.consoles[@cid]
|
||||
|
||||
if(params[:cmd])
|
||||
if (params[:cmd].strip.length > 0)
|
||||
@console.write(params[:cmd] + "\n")
|
||||
end
|
||||
send_data(@console.read(), :type => "application/octet-stream")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -13,19 +13,30 @@
|
|||
</head>
|
||||
|
||||
<body onload="console_init()">
|
||||
|
||||
<div id="console_window">
|
||||
<pre>
|
||||
<%= Msf::Ui::Banner.to_s %>
|
||||
|
||||
=[ msf <%= Msf::Framework::Version %>
|
||||
+ -- --=[ <%= $msframework.exploits.length %> exploits - <%= $msframework.payloads.length %> payloads
|
||||
+ -- --=[ <%= $msframework.encoders.length %> encoders - <%= $msframework.nops.length %> nops
|
||||
=[ <%= $msframework.exploits.length %> aux
|
||||
|
||||
</pre>
|
||||
|
||||
<pre>
|
||||
<div id="console_output" class="output">
|
||||
<%=h @console.read %>
|
||||
</div>
|
||||
<textarea id="console_input" class="input" wrap="off" onkeydown="console_keydown(event)" rows="1"></textarea></div>
|
||||
</pre>
|
||||
|
||||
<textarea
|
||||
rows="1"
|
||||
id="console_prompt"
|
||||
class="prompt"
|
||||
disabled="true"
|
||||
></textarea>
|
||||
<textarea
|
||||
id="console_input"
|
||||
class="input"
|
||||
wrap="off"
|
||||
onkeydown="return console_keydown(event)"
|
||||
rows="1"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -48,6 +48,5 @@ require 'rex'
|
|||
require 'msf/ui'
|
||||
require 'msf/base'
|
||||
|
||||
Msf::Config.init
|
||||
Msf::Logging.enable_log_source('msfweb', 5)
|
||||
$msframework = Msf::Simple::Framework.create
|
||||
$msfweb = Msf::Ui::Web::Driver.new({'LogLevel' => 5})
|
||||
$msframework = $msfweb.framework
|
||||
|
|
|
@ -8,6 +8,7 @@ var console_history = new Array(); // Commands history
|
|||
var console_hindex = 0; // Index to current command history
|
||||
var console_input; // Object to console input
|
||||
var console_output; // Object to console output
|
||||
var console_prompt; // Object to console prompt
|
||||
|
||||
function console_refocus() {
|
||||
console_input.blur();
|
||||
|
@ -24,6 +25,7 @@ function console_printline(s, type) {
|
|||
}
|
||||
}
|
||||
|
||||
var prompt = 'msf> ';
|
||||
var console_commands = {
|
||||
print : function print(s) {
|
||||
console_printline(s, "info");
|
||||
|
@ -49,12 +51,33 @@ function console_execute() {
|
|||
}
|
||||
}
|
||||
|
||||
function console_update_output(req) {
|
||||
console_printline(req.responseText);
|
||||
console_input.focus();
|
||||
}
|
||||
|
||||
function console_keydown(e) {
|
||||
|
||||
if (e.keyCode == 8) {
|
||||
window.title = console_input.value;
|
||||
}
|
||||
|
||||
if (e.keyCode == 13) { // enter
|
||||
console_history.push(console_input.value);
|
||||
try { console_execute(); } catch(er) { alert(er); };
|
||||
setTimeout(function() { console_input.value = "msf> "; }, 0);
|
||||
console_printline("[-] Unknown command: " + console_input.value);
|
||||
|
||||
new Ajax.Updater("console_update", document.location, {
|
||||
asynchronous:true,
|
||||
evalScripts:true,
|
||||
parameters:"cmd=" + escape(console_input.value),
|
||||
onComplete:console_update_output
|
||||
});
|
||||
|
||||
console_input.value = "";
|
||||
console_input.focus();
|
||||
console_prompt = prompt;
|
||||
return false;
|
||||
|
||||
} else if (e.keyCode == 38) { // up
|
||||
// TODO: place upper cmd in history on console_input.value
|
||||
} else if (e.keyCode == 40) { // down
|
||||
|
@ -70,7 +93,9 @@ function console_init() {
|
|||
|
||||
console_input = document.getElementById("console_input");
|
||||
console_output = document.getElementById("console_output");
|
||||
console_input.innerHTML = "msf> ";
|
||||
console_prompt = document.getElementById("console_prompt");
|
||||
|
||||
console_prompt.value = prompt;
|
||||
console_input.focus();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -9,30 +9,30 @@
|
|||
html,body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: #444;
|
||||
background: #000000;
|
||||
color: #fff;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#console_window {
|
||||
width: 600px;
|
||||
width: 100%;
|
||||
margin: 1em;
|
||||
background: #000000;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#output {
|
||||
width: 100%;
|
||||
white-space: pre;
|
||||
white-space: -moz-pre-wrap;
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
|
||||
#input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,19 @@ html,body {
|
|||
border: none;
|
||||
font: inherit;
|
||||
font-weight: bold;
|
||||
margin-top: .5em;
|
||||
color: #fff;
|
||||
padding: 0;
|
||||
margin-top: 1em;
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
.prompt {
|
||||
width: 3em;
|
||||
margin-top: 1em;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: #000000;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,98 @@ module Web
|
|||
|
||||
require 'msf/ui/web/comm'
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a console instance for use by the web interface
|
||||
#
|
||||
###
|
||||
|
||||
class WebConsole
|
||||
attr_accessor :pipe
|
||||
attr_accessor :console
|
||||
attr_accessor :console_id
|
||||
attr_accessor :last_access
|
||||
attr_accessor :framework
|
||||
|
||||
|
||||
class WebConsolePipe < Rex::IO::BidirectionalPipe
|
||||
|
||||
attr_accessor :input
|
||||
attr_accessor :output
|
||||
attr_accessor :prompt
|
||||
attr_accessor :killed
|
||||
|
||||
def eof?
|
||||
self.pipe_input.eof?
|
||||
end
|
||||
|
||||
def intrinsic_shell?
|
||||
true
|
||||
end
|
||||
|
||||
def supports_readline
|
||||
false
|
||||
end
|
||||
|
||||
def _print_prompt
|
||||
end
|
||||
|
||||
def pgets
|
||||
self.pipe_input.gets
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def initialize(framework, console_id)
|
||||
# Configure the framework
|
||||
self.framework = framework
|
||||
|
||||
# Configure the ID
|
||||
self.console_id = console_id
|
||||
|
||||
# Create a new pipe
|
||||
self.pipe = WebConsolePipe.new
|
||||
self.pipe.input = self.pipe.pipe_input
|
||||
|
||||
# Create a read subscriber
|
||||
self.pipe.create_subscriber('msfweb')
|
||||
|
||||
# Initialize the console with our pipe
|
||||
self.console = Msf::Ui::Console::Driver.new(
|
||||
'msf>',
|
||||
'>',
|
||||
{
|
||||
'Framework' => self.framework,
|
||||
'LocalInput' => self.pipe,
|
||||
'LocalOutput' => self.pipe,
|
||||
}
|
||||
)
|
||||
|
||||
Thread.new { self.console.run }
|
||||
|
||||
update_access()
|
||||
end
|
||||
|
||||
def update_access
|
||||
self.last_access = Time.now
|
||||
end
|
||||
|
||||
def read
|
||||
update_access
|
||||
self.pipe.read_subscriber('msfweb')
|
||||
end
|
||||
|
||||
def write(buf)
|
||||
update_access
|
||||
self.pipe.write_input(buf)
|
||||
end
|
||||
|
||||
def shutdown
|
||||
self.pipe.killed = true
|
||||
self.pipe.close
|
||||
end
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a user interface driver on a web interface.
|
||||
|
@ -16,65 +108,14 @@ require 'msf/ui/web/comm'
|
|||
###
|
||||
class Driver < Msf::Ui::Driver
|
||||
|
||||
|
||||
attr_accessor :framework # :nodoc:
|
||||
attr_accessor :consoles # :nodoc:
|
||||
attr_accessor :last_console # :nodoc:
|
||||
|
||||
ConfigCore = "framework/core"
|
||||
ConfigGroup = "framework/ui/web"
|
||||
|
||||
@@Eid = 0
|
||||
|
||||
#
|
||||
# Returns the next unique exploit identifier.
|
||||
#
|
||||
def self.next_eid
|
||||
@@Eid += 1
|
||||
@@Eid.to_s
|
||||
end
|
||||
|
||||
#
|
||||
# The msfweb resource handler that wrappers the default Erb handler.
|
||||
#
|
||||
class ResourceHandler < Rex::Proto::Http::Handler::Erb
|
||||
def initialize(server, root_path, framework, driver, opts = {})
|
||||
opts['ErbCallback'] = ::Proc.new { |erb, cli, request, response|
|
||||
query_string = request.qstring
|
||||
meta_vars = request.meta_vars
|
||||
erb.result(binding)
|
||||
}
|
||||
|
||||
super(server, root_path, opts)
|
||||
|
||||
self.framework = framework
|
||||
self.driver = driver
|
||||
end
|
||||
|
||||
attr_accessor :framework # :nodoc:
|
||||
attr_accessor :driver # :nodoc:
|
||||
end
|
||||
|
||||
#
|
||||
# The default port to listen for HTTP requests on.
|
||||
#
|
||||
DefaultPort = 55555
|
||||
|
||||
#
|
||||
# The default host to listen for HTTP requests on.
|
||||
#
|
||||
DefaultHost = "127.0.0.1"
|
||||
|
||||
#
|
||||
# The default root directory for requests.
|
||||
#
|
||||
DefaultRoot = "/"
|
||||
|
||||
#
|
||||
# The default local directory for msfweb.
|
||||
#
|
||||
DefaultLocalDirectory = Msf::Config.data_directory + File::SEPARATOR + "msfweb"
|
||||
|
||||
#
|
||||
# The default index script.
|
||||
#
|
||||
DefaultIndex = "index.rhtml"
|
||||
|
||||
#
|
||||
# Initializes a web driver instance and prepares it for listening to HTTP
|
||||
# requests. The constructor takes a hash of options that can control how
|
||||
|
@ -87,6 +128,9 @@ class Driver < Msf::Ui::Driver
|
|||
# Set the passed options hash for referencing later on.
|
||||
self.opts = opts
|
||||
|
||||
# Initalize the consoles set
|
||||
self.consoles = {}
|
||||
|
||||
# Initialize configuration
|
||||
Msf::Config.init
|
||||
|
||||
|
@ -95,144 +139,55 @@ class Driver < Msf::Ui::Driver
|
|||
|
||||
# Initialize attributes
|
||||
self.framework = Msf::Simple::Framework.create
|
||||
|
||||
# Initialize the termination event.
|
||||
self.term_event = Rex::Sync::Event.new
|
||||
|
||||
# Include common helper stuff. If there is no common stuff to be
|
||||
# included, then we'll just catch the exception and move on with our
|
||||
# life.
|
||||
begin
|
||||
if ($:.include?(server_local_directory) == false)
|
||||
$:.unshift(server_local_directory)
|
||||
require 'msfweb_common'
|
||||
end
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Starts the HTTP server and waits for termination.
|
||||
#
|
||||
def run
|
||||
self.service = Rex::ServiceManager.start(Rex::Proto::Http::Server,
|
||||
port = (opts['ServerPort'] || DefaultPort).to_i,
|
||||
host = (opts['ServerHost'] || DefaultHost))
|
||||
|
||||
ilog("Web server started on #{host}:#{port}", LogSource)
|
||||
|
||||
# Mount the server root directory on the web server instance. We pass
|
||||
# it a custom ErbCallback so that we can have it run in a context that
|
||||
# has the framework instance defined.
|
||||
service.mount(
|
||||
server_root,
|
||||
ResourceHandler,
|
||||
false,
|
||||
server_local_directory,
|
||||
framework,
|
||||
self)
|
||||
|
||||
# Add a resource that will be responsible for waiting for channels to
|
||||
# have input that needs to be read from and written back to http
|
||||
# clients. Since this call will block for an extended period of time,
|
||||
# we set the long call flag to cause it to run in the context of a
|
||||
# thread.
|
||||
service.add_resource("/internal/comm_read",
|
||||
'Proc' => Proc.new { |client, request|
|
||||
begin
|
||||
Comm.read_channels(client, request)
|
||||
rescue ::Exception
|
||||
dlog("comm_read: #{$!}")
|
||||
end
|
||||
},
|
||||
'LongCall' => true)
|
||||
|
||||
# Add a resource for writing to channels.
|
||||
service.add_resource("/internal/comm_write",
|
||||
'Proc' => Proc.new { |client, request|
|
||||
begin
|
||||
Comm.write_channel(client, request)
|
||||
rescue ::Exception
|
||||
dlog("comm_write: #{$!}")
|
||||
end
|
||||
})
|
||||
|
||||
# Add a resource that will be used to create a channel for a given
|
||||
# resource.
|
||||
service.add_resource("/internal/comm_create",
|
||||
'Proc' => Proc.new { |client, request|
|
||||
begin
|
||||
Comm.create_channel(client, request)
|
||||
rescue ::Exception
|
||||
dlog("comm_create: #{$!}")
|
||||
end
|
||||
})
|
||||
|
||||
# Initialize the console count
|
||||
self.last_console = 0
|
||||
|
||||
# Give the comm an opportunity to set up so that it can receive
|
||||
# notifications about session creation and so on.
|
||||
# notifications about session creation and so on.
|
||||
Comm.setup(framework)
|
||||
end
|
||||
|
||||
# Wait for the termination event to be set.
|
||||
term_event.wait
|
||||
|
||||
# Stop the source and clean it up.
|
||||
Rex::ServiceManager.stop_service(service)
|
||||
|
||||
service.deref
|
||||
|
||||
def create_console
|
||||
# Destroy any unused consoles
|
||||
clean_consoles
|
||||
|
||||
console = WebConsole.new(self.framework, self.last_console)
|
||||
self.last_console += 1
|
||||
self.consoles[console.console_id.to_s] = console
|
||||
console.console_id.to_s
|
||||
end
|
||||
|
||||
def write_console(id, buf)
|
||||
self.consoles[id] ? self.consoles.write(buf) : nil
|
||||
end
|
||||
|
||||
def read_console(id)
|
||||
self.consoles[id] ? self.consoles.read() : nil
|
||||
end
|
||||
|
||||
def clean_consoles(timeout=300)
|
||||
self.consoles.each_pair do |id, con|
|
||||
if (con.last_access + timeout < Time.now)
|
||||
con.shutdown
|
||||
self.consoles.delete(id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Stub
|
||||
#
|
||||
def run
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
# Sets the event that will cause the web service to terminate.
|
||||
#
|
||||
def terminate
|
||||
term_event.set
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the root resource name, such as '/msfweb'.
|
||||
#
|
||||
def server_root
|
||||
opts['ServerRoot'] || DefaultRoot
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the server index, such as 'index.rhtml'.
|
||||
#
|
||||
def server_index
|
||||
opts['ServerIndex'] || DefaultIndex
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the local directory that will hold the files to be serviced.
|
||||
#
|
||||
def server_local_directory
|
||||
opts['ServerLocalDirectory'] || DefaultLocalDirectory
|
||||
end
|
||||
|
||||
#
|
||||
# The framework instance associated with this driver.
|
||||
#
|
||||
attr_reader :framework
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :framework # :nodoc:
|
||||
attr_accessor :opts # :nodoc:
|
||||
|
||||
#
|
||||
# The internal event used to cause the web service to stop.
|
||||
#
|
||||
attr_accessor :term_event
|
||||
|
||||
#
|
||||
# The internal service context.
|
||||
#
|
||||
attr_accessor :service
|
||||
|
||||
#
|
||||
# Initializes logging for the web server.
|
||||
# Initializes logging for the web interface
|
||||
#
|
||||
def initialize_logging
|
||||
level = (opts['LogLevel'] || 0).to_i
|
||||
|
|
|
@ -5,15 +5,17 @@ require 'rex/ui/text/output'
|
|||
require 'rex/ui/text/output/buffer'
|
||||
require 'rex/ui/text/input/buffer'
|
||||
|
||||
class BidirectionalPipe
|
||||
|
||||
include Rex::UI::Text::Output
|
||||
class BidirectionalPipe < Rex::Ui::Text::Input
|
||||
|
||||
def initialize
|
||||
@subscribers_out = {}
|
||||
@pipe_input = Rex::Ui::Text::Input::Buffer.new
|
||||
end
|
||||
|
||||
def pipe_input
|
||||
@pipe_input
|
||||
end
|
||||
|
||||
def close
|
||||
@pipe_input.close
|
||||
end
|
||||
|
|
|
@ -13,9 +13,12 @@ require 'rex/io/stream_abstraction'
|
|||
###
|
||||
class Input::Buffer < Rex::Ui::Text::Input
|
||||
|
||||
def initialize
|
||||
@sock = Rex::IO::StreamAbstraction.new
|
||||
class BufferSock
|
||||
include Rex::IO::StreamAbstraction
|
||||
end
|
||||
|
||||
def initialize
|
||||
@sock = BufferSock.new
|
||||
@sock.initialize_abstraction
|
||||
end
|
||||
|
||||
|
@ -23,10 +26,6 @@ class Input::Buffer < Rex::Ui::Text::Input
|
|||
@sock.cleanup_abstraction
|
||||
end
|
||||
|
||||
def supports_readline
|
||||
false
|
||||
end
|
||||
|
||||
def sysread(len = 1)
|
||||
@sock.rsock.sysread(1)
|
||||
end
|
||||
|
|
|
@ -38,7 +38,7 @@ module Shell
|
|||
# Initialize the prompt
|
||||
self.init_prompt = prompt
|
||||
self.prompt_char = prompt_char
|
||||
|
||||
|
||||
# Initialize the user interface handles
|
||||
init_ui(Input::Stdio.new, Output::Stdio.new)
|
||||
end
|
||||
|
@ -47,6 +47,7 @@ module Shell
|
|||
# Initializes the user interface input/output classes.
|
||||
#
|
||||
def init_ui(in_input = nil, in_output = nil)
|
||||
|
||||
# Initialize the input and output methods
|
||||
self.input = in_input
|
||||
self.output = in_output
|
||||
|
|
Loading…
Reference in New Issue