Sessions can be detached and killed from the console
git-svn-id: file:///home/svn/framework3/trunk@4437 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
06ac34faf1
commit
51d61c161d
|
@ -9,47 +9,6 @@ class ConsoleController < ApplicationController
|
|||
#
|
||||
def index
|
||||
|
||||
# Work around rails stupidity
|
||||
if(not $webrick_hooked_console)
|
||||
|
||||
$webrick.mount_proc("/_console") do |req, res|
|
||||
|
||||
res['Content-Type'] = "text/javascript"
|
||||
|
||||
m = req.path_info.match(/cid=(\d+)/)
|
||||
if (m and m[1] and $msfweb.consoles[m[1]])
|
||||
console = $msfweb.consoles[m[1]]
|
||||
|
||||
out = ''
|
||||
tsp = Time.now.to_i
|
||||
prompt_old = console.prompt
|
||||
|
||||
# Poll the console output for 15 seconds
|
||||
while( tsp + 15 > Time.now.to_i and out.length == 0 and console.prompt == prompt_old)
|
||||
out = console.read()
|
||||
select(nil, nil, nil, 0.10)
|
||||
end
|
||||
|
||||
out = out.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
pro = console.prompt.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
|
||||
if (console.busy)
|
||||
pro = '(running)'.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
end
|
||||
|
||||
script = "// Metasploit Web Console Data\n"
|
||||
script += "var con_prompt = unescape('#{pro}');\n"
|
||||
script += "var con_update = unescape('#{out}');\n"
|
||||
|
||||
res.body = script
|
||||
else
|
||||
res.body = '// Invalid console ID'
|
||||
end
|
||||
end
|
||||
|
||||
$webrick_hooked_console = true
|
||||
end
|
||||
|
||||
cid = params[:id]
|
||||
|
||||
if (not (cid and $msfweb.consoles[cid]))
|
||||
|
@ -58,28 +17,30 @@ class ConsoleController < ApplicationController
|
|||
return
|
||||
end
|
||||
|
||||
|
||||
script = "// Metasploit Web Console Data\n"
|
||||
out = ""
|
||||
|
||||
@cid = params[:id]
|
||||
@console = $msfweb.consoles[@cid]
|
||||
|
||||
if(params[:cmd])
|
||||
out = ''
|
||||
|
||||
if (params[:cmd].strip.length > 0)
|
||||
if(params[:cmd])
|
||||
@console.write(params[:cmd] + "\n")
|
||||
end
|
||||
|
||||
out = out.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
pro = @console.prompt.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
|
||||
if (@console.busy)
|
||||
pro = '(running)'.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
if(params[:read])
|
||||
out = @console.read() || ''
|
||||
end
|
||||
|
||||
script = "// Metasploit Web Console Data\n"
|
||||
script += "var con_prompt = unescape('#{pro}');\n"
|
||||
script += "var con_update = unescape('#{out}');\n"
|
||||
|
||||
send_data(script, :type => "text/javascript")
|
||||
if(params[:special])
|
||||
case params[:special]
|
||||
when 'kill'
|
||||
@console.session_kill
|
||||
when 'detach'
|
||||
@console.session_detach
|
||||
end
|
||||
end
|
||||
|
||||
if(params[:tab])
|
||||
|
@ -116,22 +77,24 @@ class ConsoleController < ApplicationController
|
|||
cmdl = cmd_top[0, depth]
|
||||
end
|
||||
|
||||
out = "\n" + opts.map{ |c| ">> " + c }.join("\n")
|
||||
out << "\n" + opts.map{ |c| ">> " + c }.join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
tln = cmdl.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
script += "var con_tabbed = unescape('#{tln}');\n"
|
||||
end
|
||||
|
||||
if(params[:read])
|
||||
|
||||
out = out.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
pro = @console.prompt.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
tln = cmdl.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
|
||||
if (@console.busy)
|
||||
pro = '(running)'.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
end
|
||||
|
||||
script = "// Metasploit Web Console Data\n"
|
||||
script += "var con_prompt = unescape('#{pro}');\n"
|
||||
script += "var con_update = unescape('#{out}');\n"
|
||||
script += "var con_tabbed = unescape('#{tln}');\n"
|
||||
|
||||
send_data(script, :type => "text/javascript")
|
||||
end
|
||||
|
|
|
@ -14,57 +14,23 @@ class SessionsController < ApplicationController
|
|||
end
|
||||
|
||||
def interact
|
||||
# Work around rails stupidity
|
||||
if(not $webrick_hooked_session)
|
||||
|
||||
$webrick.mount_proc("/_session") do |req, res|
|
||||
|
||||
res['Content-Type'] = "text/javascript"
|
||||
|
||||
m = req.path_info.match(/cid=(\d+)/)
|
||||
if (m and m[1] and $msfweb.sessions[m[1].to_i])
|
||||
cid = m[1].to_i
|
||||
|
||||
$msfweb.connect_session(cid)
|
||||
|
||||
out = ''
|
||||
tsp = Time.now.to_i
|
||||
|
||||
# Poll the session output for 15 seconds
|
||||
while( tsp + 15 > Time.now.to_i and out.length == 0)
|
||||
out = $msfweb.read_session(cid)
|
||||
select(nil, nil, nil, 0.10)
|
||||
end
|
||||
|
||||
out = out.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
|
||||
script = "// Metasploit Web Session Data\n"
|
||||
script += "var ses_update = unescape('#{out}');\n"
|
||||
|
||||
res.body = script
|
||||
else
|
||||
res.body = '// Invalid session ID'
|
||||
end
|
||||
end
|
||||
|
||||
$webrick_hooked_session = true
|
||||
end
|
||||
|
||||
cid = params[:id].to_i
|
||||
|
||||
$msfweb.connect_session(cid)
|
||||
|
||||
if(params[:cmd])
|
||||
|
||||
if (params[:cmd].strip.length > 0)
|
||||
$msfweb.write_session(cid, params[:cmd] + "\n")
|
||||
end
|
||||
|
||||
script = "// Metasploit Web Session Data\n"
|
||||
script += "var ses_update = unescape('');\n"
|
||||
|
||||
if (params[:read])
|
||||
$msfweb.connect_session(cid)
|
||||
out = $msfweb.read_session(cid) || ''
|
||||
out = out.unpack('C*').map{|c| sprintf("%%%.2x", c)}.join
|
||||
script = "// Metasploit Web Session Data\n"
|
||||
script += "var ses_update = unescape('#{out}');\n"
|
||||
send_data(script, :type => "text/javascript")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -54,16 +54,4 @@ require 'msf/base'
|
|||
|
||||
$msfweb = Msf::Ui::Web::Driver.new({'LogLevel' => 5})
|
||||
$msframework = $msfweb.framework
|
||||
$webrick = nil
|
||||
$webrick_hooked_console = false
|
||||
$webrick_hooked_session = false
|
||||
|
||||
module WEBrickHooker
|
||||
def initialize(*args)
|
||||
$webrick = self
|
||||
super(*args)
|
||||
end
|
||||
end
|
||||
|
||||
WEBrick::HTTPServer.class_eval("include WEBrickHooker")
|
||||
|
||||
|
|
|
@ -19,13 +19,36 @@ var con_prompt = "";
|
|||
var con_update = "";
|
||||
var con_tabbed = "";
|
||||
|
||||
|
||||
|
||||
// Internal commands
|
||||
var cmd_internal =
|
||||
{
|
||||
help:function() {
|
||||
console_printline(" Web Console Internal Commands\n");
|
||||
console_printline("=========================================\n\n");
|
||||
console_printline(" /help Show this text\n");
|
||||
console_printline(" /clear Clear the screen\n");
|
||||
console_printline(" /detach Detach an active session\n");
|
||||
console_printline(" /kill Abort an active session\n");
|
||||
console_printline("\n");
|
||||
},
|
||||
clear:function() {
|
||||
console_output.innerHTML = '';
|
||||
},
|
||||
detach:function() {
|
||||
console_printline(">> Detaching from any active session...\n");
|
||||
new Ajax.Updater("console_update", document.location, {
|
||||
asynchronous:true,
|
||||
evalScripts:true,
|
||||
parameters:"special=detach"
|
||||
});
|
||||
},
|
||||
kill:function() {
|
||||
console_printline(">> Killing any active session...\n");
|
||||
new Ajax.Updater("console_update", document.location, {
|
||||
asynchronous:true,
|
||||
evalScripts:true,
|
||||
parameters:"special=kill"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -54,9 +77,10 @@ function console_refocus() {
|
|||
}
|
||||
|
||||
function console_read() {
|
||||
new Ajax.Updater("console_update", '/_console/cid=' + console_id, {
|
||||
new Ajax.Updater("console_update", document.location, {
|
||||
asynchronous:true,
|
||||
evalScripts:true,
|
||||
parameters:"read=yes",
|
||||
onComplete:console_read_output
|
||||
});
|
||||
}
|
||||
|
@ -85,7 +109,7 @@ function console_read_output(req) {
|
|||
console_update_output(req);
|
||||
|
||||
// Reschedule the console reader
|
||||
setTimeout(console_read, 1);
|
||||
setTimeout(console_read, 1000);
|
||||
}
|
||||
|
||||
function console_update_output(req) {
|
||||
|
@ -99,8 +123,10 @@ function console_update_output(req) {
|
|||
}
|
||||
|
||||
console_prompt.innerHTML = con_prompt;
|
||||
console_refocus();
|
||||
|
||||
if(con_update && con_update.length > 0) {
|
||||
window.scrollTo(0, 10000000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -109,7 +135,10 @@ function console_update_tabs(req) {
|
|||
|
||||
status_free();
|
||||
|
||||
if (con_update.length > 0) {
|
||||
console_printline(con_update, 'output_line');
|
||||
}
|
||||
|
||||
console_prompt.innerHTML = con_prompt;
|
||||
console_input.value = con_tabbed;
|
||||
|
||||
|
@ -129,19 +158,23 @@ function console_keypress(e) {
|
|||
|
||||
console_printline("\n>> " + console_input.value + "\n\n", 'output_line')
|
||||
|
||||
if(cmd_internal[console_input.value]) {
|
||||
cmd_internal[console_input.value]();
|
||||
if(console_input.value[0] == '/') {
|
||||
cmd_name = console_input.value.substring(1);
|
||||
|
||||
if(cmd_internal[cmd_name]) {
|
||||
cmd_internal[cmd_name]();
|
||||
console_input.value = "";
|
||||
console_input.focus();
|
||||
return keystroke_block(e);
|
||||
}
|
||||
}
|
||||
|
||||
status_busy();
|
||||
|
||||
new Ajax.Updater("console_update", document.location, {
|
||||
asynchronous:true,
|
||||
evalScripts:true,
|
||||
parameters:"cmd=" + escape(console_input.value),
|
||||
parameters:"read=yes&cmd=" + escape(console_input.value),
|
||||
onComplete:console_update_output
|
||||
});
|
||||
|
||||
|
@ -181,7 +214,7 @@ function console_keydown(e) {
|
|||
new Ajax.Updater("console_update", document.location, {
|
||||
asynchronous:true,
|
||||
evalScripts:true,
|
||||
parameters:"tab=" + escape(console_input.value),
|
||||
parameters:"read=yes&tab=" + escape(console_input.value),
|
||||
onComplete:console_update_tabs
|
||||
});
|
||||
return keystroke_block(e);
|
||||
|
|
|
@ -54,9 +54,10 @@ function session_refocus() {
|
|||
}
|
||||
|
||||
function session_read() {
|
||||
new Ajax.Updater("session_update", '/_session/cid=' + session_id, {
|
||||
new Ajax.Updater("session_update", document.location, {
|
||||
asynchronous:true,
|
||||
evalScripts:true,
|
||||
parameters:"read=yes",
|
||||
onComplete:session_read_output
|
||||
});
|
||||
}
|
||||
|
@ -84,8 +85,8 @@ function session_read_output(req) {
|
|||
// Call the console updated
|
||||
session_update_output(req);
|
||||
|
||||
// Reschedule the console reader
|
||||
setTimeout(session_read, 1);
|
||||
// Reschedule the session reader
|
||||
setTimeout(session_read, 1000);
|
||||
}
|
||||
|
||||
function session_update_output(req) {
|
||||
|
@ -127,7 +128,7 @@ function session_keypress(e) {
|
|||
new Ajax.Updater("session_update", document.location, {
|
||||
asynchronous:true,
|
||||
evalScripts:true,
|
||||
parameters:"cmd=" + escape(session_input.value),
|
||||
parameters:"read=yes&cmd=" + escape(session_input.value),
|
||||
onComplete:session_update_output
|
||||
});
|
||||
|
||||
|
|
|
@ -42,11 +42,11 @@ html,body {
|
|||
border: none;
|
||||
padding: 0;
|
||||
background: #000000;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.input {
|
||||
font: inherit;
|
||||
font-family: fixed, console, terminal;
|
||||
font-family: monospace, console, fixed, terminal;
|
||||
background: #000000;
|
||||
border: 0;
|
||||
color: white;
|
||||
|
@ -57,9 +57,8 @@ html,body {
|
|||
|
||||
.prompt {
|
||||
color: white;
|
||||
font: inherit;
|
||||
font-family: monospace, console, fixed, terminal;
|
||||
text-align: right;
|
||||
font-family: fixed;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
|
@ -68,6 +67,11 @@ html,body {
|
|||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#console_input {
|
||||
font-size: 11px;
|
||||
font-family: monospace, console, fixed, terminal;
|
||||
}
|
||||
|
||||
#console_status {
|
||||
color: #fff;
|
||||
font-family: monospace;
|
||||
|
|
|
@ -43,6 +43,20 @@ module CommandDispatcher
|
|||
driver.active_module = mod
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the active session if one has been selected, otherwise nil is
|
||||
# returned.
|
||||
#
|
||||
def active_session
|
||||
driver.active_session
|
||||
end
|
||||
|
||||
#
|
||||
# Sets the active session for this driver instance.
|
||||
#
|
||||
def active_session=(mod)
|
||||
driver.active_session = mod
|
||||
end
|
||||
#
|
||||
# Checks to see if the driver is defanged.
|
||||
#
|
||||
|
@ -68,12 +82,6 @@ module CommandDispatcher
|
|||
#
|
||||
attr_accessor :driver
|
||||
|
||||
|
||||
#
|
||||
# The active, interactive session, if any
|
||||
#
|
||||
attr_accessor :active_session
|
||||
|
||||
end
|
||||
|
||||
###
|
||||
|
|
|
@ -266,6 +266,10 @@ class Driver < Msf::Ui::Driver
|
|||
# The active module associated with the driver.
|
||||
#
|
||||
attr_accessor :active_module
|
||||
#
|
||||
# The active session associated with the driver.
|
||||
#
|
||||
attr_accessor :active_session
|
||||
|
||||
#
|
||||
# If defanged is true, dangerous functionality, such as exploitation, irb,
|
||||
|
|
|
@ -105,6 +105,19 @@ class WebConsole
|
|||
def busy
|
||||
self.console.busy
|
||||
end
|
||||
|
||||
def session_detach
|
||||
if(self.console.active_session)
|
||||
self.console.active_session.detach()
|
||||
end
|
||||
end
|
||||
|
||||
def session_kill
|
||||
if(self.console.active_session)
|
||||
self.console.active_session.kill()
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -27,8 +27,26 @@ class Client
|
|||
'vhost' => self.hostname,
|
||||
'version' => '1.1',
|
||||
'agent' => "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
|
||||
'uri_encode_mode' => 'hex-normal',
|
||||
'uri_full_url' => false
|
||||
#
|
||||
# Evasion options
|
||||
#
|
||||
'uri_encode_mode' => 'hex-normal', # hex-all, hex-random, u-normal, u-random, u-all
|
||||
'uri_full_url' => false, # bool
|
||||
'pad_method_uri_count' => 1, # integer
|
||||
'pad_uri_version_count' => 1, # integer
|
||||
'pad_method_uri_type' => 'space', # space, tab, apache
|
||||
'pad_uri_version_type' => 'space', # space, tab, apache
|
||||
'method_random_valid' => false, # bool
|
||||
'method_random_invalid' => false, # bool
|
||||
'method_random_case' => false, # bool
|
||||
'version_random_valid' => false, # bool
|
||||
'version_random_invalid' => false, # bool
|
||||
'version_random_case' => false, # bool
|
||||
'uri_dir_self_reference' => false, # bool
|
||||
'uri_dir_fake_relative' => false, # bool
|
||||
'uri_use_backslashes' => false, # bool
|
||||
'pad_fake_headers' => false, # bool
|
||||
'pad_fake_headers_count' => 16, # integer
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -37,7 +55,7 @@ class Client
|
|||
#
|
||||
def set_config(opts = {})
|
||||
opts.each_pair do |var,val|
|
||||
config[var]=val
|
||||
self.config[var]=val
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -336,6 +354,24 @@ class Client
|
|||
# Return the uri
|
||||
#
|
||||
def set_uri(uri)
|
||||
|
||||
if (self.config['uri_dir_self_reference'])
|
||||
uri.gsub!('/', '/./')
|
||||
end
|
||||
|
||||
if (self.config['uri_dir_fake_relative'])
|
||||
buf = ""
|
||||
uri.split('/').each do |part|
|
||||
cnt = rand(8)+2
|
||||
1.upto(cnt) { |idx|
|
||||
buf += "/" + Rex::Text.rand_text_alphanumeric(rand(32)+1)
|
||||
}
|
||||
buf += ("/.." * cnt)
|
||||
buf += "/" + part
|
||||
end
|
||||
uri = buf
|
||||
end
|
||||
|
||||
if (self.config['uri_full_url'])
|
||||
url = self.ssl ? "https" : "http"
|
||||
url += self.config['vhost']
|
||||
|
@ -349,11 +385,26 @@ class Client
|
|||
|
||||
#
|
||||
# Return the cgi
|
||||
# TODO:
|
||||
# * Implement self-referential directories
|
||||
# * Implement bogus relative directories
|
||||
#
|
||||
def set_cgi(uri)
|
||||
|
||||
if (self.config['uri_dir_self_reference'])
|
||||
uri.gsub!('/', '/./')
|
||||
end
|
||||
|
||||
if (self.config['uri_dir_fake_relative'])
|
||||
buf = ""
|
||||
uri.split('/').each do |part|
|
||||
cnt = rand(8)+2
|
||||
1.upto(cnt) { |idx|
|
||||
buf += "/" + Rex::Text.rand_text_alphanumeric(rand(32)+1)
|
||||
}
|
||||
buf += ("/.." * cnt)
|
||||
buf += "/" + part
|
||||
end
|
||||
uri = buf
|
||||
end
|
||||
|
||||
url = uri
|
||||
|
||||
if (self.config['uri_full_url'])
|
||||
|
@ -370,22 +421,42 @@ class Client
|
|||
# Return the HTTP method string
|
||||
#
|
||||
def set_method(method)
|
||||
# TODO:
|
||||
# * Randomize case
|
||||
# * Replace with random valid method
|
||||
# * Replace with random invalid method
|
||||
method
|
||||
ret = method
|
||||
|
||||
if (self.config['method_random_valid'])
|
||||
ret = ['GET', 'POST', 'HEAD'][rand(3)]
|
||||
end
|
||||
|
||||
if (self.config['method_random_invalid'])
|
||||
ret = Rex::Text.rand_text_alpha(rand(20)+1)
|
||||
end
|
||||
|
||||
if (self.config['method_random_case'])
|
||||
ret = Rex::Text.to_rand_base(ret)
|
||||
end
|
||||
|
||||
ret += "\r\n"
|
||||
end
|
||||
|
||||
#
|
||||
# Return the HTTP version string
|
||||
#
|
||||
def set_version(protocol, version)
|
||||
# TODO:
|
||||
# * Randomize case
|
||||
# * Replace with random valid versions
|
||||
# * Replace with random invalid versions
|
||||
protocol + "/" + version + "\r\n"
|
||||
ret = protocol + "/" + version
|
||||
|
||||
if (self.config['version_random_valid'])
|
||||
ret = protocol + "/" + ['1.0', '1.1'][rand(2)]
|
||||
end
|
||||
|
||||
if (self.config['version_random_invalid'])
|
||||
ret = Rex::Text.rand_text_alphanumeric(rand(20)+1)
|
||||
end
|
||||
|
||||
if (self.config['version_random_case'])
|
||||
ret = Rex::Text.to_rand_base(ret)
|
||||
end
|
||||
|
||||
ret += "\r\n"
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -407,18 +478,44 @@ class Client
|
|||
# Return the spacing between the method and uri
|
||||
#
|
||||
def set_method_uri_spacer
|
||||
# TODO:
|
||||
# * Support different space types
|
||||
" "
|
||||
len = self.config['pad_method_uri_count']
|
||||
set = " "
|
||||
buf = ""
|
||||
|
||||
case self.config['pad_method_uri_type']
|
||||
when 'tab'
|
||||
set = "\t"
|
||||
when 'apache'
|
||||
set = "\t \x0b\x0c\x0d"
|
||||
end
|
||||
|
||||
while(buf.length < len)
|
||||
buf << set[ rand(set.length) ]
|
||||
end
|
||||
|
||||
return buf
|
||||
end
|
||||
|
||||
#
|
||||
# Return the spacing between the uri and the version
|
||||
#
|
||||
def set_uri_version_spacer
|
||||
# TODO:
|
||||
# * Support different space types
|
||||
" "
|
||||
len = self.config['pad_uri_version_count']
|
||||
set = " "
|
||||
buf = ""
|
||||
|
||||
case self.config['pad_uri_version_type']
|
||||
when 'tab'
|
||||
set = "\t"
|
||||
when 'apache'
|
||||
set = "\t \x0b\x0c\x0d"
|
||||
end
|
||||
|
||||
while(buf.length < len)
|
||||
buf << set[ rand(set.length) ]
|
||||
end
|
||||
|
||||
return buf
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -491,6 +588,15 @@ class Client
|
|||
def set_extra_headers(headers)
|
||||
buf = ''
|
||||
|
||||
if (self.config['pad_fake_headers'])
|
||||
1.upto(self.config['pad_fake_headers_count']) do |i|
|
||||
buf += set_formatted_header(
|
||||
Rex::Text.rand_text_alphanumeric(rand(32)+1),
|
||||
Rex::Text.rand_text_alphanumeric(rand(32)+1)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
headers.each_pair do |var,val|
|
||||
buf += set_formatted_header(var, val)
|
||||
end
|
||||
|
|
|
@ -349,6 +349,17 @@ module Text
|
|||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a string to random case
|
||||
#
|
||||
def self.to_rand_case(str)
|
||||
buf = str.dup
|
||||
0.upto(str.length) do |i|
|
||||
buf[i,1] = rand(2) == 0 ? str[i,1].upcase : str[i,1].downcase
|
||||
end
|
||||
return buf
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a hex string to a raw string
|
||||
#
|
||||
|
@ -478,6 +489,7 @@ module Text
|
|||
#
|
||||
##
|
||||
|
||||
|
||||
# Generates a random character.
|
||||
def self.rand_char(bad, chars = AllChars)
|
||||
rand_text(1, bad, chars)
|
||||
|
|
|
@ -78,7 +78,6 @@ module Interactive
|
|||
reset_ui()
|
||||
|
||||
ensure
|
||||
|
||||
# Mark this as completed
|
||||
self.completed = true
|
||||
end
|
||||
|
@ -183,7 +182,7 @@ protected
|
|||
while self.interacting
|
||||
|
||||
# Select input and rstream
|
||||
sd = Rex::ThreadSafe.select([ _local_fd, _remote_fd(stream) ], nil, nil, 0.50)
|
||||
sd = Rex::ThreadSafe.select([ _local_fd, _remote_fd(stream) ], nil, nil, 0.25)
|
||||
|
||||
# Cycle through the items that have data
|
||||
# From the stream? Write to user_output.
|
||||
|
@ -195,6 +194,8 @@ protected
|
|||
_stream_read_local_write_remote(stream)
|
||||
end
|
||||
} if (sd)
|
||||
|
||||
Thread.pass
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue