Merge branch 'master' into wordpress-xmlrpc-login-scanner

bug/bundler_fix
David Maloney 2014-08-29 12:50:34 -05:00
commit 0e14b271a1
No known key found for this signature in database
GPG Key ID: DEDBA9DC3A913DB2
557 changed files with 30020 additions and 22545 deletions

3
.gitignore vendored
View File

@ -48,6 +48,9 @@ tags
*.opensdf
*.user
# Rails log directory
/log
# ignore release/debug folders for exploits
external/source/exploits/**/Debug
external/source/exploits/**/Release

View File

@ -1,18 +1,78 @@
LineLength:
# This list was intially created by analyzing the last three months (51
# modules) committed to Metasploit Framework. Many, many older modules
# will have offenses, but this should at least provide a baseline for
# new modules.
#
# Updates to this file should include a 'Description' parameter for any
# explaination needed.
# inherit_from: .rubocop_todo.yml
Style/ClassLength:
Description: 'Most Metasploit modules are quite large. This is ok.'
Enabled: true
Exclude:
- 'modules/**/*'
Style/Documentation:
Enabled: true
Description: 'Most Metasploit modules do not have class documentation.'
Exclude:
- 'modules/**/*'
Style/Encoding:
Enabled: true
Description: 'We prefer binary to UTF-8.'
EnforcedStyle: 'when_needed'
Style/LineLength:
Description: >-
Metasploit modules often pattern match against very
long strings when identifying targets.
Enabled: true
Max: 180
MethodLength:
Style/MethodLength:
Enabled: true
Max: 100
Description: >-
While the style guide suggests 10 lines, exploit definitions
often exceed 200 lines.
Max: 300
Style/ClassLength:
Exclude:
# Most modules are quite large and all contained in one class. This is OK.
- 'modules/**/*'
# Basically everything in metasploit needs binary encoding, not UTF-8.
# Disable this here and enforce it through msftidy
Style/Encoding:
Enabled: false
Style/NumericLiterals:
Enabled: false
Description: 'This often hurts readability for exploit-ish code.'
Style/SpaceInsideBrackets:
Enabled: false
Description: 'Until module template are final, most modules will fail this.'
Style/StringLiterals:
Enabled: false
Description: 'Single vs double quote fights are largely unproductive.'
Style/WordArray:
Enabled: false
Description: 'Metasploit prefers consistent use of []'
Style/RedundantBegin:
Exclude:
# this pattern is very common and somewhat unavoidable
# def run_host(ip)
# begin
# ...
# rescue ...
# ...
# ensure
# disconnect
# end
# end
- 'modules/**/*'
Documentation:
Exclude:

View File

@ -1,3 +1,6 @@
env:
- RAKE_TASK=cucumber
- RAKE_TASK=spec
language: ruby
before_install:
- rake --version
@ -14,6 +17,7 @@ before_script:
- bundle exec rake --version
- bundle exec rake db:create
- bundle exec rake db:migrate
script: "bundle exec rake $RAKE_TASK"
rvm:
#- '1.8.7'

View File

@ -3,6 +3,8 @@
--exclude \.ut\.rb/
--exclude \.ts\.rb/
--files CONTRIBUTING.md,COPYING,HACKING,LICENSE
app/**/*.rb
lib/msf/**/*.rb
lib/metasploit/**/*.rb
lib/rex/**/*.rb
plugins/**/*.rb

View File

@ -33,7 +33,7 @@ and Metasploit's [Common Coding Mistakes](https://github.com/rapid7/metasploit-f
## Code Contributions
* **Do** stick to the [Ruby style guide](https://github.com/bbatsov/ruby-style-guide).
* Similarly, **try** to get Rubocop passing or at least relatively quiet against the files added/modified as part of your contribution
* *Do* get [Rubocop](https://rubygems.org/search?query=rubocop) relatively quiet against the code you are adding or modifying.
* **Do** follow the [50/72 rule](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) for Git commit messages.
* **Do** create a [topic branch](http://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches) to work on instead of working directly on `master`.

85
Gemfile
View File

@ -1,68 +1,57 @@
source 'https://rubygems.org'
# Need 3+ for ActiveSupport::Concern
gem 'activesupport', '>= 3.0.0', '< 4.0.0'
# Needed for some admin modules (cfme_manageiq_evm_pass_reset.rb)
gem 'bcrypt'
# Needed for some admin modules (scrutinizer_add_user.rb)
gem 'json'
# Needed for Meterpreter on Windows, soon others.
gem 'meterpreter_bins', '0.0.6'
# Needed by msfgui and other rpc components
gem 'msgpack'
# Needed by anemone crawler
gem 'nokogiri'
# Needed by db.rb and Msf::Exploit::Capture
gem 'packetfu', '1.1.9'
# Needed by JSObfu
gem 'rkelly-remix', '0.0.6'
# Needed by anemone crawler
gem 'robots'
# Needed for some post modules
gem 'sqlite3'
# Add default group gems to `metasploit-framework.gemspec`:
# spec.add_runtime_dependency '<name>', [<version requirements>]
gemspec
group :db do
# Needed for Msf::DbManager
gem 'activerecord', '>= 3.0.0', '< 4.0.0'
# Metasploit::Credential database models
gem 'metasploit-credential', '>= 0.9.0'
# Database models shared between framework and Pro.
gem 'metasploit_data_models', '0.17.0'
gem 'metasploit_data_models', '~> 0.19'
# Needed for module caching in Mdm::ModuleDetails
gem 'pg', '>= 0.11'
end
group :development do
# Markdown formatting for yard
gem 'redcarpet'
# generating documentation
gem 'yard'
# for development and testing purposes
gem 'pry'
end
group :development, :test do
# supplies factories for producing model instance for specs
# Version 4.1.0 or newer is needed to support generate calls without the
# 'FactoryGirl.' in factory definitions syntax.
gem 'factory_girl', '>= 4.1.0'
# automatically include factories from spec/factories
gem 'factory_girl_rails'
# Make rspec output shorter and more useful
gem 'fivemat', '1.2.1'
# running documentation generation tasks and rspec tasks
gem 'rake', '>= 10.0.0'
# testing framework
gem 'rspec', '>= 2.12', '< 3.0.0'
# Define `rake spec`. Must be in development AND test so that its available by default as a rake test when the
# environment is development
gem 'rspec-rails' , '>= 2.12', '< 3.0.0'
end
group :pcap do
gem 'network_interface', '~> 0.0.1'
# For sniffer and raw socket modules
gem 'pcaprub'
end
group :development do
# Style/sanity checking Ruby code
gem 'rubocop'
# Markdown formatting for yard
gem 'redcarpet'
# generating documentation
gem 'yard'
end
group :development, :test do
# supplies factories for producing model instance for specs
# Version 4.1.0 or newer is needed to support generate calls without the
# 'FactoryGirl.' in factory definitions syntax.
gem 'factory_girl', '>= 4.1.0'
# Make rspec output shorter and more useful
gem 'fivemat', '1.2.1'
# running documentation generation tasks and rspec tasks
gem 'rake', '>= 10.0.0'
end
group :test do
# Removes records from database created during tests. Can't use rspec-rails'
# transactional fixtures because multiple connections are in use so
# transactions won't work.
gem 'database_cleaner'
# testing framework
gem 'rspec', '>= 2.12'
# cucumber extension for testing command line applications, like msfconsole
gem 'aruba'
# cucumber + automatic database cleaning with database_cleaner
gem 'cucumber-rails'
gem 'shoulda-matchers'
# code coverage for tests
# any version newer than 0.5.4 gives an Encoding error when trying to read the source files.

View File

@ -27,8 +27,6 @@ end
# Create a custom group
group :local do
# Use pry to help view and interact with objects in the framework
gem 'pry', '~> 0.9'
# Use pry-debugger to step through code during development
gem 'pry-debugger', '~> 0.2'
# Add the lab gem so that the 'lab' plugin will work again

View File

@ -1,105 +1,206 @@
PATH
remote: .
specs:
metasploit-framework (4.10.1.pre.dev)
actionpack (< 4.0.0)
activesupport (>= 3.0.0, < 4.0.0)
bcrypt
json
metasploit-model (~> 0.26.1)
meterpreter_bins (= 0.0.7)
msgpack
nokogiri
packetfu (= 1.1.9)
railties
rkelly-remix (= 0.0.6)
robots
rubyzip (~> 1.1)
sqlite3
tzinfo
GEM
remote: https://rubygems.org/
specs:
activemodel (3.2.14)
activesupport (= 3.2.14)
actionpack (3.2.19)
activemodel (= 3.2.19)
activesupport (= 3.2.19)
builder (~> 3.0.0)
activerecord (3.2.14)
activemodel (= 3.2.14)
activesupport (= 3.2.14)
erubis (~> 2.7.0)
journey (~> 1.0.4)
rack (~> 1.4.5)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
activemodel (3.2.19)
activesupport (= 3.2.19)
builder (~> 3.0.0)
activerecord (3.2.19)
activemodel (= 3.2.19)
activesupport (= 3.2.19)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.14)
activesupport (3.2.19)
i18n (~> 0.6, >= 0.6.4)
multi_json (~> 1.0)
arel (3.0.2)
ast (2.0.0)
arel (3.0.3)
arel-helpers (2.0.1)
activerecord (>= 3.1.0, < 5)
aruba (0.6.1)
childprocess (>= 0.3.6)
cucumber (>= 1.1.1)
rspec-expectations (>= 2.7.0)
bcrypt (3.1.7)
builder (3.0.4)
database_cleaner (1.1.1)
diff-lcs (1.2.4)
factory_girl (4.2.0)
capybara (2.4.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
childprocess (0.5.3)
ffi (~> 1.0, >= 1.0.11)
coderay (1.1.0)
cucumber (1.2.1)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
gherkin (~> 2.11.0)
json (>= 1.4.6)
cucumber-rails (1.3.0)
capybara (>= 1.1.2)
cucumber (>= 1.1.8)
nokogiri (>= 1.5.0)
diff-lcs (1.2.5)
erubis (2.7.0)
factory_girl (4.4.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.4.1)
factory_girl (~> 4.4.0)
railties (>= 3.0.0)
ffi (1.9.3)
fivemat (1.2.1)
i18n (0.6.5)
json (1.8.0)
metasploit_data_models (0.17.0)
activerecord (>= 3.2.13)
activesupport
gherkin (2.11.6)
json (>= 1.7.6)
hike (1.2.3)
i18n (0.6.11)
journey (1.0.4)
json (1.8.1)
metasploit-concern (0.1.1)
activesupport (~> 3.0, >= 3.0.0)
metasploit-credential (0.9.0)
metasploit-concern (~> 0.1.0)
metasploit-model (~> 0.26.1)
metasploit_data_models (~> 0.19.4)
pg
meterpreter_bins (0.0.6)
mini_portile (0.5.1)
msgpack (0.5.5)
rubyntlm
rubyzip (~> 1.1)
metasploit-model (0.26.1)
activesupport
metasploit_data_models (0.19.4)
activerecord (>= 3.2.13, < 4.0.0)
activesupport
arel-helpers
metasploit-concern (~> 0.1.0)
metasploit-model (~> 0.26.1)
pg
meterpreter_bins (0.0.7)
method_source (0.8.2)
mime-types (2.3)
mini_portile (0.6.0)
msgpack (0.5.8)
multi_json (1.0.4)
network_interface (0.0.1)
nokogiri (1.6.0)
mini_portile (~> 0.5.0)
nokogiri (1.6.3.1)
mini_portile (= 0.6.0)
packetfu (1.1.9)
parser (2.1.9)
ast (>= 1.1, < 3.0)
slop (~> 3.4, >= 3.4.5)
pcaprub (0.11.3)
pg (0.16.0)
powerpack (0.0.9)
rainbow (2.0.0)
rake (10.1.0)
redcarpet (3.0.0)
pg (0.17.1)
pry (0.10.0)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
rack (1.4.5)
rack-cache (1.2)
rack (>= 0.4)
rack-ssl (1.3.4)
rack
rack-test (0.6.2)
rack (>= 1.0)
railties (3.2.19)
actionpack (= 3.2.19)
activesupport (= 3.2.19)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
rake (10.3.2)
rdoc (3.12.2)
json (~> 1.4)
redcarpet (3.1.2)
rkelly-remix (0.0.6)
robots (0.10.1)
rspec (2.14.1)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (2.14.5)
rspec-expectations (2.14.2)
rspec (2.99.0)
rspec-core (~> 2.99.0)
rspec-expectations (~> 2.99.0)
rspec-mocks (~> 2.99.0)
rspec-collection_matchers (1.0.0)
rspec-expectations (>= 2.99.0.beta1)
rspec-core (2.99.1)
rspec-expectations (2.99.2)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.3)
rubocop (0.23.0)
json (>= 1.7.7, < 2)
parser (~> 2.1.9)
powerpack (~> 0.0.6)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.4)
ruby-progressbar (1.5.1)
shoulda-matchers (2.3.0)
activesupport (>= 3.0.0)
rspec-mocks (2.99.2)
rspec-rails (2.99.0)
actionpack (>= 3.0)
activemodel (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-collection_matchers
rspec-core (~> 2.99.0)
rspec-expectations (~> 2.99.0)
rspec-mocks (~> 2.99.0)
rubyntlm (0.4.0)
rubyzip (1.1.6)
shoulda-matchers (2.6.2)
simplecov (0.5.4)
multi_json (~> 1.0.3)
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
slop (3.5.0)
slop (3.6.0)
sprockets (2.2.2)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.9)
timecop (0.6.3)
tzinfo (0.3.37)
yard (0.8.7)
thor (0.19.1)
tilt (1.4.1)
timecop (0.7.1)
tzinfo (0.3.40)
xpath (2.0.0)
nokogiri (~> 1.3)
yard (0.8.7.4)
PLATFORMS
ruby
DEPENDENCIES
activerecord (>= 3.0.0, < 4.0.0)
activesupport (>= 3.0.0, < 4.0.0)
bcrypt
database_cleaner
aruba
cucumber-rails
factory_girl (>= 4.1.0)
factory_girl_rails
fivemat (= 1.2.1)
json
metasploit_data_models (= 0.17.0)
meterpreter_bins (= 0.0.6)
msgpack
metasploit-credential (>= 0.9.0)
metasploit-framework!
metasploit_data_models (~> 0.19)
network_interface (~> 0.0.1)
nokogiri
packetfu (= 1.1.9)
pcaprub
pg (>= 0.11)
pry
rake (>= 10.0.0)
redcarpet
rkelly-remix (= 0.0.6)
robots
rspec (>= 2.12)
rubocop
rspec (>= 2.12, < 3.0.0)
rspec-rails (>= 2.12, < 3.0.0)
shoulda-matchers
simplecov (= 0.5.4)
sqlite3
timecop
yard

View File

@ -1,81 +1,11 @@
require 'bundler/setup'
pathname = Pathname.new(__FILE__)
root = pathname.parent
# add metasploit-framework/lib to load paths so rake files can just require
# files normally without having to use __FILE__ and recalculating root and the
# path to lib
lib_pathname = root.join('lib')
$LOAD_PATH.unshift(lib_pathname.to_s)
#!/usr/bin/env rake
require File.expand_path('../config/application', __FILE__)
require 'metasploit/framework/require'
# @note must be before `Metasploit::Framework::Application.load_tasks`
#
# load rake files like a rails engine
#
# define db rake tasks from activerecord if activerecord is in the bundle. activerecord could be not in the bundle if
# the user installs with `bundle install --without db`
Metasploit::Framework::Require.optionally_active_record_railtie
rakefile_glob = root.join('lib', 'tasks', '**', '*.rake').to_path
Dir.glob(rakefile_glob) do |rakefile|
# Skip database tasks, will load them later if MDM is present
next if rakefile =~ /database\.rake$/
load rakefile
end
print_without = false
begin
require 'rspec/core/rake_task'
rescue LoadError
puts "rspec not in bundle, so can't set up spec tasks. " \
"To run specs ensure to install the development and test groups."
print_without = true
else
RSpec::Core::RakeTask.new(:spec => 'db:test:prepare')
task :default => :spec
end
# Require yard before loading metasploit_data_models rake tasks as the yard tasks won't be defined if
# YARD is not defined when yard.rake is loaded.
begin
require 'yard'
rescue LoadError
puts "yard not in bundle, so can't set up yard tasks. " \
"To generate documentation ensure to install the development group."
print_without = true
end
begin
require 'metasploit_data_models'
rescue LoadError
puts "metasploit_data_models not in bundle, so can't set up db tasks. " \
"To run database tasks, ensure to install the db bundler group."
print_without = true
else
load 'lib/tasks/database.rake'
metasploit_data_models_task_glob = MetasploitDataModels.root.join(
'lib',
'tasks',
'**',
'*.rake'
).to_s
# include tasks from metasplioit_data_models, such as `rake yard`.
# metasploit-framework specific yard options are in .yardopts
Dir.glob(metasploit_data_models_task_glob) do |path|
load path
end
end
if print_without
puts "Bundle currently installed " \
"'--without #{Bundler.settings.without.join(' ')}'."
puts "To clear the without option do `bundle install --without ''` " \
"(the --without flag with an empty string) or " \
"`rm -rf .bundle` to remove the .bundle/config manually and " \
"then `bundle install`"
end
Metasploit::Framework::Application.load_tasks

View File

@ -0,0 +1,23 @@
# Adds associations to `Metasploit::Credential::Core` which are inverses of association on models under
# {BruteForce::Reuse}.
require 'metasploit/framework/credential'
module Metasploit::Credential::Core::ToCredential
extend ActiveSupport::Concern
included do
def to_credential
Metasploit::Framework::Credential.new(
public: public.try(:username),
private: private.try(:data),
private_type: private.try(:type).try(:demodulize).try(:underscore).try(:to_sym),
realm: realm.try(:value),
realm_key: realm.try(:key),
parent: self
)
end
end
end

View File

@ -0,0 +1,2 @@
require 'metasploit/framework/file_path_validator'
require 'metasploit/framework/executable_path_validator'

View File

@ -0,0 +1,16 @@
module Metasploit
module Framework
# This is a ActiveModel custom validator that assumes the attribute
# is supposed to be the path to a regular file. It checks whether the
# file exists and whether or not it is an executable file.
class ExecutablePathValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless ::File.executable? value
record.errors[attribute] << (options[:message] || "is not a valid path to an executable file")
end
end
end
end
end

View File

@ -0,0 +1,16 @@
module Metasploit
module Framework
# This is a ActiveModel custom validator that assumes the attribute
# is supposed to be the path to a regular file. It checks whether the
# file exists and whether or not it is a regular file.
class FilePathValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless ::File.file? value
record.errors[attribute] << (options[:message] || "is not a valid path to a regular file")
end
end
end
end
end

42
config/application.rb Normal file
View File

@ -0,0 +1,42 @@
require 'rails'
require File.expand_path('../boot', __FILE__)
all_environments = [
:development,
:production,
:test
]
Bundler.require(
*Rails.groups(
db: all_environments,
pcap: all_environments
)
)
#
# Railties
#
# For compatibility with jquery-rails (and other engines that need action_view) in pro
require 'action_view/railtie'
#
# Project
#
require 'metasploit/framework/common_engine'
require 'metasploit/framework/database'
module Metasploit
module Framework
class Application < Rails::Application
include Metasploit::Framework::CommonEngine
config.paths['config/database'] = [Metasploit::Framework::Database.configurations_pathname.try(:to_path)]
end
end
end
# Silence warnings about this defaulting to true
I18n.enforce_available_locales = true

39
config/boot.rb Normal file
View File

@ -0,0 +1,39 @@
require 'pathname'
require 'rubygems'
GEMFILE_EXTENSIONS = [
'.local',
''
]
msfenv_real_pathname = Pathname.new(__FILE__).realpath
root = msfenv_real_pathname.parent.parent
unless ENV['BUNDLE_GEMFILE']
require 'pathname'
GEMFILE_EXTENSIONS.each do |extension|
extension_pathname = root.join("Gemfile#{extension}")
if extension_pathname.readable?
ENV['BUNDLE_GEMFILE'] = extension_pathname.to_path
break
end
end
end
begin
require 'bundler'
rescue LoadError
$stderr.puts "[*] Metasploit requires the Bundler gem to be installed"
$stderr.puts " $ gem install bundler"
exit(0)
end
Bundler.setup
lib_path = root.join('lib').to_path
unless $LOAD_PATH.include? lib_path
$LOAD_PATH.unshift lib_path
end

8
config/cucumber.yml Normal file
View File

@ -0,0 +1,8 @@
<%
rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip"
%>
default: <%= std_opts %> features
wip: --tags @wip:3 --wip features
rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip

5
config/environment.rb Normal file
View File

@ -0,0 +1,5 @@
# Load the rails application
require File.expand_path('../application', __FILE__)
# Initialize the rails application
Metasploit::Framework::Application.initialize!

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20130717150737) do
ActiveRecord::Schema.define(:version => 20140801150537) do
create_table "api_keys", :force => true do |t|
t.text "token"
@ -28,6 +28,16 @@ ActiveRecord::Schema.define(:version => 20130717150737) do
t.datetime "updated_at"
end
create_table "credential_cores_tasks", :id => false, :force => true do |t|
t.integer "core_id"
t.integer "task_id"
end
create_table "credential_logins_tasks", :id => false, :force => true do |t|
t.integer "login_id"
t.integer "task_id"
end
create_table "creds", :force => true do |t|
t.integer "service_id", :null => false
t.datetime "created_at", :null => false
@ -167,6 +177,113 @@ ActiveRecord::Schema.define(:version => 20130717150737) do
t.binary "prefs"
end
create_table "metasploit_credential_cores", :force => true do |t|
t.integer "origin_id", :null => false
t.string "origin_type", :null => false
t.integer "private_id"
t.integer "public_id"
t.integer "realm_id"
t.integer "workspace_id", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "logins_count", :default => 0
end
add_index "metasploit_credential_cores", ["origin_type", "origin_id"], :name => "index_metasploit_credential_cores_on_origin_type_and_origin_id"
add_index "metasploit_credential_cores", ["private_id"], :name => "index_metasploit_credential_cores_on_private_id"
add_index "metasploit_credential_cores", ["public_id"], :name => "index_metasploit_credential_cores_on_public_id"
add_index "metasploit_credential_cores", ["realm_id"], :name => "index_metasploit_credential_cores_on_realm_id"
add_index "metasploit_credential_cores", ["workspace_id", "private_id"], :name => "unique_private_metasploit_credential_cores", :unique => true
add_index "metasploit_credential_cores", ["workspace_id", "public_id", "private_id"], :name => "unique_realmless_metasploit_credential_cores", :unique => true
add_index "metasploit_credential_cores", ["workspace_id", "public_id"], :name => "unique_public_metasploit_credential_cores", :unique => true
add_index "metasploit_credential_cores", ["workspace_id", "realm_id", "private_id"], :name => "unique_publicless_metasploit_credential_cores", :unique => true
add_index "metasploit_credential_cores", ["workspace_id", "realm_id", "public_id", "private_id"], :name => "unique_complete_metasploit_credential_cores", :unique => true
add_index "metasploit_credential_cores", ["workspace_id", "realm_id", "public_id"], :name => "unique_privateless_metasploit_credential_cores", :unique => true
add_index "metasploit_credential_cores", ["workspace_id"], :name => "index_metasploit_credential_cores_on_workspace_id"
create_table "metasploit_credential_logins", :force => true do |t|
t.integer "core_id", :null => false
t.integer "service_id", :null => false
t.string "access_level"
t.string "status", :null => false
t.datetime "last_attempted_at"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "metasploit_credential_logins", ["core_id", "service_id"], :name => "index_metasploit_credential_logins_on_core_id_and_service_id", :unique => true
add_index "metasploit_credential_logins", ["service_id", "core_id"], :name => "index_metasploit_credential_logins_on_service_id_and_core_id", :unique => true
create_table "metasploit_credential_origin_cracked_passwords", :force => true do |t|
t.integer "metasploit_credential_core_id", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "metasploit_credential_origin_cracked_passwords", ["metasploit_credential_core_id"], :name => "originating_credential_cores"
create_table "metasploit_credential_origin_imports", :force => true do |t|
t.text "filename", :null => false
t.integer "task_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "metasploit_credential_origin_imports", ["task_id"], :name => "index_metasploit_credential_origin_imports_on_task_id"
create_table "metasploit_credential_origin_manuals", :force => true do |t|
t.integer "user_id", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "metasploit_credential_origin_manuals", ["user_id"], :name => "index_metasploit_credential_origin_manuals_on_user_id"
create_table "metasploit_credential_origin_services", :force => true do |t|
t.integer "service_id", :null => false
t.text "module_full_name", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "metasploit_credential_origin_services", ["service_id", "module_full_name"], :name => "unique_metasploit_credential_origin_services", :unique => true
create_table "metasploit_credential_origin_sessions", :force => true do |t|
t.text "post_reference_name", :null => false
t.integer "session_id", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "metasploit_credential_origin_sessions", ["session_id", "post_reference_name"], :name => "unique_metasploit_credential_origin_sessions", :unique => true
create_table "metasploit_credential_privates", :force => true do |t|
t.string "type", :null => false
t.text "data", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "jtr_format"
end
add_index "metasploit_credential_privates", ["type", "data"], :name => "index_metasploit_credential_privates_on_type_and_data", :unique => true
create_table "metasploit_credential_publics", :force => true do |t|
t.string "username", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "metasploit_credential_publics", ["username"], :name => "index_metasploit_credential_publics_on_username", :unique => true
create_table "metasploit_credential_realms", :force => true do |t|
t.string "key", :null => false
t.string "value", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "metasploit_credential_realms", ["key", "value"], :name => "index_metasploit_credential_realms_on_key_and_value", :unique => true
create_table "mod_refs", :force => true do |t|
t.string "module", :limit => 1024
t.string "mtype", :limit => 128

View File

@ -7,7 +7,7 @@ CLASSES = Exploit.java
all: $(CLASSES:.java=.class)
install:
mv *.class ../../../../data/exploits/CVE-2013-3465/
mv *.class ../../../../data/exploits/CVE-2013-2465/
clean:
rm -rf *.class

View File

@ -0,0 +1,158 @@
Feature: `msfconsole` `database.yml`
In order to connect to the database in `msfconsole`
As a user calling `msfconsole` from a terminal
I want to be able to set the path of the `database.yml` in one of 4 locations (in order of precedence):
1. An explicit argument to the `-y` flag to `msfconsole`
2. The MSF_DATABASE_CONFIG environment variable
3. The user's `~/.msf4/database.yml`
4. `config/database.yml` in the metasploit-framework checkout location.
Scenario: With all 4 locations, --yaml wins
Given a file named "command_line.yml" with:
"""
test:
adapter: postgresql
database: command_line_metasploit_framework_test
username: command_line_metasploit_framework_test
"""
And a file named "msf_database_config.yml" with:
"""
test:
adapter: postgresql
database: environment_metasploit_framework_test
username: environment_metasploit_framework_test
"""
And I set the environment variables to:
| variable | value |
| MSF_DATABASE_CONFIG | msf_database_config.yml |
And a directory named "home"
And I cd to "home"
And a mocked home directory
And a directory named ".msf4"
And I cd to ".msf4"
And a file named "database.yml" with:
"""
test:
adapter: postgresql
database: user_metasploit_framework_test
username: user_metasploit_framework_test
"""
And I cd to "../.."
And the project "database.yml" exists with:
"""
test:
adapter: postgresql
database: project_metasploit_framework_test
username: project_metasploit_framework_test
"""
When I run `msfconsole --environment test --yaml command_line.yml` interactively
And I wait for stdout to contain "Free Metasploit Pro trial: http://r-7.co/trymsp"
And I type "exit"
Then the output should contain "command_line_metasploit_framework_test"
Scenario: Without --yaml, MSF_DATABASE_CONFIG wins
Given a file named "msf_database_config.yml" with:
"""
test:
adapter: postgresql
database: environment_metasploit_framework_test
username: environment_metasploit_framework_test
"""
And I set the environment variables to:
| variable | value |
| MSF_DATABASE_CONFIG | msf_database_config.yml |
And a directory named "home"
And I cd to "home"
And a mocked home directory
And a directory named ".msf4"
And I cd to ".msf4"
And a file named "database.yml" with:
"""
test:
adapter: postgresql
database: user_metasploit_framework_test
username: user_metasploit_framework_test
"""
And I cd to "../.."
And the project "database.yml" exists with:
"""
test:
adapter: postgresql
database: project_metasploit_framework_test
username: project_metasploit_framework_test
"""
When I run `msfconsole --environment test` interactively
And I wait for stdout to contain "Free Metasploit Pro trial: http://r-7.co/trymsp"
And I type "exit"
Then the output should contain "environment_metasploit_framework_test"
Scenario: Without --yaml or MSF_DATABASE_CONFIG, ~/.msf4/database.yml wins
Given I unset the environment variables:
| variable |
| MSF_DATABASE_CONFIG |
And a directory named "home"
And I cd to "home"
And a mocked home directory
And a directory named ".msf4"
And I cd to ".msf4"
And a file named "database.yml" with:
"""
test:
adapter: postgresql
database: user_metasploit_framework_test
username: user_metasploit_framework_test
"""
And I cd to "../.."
And the project "database.yml" exists with:
"""
test:
adapter: postgresql
database: project_metasploit_framework_test
username: project_metasploit_framework_test
"""
When I run `msfconsole --environment test` interactively
And I wait for stdout to contain "Free Metasploit Pro trial: http://r-7.co/trymsp"
And I type "exit"
Then the output should contain "user_metasploit_framework_test"
Scenario: Without --yaml, MSF_DATABASE_CONFIG or ~/.msf4/database.yml, project "database.yml" wins
Given I unset the environment variables:
| variable |
| MSF_DATABASE_CONFIG |
And a directory named "home"
And I cd to "home"
And a mocked home directory
And I cd to "../.."
And the project "database.yml" exists with:
"""
test:
adapter: postgresql
database: project_metasploit_framework_test
username: project_metasploit_framework_test
"""
When I run `msfconsole --environment test` interactively
And I wait for stdout to contain "Free Metasploit Pro trial: http://r-7.co/trymsp"
And I type "exit"
Then the output should contain "project_metasploit_framework_test"
Scenario: Without --yaml, MSF_DATABASE_CONFIG, ~/.msf4/database.yml, or project "database.yml", no database connection
Given I unset the environment variables:
| variable |
| MSF_DATABASE_CONFIG |
And a directory named "home"
And I cd to "home"
And a mocked home directory
And I cd to "../.."
And the project "database.yml" does not exist
When I run `msfconsole --environment test` interactively
And I wait for stdout to contain "Free Metasploit Pro trial: http://r-7.co/trymsp"
And I type "db_status"
And I type "exit"
Then the output should not contain "command_line_metasploit_framework_test"
And the output should not contain "environment_metasploit_framework_test"
And the output should not contain "user_metasploit_framework_test"
And the output should not contain "project_metasploit_framework_test"
And the output should contain "[*] postgresql selected, no connection"

View File

@ -0,0 +1,20 @@
Given /^I unset the environment variables:$/ do |table|
table.hashes.each do |row|
variable = row['variable'].to_s.upcase
# @todo add extension to Announcer
announcer.instance_eval do
if @options[:env]
print "$ unset #{variable}"
end
end
current_value = ENV.delete(variable)
# if original_env already has the key, then the true original was already recorded from a previous unset or set,
# so don't record the current value as it will cause ENV not to be restored after the Scenario.
unless original_env.key? variable
original_env[variable] = current_value
end
end
end

View File

@ -0,0 +1,14 @@
require 'metasploit/framework/database/cucumber'
Given /^the project "database.yml" does not exist$/ do
Metasploit::Framework::Database::Cucumber.backup_project_configurations
end
Given /^the project "database.yml" exists with:$/ do |file_content|
Metasploit::Framework::Database::Cucumber.backup_project_configurations
write_file(Metasploit::Framework::Database::Cucumber.project_configurations_path, file_content)
end
After do
Metasploit::Framework::Database::Cucumber.restore_project_configurations
end

26
features/support/bin/stty Executable file
View File

@ -0,0 +1,26 @@
#!/usr/bin/env ruby
case ARGV[0]
when 'size'
puts "30 134"
when '-a'
puts <<EOS
speed 38400 baud; 30 rows; 134 columns;
lflags: icanon isig iexten echo echoe echok echoke -echonl echoctl
-echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo
-extproc
iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8
-ignbrk brkint -inpck -ignpar -parmrk
oflags: opost onlcr -oxtabs -onocr -onlret
cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow
-dtrflow -mdmbuf
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;
min = 1; quit = ^\; reprint = ^R; start = ^Q; status = ^T;
stop = ^S; susp = ^Z; time = 0; werase = ^W;
EOS
when '-g'
puts "gfmt1:cflag=4b00:iflag=6b02:lflag=200005cf:oflag=3:discard=f:dsusp=19:eof=4:eol=ff:eol2=ff:erase=7f:intr=3:kill=15:lnext=16:min=1:quit=1c:reprint=12:start=11:status=14:stop=13:susp=1a:time=0:werase=17:ispeed=38400:ospeed=38400"
end
exit 0

31
features/support/env.rb Normal file
View File

@ -0,0 +1,31 @@
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
# It is recommended to regenerate this file in the future when you upgrade to a
# newer version of cucumber-rails. Consider adding your own code to a new file
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
# files.
require 'cucumber/rails'
require 'aruba/cucumber'
# Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In
# order to ease the transition to Capybara we set the default here. If you'd
# prefer to use XPath just remove this line and adjust any selectors in your
# steps to use the XPath syntax.
Capybara.default_selector = :css
# By default, any exception happening in your Rails application will bubble up
# to Cucumber so that your scenario will fail. This is a different from how
# your application behaves in the production environment, where an error page will
# be rendered instead.
#
# Sometimes we want to override this default behaviour and allow Rails to rescue
# exceptions and display an error page (just like when the app is running in production).
# Typical scenarios where you want to do this is when you test your error pages.
# There are two ways to allow Rails to rescue exceptions:
#
# 1) Tag your scenario (or feature) with @allow-rescue
#
# 2) Set the value below to true. Beware that doing this globally is not
# recommended as it will mask a lot of errors for you!
#
ActionController::Base.allow_rescue = false

View File

@ -0,0 +1,4 @@
Before do
set_env('RAILS_ENV', 'test')
@aruba_timeout_seconds = 3.minutes
end

11
features/support/stty.rb Normal file
View File

@ -0,0 +1,11 @@
require 'pathname'
support = Pathname.new(__FILE__).realpath.parent
paths = [
# adds support/bin at the front of the path so that the support/bin/stty script will be used to fake system stty
# output.
support.join('bin').to_path,
ENV['PATH']
]
ENV['PATH'] = paths.join(File::PATH_SEPARATOR)

View File

@ -378,6 +378,10 @@ module Kernel #:nodoc:all
# This method handles the loading of FASTLIB archives
#
def fastlib_require(name)
if name.respond_to? :to_path
name = name.to_path
end
name = name + ".rb" if not name =~ /\.rb$/
return false if fastlib_already_loaded?(name)
return false if fastlib_already_tried?(name)

View File

@ -1,3 +1,29 @@
#
# Gems
#
# gems must load explicitly any gem declared in gemspec
# @see https://github.com/bundler/bundler/issues/2018#issuecomment-6819359
#
require 'active_support'
require 'bcrypt'
require 'json'
require 'msgpack'
require 'metasploit/model'
require 'nokogiri'
require 'packetfu'
# railties has not autorequire defined
# rkelly-remix is a fork of rkelly, so it's autorequire is 'rkelly' and not 'rkelly-remix'
require 'rkelly'
require 'robots'
require 'zip'
#
# Project
#
require 'msf/core'
# Top-level namespace that is shared between {Metasploit::Framework
# metasploit-framework} and pro, which uses Metasploit::Pro.
module Metasploit
@ -5,30 +31,6 @@ module Metasploit
# works in compatible manner with activerecord's rake tasks and other
# railties.
module Framework
# Returns the environment for {Metasploit::Framework}. Checks
# `METASPLOIT_FRAMEWORK_ENV` environment variable for value. Defaults to
# `'development'` if `METASPLOIT_FRAMEWORK_ENV` is not set in the
# environment variables.
#
# {env} is a ActiveSupport::StringInquirer like `Rails.env` so it can be
# queried for its value.
#
# @example check if environment is development
# if Metasploit::Framework.env.development?
# # runs only when in development
# end
#
# @return [ActiveSupport::StringInquirer] the environment name
def self.env
unless instance_variable_defined? :@env
name = ENV['METASPLOIT_FRAMEWORK_ENV']
name ||= 'development'
@env = ActiveSupport::StringInquirer.new(name)
end
@env
end
# Returns the root of the metasploit-framework project. Use in place of
# `Rails.root`.
#
@ -42,4 +44,4 @@ module Metasploit
@root
end
end
end
end

View File

@ -0,0 +1,323 @@
# -*- coding: binary -*-
require 'msf/core'
require 'msf/core/exploit/tcp'
module Metasploit
module Framework
module AFP
module Client
def next_id
@request_id ||= -1
@request_id += 1
@request_id
end
def get_info
packet = "\00" # Flag: Request
packet << "\x03" # Command: FPGetSrvrInfo
packet << [next_id].pack('n') # requestID
packet << "\x00\x00\x00\x00" # Data offset
packet << "\x00\x00\x00\x00" # Length
packet << "\x00\x00\x00\x00" # Reserved
sock.put(packet)
response = sock.timed_read(1024)
return parse_info_response(response)
end
def open_session
packet = "\00" # Flag: Request
packet << "\x04" # Command: DSIOpenSession
packet << [next_id].pack('n') # requestID
packet << "\x00\x00\x00\x00" # Data offset
packet << "\x00\x00\x00\x06" # Length
packet << "\x00\x00\x00\x00" # Reserved
packet << "\x01" # Attention Quantum
packet << "\x04" # Length
packet << "\x00\x00\x04\x00" # 1024
sock.put(packet)
response = sock.timed_read(1024)
return parse_open_session_response(response)
end
def login(user, pass)
if user == ''
return no_user_authent_login
end
p = OpenSSL::BN.new("BA2873DFB06057D43F2024744CEEE75B", 16)
g = OpenSSL::BN.new("7", 10)
ra = OpenSSL::BN.new('86F6D3C0B0D63E4B11F113A2F9F19E3BBBF803F28D30087A1450536BE979FD42', 16)
ma = g.mod_exp(ra, p)
padded_user = (user.length + 1) % 2 != 0 ? user + "\x00" : user
bin_user = [padded_user.length, padded_user].pack("Ca*")
length = 18 + bin_user.length + ma.to_s(2).length
packet = "\00" # Flag: Request
packet << "\x02" # Command: DSICommand
packet << [next_id].pack('n') # requestID
packet << "\x00\x00\x00\x00" # Data offset
packet << [length].pack('N') # Length (42)
packet << "\x00\x00\x00\x00" # Reserved
packet << "\x12" # AFPCommand: FPLogin (18)
packet << "\x06\x41\x46\x50\x33\x2e\x31" # AFPVersion: AFP3.1
packet << "\x09\x44\x48\x43\x41\x53\x54\x31\x32\x38" #UAM: DHCAST128
packet << bin_user # username
packet << ma.to_s(2) # random number
sock.put(packet)
begin
response = sock.timed_read(1024, self.login_timeout)
rescue Timeout::Error
#vprint_error("AFP #{rhost}:#{rport} Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)")
return :connection_error
end
flags, command, request_id, error_code, length, reserved = parse_header(response)
case error_code
when -5001 #kFPAuthContinue
return parse_login_response_add_send_login_count(response, {:p => p, :g => g, :ra => ra, :ma => ma,
:password => pass, :user => user})
when -5023 #kFPUserNotAuth (User dosen't exists)
#print_status("AFP #{rhost}:#{rport} User #{user} dosen't exists")
return :skip_user
else
return :connection_error
end
end
def close_session
packet = "\00" # Flag: Request
packet << "\x01" # Command: DSICloseSession
packet << [next_id].pack('n') # requestID
packet << "\x00\x00\x00\x00" #Data offset
packet << "\x00\x00\x00\x00" #Length
packet << "\x00\x00\x00\x00" #Reserved
sock.put(packet)
end
def no_user_authent_login
packet = "\00" # Flag: Request
packet << "\x02" # Command: DSICommand
packet << [next_id].pack('n') # requestID
packet << "\x00\x00\x00\x00" # Data offset
packet << "\x00\x00\x00\x18" # Length (24)
packet << "\x00\x00\x00\x00" # Reserved
packet << "\x12" # AFPCommand: FPLogin (18)
packet << "\x06\x41\x46\x50\x33\x2e\x31" #AFP3.1
packet << "\x0f\x4e\x6f\x20\x55\x73\x65\x72\x20\x41\x75\x74\x68\x65\x6e\x74" #UAM: No User Authent
sock.put(packet)
begin
response = sock.timed_read(1024, self.login_timeout)
rescue Timeout::Error
vprint_error("AFP #{rhost}:#{rport} Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)")
return :connection_error
end
flags, command, request_id, error_code, length, reserved = parse_header(response)
if error_code == 0
return :true
else
return false
end
end
def parse_login_response_add_send_login_count(response, data)
dhx_s2civ = 'CJalbert'
dhx_c2civ = 'LWallace'
flags, command, request_id, error_code, length, reserved = parse_header(response)
body = get_body(response, length)
id, mb, enc_data = body.unpack("nH32a*")
mb = OpenSSL::BN.new(mb, 16)
k = mb.mod_exp(data[:ra], data[:p] )
cipher = OpenSSL::Cipher.new('cast5-cbc').decrypt
cipher.key = k.to_s(2)
cipher.iv = dhx_s2civ
cipher.padding = 0
nonce = cipher.update(enc_data)
nonce << cipher.final
nonce = nonce[0..15]
nonce = OpenSSL::BN.new(nonce, 2) + 1
plain_text = nonce.to_s(2) + data[:password].ljust(64, "\x00")
cipher = OpenSSL::Cipher.new('cast5-cbc').encrypt
cipher.key = k.to_s(2)
cipher.iv = dhx_c2civ
auth_response = cipher.update(plain_text)
auth_response << cipher.final
packet = "\00" # Flag: Request
packet << "\x02" # Command: DSICommand
packet << [next_id].pack('n') # requestID
packet << "\x00\x00\x00\x00" # Data offset
packet << [auth_response.length + 2].pack("N") # Length
packet << "\x00\x00\x00\x00" # Reserved
packet << "\x13" # AFPCommand: FPLoginCont (19)
packet << "\x00"
packet << [id].pack('n')
packet << auth_response
sock.put(packet)
begin
response = sock.timed_read(1024, self.login_timeout)
rescue Timeout::Error
vprint_error("AFP #{rhost}:#{rport} Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)")
return :connection_error
end
flags, command, request_id, error_code, length, reserved = parse_header(response)
if error_code == 0
return true
else
return false
end
end
def parse_open_session_response(response)
_, _, _, error_code, _, _ = parse_header(response)
return error_code == 0 ? true : false
end
def parse_info_response(response)
parsed_data = {}
flags, command, request_id, error_code, length, reserved = parse_header(response)
raise "AFP #{rhost}:#{rport} Server response with error" if error_code != 0
body = get_body(response, length)
machine_type_offset, afp_versions_offset, uam_count_offset, icon_offset, server_flags =
body.unpack('nnnnn')
server_name_length = body.unpack('@10C').first
parsed_data[:server_name] = body.unpack("@11A#{server_name_length}").first
pos = 11 + server_name_length
pos += 1 if pos % 2 != 0 #padding
server_signature_offset, network_addresses_offset, directory_names_offset,
utf8_servername_offset = body.unpack("@#{pos}nnnn")
parsed_data[:machine_type] = read_pascal_string(body, machine_type_offset)
parsed_data[:versions] = read_array(body, afp_versions_offset)
parsed_data[:uams] = read_array(body, uam_count_offset)
# skiped icon
parsed_data[:server_flags] = parse_flags(server_flags)
parsed_data[:signature] = body.unpack("@#{server_signature_offset}H32").first
network_addresses = read_array(body, network_addresses_offset, true)
parsed_data[:network_addresses] = parse_network_addresses(network_addresses)
# skiped directory names
#Error catching for offset issues on this field. Need better error ahndling all through here
begin
parsed_data[:utf8_server_name] = read_utf8_pascal_string(body, utf8_servername_offset)
rescue
parsed_data[:utf8_server_name] = "N/A"
end
return parsed_data
end
def parse_header(packet)
header = packet.unpack('CCnNNN') #ruby 1.8.7 don't support unpacking signed integers in big-endian order
header[3] = packet[4..7].reverse.unpack("l").first
return header
end
def get_body(packet, body_length)
body = packet[16..body_length + 15]
raise "AFP #{rhost}:#{rport} Invalid body length" if body.length != body_length
return body
end
def read_pascal_string(str, offset)
length = str.unpack("@#{offset}C").first
return str.unpack("@#{offset + 1}A#{length}").first
end
def read_utf8_pascal_string(str, offset)
length = str.unpack("@#{offset}n").first
return str[offset + 2..offset + length + 1]
end
def read_array(str, offset, afp_network_address=false)
size = str.unpack("@#{offset}C").first
pos = offset + 1
result = []
size.times do
result << read_pascal_string(str, pos)
pos += str.unpack("@#{pos}C").first
pos += 1 unless afp_network_address
end
return result
end
def parse_network_addresses(network_addresses)
parsed_addreses = []
network_addresses.each do |address|
case address.unpack('C').first
when 0 #Reserved
next
when 1 # Four-byte IP address
parsed_addreses << IPAddr.ntop(address[1..4]).to_s
when 2 # Four-byte IP address followed by a two-byte port number
parsed_addreses << "#{IPAddr.ntop(address[1..4])}:#{address[5..6].unpack("n").first}"
when 3 # DDP address (depricated)
next
when 4 # DNS name (maximum of 254 bytes)
parsed_addreses << address[1..address.length - 1]
when 5 # This functionality is deprecated.
next
when 6 # IPv6 address (16 bytes)
parsed_addreses << "[#{IPAddr.ntop(address[1..16])}]"
when 7 # IPv6 address (16 bytes) followed by a two-byte port number
parsed_addreses << "[#{IPAddr.ntop(address[1..16])}]:#{address[17..18].unpack("n").first}"
else # Something wrong?
raise "Error parsing network addresses"
end
end
return parsed_addreses
end
def parse_flags(flags)
flags = flags.to_s(2)
result = {}
result['Super Client'] = flags[0,1] == '1' ? true : false
result['UUIDs'] = flags[5,1] == '1' ? true : false
result['UTF8 Server Name'] = flags[6,1] == '1' ? true : false
result['Open Directory'] = flags[7,1] == '1' ? true : false
result['Reconnect'] = flags[8,1] == '1' ? true : false
result['Server Notifications'] = flags[9,1] == '1' ? true : false
result['TCP/IP'] = flags[10,1] == '1' ? true : false
result['Server Signature'] = flags[11,1] == '1' ? true : false
result['Server Messages'] = flags[12,1] == '1' ? true : false
result['Password Saving Prohibited'] = flags[13,1] == '1' ? true : false
result['Password Changing'] = flags[14,1] == '1' ? true : false
result['Copy File'] = flags[5,1] == '1' ? true : false
return result
end
end
end
end
end

View File

@ -0,0 +1,7 @@
module Metasploit
module Framework
module API
end
end
end

View File

@ -0,0 +1,16 @@
module Metasploit
module Framework
module API
# @note This is a like. The API version is not semantically version and it's version has actually never changed
# even though API changes have occured. DO NOT base compatibility on this version.
module Version
MAJOR = 1
MINOR = 0
PATCH = 0
end
VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::PATCH}"
GEM_VERSION = Gem::Version.new(VERSION)
end
end
end

View File

@ -0,0 +1,26 @@
#
# Gems
#
# have to be exact so minimum is loaded prior to parsing arguments which could
# influence loading.
require 'active_support/dependencies/autoload'
# @note Must use the nested declaration of the
# {Metasploit::Framework::Command} namespace because commands need to be able
# to be required directly without any other part of metasploit-framework
# besides config/boot so that the commands can parse arguments, setup
# RAILS_ENV, and load config/application.rb correctly.
module Metasploit
module Framework
module Command
# Namespace for commands for metasploit-framework. There are
# corresponding classes in the {Metasploit::Framework::ParsedOptions}
# namespace, which handle for parsing the options for each command.
extend ActiveSupport::Autoload
autoload :Base
autoload :Console
end
end
end

View File

@ -0,0 +1,108 @@
#
# Gems
#
require 'active_support/core_ext/module/introspection'
#
# Project
#
require 'metasploit/framework/command'
require 'metasploit/framework/parsed_options'
require 'metasploit/framework/require'
# Based on pattern used for lib/rails/commands in the railties gem.
class Metasploit::Framework::Command::Base
#
# Attributes
#
# @!attribute [r] application
# The Rails application for metasploit-framework.
#
# @return [Metasploit::Framework::Application]
attr_reader :application
# @!attribute [r] parsed_options
# The parsed options from the command line.
#
# @return (see parsed_options)
attr_reader :parsed_options
#
# Class Methods
#
# @note {require_environment!} should be called to load
# `config/application.rb` to so that the RAILS_ENV can be set from the
# command line options in `ARGV` prior to `Rails.env` being set.
# @note After returning, `Rails.application` will be defined and configured.
#
# Parses `ARGV` for command line arguments to configure the
# `Rails.application`.
#
# @return (see parsed_options)
def self.require_environment!
parsed_options = self.parsed_options
# RAILS_ENV must be set before requiring 'config/application.rb'
parsed_options.environment!
ARGV.replace(parsed_options.positional)
# allow other Rails::Applications to use this command
if !defined?(Rails) || Rails.application.nil?
# @see https://github.com/rails/rails/blob/v3.2.17/railties/lib/rails/commands.rb#L39-L40
require Pathname.new(__FILE__).parent.parent.parent.parent.parent.join('config', 'application')
end
# have to configure before requiring environment because
# config/environment.rb calls initialize! and the initializers will use
# the configuration from the parsed options.
parsed_options.configure(Rails.application)
Rails.application.require_environment!
parsed_options
end
def self.parsed_options
parsed_options_class.new
end
def self.parsed_options_class
@parsed_options_class ||= parsed_options_class_name.constantize
end
def self.parsed_options_class_name
@parsed_options_class_name ||= "#{parent.parent}::ParsedOptions::#{name.demodulize}"
end
def self.start
parsed_options = require_environment!
new(application: Rails.application, parsed_options: parsed_options).start
end
#
# Instance Methods
#
# @param attributes [Hash{Symbol => ActiveSupport::OrderedOptions,Rails::Application}]
# @option attributes [Rails::Application] :application
# @option attributes [ActiveSupport::OrderedOptions] :parsed_options
# @raise [KeyError] if :application is not given
# @raise [KeyError] if :parsed_options is not given
def initialize(attributes={})
@application = attributes.fetch(:application)
@parsed_options = attributes.fetch(:parsed_options)
end
# @abstract Use {#application} to start this command.
#
# Starts this command.
#
# @return [void]
# @raise [NotImplementedError]
def start
raise NotImplementedError
end
end

View File

@ -0,0 +1,64 @@
#
# Project
#
require 'metasploit/framework/command'
require 'metasploit/framework/command/base'
# Based on pattern used for lib/rails/commands in the railties gem.
class Metasploit::Framework::Command::Console < Metasploit::Framework::Command::Base
def start
case parsed_options.options.subcommand
when :version
$stderr.puts "Framework Version: #{Metasploit::Framework::VERSION}"
else
driver.run
end
end
private
# The console UI driver.
#
# @return [Msf::Ui::Console::Driver]
def driver
unless @driver
# require here so minimum loading is done before {start} is called.
require 'msf/ui'
@driver = Msf::Ui::Console::Driver.new(
Msf::Ui::Console::Driver::DefaultPrompt,
Msf::Ui::Console::Driver::DefaultPromptChar,
driver_options
)
end
@driver
end
def driver_options
unless @driver_options
options = parsed_options.options
driver_options = {}
driver_options['Config'] = options.framework.config
driver_options['ConfirmExit'] = options.console.confirm_exit
driver_options['DatabaseEnv'] = options.environment
driver_options['DatabaseMigrationPaths'] = options.database.migrations_paths
driver_options['DatabaseYAML'] = options.database.config
driver_options['Defanged'] = options.console.defanged
driver_options['DisableBanner'] = options.console.quiet
driver_options['DisableDatabase'] = options.database.disable
driver_options['LocalOutput'] = options.console.local_output
driver_options['ModulePath'] = options.modules.path
driver_options['Plugins'] = options.console.plugins
driver_options['RealReadline'] = options.console.real_readline
driver_options['Resource'] = options.console.resources
driver_options['XCommands'] = options.console.commands
@driver_options = driver_options
end
@driver_options
end
end

View File

@ -0,0 +1,76 @@
#
# Standard Library
#
require 'fileutils'
# `Rails::Engine` behavior common to both {Metasploit::Framework::Application} and {Metasploit::Framework::Engine}.
module Metasploit::Framework::CommonEngine
extend ActiveSupport::Concern
included do
#
# config
#
# Force binary encoding to remove necessity to set external and internal encoding when construct Strings from
# from files. Socket#read always returns a String in ASCII-8bit encoding
#
# @see http://rubydoc.info/stdlib/core/IO:read
config.before_initialize do
encoding = 'binary'
Encoding.default_external = encoding
Encoding.default_internal = encoding
end
config.root = Msf::Config::install_root
config.paths.add 'data/meterpreter', glob: '**/ext_*'
config.paths.add 'modules'
config.active_support.deprecation = :notify
#
# `initializer`s
#
initializer 'metasploit_framework.merge_meterpreter_extensions' do
Rails.application.railties.engines.each do |engine|
merge_meterpreter_extensions(engine)
end
# The Rails.application itself could have paths['data/meterpreter'], but will not be part of
# Rails.application.railties.engines because only direct subclasses of `Rails::Engine` are returned.
merge_meterpreter_extensions(Rails.application)
end
end
#
# Instance Methods
#
private
# Merges the meterpreter extensions from `engine`'s `paths['data/meterpreter]`.
#
# @param engine [Rails::Engine] a Rails engine or application that has meterpreter extensions
# @return [void]
# @todo Make metasploit-framework look for meterpreter extension in paths['data/meterpreter'] from the engine instead of copying them.
def merge_meterpreter_extensions(engine)
data_meterpreter_paths = engine.paths['data/meterpreter']
# may be `nil` since 'data/meterpreter' is not part of the core Rails::Engine paths set.
if data_meterpreter_paths
source_paths = data_meterpreter_paths.existent
destination_directory = root.join('data', 'meterpreter').to_path
source_paths.each do |source_path|
basename = File.basename(source_path)
destination_path = File.join(destination_directory, basename)
unless destination_path == source_path
FileUtils.copy(source_path, destination_directory)
end
end
end
end
end

View File

@ -0,0 +1,74 @@
require 'metasploit/framework/credential'
module Metasploit
module Framework
# 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}
# 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
# @!attribute pass_file
# Path to a file containing passwords, one per line
# @return [String]
attr_accessor :pass_file
# @!attribute password
# @return [String]
attr_accessor :password
# @!attribute prepended_creds
# List of credentials to be tried before any others
#
# @see #prepend_cred
# @return [Array<Credential>]
attr_accessor :prepended_creds
# @option opts [String] :pass_file See {#pass_file}
# @option opts [String] :password See {#password}
# @option opts [Array<Credential>] :prepended_creds ([]) See {#prepended_creds}
def initialize(opts = {})
opts.each do |attribute, value|
public_send("#{attribute}=", value)
end
self.prepended_creds ||= []
end
# Combines all the provided credential sources into a stream of {Credential}
# objects, yielding them one at a time
#
# @yieldparam credential [Metasploit::Framework::Credential]
# @return [void]
def each
begin
if pass_file.present?
pass_fd = File.open(pass_file, 'r:binary')
pass_fd.each_line do |line|
line.chomp!
yield Metasploit::Framework::Credential.new(public: line, paired: false)
end
end
if password.present?
yield Metasploit::Framework::Credential.new(public: password, paired: false)
end
ensure
pass_fd.close if pass_fd && !pass_fd.closed?
end
end
# Add {Credential credentials} that will be yielded by {#each}
#
# @see prepended_creds
# @param cred [Credential]
# @return [self]
def prepend_cred(cred)
prepended_creds.unshift cred
self
end
end
end
end

View File

@ -0,0 +1,7 @@
module Metasploit
module Framework
module Core
end
end
end

View File

@ -0,0 +1,19 @@
require 'metasploit/framework/version'
module Metasploit
module Framework
# @note This is a lie. The core libraries are not semantically versioned. This is currently just linked to the
# Metasploit::Framework::Version, which is also not semantically versioned.
module Core
module Version
MAJOR = Metasploit::Framework::Version::MAJOR
MINOR = Metasploit::Framework::Version::MINOR
PATCH = Metasploit::Framework::Version::PATCH
PRERELEASE = Metasploit::Framework::Version::PRERELEASE
end
VERSION = Metasploit::Framework::VERSION
GEM_VERSION = Gem::Version.new(Metasploit::Framework::GEM_VERSION)
end
end
end

View File

@ -0,0 +1,119 @@
require 'active_model'
module Metasploit
module Framework
# This class provides an in-memory representation of a conceptual Credential
#
# It contains the public, private, and realm if any.
class Credential
include ActiveModel::Validations
# @!attribute paired
# @return [Boolean] Whether BOTH a public and private are required
# (defaults to `true`)
attr_accessor :paired
# @!attribute parent
# @return [Object] the parent object that had .to_credential called on it to create this object
attr_accessor :parent
# @!attribute private
# The private credential component (e.g. username)
#
# @return [String] if {#paired} is `true` or {#private} is `nil`
# @return [String, nil] if {#paired} is `false` or {#private} is not `nil`.
attr_accessor :private
# @!attribute private_type
# The type of private credential this object represents, e.g. a
# password or an NTLM hash.
#
# @return [String]
attr_accessor :private_type
# @!attribute public
# The public credential component (e.g. password)
#
# @return [String] if {#paired} is `true` or {#public} is `nil`
# @return [String, nil] if {#paired} is `false` or {#public} is not `nil`.
attr_accessor :public
# @!attribute realm
# @return [String,nil] The realm credential component (e.g domain name)
attr_accessor :realm
# @!attribute realm
# @return [String,nil] The type of {#realm}
attr_accessor :realm_key
validates :paired,
inclusion: { in: [true, false] }
# If we have no public we MUST have a private (e.g. SNMP Community String)
validates :private,
exclusion: { in: [nil] },
if: "public.nil? or paired"
# These values should be #demodularized from subclasses of
# `Metasploit::Credential::Private`
validates :private_type,
inclusion: { in: [ :password, :ntlm_hash, :ssh_key ] },
if: "private_type.present?"
# If we have no private we MUST have a public
validates :public,
presence: true,
if: "private.nil? or paired"
# @param attributes [Hash{Symbol => String,nil}]
def initialize(attributes={})
attributes.each do |attribute, value|
public_send("#{attribute}=", value)
end
self.paired = true if self.paired.nil?
end
def inspect
"#<#{self.class} \"#{self}\" >"
end
def to_s
if realm && realm_key == Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
"#{self.realm}\\#{self.public}:#{self.private}"
else
"#{self.public}:#{self.private}#{at_realm}"
end
end
def ==(other)
other.respond_to?(:public) && other.public == self.public &&
other.respond_to?(:private) && other.private == self.private &&
other.respond_to?(:realm) && other.realm == self.realm
end
def to_credential
self.parent = self
self
end
# This method takes all of the attributes of the {Credential} and spits
# them out in a hash compatible with the create_credential calls.
#
# @return [Hash] a hash compatible with #create_credential
def to_h
{
private_data: private,
private_type: private_type,
username: public,
realm_key: realm_key,
realm_value: realm
}
end
private
def at_realm
if self.realm.present?
"@#{self.realm}"
else
""
end
end
end
end
end

View File

@ -0,0 +1,158 @@
require 'metasploit/framework/credential'
class Metasploit::Framework::CredentialCollection
# @!attribute blank_passwords
# Whether each username should be tried with a blank password
# @return [Boolean]
attr_accessor :blank_passwords
# @!attribute pass_file
# Path to a file containing passwords, one per line
# @return [String]
attr_accessor :pass_file
# @!attribute password
# @return [String]
attr_accessor :password
# @!attribute prepended_creds
# List of credentials to be tried before any others
#
# @see #prepend_cred
# @return [Array<Credential>]
attr_accessor :prepended_creds
# @!attribute realm
# @return [String]
attr_accessor :realm
# @!attribute user_as_pass
# Whether each username should be tried as a password for that user
# @return [Boolean]
attr_accessor :user_as_pass
# @!attribute user_file
# Path to a file containing usernames, one per line
# @return [String]
attr_accessor :user_file
# @!attribute username
# @return [String]
attr_accessor :username
# @!attribute userpass_file
# Path to a file containing usernames and passwords separated by a space,
# one pair per line
# @return [String]
attr_accessor :userpass_file
# @option opts [Boolean] :blank_passwords See {#blank_passwords}
# @option opts [String] :pass_file See {#pass_file}
# @option opts [String] :password See {#password}
# @option opts [Array<Credential>] :prepended_creds ([]) See {#prepended_creds}
# @option opts [Boolean] :user_as_pass See {#user_as_pass}
# @option opts [String] :user_file See {#user_file}
# @option opts [String] :username See {#username}
# @option opts [String] :userpass_file See {#userpass_file}
def initialize(opts = {})
opts.each do |attribute, value|
public_send("#{attribute}=", value)
end
self.prepended_creds ||= []
end
# Add {Credential credentials} that will be yielded by {#each}
#
# @see prepended_creds
# @param cred [Credential]
# @return [self]
def prepend_cred(cred)
prepended_creds.unshift cred
self
end
# Combines all the provided credential sources into a stream of {Credential}
# objects, yielding them one at a time
#
# @yieldparam credential [Metasploit::Framework::Credential]
# @return [void]
def each
if pass_file.present?
pass_fd = File.open(pass_file, 'r:binary')
end
prepended_creds.each { |c| yield c }
if username.present?
if password.present?
yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))
end
if user_as_pass
yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)
end
if blank_passwords
yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)
end
if pass_fd
pass_fd.each_line do |pass_from_file|
pass_from_file.chomp!
yield Metasploit::Framework::Credential.new(public: username, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
end
pass_fd.seek(0)
end
end
if user_file.present?
File.open(user_file, 'r:binary') do |user_fd|
user_fd.each_line do |user_from_file|
user_from_file.chomp!
if password
yield Metasploit::Framework::Credential.new(public: user_from_file, private: password, realm: realm, private_type: private_type(password) )
end
if user_as_pass
yield Metasploit::Framework::Credential.new(public: user_from_file, private: user_from_file, realm: realm, private_type: :password)
end
if blank_passwords
yield Metasploit::Framework::Credential.new(public: user_from_file, private: "", realm: realm, private_type: :password)
end
if pass_fd
pass_fd.each_line do |pass_from_file|
pass_from_file.chomp!
yield Metasploit::Framework::Credential.new(public: user_from_file, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
end
pass_fd.seek(0)
end
end
end
end
if userpass_file.present?
File.open(userpass_file, 'r:binary') do |userpass_fd|
userpass_fd.each_line do |line|
user, pass = line.split(" ", 2)
if pass.blank?
pass = ''
else
pass.chomp!
end
yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)
end
end
end
ensure
pass_fd.close if pass_fd && !pass_fd.closed?
end
private
def private_type(private)
if private =~ /[0-9a-f]{32}:[0-9a-f]{32}/
:ntlm_hash
else
:password
end
end
end

View File

@ -1,14 +1,100 @@
require 'metasploit/framework'
require 'msf/base/config'
module Metasploit
module Framework
module Database
def self.configurations
YAML.load_file(configurations_pathname)
#
# CONSTANTS
#
CONFIGURATIONS_PATHNAME_PRECEDENCE = [
:environment_configurations_pathname,
:user_configurations_pathname,
:project_configurations_pathname
]
#
# Module Methods
#
# 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}
# @return [Pathname] if configuration pathname exists.
# @return [nil] if configuration pathname does not exist.
def self.configurations_pathname(options={})
options.assert_valid_keys(:path)
path = options[:path]
if path.present?
pathname = Pathname.new(path)
else
pathname = configurations_pathnames.first
end
if pathname.present? && pathname.exist?
pathname
else
nil
end
end
def self.configurations_pathname
Metasploit::Framework.root.join('config', 'database.yml')
# Return configuration pathnames that exist.
#
# Returns `Pathnames` in order of precedence
#
# 1. {environment_configurations_pathname}
# 2. {user_configurations_pathname}
# 3. {project_configurations_pathname}
#
# @return [Array<Pathname>]
def self.configurations_pathnames
configurations_pathnames = []
CONFIGURATIONS_PATHNAME_PRECEDENCE.each do |configurations_pathname_message|
configurations_pathname = public_send(configurations_pathname_message)
if !configurations_pathname.nil? && configurations_pathname.exist?
configurations_pathnames << configurations_pathname
end
end
configurations_pathnames
end
# Pathname to `database.yml` pointed to by `MSF_DATABASE_CONFIG` environment variable.
#
# @return [Pathname] if `MSF_DATABASE_CONFIG` is not blank.
# @return [nil] otherwise
def self.environment_configurations_pathname
msf_database_config = ENV['MSF_DATABASE_CONFIG']
if msf_database_config.blank?
msf_database_config = nil
else
msf_database_config = Pathname.new(msf_database_config)
end
msf_database_config
end
# Pathname to `database.yml` for the metasploit-framework project in `config/database.yml`.
#
# @return [Pathname]
def self.project_configurations_pathname
root = Pathname.new(__FILE__).realpath.parent.parent.parent.parent
root.join('config', 'database.yml')
end
# Pathname to `database.yml` in the user's config directory.
#
# @return [Pathname] if the user has a `database.yml` in their config directory (`~/.msf4` by default).
# @return [nil] if the user does not have a `database.yml` in their config directory.
def self.user_configurations_pathname
Pathname.new(Msf::Config.get_config_root).join('database.yml')
end
end
end

View File

@ -0,0 +1,36 @@
require 'metasploit/framework/database'
module Metasploit::Framework::Database::Cucumber
def self.project_configurations_path
Rails.root.join('config', 'database.yml').to_path
end
def self.backup_project_configurations
if File.exist?(project_configurations_path)
# assume that the backup file is from a previously aborted run and it contains the real database.yml data, so
# just delete the fake database.yml and the After hook will restore the real database.yml from the backup location
if File.exist?(backup_project_configurations_path)
File.delete(project_configurations_path)
else
# project contains the real database.yml and there was no previous, aborted run.
File.rename(project_configurations_path, backup_project_configurations_path)
end
end
end
def self.backup_project_configurations_path
"#{project_configurations_path}.cucumber.bak"
end
def self.restore_project_configurations
if File.exist?(backup_project_configurations_path)
if File.exist?(project_configurations_path)
# Remove fake, leftover database.yml
File.delete(project_configurations_path)
end
File.rename(backup_project_configurations_path, project_configurations_path)
end
end
end

View File

@ -0,0 +1,19 @@
#
# Gems
#
require 'rails/engine'
#
# Project
#
require 'metasploit/framework/common_engine'
module Metasploit
module Framework
class Engine < Rails::Engine
include Metasploit::Framework::CommonEngine
end
end
end

View File

@ -0,0 +1,276 @@
require 'metasploit/framework/tcp/client'
module Metasploit
module Framework
module Ftp
module Client
include Metasploit::Framework::Tcp::Client
#
# This method establishes an FTP connection to host and port specified by
# the 'rhost' and 'rport' methods. After connecting, the banner
# message is read in and stored in the 'banner' attribute.
#
def connect(global = true)
fd = super(global)
@ftpbuff = '' unless @ftpbuff
# Wait for a banner to arrive...
self.banner = recv_ftp_resp(fd)
# Return the file descriptor to the caller
fd
end
#
# This method handles establishing datasocket for data channel
#
def data_connect(mode = nil, nsock = self.sock)
if mode
res = send_cmd([ 'TYPE' , mode ], true, nsock)
return nil if not res =~ /^200/
end
# force datasocket to renegotiate
self.datasocket.shutdown if self.datasocket != nil
res = send_cmd(['PASV'], true, nsock)
return nil if not res =~ /^227/
# 227 Entering Passive Mode (127,0,0,1,196,5)
if res =~ /\((\d+)\,(\d+),(\d+),(\d+),(\d+),(\d+)/
# convert port to FTP syntax
datahost = "#{$1}.#{$2}.#{$3}.#{$4}"
dataport = ($5.to_i * 256) + $6.to_i
self.datasocket = Rex::Socket::Tcp.create('PeerHost' => datahost, 'PeerPort' => dataport)
end
self.datasocket
end
#
# This method handles disconnecting our data channel
#
def data_disconnect
self.datasocket.shutdown
self.datasocket = nil
end
#
# Connect and login to the remote FTP server using the credentials
# that have been supplied in the exploit options.
#
def connect_login(user,pass,global = true)
ftpsock = connect(global)
if !(user and pass)
return false
end
res = send_user(user, ftpsock)
if (res !~ /^(331|2)/)
return false
end
if (pass)
res = send_pass(pass, ftpsock)
if (res !~ /^2/)
return false
end
end
return true
end
#
# This method logs in as the supplied user by transmitting the FTP
# 'USER <user>' command.
#
def send_user(user, nsock = self.sock)
raw_send("USER #{user}\r\n", nsock)
recv_ftp_resp(nsock)
end
#
# This method completes user authentication by sending the supplied
# password using the FTP 'PASS <pass>' command.
#
def send_pass(pass, nsock = self.sock)
raw_send("PASS #{pass}\r\n", nsock)
recv_ftp_resp(nsock)
end
#
# This method sends a QUIT command.
#
def send_quit(nsock = self.sock)
raw_send("QUIT\r\n", nsock)
recv_ftp_resp(nsock)
end
#
# This method sends one command with zero or more parameters
#
def send_cmd(args, recv = true, nsock = self.sock)
cmd = args.join(" ") + "\r\n"
ret = raw_send(cmd, nsock)
if (recv)
return recv_ftp_resp(nsock)
end
return ret
end
#
# This method transmits the command in args and receives / uploads DATA via data channel
# For commands not needing data, it will fall through to the original send_cmd
#
# For commands that send data only, the return will be the server response.
# For commands returning both data and a server response, an array will be returned.
#
# NOTE: This function always waits for a response from the server.
#
def send_cmd_data(args, data, mode = 'a', nsock = self.sock)
type = nil
# implement some aliases for various commands
if (args[0] =~ /^DIR$/i || args[0] =~ /^LS$/i)
# TODO || args[0] =~ /^MDIR$/i || args[0] =~ /^MLS$/i
args[0] = "LIST"
type = "get"
elsif (args[0] =~ /^GET$/i)
args[0] = "RETR"
type = "get"
elsif (args[0] =~ /^PUT$/i)
args[0] = "STOR"
type = "put"
end
# fall back if it's not a supported data command
if not type
return send_cmd(args, true, nsock)
end
# Set the transfer mode and connect to the remove server
return nil if not data_connect(mode)
# Our pending command should have got a connection now.
res = send_cmd(args, true, nsock)
# make sure could open port
return nil unless res =~ /^(150|125) /
# dispatch to the proper method
if (type == "get")
# failed listings jsut disconnect..
begin
data = self.datasocket.get_once(-1, ftp_timeout)
rescue ::EOFError
data = nil
end
else
sent = self.datasocket.put(data)
end
# close data channel so command channel updates
data_disconnect
# get status of transfer
ret = nil
if (type == "get")
ret = recv_ftp_resp(nsock)
ret = [ ret, data ]
else
ret = recv_ftp_resp(nsock)
end
ret
end
#
# This method transmits a FTP command and waits for a response. If one is
# received, it is returned to the caller.
#
def raw_send_recv(cmd, nsock = self.sock)
nsock.put(cmd)
nsock.get_once(-1, ftp_timeout)
end
#
# This method reads an FTP response based on FTP continuation stuff
#
def recv_ftp_resp(nsock = self.sock)
found_end = false
resp = ""
left = ""
if !@ftpbuff.empty?
left << @ftpbuff
@ftpbuff = ""
end
while true
data = nsock.get_once(-1, ftp_timeout)
if not data
@ftpbuff << resp
@ftpbuff << left
return data
end
got = left + data
left = ""
# handle the end w/o newline case
enlidx = got.rindex(0x0a.chr)
if enlidx != (got.length-1)
if not enlidx
left << got
next
else
left << got.slice!((enlidx+1)..got.length)
end
end
# split into lines
rarr = got.split(/\r?\n/)
rarr.each do |ln|
if not found_end
resp << ln
resp << "\r\n"
if ln.length > 3 and ln[3,1] == ' '
found_end = true
end
else
left << ln
left << "\r\n"
end
end
if found_end
@ftpbuff << left
return resp
end
end
end
#
# This method transmits a FTP command and does not wait for a response
#
def raw_send(cmd, nsock = self.sock)
nsock.put(cmd)
end
def ftp_timeout
raise NotImplementedError
end
protected
#
# This attribute holds the banner that was read in after a successful call
# to connect or connect_login.
#
attr_accessor :banner, :datasocket
end
end
end
end

View File

@ -0,0 +1,272 @@
module Metasploit
module Framework
module JtR
class JohnNotFoundError < StandardError
end
class Cracker
include ActiveModel::Validations
# @!attribute config
# @return [String] The path to an optional config file for John to use
attr_accessor :config
# @!attribute format
# @return [String] The hash format to try
attr_accessor :format
# @!attribute hash_path
# @return [String] The path to the file containing the hashes
attr_accessor :hash_path
# @!attribute incremental
# @return [String] The incremental mode to use
attr_accessor :incremental
# @!attribute john_path
# This attribute allows the user to specify a john binary to use.
# If not supplied, the Cracker will search the PATH for a suitable john binary
# and finally fall back to the pre-compiled versions shipped with Metasploit.
#
# @return [String] The file path to an alternative John binary to use
attr_accessor :john_path
# @!attribute max_runtime
# @return [Fixnum] An optional maximum duration of the cracking attempt in seconds
attr_accessor :max_runtime
# @!attribute pot
# @return [String] The file path to an alternative John pot file to use
attr_accessor :pot
# @!attribute rules
# @return [String] The wordlist mangling rules to use inside John
attr_accessor :rules
# @!attribute wordlist
# @return [String] The file path to the wordlist to use
attr_accessor :wordlist
validates :config, :'Metasploit::Framework::File_path' => true, if: 'config.present?'
validates :hash_path, :'Metasploit::Framework::File_path' => true, if: 'hash_path.present?'
validates :john_path, :'Metasploit::Framework::Executable_path' => true, if: 'john_path.present?'
validates :pot, :'Metasploit::Framework::File_path' => true, if: 'pot.present?'
validates :max_runtime,
numericality: {
only_integer: true,
greater_than_or_equal_to: 0
}, if: 'max_runtime.present?'
validates :wordlist, :'Metasploit::Framework::File_path' => true, if: 'wordlist.present?'
# @param attributes [Hash{Symbol => String,nil}]
def initialize(attributes={})
attributes.each do |attribute, value|
public_send("#{attribute}=", value)
end
end
# This method follows a decision tree to determine the path
# to the John the Ripper binary we should use.
#
# @return [NilClass] if a binary path could not be found
# @return [String] the path to the selected JtR binary
def binary_path
# Always prefer a manually entered path
if john_path && ::File.file?(john_path)
bin_path = john_path
else
# Look in the Environment PATH for the john binary
path = Rex::FileUtils.find_full_path("john") ||
Rex::FileUtils.find_full_path("john.exe")
if path && ::File.file?(path)
bin_path = path
else
# If we can't find john anywhere else, look at our precompiled binaries
bin_path = select_shipped_binary
end
end
raise JohnNotFoundError, 'No suitable John binary was found on the system' if bin_path.blank?
bin_path
end
# This method runs the command from {#crack_command} and yields each line of output.
#
# @yield [String] a line of output from the john command
# @return [void]
def crack
::IO.popen(crack_command, "rb") do |fd|
fd.each_line do |line|
yield line
end
end
end
# This method builds an array for the command to actually run the cracker.
# It builds the command from all of the attributes on the class.
#
# @raise [JohnNotFoundError] if a suitable John binary was never found
# @return [Array] An array set up for {::IO.popen} to use
def crack_command
cmd_string = binary_path
cmd = [ cmd_string, '--session=' + john_session_id, '--nolog' ]
if config.present?
cmd << ( "--config=" + config )
else
cmd << ( "--config=" + john_config_file )
end
if pot.present?
cmd << ( "--pot=" + pot )
else
cmd << ( "--pot=" + john_pot_file)
end
if format.present?
cmd << ( "--format=" + format )
end
if wordlist.present?
cmd << ( "--wordlist=" + wordlist )
end
if incremental.present?
cmd << ( "--incremental=" + incremental )
end
if rules.present?
cmd << ( "--rules=" + rules )
end
if max_runtime.present?
cmd << ( "--max-run-time=" + max_runtime.to_s)
end
cmd << hash_path
end
# This runs the show command in john and yields cracked passwords.
#
# @yield [String] the output lines from the command
# @return [void]
def each_cracked_password
::IO.popen(show_command, "rb") do |fd|
fd.each_line do |line|
yield line
end
end
end
# This method returns the path to a default john.conf file.
#
# @return [String] the path to the default john.conf file
def john_config_file
::File.join( ::Msf::Config.data_directory, "john", "confs", "john.conf" )
end
# This method returns the path to a default john.pot file.
#
# @return [String] the path to the default john.pot file
def john_pot_file
::File.join( ::Msf::Config.config_directory, "john.pot" )
end
# This method is a getter for a random Session ID for John.
# It allows us to dinstiguish between cracking sessions.
#
# @ return [String] the Session ID to use
def john_session_id
@session_id ||= ::Rex::Text.rand_text_alphanumeric(8)
end
# This method builds the command to show the cracked passwords.
#
# @raise [JohnNotFoundError] if a suitable John binary was never found
# @return [Array] An array set up for {::IO.popen} to use
def show_command
cmd_string = binary_path
pot_file = pot || john_pot_file
cmd = [cmd_string, "--show", "--pot=#{pot_file}", "--format=#{format}" ]
if config
cmd << "--config=#{config}"
else
cmd << ( "--config=" + john_config_file )
end
cmd << hash_path
end
private
# This method tries to identify the correct version of the pre-shipped
# JtR binaries to use based on the platform.
#
# @return [NilClass] if the correct binary could not be determined
# @return [String] the path to the selected binary
def select_shipped_binary
cpuinfo_base = ::File.join(Msf::Config.data_directory, "cpuinfo")
run_path = nil
if File.directory?(cpuinfo_base)
data = nil
case ::RUBY_PLATFORM
when /mingw|cygwin|mswin/
fname = "#{cpuinfo_base}/cpuinfo.exe"
if File.exists?(fname) and File.executable?(fname)
data = %x{"#{fname}"} rescue nil
end
case data
when /sse2/
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.win32.sse2", "john.exe")
when /mmx/
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.win32.mmx", "john.exe")
else
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.win32.any", "john.exe")
end
when /x86_64-linux/
fname = "#{cpuinfo_base}/cpuinfo.ia64.bin"
if File.exists? fname
::FileUtils.chmod(0755, fname) rescue nil
data = %x{"#{fname}"} rescue nil
end
case data
when /mmx/
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x64.mmx", "john")
else
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.any", "john")
end
when /i[\d]86-linux/
fname = "#{cpuinfo_base}/cpuinfo.ia32.bin"
if File.exists? fname
::FileUtils.chmod(0755, fname) rescue nil
data = %x{"#{fname}"} rescue nil
end
case data
when /sse2/
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.sse2", "john")
when /mmx/
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.mmx", "john")
else
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.any", "john")
end
end
end
run_path
end
end
end
end
end

View File

@ -0,0 +1,20 @@
module Metasploit
module Framework
module JtR
# This class is the generic Exception raised by a {Wordlist} when
# it fails validation. It rolls up all validation errors into a
# single exception so that all errors can be dealt with at once.
class InvalidWordlist < StandardError
attr_reader :model
def initialize(model)
@model = model
errors = @model.errors.full_messages.join(', ')
super(errors)
end
end
end
end
end

View File

@ -0,0 +1,429 @@
require 'metasploit/framework/jtr/invalid_wordlist'
module Metasploit
module Framework
module JtR
class Wordlist
include ActiveModel::Validations
# A mapping of the mutation substitution rules
MUTATIONS = {
'@' => 'a',
'0' => 'o',
'3' => 'e',
'$' => 's',
'7' => 't',
'1' => 'l',
'5' => 's'
}
# @!attribute appenders
# @return [Array] an array of strings to append to each word
attr_accessor :appenders
# @!attribute custom_wordlist
# @return [String] the path to a custom wordlist file to include
attr_accessor :custom_wordlist
# @!attribute mutate
# @return [TrueClass] if you want each word mutated as it is added
# @return [FalseClass] if you do not want each word mutated
attr_accessor :mutate
# @!attribute prependers
# @return [Array] an array of strings to prepend to each word
attr_accessor :prependers
# @!attribute use_common_root
# @return [TrueClass] if you want to use the common root words wordlist
# @return [FalseClass] if you do not want to use the common root words wordlist
attr_accessor :use_common_root
# @!attribute use_creds
# @return [TrueClass] if you want to seed the wordlist with existing credential data from the database
# @return [FalseClass] if you do not want to seed the wordlist with existing credential data from the database
attr_accessor :use_creds
# @!attribute use_db_info
# @return [TrueClass] if you want to seed the wordlist with looted database names and schemas
# @return [FalseClass] if you do not want to seed the wordlist with looted database names and schemas
attr_accessor :use_db_info
# @!attribute use_default_wordlist
# @return [TrueClass] if you want to use the default wordlist
# @return [FalseClass] if you do not want to use the default wordlist
attr_accessor :use_default_wordlist
# @!attribute use_hostnames
# @return [TrueClass] if you want to seed the wordlist with existing hostnames from the database
# @return [FalseClass] if you do not want to seed the wordlist with existing hostnames from the database
attr_accessor :use_hostnames
# @!attribute workspace
# @return [Mdm::Workspace] the workspace this cracker is for.
attr_accessor :workspace
validates :custom_wordlist, :'Metasploit::Framework::File_path' => true, if: 'custom_wordlist.present?'
validates :mutate,
inclusion: { in: [true, false], message: "must be true or false" }
validates :use_common_root,
inclusion: { in: [true, false], message: "must be true or false" }
validates :use_creds,
inclusion: { in: [true, false], message: "must be true or false" }
validates :use_db_info,
inclusion: { in: [true, false], message: "must be true or false" }
validates :use_default_wordlist,
inclusion: { in: [true, false], message: "must be true or false" }
validates :use_hostnames,
inclusion: { in: [true, false], message: "must be true or false" }
validates :workspace,
presence: true
# @param attributes [Hash{Symbol => String,nil}]
def initialize(attributes={})
attributes.each do |attribute, value|
public_send("#{attribute}=", value)
end
@appenders ||= []
@prependers ||= []
end
# This method takes a word, and appends each word from the appenders list
# and yields the new words.
#
# @yieldparam word [String] the expanded word
# @return [void]
def each_appended_word(word='')
yield word
appenders.each do |suffix|
yield "#{word}#{suffix}"
end
end
# This method checks all the attributes set on the object and calls
# the appropriate enumerators for each option and yields the results back
# up the call-chain.
#
# @yieldparam word [String] the expanded word
# @return [void]
def each_base_word
# Make sure are attributes are all valid first!
valid!
# Yield the expanded form of each line of the custom wordlist if one was given
if custom_wordlist.present?
each_custom_word do |word|
yield word unless word.blank?
end
end
# Yield each word from the common root words list if it was selected
if use_common_root
each_root_word do |word|
yield word unless word.blank?
end
end
# If the user has selected use_creds we yield each password, username, and realm name
# that currently exists in the database.
if use_creds
each_cred_word do |word|
yield word unless word.blank?
end
end
if use_db_info
each_database_word do |word|
yield word unless word.blank?
end
end
if use_default_wordlist
each_default_word do |word|
yield word unless word.blank?
end
end
if use_hostnames
each_hostname_word do |word|
yield word unless word.blank?
end
end
end
# This method searches all saved Credentials in the database
# and yields all passwords, usernames, and realm names it finds.
#
# @yieldparam word [String] the expanded word
# @return [void]
def each_cred_word
# We don't want all Private types here. Only Passwords make sense for inclusion in the wordlist.
Metasploit::Credential::Password.all.each do |password|
yield password.data
end
Metasploit::Credential::Public.all.each do |public|
yield public.username
end
Metasploit::Credential::Realm.all.each do |realm|
yield realm.value
end
end
# This method reads the file provided as custom_wordlist and yields
# the expanded form of each word in the list.
#
# @yieldparam word [String] the expanded word
# @return [void]
def each_custom_word
::File.open(custom_wordlist, "rb") do |fd|
fd.each_line do |line|
expanded_words(line) do |word|
yield word
end
end
end
end
# This method searches the notes in the current workspace
# for DB instance names, database names, table names, and
# column names gathered from live database servers. It yields
# each one that it finds.
#
# @yieldparam word [String] the expanded word
# @return [void]
def each_database_word
# Yield database, table and column names from any looted database schemas
workspace.notes.where('ntype like ?', '%.schema%').each do |note|
expanded_words(note.data['DBName']) do |word|
yield word
end
note.data['Tables'].each do |table|
expanded_words(table['TableName']) do |word|
yield word
end
table['Columns'].each do |column|
expanded_words(column['ColumnName']) do |word|
yield word
end
end
end
end
# Yield any capture MSSQL Instance names
workspace.notes.find(:all, :conditions => ['ntype=?', 'mssql.instancename']).each do |note|
expanded_words(note.data['InstanceName']) do |word|
yield word
end
end
end
# This method yields expanded words taken from the default john
# wordlist that we ship in the data directory.
#
# @yieldparam word [String] the expanded word
# @return [void]
def each_default_word
::File.open(default_wordlist_path, "rb") do |fd|
fd.each_line do |line|
expanded_words(line) do |word|
yield word
end
end
end
end
# This method yields the expanded words out of all the hostnames
# found in the current workspace.
#
# @yieldparam word [String] the expanded word
# @return [void]
def each_hostname_word
workspace.hosts.all.each do |host|
unless host.name.nil?
expanded_words(host.name) do |word|
yield nil
end
end
end
end
# This method checks to see if the user asked for mutations. If mutations
# have been enabled, then it creates all the unique mutations and yields
# each result.
#
# @yieldparam word [String] the expanded word
# @return [void]
def each_mutated_word(word='')
mutants = [ ]
# Run the mutations only if the option is set
if mutate
mutants = mutants + mutate_word(word)
end
mutants << word
mutants.uniq.each do |mutant|
yield mutant
end
end
# This method takes a word, and prepends each word from the prependers list
# and yields the new words.
#
# @yieldparam word [String] the expanded word
# @return [void]
def each_prepended_word(word='')
yield word
prependers.each do |prefix|
yield "#{prefix}#{word}"
end
end
# This method reads the common_roots.txt wordlist
# expands any words in the list and yields them.
#
# @yieldparam word [String] the expanded word
# @return [void]
def each_root_word
::File.open(common_root_words_path, "rb") do |fd|
fd.each_line do |line|
expanded_words(line) do |word|
yield word
end
end
end
end
# This method wraps around all the other enumerators. It processes
# all of the options and yields each word generated by the options
# selected.
#
# @yieldparam word [String] the word to write out to the wordlist file
# @return [void]
def each_word
each_base_word do |base_word|
each_mutated_word(base_word) do |mutant|
each_prepended_word(mutant) do |prepended|
yield prepended
end
each_appended_word(mutant) do |appended|
yield appended
end
end
end
end
# This method takes a string and splits it on non-word characters
# and the underscore. It does this to find likely distinct words
# in the string. It then yields each 'word' found this way.
#
# @param word [String] the string to split apart
# @yieldparam expanded [String] the expanded words
# @return [void]
def expanded_words(word='')
word.split(/[\W_]+/).each do |expanded|
yield expanded
end
end
# This method takes a word and applies various mutation rules to that word
# and returns an array of all the mutated forms.
#
# @param word [String] the word to apply the mutations to
# @return [Array<String>] An array containing all the mutated forms of the word
def mutate_word(word)
results = []
# Iterate through combinations to create each possible mutation
mutation_keys.each do |iteration|
next if iteration.flatten.empty?
intermediate = word.dup
subsititutions = iteration.collect { |key| MUTATIONS[key] }
intermediate.tr!(subsititutions.join, iteration.join)
results << intermediate
end
results.flatten.uniq
end
# A getter for a memoized version fo the mutation keys list
#
# @return [Array<Array>] a 2D array of all mutation combinations
def mutation_keys
@mutation_keys ||= generate_mutation_keys
end
# This method takes all the options provided and streams the generated wordlist out
# to a {Rex::Quickfile} and returns the {Rex::Quickfile}.
#
# @return [Rex::Quickfile] The {Rex::Quickfile} object that the wordlist has been written to
def to_file
valid!
wordlist_file = Rex::Quickfile.new("jtrtmp")
each_word do |word|
wordlist_file.puts word
end
wordlist_file
end
# Raise an exception if the attributes are not valid.
#
# @raise [Invalid] if the attributes are not valid on this scanner
# @return [void]
def valid!
unless valid?
raise Metasploit::Framework::JtR::InvalidWordlist.new(self)
end
nil
end
private
# This method returns the path to the common_roots.txt wordlist
#
# @return [String] the file path to the common_roots.txt file
def common_root_words_path
::File.join(Msf::Config.data_directory, 'john', 'wordlists', 'common_roots.txt')
end
# This method returns the path to the passwords.lst wordlist
#
# @return [String] the file path to the passwords.lst file
def default_wordlist_path
::File.join(Msf::Config.data_directory, 'john', 'wordlists', 'password.lst')
end
def generate_mutation_keys
iterations = MUTATIONS.keys.dup
# Find PowerSet of all possible mutation combinations
iterations.inject([[]]) do |accumulator,mutation_key|
power_set = []
accumulator.each do |i|
power_set << i
power_set << i+[mutation_key]
end
power_set
end
end
end
end
end
end

View File

@ -0,0 +1,47 @@
require 'metasploit/framework/credential'
module Metasploit
module Framework
# This module provides the namespace for all LoginScanner classes.
# LoginScanners are the classes that provide functionality for testing
# authentication against various different protocols and mechanisms.
module LoginScanner
require 'metasploit/framework/login_scanner/result'
require 'metasploit/framework/login_scanner/invalid'
# Gather a list of LoginScanner classes that can potentially be
# used for a given `service`, which should usually be an
# `Mdm::Service` object, but can be anything that responds to
# #name and #port.
#
# @param service [Mdm::Service,#port,#name]
# @return [Array<LoginScanner::Base>] A collection of LoginScanner
# classes that will probably give useful results when run
# against `service`.
def self.classes_for_service(service)
unless @required
# Make sure we've required all the scanner classes
dir = File.expand_path("../login_scanner/", __FILE__)
Dir.glob(File.join(dir, "*.rb")).each do |f|
require f if File.file?(f)
end
@required = true
end
self.constants.map{|sym| const_get(sym)}.select do |const|
next unless const.kind_of?(Class)
(
const.const_defined?(:LIKELY_PORTS) &&
const.const_get(:LIKELY_PORTS).include?(service.port)
) || (
const.const_defined?(:LIKELY_SERVICE_NAMES) &&
const.const_get(:LIKELY_SERVICE_NAMES).include?(service.name)
)
end
end
end
end
end

View File

@ -0,0 +1,55 @@
require 'metasploit/framework/tcp/client'
require 'metasploit/framework/afp/client'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with Apple Filing
# Protocol.
class AFP
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::Tcp::Client
include Metasploit::Framework::AFP::Client
DEFAULT_PORT = 548
LIKELY_PORTS = [ DEFAULT_PORT ]
LIKELY_SERVICE_NAMES = [ "afp" ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = nil
# @!attribute login_timeout
# @return [Integer] Number of seconds to wait before giving up
attr_accessor :login_timeout
def attempt_login(credential)
begin
connect
rescue Rex::ConnectionError, EOFError, Timeout::Error
status = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
else
success = login(credential.public, credential.private)
status = (success == true) ? Metasploit::Model::Login::Status::SUCCESSFUL : Metasploit::Model::Login::Status::INCORRECT
end
result = Result.new(credential: credential, status: status)
result.host = host
result.port = port
result.protocol = 'tcp'
result.service_name = 'afp'
result
end
def set_sane_defaults
self.connection_timeout ||= 30
self.port ||= DEFAULT_PORT
self.max_send_size ||= 0
self.send_delay ||= 0
end
end
end
end
end

View File

@ -0,0 +1,76 @@
require 'metasploit/framework/login_scanner/http'
module Metasploit
module Framework
module LoginScanner
# Tomcat Manager login scanner
class Axis2 < HTTP
DEFAULT_PORT = 8080
# Inherit LIKELY_PORTS,LIKELY_SERVICE_NAMES, and REALM_KEY from HTTP
CAN_GET_SESSION = true
PRIVATE_TYPES = [ :password ]
# (see Base#attempt_login)
def attempt_login(credential)
http_client = Rex::Proto::Http::Client.new(
host, port, {}, ssl, ssl_version
)
result_opts = {
credential: credential,
host: host,
port: port,
protocol: 'tcp'
}
if ssl
result_opts[:service_name] = 'https'
else
result_opts[:service_name] = 'http'
end
begin
http_client.connect
body = "userName=#{Rex::Text.uri_encode(credential.public)}&password=#{Rex::Text.uri_encode(credential.private)}&submit=+Login+"
request = http_client.request_cgi(
'uri' => uri,
'method' => "POST",
'data' => body,
)
response = http_client.send_recv(request)
if response && response.code == 200 && response.body.include?("upload")
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: response)
else
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: response)
end
rescue ::EOFError, Rex::ConnectionError, ::Timeout::Error
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
end
Result.new(result_opts)
end
# (see Base#set_sane_defaults)
def set_sane_defaults
self.uri = "/axis2/axis2-admin/login" if self.uri.nil?
@method = "POST".freeze
super
end
# The method *must* be "POST", so don't let the user change it
# @raise [RuntimeError]
def method=(_)
raise RuntimeError, "Method must be POST for Axis2"
end
end
end
end
end

View File

@ -0,0 +1,222 @@
require 'metasploit/framework/login_scanner'
module Metasploit
module Framework
module LoginScanner
# This module provides the base behaviour for all of
# the LoginScanner classes. All of the LoginScanners
# should include this module to establish base behaviour
module Base
extend ActiveSupport::Concern
include ActiveModel::Validations
included do
# @!attribute connection_timeout
# @return [Fixnum] The timeout in seconds for a single SSH connection
attr_accessor :connection_timeout
# @!attribute cred_details
# @return [CredentialCollection] Collection of Credential objects
attr_accessor :cred_details
# @!attribute host
# @return [String] The IP address or hostname to connect to
attr_accessor :host
# @!attribute port
# @return [Fixnum] The port to connect to
attr_accessor :port
# @!attribute proxies
# @return [String] The proxy directive to use for the socket
attr_accessor :proxies
# @!attribute stop_on_success
# @return [Boolean] Whether the scanner should stop when it has found one working Credential
attr_accessor :stop_on_success
validates :connection_timeout,
presence: true,
numericality: {
only_integer: true,
greater_than_or_equal_to: 1
}
validates :cred_details, presence: true
validates :host, presence: true
validates :port,
presence: true,
numericality: {
only_integer: true,
greater_than_or_equal_to: 1,
less_than_or_equal_to: 65535
}
validates :stop_on_success,
inclusion: { in: [true, false] }
validate :host_address_must_be_valid
validate :validate_cred_details
# @param attributes [Hash{Symbol => String,nil}]
def initialize(attributes={})
attributes.each do |attribute, value|
public_send("#{attribute}=", value)
end
set_sane_defaults
end
# Attempt a single login against the service with the given
# {Credential credential}.
#
# @param credential [Credential] The credential object to attmpt to
# login with
# @return [Result] A Result object indicating success or failure
# @abstract Protocol-specific scanners must implement this for their
# respective protocols
def attempt_login(credential)
raise NotImplementedError
end
def each_credential
cred_details.each do |raw_cred|
# This could be a Credential object, or a Credential Core, or an Attempt object
# so make sure that whatever it is, we end up with a Credential.
credential = raw_cred.to_credential
if credential.realm.present? && self.class::REALM_KEY.present?
credential.realm_key = self.class::REALM_KEY
yield credential
elsif credential.realm.blank? && self.class::REALM_KEY.present? && self.class::DEFAULT_REALM.present?
credential.realm_key = self.class::REALM_KEY
credential.realm = self.class::DEFAULT_REALM
yield credential
elsif credential.realm.present? && self.class::REALM_KEY.blank?
second_cred = credential.dup
# Strip the realm off here, as we don't want it
credential.realm = nil
credential.realm_key = nil
yield credential
# Some services can take a domain in the username like this even though
# they do not explicitly take a domain as part of the protocol.
second_cred.public = "#{second_cred.realm}\\#{second_cred.public}"
second_cred.realm = nil
second_cred.realm_key = nil
yield second_cred
else
yield credential
end
end
end
# Attempt to login with every {Credential credential} in
# {#cred_details}, by calling {#attempt_login} once for each.
#
# If a successful login is found for a user, no more attempts
# will be made for that user.
#
# @yieldparam result [Result] The {Result} object for each attempt
# @yieldreturn [void]
# @return [void]
def scan!
valid!
# Keep track of connection errors.
# If we encounter too many, we will stop.
consecutive_error_count = 0
total_error_count = 0
successful_users = Set.new
each_credential do |credential|
# For Pro bruteforce Reuse and Guess we need to note that we skipped an attempt.
if successful_users.include?(credential.public)
if credential.parent.respond_to?(:skipped)
credential.parent.skipped = true
credential.parent.save!
end
next
end
result = attempt_login(credential)
result.freeze
yield result if block_given?
if result.success?
consecutive_error_count = 0
break if stop_on_success
successful_users << credential.public
else
if result.status == Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
consecutive_error_count += 1
total_error_count += 1
break if consecutive_error_count >= 3
break if total_error_count >= 10
end
end
end
nil
end
# Raise an exception if this scanner's attributes are not valid.
#
# @raise [Invalid] if the attributes are not valid on this scanner
# @return [void]
def valid!
unless valid?
raise Metasploit::Framework::LoginScanner::Invalid.new(self)
end
nil
end
private
# This method validates that the host address is both
# of a valid type and is resolveable.
# @return [void]
def host_address_must_be_valid
if host.kind_of? String
begin
resolved_host = ::Rex::Socket.getaddress(host, true)
if host =~ /^\d{1,3}(\.\d{1,3}){1,3}$/
unless host =~ Rex::Socket::MATCH_IPV4
errors.add(:host, "could not be resolved")
end
end
self.host = resolved_host
rescue
errors.add(:host, "could not be resolved")
end
else
errors.add(:host, "must be a string")
end
end
# This is a placeholder method. Each LoginScanner class
# will override this with any sane defaults specific to
# its own behaviour.
# @abstract
# @return [void]
def set_sane_defaults
self.connection_timeout = 30 if self.connection_timeout.nil?
end
# This method validates that the credentials supplied
# are all valid.
# @return [void]
def validate_cred_details
unless cred_details.respond_to? :each
errors.add(:cred_details, "must respond to :each")
end
end
end
end
end
end
end

View File

@ -0,0 +1,139 @@
require 'metasploit/framework/tcp/client'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with DB2 Database servers.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results.
class DB2
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::Tcp::Client
DEFAULT_PORT = 50000
DEFAULT_REALM = 'toolsdb'
LIKELY_PORTS = [ DEFAULT_PORT ]
# @todo XXX
LIKELY_SERVICE_NAMES = [ ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = Metasploit::Model::Realm::Key::DB2_DATABASE
# @see Base#attempt_login
def attempt_login(credential)
result_options = {
credential: credential
}
begin
probe_data = send_probe(credential.realm)
if probe_data.empty?
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
else
if authenticate?(credential)
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
else
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
end
end
rescue ::Rex::ConnectionError, ::Rex::ConnectionTimeout, ::Rex::Proto::DRDA::RespError,::Timeout::Error => e
result_options.merge!({
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
proof: e.message
})
end
result = ::Metasploit::Framework::LoginScanner::Result.new(result_options)
result.host = host
result.port = port
result.protocol = 'tcp'
result.service_name = 'db2'
result
end
private
# This method takes the credential and actually attempts the authentication
# @param credential [Credential] The Credential object to authenticate with.
# @return [Boolean] Whether the authentication was successful
def authenticate?(credential)
# Send the login packet and get a response packet back
login_packet = Rex::Proto::DRDA::Utils.client_auth(:dbname => credential.realm,
:dbuser => credential.public,
:dbpass => credential.private
)
sock.put login_packet
response = sock.get_once
if valid_response?(response)
if successful_login?(response)
true
else
false
end
else
false
end
end
# This method opens a socket to the target DB2 server.
# It then sends a client probe on that socket to get information
# back on the server.
# @param database_name [String] The name of the database to probe
# @return [Hash] A hash containing the server information from the probe reply
def send_probe(database_name)
disconnect if self.sock
connect
probe_packet = Rex::Proto::DRDA::Utils.client_probe(database_name)
sock.put probe_packet
response = sock.get_once
response_data = {}
if valid_response?(response)
packet = Rex::Proto::DRDA::SERVER_PACKET.new.read(response)
response_data = Rex::Proto::DRDA::Utils.server_packet_info(packet)
end
response_data
end
# This method sets the sane defaults for things
# like timeouts and TCP evasion options
def set_sane_defaults
self.connection_timeout ||= 30
self.port ||= DEFAULT_PORT
self.max_send_size ||= 0
self.send_delay ||= 0
self.ssl = false if self.ssl.nil?
end
# This method takes a response packet and checks to see
# if the authentication was actually successful.
#
# @param response [String] The unprocessed response packet
# @return [Boolean] Whether the authentication was successful
def successful_login?(response)
packet = Rex::Proto::DRDA::SERVER_PACKET.new.read(response)
packet_info = Rex::Proto::DRDA::Utils.server_packet_info(packet)
if packet_info[:db_login_success]
true
else
false
end
end
# This method provides a simple test on whether the response
# packet was valid.
#
# @param response [String] The response to examine from the socket
# @return [Boolean] Whether the response is valid
def valid_response?(response)
response && response.length > 0
end
end
end
end
end

View File

@ -0,0 +1,80 @@
require 'metasploit/framework/ftp/client'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with FTP.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results.
class FTP
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::Ftp::Client
DEFAULT_PORT = 21
LIKELY_PORTS = [ DEFAULT_PORT, 2121 ]
LIKELY_SERVICE_NAMES = [ 'ftp' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = nil
# @!attribute ftp_timeout
# @return [Fixnum] The timeout in seconds to wait for a response to an FTP command
attr_accessor :ftp_timeout
validates :ftp_timeout,
presence: true,
numericality: {
only_integer: true,
greater_than_or_equal_to: 1
}
# (see Base#attempt_login)
def attempt_login(credential)
result_options = {
credential: credential
}
begin
success = connect_login(credential.public, credential.private)
rescue ::EOFError, Rex::AddressInUse, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
success = false
end
if success
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
elsif !(result_options.has_key? :status)
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
end
result = ::Metasploit::Framework::LoginScanner::Result.new(result_options)
result.host = host
result.port = port
result.protocol = 'tcp'
result.service_name = 'ftp'
result
end
private
# This method sets the sane defaults for things
# like timeouts and TCP evasion options
def set_sane_defaults
self.connection_timeout ||= 30
self.port ||= DEFAULT_PORT
self.max_send_size ||= 0
self.send_delay ||= 0
self.ftp_timeout ||= 16
end
end
end
end
end

View File

@ -0,0 +1,132 @@
require 'rex/proto/http'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
module Metasploit
module Framework
module LoginScanner
#
# HTTP-specific login scanner.
#
class HTTP
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
DEFAULT_REALM = nil
DEFAULT_PORT = 80
DEFAULT_SSL_PORT = 443
LIKELY_PORTS = [ 80, 443, 8000, 8080 ]
LIKELY_SERVICE_NAMES = [ 'http', 'https' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
# @!attribute uri
# @return [String] The path and query string on the server to
# authenticate to.
attr_accessor :uri
# @!attribute uri
# @return [String] HTTP method, e.g. "GET", "POST"
attr_accessor :method
validates :uri, presence: true, length: { minimum: 1 }
validates :method,
presence: true,
length: { minimum: 1 }
# Attempt a single login with a single credential against the target.
#
# @param credential [Credential] The credential object to attempt to
# login with.
# @return [Result] A Result object indicating success or failure
def attempt_login(credential)
ssl = false if ssl.nil?
result_opts = {
credential: credential,
status: Metasploit::Model::Login::Status::INCORRECT,
proof: nil,
host: host,
port: port,
protocol: 'tcp'
}
if ssl
result_opts[:service_name] = 'https'
else
result_opts[:service_name] = 'http'
end
http_client = Rex::Proto::Http::Client.new(
host, port, {}, ssl, ssl_version,
nil, credential.public, credential.private
)
if credential.realm
http_client.set_config('domain' => credential.realm)
end
begin
http_client.connect
request = http_client.request_cgi(
'uri' => uri,
'method' => method
)
# First try to connect without logging in to make sure this
# resource requires authentication. We use #_send_recv for
# that instead of #send_recv.
response = http_client._send_recv(request)
if response && response.code == 401 && response.headers['WWW-Authenticate']
# Now send the creds
response = http_client.send_auth(
response, request.opts, connection_timeout, true
)
if response && response.code == 200
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: response.headers)
end
else
result_opts.merge!(status: Metasploit::Model::Login::Status::NO_AUTH_REQUIRED)
end
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
ensure
http_client.close
end
Result.new(result_opts)
end
private
# This method sets the sane defaults for things
# like timeouts and TCP evasion options
def set_sane_defaults
self.connection_timeout ||= 20
self.max_send_size = 0 if self.max_send_size.nil?
self.send_delay = 0 if self.send_delay.nil?
self.uri = '/' if self.uri.blank?
self.method = 'GET' if self.method.blank?
# Note that this doesn't cover the case where ssl is unset and
# port is something other than a default. In that situtation,
# we don't know what the user has in mind so we have to trust
# that they're going to do something sane.
if !(self.ssl) && self.port.nil?
self.port = self.class::DEFAULT_PORT
self.ssl = false
elsif self.ssl && self.port.nil?
self.port = self.class::DEFAULT_SSL_PORT
elsif self.ssl.nil? && self.port == self.class::DEFAULT_PORT
self.ssl = false
elsif self.ssl.nil? && self.port == self.class::DEFAULT_SSL_PORT
self.ssl = true
end
nil
end
end
end
end
end

View File

@ -0,0 +1,22 @@
module Metasploit
module Framework
module LoginScanner
# This class is the generic Exception raised by LoginScanners when
# they fail validation. It rolls up all validation errors into a
# single exception so that all errors can be dealt with at once.
class Invalid < StandardError
attr_reader :model
def initialize(model)
@model = model
errors = @model.errors.full_messages.join(', ')
super(errors)
end
end
end
end
end

View File

@ -0,0 +1,78 @@
require 'metasploit/framework/mssql/client'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
require 'metasploit/framework/login_scanner/ntlm'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with Microsoft SQL Servers.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results
class MSSQL
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::LoginScanner::NTLM
include Metasploit::Framework::MSSQL::Client
DEFAULT_PORT = 1433
DEFAULT_REALM = 'WORKSTATION'
# Lifted from lib/msf/core/exploit/mssql.rb
LIKELY_PORTS = [ 1433, 1434, 1435, 14330, 2533, 9152, 2638 ]
# Lifted from lib/msf/core/exploit/mssql.rb
LIKELY_SERVICE_NAMES = [ 'ms-sql-s', 'ms-sql2000', 'sybase' ]
PRIVATE_TYPES = [ :password, :ntlm_hash ]
REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
# @!attribute windows_authentication
# @return [Boolean] Whether to use Windows Authentication instead of SQL Server Auth.
attr_accessor :windows_authentication
validates :windows_authentication,
inclusion: { in: [true, false] }
def attempt_login(credential)
result_options = {
credential: credential,
host: host,
port: port,
protocol: 'tcp',
service_name: 'mssql'
}
begin
if mssql_login(credential.public, credential.private, '', credential.realm)
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
else
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
end
rescue ::Rex::ConnectionError
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
end
::Metasploit::Framework::LoginScanner::Result.new(result_options)
end
private
def set_sane_defaults
self.connection_timeout ||= 30
self.port ||= DEFAULT_PORT
self.max_send_size ||= 0
self.send_delay ||= 0
# Don't use ||= with booleans
self.send_lm = true if self.send_lm.nil?
self.send_ntlm = true if self.send_ntlm.nil?
self.send_spn = true if self.send_spn.nil?
self.use_lmkey = false if self.use_lmkey.nil?
self.use_ntlm2_session = true if self.use_ntlm2_session.nil?
self.use_ntlmv2 = true if self.use_ntlmv2.nil?
self.windows_authentication = false if self.windows_authentication.nil?
end
end
end
end
end

View File

@ -0,0 +1,95 @@
require 'metasploit/framework/tcp/client'
require 'rbmysql'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with MySQL Database servers.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results.
class MySQL
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::Tcp::Client
DEFAULT_PORT = 3306
LIKELY_PORTS = [ 3306 ]
LIKELY_SERVICE_NAMES = [ 'mysql' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = nil
def attempt_login(credential)
result_options = {
credential: credential,
host: host,
port: port,
protocol: 'tcp',
service_name: 'mysql'
}
# manage our behind the scenes socket. Close any existing one and open a new one
disconnect if self.sock
connect
begin
::RbMysql.connect({
:host => host,
:port => port,
:read_timeout => 300,
:write_timeout => 300,
:socket => sock,
:user => credential.public,
:password => credential.private,
:db => ''
})
rescue Errno::ECONNREFUSED
result_options.merge!({
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
proof: "Connection refused"
})
rescue RbMysql::ClientError
result_options.merge!({
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
proof: "Connection timeout"
})
rescue Errno::ETIMEDOUT
result_options.merge!({
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
proof: "Operation Timed out"
})
rescue RbMysql::HostNotPrivileged
result_options.merge!({
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
proof: "Unable to login from this host due to policy"
})
rescue RbMysql::AccessDeniedError
result_options.merge!({
status: Metasploit::Model::Login::Status::INCORRECT,
proof: "Access Denied"
})
end
unless result_options[:status]
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
end
::Metasploit::Framework::LoginScanner::Result.new(result_options)
end
# This method sets the sane defaults for things
# like timeouts and TCP evasion options
def set_sane_defaults
self.connection_timeout ||= 30
self.port ||= DEFAULT_PORT
self.max_send_size ||= 0
self.send_delay ||= 0
end
end
end
end
end

View File

@ -0,0 +1,61 @@
require 'metasploit/framework/login_scanner'
module Metasploit
module Framework
module LoginScanner
# This Concern provides the basic accessors and validations
# for protocols that require the use of NTLM for Authentication.
module NTLM
extend ActiveSupport::Concern
include ActiveModel::Validations
included do
# @!attribute send_lm
# @return [Boolean] Whether to always send the LANMAN response(except if using NTLM2 Session)
attr_accessor :send_lm
# @!attribute send_ntlm
# @return [Boolean] Whether to use NTLM responses
attr_accessor :send_ntlm
# @!attribute send_spn
# @return [Boolean] Whether to support SPN for newer Windows OSes
attr_accessor :send_spn
# @!attribute use_lmkey
# @return [Boolean] Whether to negotiate with a LANMAN key
attr_accessor :use_lmkey
# @!attribute send_lm
# @return [Boolean] Whether to force the use of NTLM2 session
attr_accessor :use_ntlm2_session
# @!attribute send_lm
# @return [Boolean] Whether to force the use of NTLMv2 instead of NTLM2 Session
attr_accessor :use_ntlmv2
validates :send_lm,
inclusion: { in: [true, false] }
validates :send_ntlm,
inclusion: { in: [true, false] }
validates :send_spn,
inclusion: { in: [true, false] }
validates :use_lmkey,
inclusion: { in: [true, false] }
validates :use_ntlm2_session,
inclusion: { in: [true, false] }
validates :use_ntlmv2,
inclusion: { in: [true, false] }
end
end
end
end
end

View File

@ -0,0 +1,91 @@
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
require 'metasploit/framework/tcp/client'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with POP3.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results.
class POP3
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::Tcp::Client
DEFAULT_PORT = 110
LIKELY_PORTS = [ 110, 995 ]
LIKELY_SERVICE_NAMES = [ 'pop3', 'pop3s' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = nil
# This method attempts a single login with a single credential against the target
# @param credential [Credential] The credential object to attempt to login with
# @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object
def attempt_login(credential)
result_options = {
credential: credential,
status: Metasploit::Model::Login::Status::INCORRECT,
host: host,
port: port,
protocol: 'tcp',
service_name: 'pop3'
}
disconnect if self.sock
begin
connect
select([sock],nil,nil,0.4)
# Check to see if we recieved an OK?
result_options[:proof] = sock.get_once
if result_options[:proof] && result_options[:proof][/^\+OK.*/]
# If we received an OK we should send the USER
sock.put("USER #{credential.public}\r\n")
result_options[:proof] = sock.get_once
if result_options[:proof] && result_options[:proof][/^\+OK.*/]
# If we got an OK after the username we can send the PASS
sock.put("PASS #{credential.private}\r\n")
# Dovecot has a failed-auth penalty system that maxes at
# sleeping for 15 seconds before sending responses to the
# PASS command, so bump the timeout to 16.
result_options[:proof] = sock.get_once(-1, 16)
if result_options[:proof] && result_options[:proof][/^\+OK.*/]
# if the pass gives an OK, were good to go
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
end
end
end
rescue Rex::ConnectionError, EOFError, Timeout::Error, Errno::EPIPE => e
result_options.merge!(
proof: e.message,
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
)
end
disconnect if self.sock
Result.new(result_options)
end
private
# (see Base#set_sane_defaults)
def set_sane_defaults
self.connection_timeout ||= 30
self.port ||= DEFAULT_PORT
self.max_send_size ||= 0
self.send_delay ||= 0
end
end
end
end
end

View File

@ -0,0 +1,83 @@
require 'metasploit/framework/login_scanner/base'
require 'postgres_msf'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with PostgreSQL database servers.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results.
class Postgres
include Metasploit::Framework::LoginScanner::Base
DEFAULT_PORT = 5432
DEFAULT_REALM = 'template1'
LIKELY_PORTS = [ DEFAULT_PORT ]
LIKELY_SERVICE_NAMES = [ 'postgres' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = Metasploit::Model::Realm::Key::POSTGRESQL_DATABASE
# This method attempts a single login with a single credential against the target
# @param credential [Credential] The credential object to attmpt to login with
# @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object
def attempt_login(credential)
result_options = {
credential: credential,
host: host,
port: port,
protocol: 'tcp',
service_name: 'postgres'
}
db_name = credential.realm || 'template1'
if ::Rex::Socket.is_ipv6?(host)
uri = "tcp://[#{host}]:#{port}"
else
uri = "tcp://#{host}:#{port}"
end
pg_conn = nil
begin
pg_conn = Msf::Db::PostgresPR::Connection.new(db_name,credential.public,credential.private,uri)
rescue RuntimeError => e
case e.to_s.split("\t")[1]
when "C3D000"
result_options.merge!({
status: Metasploit::Model::Login::Status::INCORRECT,
proof: "C3D000, Creds were good but database was bad"
})
when "C28000", "C28P01"
result_options.merge!({
status: Metasploit::Model::Login::Status::INCORRECT,
proof: "Invalid username or password"
})
else
result_options.merge!({
status: Metasploit::Model::Login::Status::INCORRECT,
proof: e.message
})
end
end
if pg_conn
pg_conn.close
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
else
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
end
::Metasploit::Framework::LoginScanner::Result.new(result_options)
end
end
def set_sane_defaults
self.connection_timeout ||= 30
self.port ||= DEFAULT_PORT
end
end
end
end

View File

@ -0,0 +1,81 @@
module Metasploit
module Framework
module LoginScanner
# The Result class provides a standard structure in which
# LoginScanners can return the result of a login attempt
class Result
include ActiveModel::Validations
# @!attribute access_level
# @return [String] the access level gained
attr_accessor :access_level
# @!attribute credential
# @return [Credential] the Credential object the result is for
attr_accessor :credential
# @!attribute host
# @return [String] the addess of the target host for this result
attr_accessor :host
# @!attribute port
# @return [Fixnum] the port number of the service for this result
attr_accessor :port
# @!attribute proof
# @return [String,nil] the proof that the login was successful
attr_accessor :proof
# @!attribute protocol
# @return [String] the transport protocol used for this result (tcp/udp)
attr_accessor :protocol
# @!attribute service_name
# @return [String] the name to give the service for this result
attr_accessor :service_name
# @!attribute status
# @return [String] the status of the attempt. Should be a member of `Metasploit::Model::Login::Status::ALL`
attr_accessor :status
validates :status,
inclusion: {
in: Metasploit::Model::Login::Status::ALL
}
# @param attributes [Hash{Symbol => String,nil}]
def initialize(attributes={})
attributes.each do |attribute, value|
public_send("#{attribute}=", value)
end
end
def inspect
"#<#{self.class} #{credential.public}:#{credential.private}@#{credential.realm} #{status} >"
end
def success?
status == Metasploit::Model::Login::Status::SUCCESSFUL
end
# This method takes all the data inside the Result object
# and spits out a hash compatible with #create_credential
# and #create_credential_login.
#
# @return [Hash] the hash to use with #create_credential and #create_credential_login
def to_h
result_hash = credential.to_h
result_hash.merge!(
access_level: access_level,
address: host,
last_attempted_at: DateTime.now,
origin_type: :service,
port: port,
proof: proof,
protocol: protocol,
service_name: service_name,
status: status
)
result_hash.delete_if { |k,v| v.nil? }
end
end
end
end
end

View File

@ -0,0 +1,64 @@
require 'metasploit/framework/login_scanner'
module Metasploit
module Framework
module LoginScanner
# This module provides the common mixin behaviour for
# LoginScanner objects that rely on Rex Sockets for their
# underlying communication.
module RexSocket
extend ActiveSupport::Concern
included do
# @!attribute max_send_size
# @return [Fixnum] The max size of the data to encapsulate in a single packet
attr_accessor :max_send_size
# @!attribute send_delay
# @return [Fixnum] The delay between sending packets
attr_accessor :send_delay
# @!attribute ssl
# @return [Boolean] Whether the socket should use ssl
attr_accessor :ssl
# @!attribute ssl_version
# @return [String] The version of SSL to implement
attr_accessor :ssl_version
validates :max_send_size,
presence: true,
numericality: {
only_integer: true,
greater_than_or_equal_to: 0
}
validates :send_delay,
presence: true,
numericality: {
only_integer: true,
greater_than_or_equal_to: 0
}
private
def chost
'0.0.0.0'
end
def cport
0
end
def rhost
host
end
def rport
port
end
end
end
end
end
end

View File

@ -0,0 +1,270 @@
require 'rex/proto/smb'
require 'metasploit/framework'
require 'metasploit/framework/tcp/client'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
require 'metasploit/framework/login_scanner/ntlm'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with the Server Messaging
# Block protocol.
class SMB
include Metasploit::Framework::Tcp::Client
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::LoginScanner::NTLM
# Constants to be used in {Result#access_level}
module AccessLevels
# Administrative access. For SMB, this is defined as being
# able to successfully Tree Connect to the `ADMIN$` share.
# This definition is not without its problems, but suffices to
# conclude that such a user will most likely be able to use
# psexec.
ADMINISTRATOR = "Administrator"
# Guest access means our creds were accepted but the logon
# session is not associated with a real user account.
GUEST = "Guest"
end
CAN_GET_SESSION = true
DEFAULT_REALM = 'WORKSTATION'
LIKELY_PORTS = [ 139, 445 ]
LIKELY_SERVICE_NAMES = [ "smb" ]
PRIVATE_TYPES = [ :password, :ntlm_hash ]
REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
module StatusCodes
CORRECT_CREDENTIAL_STATUS_CODES = [
"STATUS_ACCOUNT_DISABLED",
"STATUS_ACCOUNT_EXPIRED",
"STATUS_ACCOUNT_RESTRICTION",
"STATUS_INVALID_LOGON_HOURS",
"STATUS_INVALID_WORKSTATION",
"STATUS_LOGON_TYPE_NOT_GRANTED",
"STATUS_PASSWORD_EXPIRED",
"STATUS_PASSWORD_MUST_CHANGE",
].freeze.map(&:freeze)
end
# @!attribute simple
# @return [Rex::Proto::SMB::SimpleClient]
attr_accessor :simple
attr_accessor :smb_chunk_size
attr_accessor :smb_name
attr_accessor :smb_native_lm
attr_accessor :smb_native_os
attr_accessor :smb_obscure_trans_pipe_level
attr_accessor :smb_pad_data_level
attr_accessor :smb_pad_file_level
attr_accessor :smb_pipe_evasion
# UNUSED
#attr_accessor :smb_pipe_read_max_size
#attr_accessor :smb_pipe_read_min_size
#attr_accessor :smb_pipe_write_max_size
#attr_accessor :smb_pipe_write_min_size
attr_accessor :smb_verify_signature
attr_accessor :smb_direct
validates :smb_chunk_size,
numericality:
{
only_integer: true,
greater_than_or_equal_to: 0
}
validates :smb_obscure_trans_pipe_level,
inclusion:
{
in: Rex::Proto::SMB::Evasions::EVASION_NONE .. Rex::Proto::SMB::Evasions::EVASION_MAX
}
validates :smb_pad_data_level,
inclusion:
{
in: Rex::Proto::SMB::Evasions::EVASION_NONE .. Rex::Proto::SMB::Evasions::EVASION_MAX
}
validates :smb_pad_file_level,
inclusion:
{
in: Rex::Proto::SMB::Evasions::EVASION_NONE .. Rex::Proto::SMB::Evasions::EVASION_MAX
}
validates :smb_pipe_evasion,
inclusion: { in: [true, false, nil] },
allow_nil: true
# UNUSED
#validates :smb_pipe_read_max_size, numericality: { only_integer: true }
#validates :smb_pipe_read_min_size, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
#validates :smb_pipe_write_max_size, numericality: { only_integer: true }
#validates :smb_pipe_write_min_size, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :smb_verify_signature,
inclusion: { in: [true, false, nil] },
allow_nil: true
# If login is successul and {Result#access_level} is not set
# then arbitrary credentials are accepted. If it is set to
# Guest, then arbitrary credentials are accepted, but given
# Guest permissions.
#
# @param domain [String] Domain to authenticate against. Use an
# empty string for local accounts.
# @return [Result]
def attempt_bogus_login(domain)
if defined?(@result_for_bogus)
return @result_for_bogus
end
cred = Credential.new(
public: Rex::Text.rand_text_alpha(8),
private: Rex::Text.rand_text_alpha(8),
realm: domain
)
@result_for_bogus = attempt_login(cred)
end
# (see Base#attempt_login)
def attempt_login(credential)
# Disable direct SMB when SMBDirect has not been set and the
# destination port is configured as 139
if self.smb_direct.nil?
self.smb_direct = case self.port
when 139 then false
when 445 then true
end
end
begin
connect
rescue ::Rex::ConnectionError => e
return Result.new(credential:credential, status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
end
proof = nil
begin
# TODO: OMG
simple.login(
smb_name,
credential.public,
credential.private,
credential.realm || "",
smb_verify_signature,
use_ntlmv2,
use_ntlm2_session,
send_lm,
use_lmkey,
send_ntlm,
smb_native_os,
smb_native_lm,
{
use_spn: send_spn,
name: host
}
)
# Windows SMB will return an error code during Session
# Setup, but nix Samba requires a Tree Connect. Try admin$
# first, since that will tell us if this user has local
# admin access. Fall back to IPC$ which should be accessible
# to any user with valid creds.
begin
simple.connect("\\\\#{host}\\admin$")
access_level = AccessLevels::ADMINISTRATOR
simple.disconnect("\\\\#{host}\\admin$")
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
simple.connect("\\\\#{host}\\IPC$")
end
# If we made it this far without raising, we have a valid
# login
status = Metasploit::Model::Login::Status::SUCCESSFUL
rescue ::Rex::Proto::SMB::Exceptions::LoginError => e
status = case e.get_error(e.error_code)
when *StatusCodes::CORRECT_CREDENTIAL_STATUS_CODES
Metasploit::Model::Login::Status::DENIED_ACCESS
when 'STATUS_LOGON_FAILURE', 'STATUS_ACCESS_DENIED'
Metasploit::Model::Login::Status::INCORRECT
else
Metasploit::Model::Login::Status::INCORRECT
end
proof = e
rescue ::Rex::Proto::SMB::Exceptions::Error => e
status = Metasploit::Model::Login::Status::INCORRECT
proof = e
rescue ::Rex::ConnectionError
status = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
end
if status == Metasploit::Model::Login::Status::SUCCESSFUL && simple.client.auth_user.nil?
access_level ||= AccessLevels::GUEST
end
result = Result.new(credential: credential, status: status, proof: proof, access_level: access_level)
result.host = host
result.port = port
result.protocol = 'tcp'
result.service_name = 'smb'
result
end
def connect
disconnect
self.sock = super
c = Rex::Proto::SMB::SimpleClient.new(sock, smb_direct)
c.client.evasion_opts['pad_data'] = smb_pad_data_level
c.client.evasion_opts['pad_file'] = smb_pad_file_level
c.client.evasion_opts['obscure_trans_pipe'] = smb_obscure_trans_pipe_level
self.simple = c
c
end
def set_sane_defaults
self.connection_timeout = 10 if self.connection_timeout.nil?
self.max_send_size = 0 if self.max_send_size.nil?
self.send_delay = 0 if self.send_delay.nil?
self.send_lm = true if self.send_lm.nil?
self.send_ntlm = true if self.send_ntlm.nil?
self.send_spn = true if self.send_spn.nil?
self.smb_chunk_size = 0 if self.smb_chunk_size.nil?
self.smb_name = "*SMBSERVER" if self.smb_name.nil?
self.smb_native_lm = "Windows 2000 5.0" if self.smb_native_os.nil?
self.smb_native_os = "Windows 2000 2195" if self.smb_native_os.nil?
self.smb_obscure_trans_pipe_level = 0 if self.smb_obscure_trans_pipe_level.nil?
self.smb_pad_data_level = 0 if self.smb_pad_data_level.nil?
self.smb_pad_file_level = 0 if self.smb_pad_file_level.nil?
self.smb_pipe_evasion = false if self.smb_pipe_evasion.nil?
#self.smb_pipe_read_max_size = 1024 if self.smb_pipe_read_max_size.nil?
#self.smb_pipe_read_min_size = 0 if self.smb_pipe_read_min_size.nil?
#self.smb_pipe_write_max_size = 1024 if self.smb_pipe_write_max_size.nil?
#self.smb_pipe_write_min_size = 0 if self.smb_pipe_write_min_size.nil?
self.smb_verify_signature = false if self.smb_verify_signature.nil?
self.use_lmkey = true if self.use_lmkey.nil?
self.use_ntlm2_session = true if self.use_ntlm2_session.nil?
self.use_ntlmv2 = true if self.use_ntlmv2.nil?
self.smb_name = self.host if self.smb_name.nil?
end
end
end
end
end

View File

@ -0,0 +1,110 @@
require 'snmp'
require 'metasploit/framework/login_scanner/base'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with SNMP.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results.
class SNMP
include Metasploit::Framework::LoginScanner::Base
DEFAULT_PORT = 161
LIKELY_PORTS = [ 161, 162 ]
LIKELY_SERVICE_NAMES = [ 'snmp' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = nil
# This method attempts a single login with a single credential against the target
# @param credential [Credential] The credential object to attmpt to login with
# @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object
def attempt_login(credential)
result_options = {
credential: credential,
host: host,
port: port,
protocol: 'udp',
service_name: 'snmp'
}
[:SNMPv1, :SNMPv2c].each do |version|
snmp_client = ::SNMP::Manager.new(
:Host => host,
:Port => port,
:Community => credential.public,
:Version => version,
:Timeout => connection_timeout,
:Retries => 2,
:Transport => ::SNMP::RexUDPTransport,
:Socket => ::Rex::Socket::Udp.create
)
result_options[:proof] = test_read_access(snmp_client)
if result_options[:proof].nil?
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
else
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
if has_write_access?(snmp_client, result_options[:proof])
result_options[:access_level] = "read-write"
else
result_options[:access_level] = "read-only"
end
end
end
::Metasploit::Framework::LoginScanner::Result.new(result_options)
end
private
# This method takes an snmp client and tests whether
# it has write access to the remote system. It sets the
# the sysDescr oid to the same value we already read.
# @param snmp_client [SNMP::Manager] The SNMP client to use
# @param value [String] the value to set sysDescr back to
# @return [Boolean] Returns true or false for if we have write access
def has_write_access?(snmp_client, value)
var_bind = ::SNMP::VarBind.new("1.3.6.1.2.1.1.1.0", ::SNMP::OctetString.new(value))
begin
resp = snmp_client.set(var_bind)
if resp.error_status == :noError
return true
end
rescue RuntimeError
return false
end
end
# Sets the connection timeout approrpiately for SNMP
# if the user did not set it.
def set_sane_defaults
self.connection_timeout = 2 if self.connection_timeout.nil?
self.port = DEFAULT_PORT if self.port.nil?
end
# This method takes an snmp client and tests whether
# it has read access to the remote system. It checks
# the sysDescr oid to use as proof
# @param snmp_client [SNMP::Manager] The SNMP client to use
# @return [String, nil] Returns a string if successful, nil if failed
def test_read_access(snmp_client)
proof = nil
begin
resp = snmp_client.get("sysDescr.0")
resp.each_varbind { |var| proof = var.value }
rescue RuntimeError
proof = nil
end
proof
end
end
end
end
end

View File

@ -0,0 +1,139 @@
require 'net/ssh'
require 'metasploit/framework/login_scanner/base'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with the Secure Shell protocol.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results.
#
class SSH
include Metasploit::Framework::LoginScanner::Base
#
# CONSTANTS
#
CAN_GET_SESSION = true
DEFAULT_PORT = 22
LIKELY_PORTS = [ DEFAULT_PORT ]
LIKELY_SERVICE_NAMES = [ 'ssh' ]
PRIVATE_TYPES = [ :password, :ssh_key ]
REALM_KEY = nil
VERBOSITIES = [
:debug,
:info,
:warn,
:error,
:fatal
]
# @!attribute ssh_socket
# @return [Net::SSH::Connection::Session] The current SSH connection
attr_accessor :ssh_socket
# @!attribute verbosity
# The verbosity level for the SSH client.
#
# @return [Symbol] An element of {VERBOSITIES}.
attr_accessor :verbosity
validates :verbosity,
presence: true,
inclusion: { in: VERBOSITIES }
# (see {Base#attempt_login})
# @note The caller *must* close {#ssh_socket}
def attempt_login(credential)
self.ssh_socket = nil
opt_hash = {
:port => port,
:disable_agent => true,
:config => false,
:verbose => verbosity,
:proxies => proxies
}
case credential.private_type
when :password, nil
opt_hash.update(
:auth_methods => ['password','keyboard-interactive'],
:password => credential.private,
)
when :ssh_key
opt_hash.update(
:auth_methods => ['publickey'],
:key_data => credential.private,
)
end
result_options = {
credential: credential
}
begin
::Timeout.timeout(connection_timeout) do
self.ssh_socket = Net::SSH.start(
host,
credential.public,
opt_hash
)
end
rescue ::EOFError, Net::SSH::Disconnect, Rex::AddressInUse, Rex::ConnectionError, ::Timeout::Error
result_options.merge!( proof: nil, status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
rescue Net::SSH::Exception
result_options.merge!( proof: nil, status: Metasploit::Model::Login::Status::INCORRECT)
end
unless result_options.has_key? :status
if ssh_socket
proof = gather_proof
result_options.merge!( proof: proof, status: Metasploit::Model::Login::Status::SUCCESSFUL)
else
result_options.merge!( proof: nil, status: Metasploit::Model::Login::Status::INCORRECT)
end
end
result = ::Metasploit::Framework::LoginScanner::Result.new(result_options)
result.host = host
result.port = port
result.protocol = 'tcp'
result.service_name = 'ssh'
result
end
private
# This method attempts to gather proof that we successfuly logged in.
# @return [String] The proof of a connection, May be empty.
def gather_proof
proof = ''
begin
Timeout.timeout(5) do
proof = ssh_socket.exec!("id\n").to_s
if(proof =~ /id=/)
proof << ssh_socket.exec!("uname -a\n").to_s
else
# Cisco IOS
if proof =~ /Unknown command or computer name/
proof = ssh_socket.exec!("ver\n").to_s
else
proof << ssh_socket.exec!("help\n?\n\n\n").to_s
end
end
end
rescue ::Exception
end
proof
end
def set_sane_defaults
self.connection_timeout = 30 if self.connection_timeout.nil?
self.port = DEFAULT_PORT if self.port.nil?
self.verbosity = :fatal if self.verbosity.nil?
end
end
end
end
end

View File

@ -0,0 +1,117 @@
require 'metasploit/framework/telnet/client'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with Telnet remote terminals.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results.
class Telnet
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::Telnet::Client
CAN_GET_SESSION = true
DEFAULT_PORT = 23
LIKELY_PORTS = [ DEFAULT_PORT ]
LIKELY_SERVICE_NAMES = [ 'telnet' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = nil
# @!attribute verbosity
# The timeout to wait for the telnet banner.
#
# @return [Fixnum]
attr_accessor :banner_timeout
# @!attribute verbosity
# The timeout to wait for the response from a telnet command.
#
# @return [Fixnum]
attr_accessor :telnet_timeout
validates :banner_timeout,
presence: true,
numericality: {
only_integer: true,
greater_than_or_equal_to: 1
}
validates :telnet_timeout,
presence: true,
numericality: {
only_integer: true,
greater_than_or_equal_to: 1
}
# (see {Base#attempt_login})
def attempt_login(credential)
result_options = {
credential: credential,
host: host,
port: port,
protocol: 'tcp',
service_name: 'telnet'
}
if connect_reset_safe == :refused
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
else
if busy_message?
self.sock.close unless self.sock.closed?
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
end
end
unless result_options[:status]
unless password_prompt?
send_user(credential.public)
end
recvd_sample = @recvd.dup
# Allow for slow echos
1.upto(10) do
recv_telnet(self.sock, 0.10) unless @recvd.nil? or @recvd[/#{@password_prompt}/]
end
if password_prompt?(credential.public)
send_pass(credential.private)
# Allow for slow echos
1.upto(10) do
recv_telnet(self.sock, 0.10) if @recvd == recvd_sample
end
end
if login_succeeded?
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
else
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
end
end
::Metasploit::Framework::LoginScanner::Result.new(result_options)
end
private
# This method sets the sane defaults for things
# like timeouts and TCP evasion options
def set_sane_defaults
self.connection_timeout ||= 30
self.max_send_size ||= 0
self.port ||= DEFAULT_PORT
self.send_delay ||= 0
self.banner_timeout ||= 25
self.telnet_timeout ||= 10
self.connection_timeout ||= 30
# Shim to set up the ivars from the old Login mixin
create_login_ivars
end
end
end
end
end

View File

@ -0,0 +1,28 @@
require 'metasploit/framework/login_scanner/http'
module Metasploit
module Framework
module LoginScanner
# Tomcat Manager login scanner
class Tomcat < HTTP
# Inherit LIKELY_PORTS,LIKELY_SERVICE_NAMES, and REALM_KEY from HTTP
CAN_GET_SESSION = true
DEFAULT_PORT = 8180
PRIVATE_TYPES = [ :password ]
# (see Base#set_sane_defaults)
def set_sane_defaults
self.uri = "/manager/html" if self.uri.nil?
self.method = "GET" if self.method.nil?
super
end
end
end
end
end

View File

@ -0,0 +1,112 @@
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
require 'metasploit/framework/tcp/client'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with vmware-auth.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results.
class VMAUTHD
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::Tcp::Client
DEFAULT_PORT = 902
LIKELY_PORTS = [ DEFAULT_PORT, 903, 912 ]
LIKELY_SERVICE_NAMES = [ 'vmauthd', 'vmware-auth' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = nil
# This method attempts a single login with a single credential against the target
# @param credential [Credential] The credential object to attempt to login with
# @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object
def attempt_login(credential)
result_options = {
credential: credential,
status: Metasploit::Model::Login::Status::INCORRECT,
proof: nil,
host: host,
port: port,
service_name: 'vmauthd',
protocol: 'tcp'
}
disconnect if self.sock
begin
connect
select([sock], nil, nil, 0.4)
# Check to see if we received an OK?
result_options[:proof] = sock.get_once
if result_options[:proof] && result_options[:proof][/^220 VMware Authentication Daemon Version.*/]
# Switch to SSL if required
swap_sock_plain_to_ssl(sock) if result_options[:proof] && result_options[:proof][/SSL/]
# If we received an OK we should send the USER
sock.put("USER #{credential.public}\r\n")
result_options[:proof] = sock.get_once
if result_options[:proof] && result_options[:proof][/^331.*/]
# If we got an OK after the username we can send the PASS
sock.put("PASS #{credential.private}\r\n")
result_options[:proof] = sock.get_once
if result_options[:proof] && result_options[:proof][/^230.*/]
# if the pass gives an OK, we're good to go
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
end
end
end
rescue Rex::ConnectionError, EOFError, Timeout::Error, Errno::EPIPE => e
result_options.merge!(
proof: e.message,
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
)
end
disconnect if self.sock
Result.new(result_options)
end
private
# (see Base#set_sane_defaults)
def set_sane_defaults
self.connection_timeout ||= 30
self.port ||= DEFAULT_PORT
self.max_send_size ||= 0
self.send_delay ||= 0
end
def swap_sock_plain_to_ssl(nsock=self.sock)
ctx = generate_ssl_context
ssl = OpenSSL::SSL::SSLSocket.new(nsock, ctx)
ssl.connect
nsock.extend(Rex::Socket::SslTcp)
nsock.sslsock = ssl
nsock.sslctx = ctx
end
def generate_ssl_context
ctx = OpenSSL::SSL::SSLContext.new(:SSLv3)
@@cached_rsa_key ||= OpenSSL::PKey::RSA.new(1024){}
ctx.key = @@cached_rsa_key
ctx.session_id_context = Rex::Text.rand_text(16)
ctx
end
end
end
end
end

View File

@ -0,0 +1,128 @@
require 'metasploit/framework/tcp/client'
require 'rex/proto/rfb'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with the VNC RFB protocol.
# It is responsible for taking a single target, and a list of credentials
# and attempting them. It then saves the results.
class VNC
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::Tcp::Client
#
# CONSTANTS
#
LIKELY_PORTS = (5900..5910).to_a
LIKELY_SERVICE_NAMES = [ 'vnc' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = nil
# Error indicating retry should occur for UltraVNC
ULTRA_VNC_RETRY_ERROR = 'connection has been rejected'
# Error indicating retry should occur for VNC 4 Server
VNC4_SERVER_RETRY_ERROR = 'Too many security failures'
# Known retry errors for all supported versions of VNC
RETRY_ERRORS = [
ULTRA_VNC_RETRY_ERROR,
VNC4_SERVER_RETRY_ERROR
]
# This method attempts a single login with a single credential against the target
# @param credential [Credential] The credential object to attmpt to login with
# @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object
def attempt_login(credential)
result_options = {
credential: credential,
host: host,
port: port,
protocol: 'tcp',
service_name: 'vnc'
}
credential.public = nil
begin
# Make our initial socket to the target
disconnect if self.sock
connect
# Create our VNC client overtop of the socket
vnc = Rex::Proto::RFB::Client.new(sock, :allow_none => false)
if vnc.handshake
if vnc_auth(vnc,credential.private)
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
else
result_options.merge!(
proof: vnc.error,
status: Metasploit::Model::Login::Status::INCORRECT
)
end
else
result_options.merge!(
proof: vnc.error,
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
)
end
rescue ::EOFError, Errno::ENOTCONN, Rex::AddressInUse, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error => e
result_options.merge!(
proof: e.message,
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
)
end
::Metasploit::Framework::LoginScanner::Result.new(result_options)
end
private
# Check the VNC error to see if we should wait and retry.
#
# @param error [String] The VNC error message received
# @return [false] don't retry
# @return [true] retry
def retry?(error)
RETRY_ERRORS.include?(error)
end
# This method sets the sane defaults for things
# like timeouts and TCP evasion options
def set_sane_defaults
self.connection_timeout ||= 30
self.port ||= 5900
self.max_send_size ||= 0
self.send_delay ||= 0
end
# This method attempts the actual VNC authentication. It has built in retries to handle
# delays built into the VNC RFB authentication.
# @param client [Rex::Proto::RFB::Client] The VNC client object to authenticate through
# @param password [String] the password to attempt the authentication with
def vnc_auth(client,password)
success = false
5.times do |n|
if client.authenticate(password)
success = true
break
end
break unless retry?(client.error)
# Wait for an increasing ammount of time before retrying
delay = (2**(n+1)) + 1
::Rex.sleep(delay)
end
success
end
end
end
end
end

View File

@ -0,0 +1,52 @@
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
require 'metasploit/framework/login_scanner/http'
module Metasploit
module Framework
module LoginScanner
# Windows Remote Management login scanner
class WinRM < HTTP
# The default port where WinRM listens. This is what you get on
# v1.1+ with `winrm quickconfig`. Note that before v1.1, the
# default was 80
DEFAULT_PORT = 5985
# The default realm is WORKSTATION which tells Windows authentication
# that it is a Local Account.
DEFAULT_REALM = 'WORKSTATION'
# The default port where WinRM listens when SSL is enabled. Note
# that before v1.1, the default was 443
DEFAULT_SSL_PORT = 5986
PRIVATE_TYPES = [ :password ]
LIKELY_PORTS = [ 80, 443, 5985, 5986 ]
REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
# Inherit LIKELY_SERVICE_NAMES, since a scanner will see it as
# just HTTP.
validates :method, inclusion: { in: ["POST"] }
# (see Base#set_sane_defaults)
def set_sane_defaults
self.uri = "/wsman" if self.uri.nil?
@method = "POST".freeze
super
end
# The method *must* be "POST", so don't let the user change it
# @raise [RuntimeError] Unconditionally
def method=(_)
raise RuntimeError, "Method must be POST for WinRM"
end
end
end
end
end

View File

@ -0,0 +1,728 @@
require 'metasploit/framework/tcp/client'
module Metasploit
module Framework
module MSSQL
module Client
include Metasploit::Framework::Tcp::Client
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
NTLM_CONST = Rex::Proto::NTLM::Constants
NTLM_UTILS = Rex::Proto::NTLM::Utils
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
# Encryption
ENCRYPT_OFF = 0x00 #Encryption is available but off.
ENCRYPT_ON = 0x01 #Encryption is available and on.
ENCRYPT_NOT_SUP = 0x02 #Encryption is not available.
ENCRYPT_REQ = 0x03 #Encryption is required.
# Packet Type
TYPE_SQL_BATCH = 1 # (Client) SQL command
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
TYPE_RPC = 3 # (Client) RPC
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
# Request Completion, Error and Info Messages, Attention Acknowledgement
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
TYPE_TDS7_LOGIN = 16 # (Client) Login
TYPE_SSPI_MESSAGE = 17 # (Client) Login
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
# Status
STATUS_NORMAL = 0x00
STATUS_END_OF_MESSAGE = 0x01
STATUS_IGNORE_EVENT = 0x02
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
#
# This method connects to the server over TCP and attempts
# to authenticate with the supplied username and password
# The global socket is used and left connected after auth
#
def mssql_login(user='sa', pass='', db='', domain_name='')
disconnect if self.sock
connect
# Send a prelogin packet and check that encryption is not enabled
if mssql_prelogin() != ENCRYPT_NOT_SUP
print_error("Encryption is not supported")
return false
end
if windows_authentication
idx = 0
pkt = ''
pkt_hdr = ''
pkt_hdr = [
TYPE_TDS7_LOGIN, #type
STATUS_END_OF_MESSAGE, #status
0x0000, #length
0x0000, # SPID
0x01, # PacketID (unused upon specification
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
0x00 #Window
]
pkt << [
0x00000000, # Size
0x71000001, # TDS Version
0x00000000, # Dummy Size
0x00000007, # Version
rand(1024+1), # PID
0x00000000, # ConnectionID
0xe0, # Option Flags 1
0x83, # Option Flags 2
0x00, # SQL Type Flags
0x00, # Reserved Flags
0x00000000, # Time Zone
0x00000000 # Collation
].pack('VVVVVVCCCCVV')
cname = Rex::Text.to_unicode( Rex::Text.rand_text_alpha(rand(8)+1) )
aname = Rex::Text.to_unicode( Rex::Text.rand_text_alpha(rand(8)+1) ) #application and library name
sname = Rex::Text.to_unicode( rhost )
dname = Rex::Text.to_unicode( db )
ntlm_options = {
:signing => false,
:usentlm2_session => use_ntlm2_session,
:use_ntlmv2 => use_ntlmv2,
:send_lm => send_lm,
:send_ntlm => send_ntlm
}
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init(domain_name, workstation_name, ntlmssp_flags)
idx = pkt.size + 50 # lengths below
pkt << [idx, cname.length / 2].pack('vv')
idx += cname.length
pkt << [0, 0].pack('vv') # User length offset must be 0
pkt << [0, 0].pack('vv') # Password length offset must be 0
pkt << [idx, aname.length / 2].pack('vv')
idx += aname.length
pkt << [idx, sname.length / 2].pack('vv')
idx += sname.length
pkt << [0, 0].pack('vv') # unused
pkt << [idx, aname.length / 2].pack('vv')
idx += aname.length
pkt << [idx, 0].pack('vv') # locales
pkt << [idx, 0].pack('vv') #db
# ClientID (should be mac address)
pkt << Rex::Text.rand_text(6)
# NTLMSSP
pkt << [idx, ntlmsspblob.length].pack('vv')
idx += ntlmsspblob.length
pkt << [idx, 0].pack('vv') # AtchDBFile
pkt << cname
pkt << aname
pkt << sname
pkt << aname
pkt << ntlmsspblob
# Total packet length
pkt[0,4] = [pkt.length].pack('V')
pkt_hdr[2] = pkt.length + 8
pkt = pkt_hdr.pack("CCnnCC") + pkt
# Rem : One have to set check_status to false here because sql server sp0 (and maybe above)
# has a strange behavior that differs from the specifications
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
resp = mssql_send_recv(pkt,15, false)
# Get default data
begin
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
# a domain.length < 3 will hit this
rescue NTLM_XCEPT::NTLMMissingChallenge
return false
end
challenge_key = blob_data[:challenge_key]
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
#netbios name
default_name = blob_data[:default_name] || ''
#netbios domain
default_domain = blob_data[:default_domain] || ''
#dns name
dns_host_name = blob_data[:dns_host_name] || ''
#dns domain
dns_domain_name = blob_data[:dns_domain_name] || ''
#Client time
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
spnopt = {:use_spn => send_spn, :name => self.rhost}
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key,
domain_name, default_name, default_domain,
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
spnopt, ntlm_options)
ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags)
# Create an SSPIMessage
idx = 0
pkt = ''
pkt_hdr = ''
pkt_hdr = [
TYPE_SSPI_MESSAGE, #type
STATUS_END_OF_MESSAGE, #status
0x0000, #length
0x0000, # SPID
0x01, # PacketID
0x00 #Window
]
pkt_hdr[2] = ntlmssp.length + 8
pkt = pkt_hdr.pack("CCnnCC") + ntlmssp
resp = mssql_send_recv(pkt)
#SQL Server Authentification
else
idx = 0
pkt = ''
pkt << [
0x00000000, # Dummy size
0x71000001, # TDS Version
0x00000000, # Size
0x00000007, # Version
rand(1024+1), # PID
0x00000000, # ConnectionID
0xe0, # Option Flags 1
0x03, # Option Flags 2
0x00, # SQL Type Flags
0x00, # Reserved Flags
0x00000000, # Time Zone
0x00000000 # Collation
].pack('VVVVVVCCCCVV')
cname = Rex::Text.to_unicode( Rex::Text.rand_text_alpha(rand(8)+1) )
uname = Rex::Text.to_unicode( user )
pname = mssql_tds_encrypt( pass )
aname = Rex::Text.to_unicode( Rex::Text.rand_text_alpha(rand(8)+1) )
sname = Rex::Text.to_unicode( rhost )
dname = Rex::Text.to_unicode( db )
idx = pkt.size + 50 # lengths below
pkt << [idx, cname.length / 2].pack('vv')
idx += cname.length
pkt << [idx, uname.length / 2].pack('vv')
idx += uname.length
pkt << [idx, pname.length / 2].pack('vv')
idx += pname.length
pkt << [idx, aname.length / 2].pack('vv')
idx += aname.length
pkt << [idx, sname.length / 2].pack('vv')
idx += sname.length
pkt << [0, 0].pack('vv')
pkt << [idx, aname.length / 2].pack('vv')
idx += aname.length
pkt << [idx, 0].pack('vv')
pkt << [idx, dname.length / 2].pack('vv')
idx += dname.length
# The total length has to be embedded twice more here
pkt << [
0,
0,
0x12345678,
0x12345678
].pack('vVVV')
pkt << cname
pkt << uname
pkt << pname
pkt << aname
pkt << sname
pkt << aname
pkt << dname
# Total packet length
pkt[0,4] = [pkt.length].pack('V')
# Embedded packet lengths
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
# Packet header and total length including header
pkt = "\x10\x01" + [pkt.length + 8].pack('n') + [0].pack('n') + [1].pack('C') + "\x00" + pkt
resp = mssql_send_recv(pkt)
end
info = {:errors => []}
info = mssql_parse_reply(resp,info)
return false if not info
info[:login_ack] ? true : false
end
#
# Parse an "environment change" TDS token
#
def mssql_parse_env(data, info)
len = data.slice!(0,2).unpack('v')[0]
buff = data.slice!(0,len)
type = buff.slice!(0,1).unpack('C')[0]
nval = ''
nlen = buff.slice!(0,1).unpack('C')[0] || 0
nval = buff.slice!(0,nlen*2).gsub("\x00", '') if nlen > 0
oval = ''
olen = buff.slice!(0,1).unpack('C')[0] || 0
oval = buff.slice!(0,olen*2).gsub("\x00", '') if olen > 0
info[:envs] ||= []
info[:envs] << { :type => type, :old => oval, :new => nval }
info
end
#
# Parse a "ret" TDS token
#
def mssql_parse_ret(data, info)
ret = data.slice!(0,4).unpack('N')[0]
info[:ret] = ret
info
end
#
# Parse a "done" TDS token
#
def mssql_parse_done(data, info)
status,cmd,rows = data.slice!(0,8).unpack('vvV')
info[:done] = { :status => status, :cmd => cmd, :rows => rows }
info
end
#
# Parse an "error" TDS token
#
def mssql_parse_error(data, info)
len = data.slice!(0,2).unpack('v')[0]
buff = data.slice!(0,len)
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
emsg = buff.slice!(0,elen * 2)
emsg.gsub!("\x00", '')
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
info
end
#
# Parse an "information" TDS token
#
def mssql_parse_info(data, info)
len = data.slice!(0,2).unpack('v')[0]
buff = data.slice!(0,len)
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
emsg = buff.slice!(0,elen * 2)
emsg.gsub!("\x00", '')
info[:infos]||= []
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
info
end
#
# Parse a "login ack" TDS token
#
def mssql_parse_login_ack(data, info)
len = data.slice!(0,2).unpack('v')[0]
buff = data.slice!(0,len)
info[:login_ack] = true
end
#
# Parse individual tokens from a TDS reply
#
def mssql_parse_reply(data, info)
info[:errors] = []
return if not data
until data.empty?
token = data.slice!(0,1).unpack('C')[0]
case token
when 0x81
mssql_parse_tds_reply(data, info)
when 0xd1
mssql_parse_tds_row(data, info)
when 0xe3
mssql_parse_env(data, info)
when 0x79
mssql_parse_ret(data, info)
when 0xfd, 0xfe, 0xff
mssql_parse_done(data, info)
when 0xad
mssql_parse_login_ack(data, info)
when 0xab
mssql_parse_info(data, info)
when 0xaa
mssql_parse_error(data, info)
when nil
break
else
info[:errors] << "unsupported token: #{token}"
end
end
info
end
#
# Parse a raw TDS reply from the server
#
def mssql_parse_tds_reply(data, info)
info[:errors] ||= []
info[:colinfos] ||= []
info[:colnames] ||= []
# Parse out the columns
cols = data.slice!(0,2).unpack('v')[0]
0.upto(cols-1) do |col_idx|
col = {}
info[:colinfos][col_idx] = col
col[:utype] = data.slice!(0,2).unpack('v')[0]
col[:flags] = data.slice!(0,2).unpack('v')[0]
col[:type] = data.slice!(0,1).unpack('C')[0]
case col[:type]
when 48
col[:id] = :tinyint
when 52
col[:id] = :smallint
when 56
col[:id] = :rawint
when 61
col[:id] = :datetime
when 34
col[:id] = :image
col[:max_size] = data.slice!(0,4).unpack('V')[0]
col[:value_length] = data.slice!(0,2).unpack('v')[0]
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
when 36
col[:id] = :string
when 38
col[:id] = :int
col[:int_size] = data.slice!(0,1).unpack('C')[0]
when 127
col[:id] = :bigint
when 165
col[:id] = :hex
col[:max_size] = data.slice!(0,2).unpack('v')[0]
when 173
col[:id] = :hex # binary(2)
col[:max_size] = data.slice!(0,2).unpack('v')[0]
when 231,175,167,239
col[:id] = :string
col[:max_size] = data.slice!(0,2).unpack('v')[0]
col[:codepage] = data.slice!(0,2).unpack('v')[0]
col[:cflags] = data.slice!(0,2).unpack('v')[0]
col[:charset_id] = data.slice!(0,1).unpack('C')[0]
else
col[:id] = :unknown
end
col[:msg_len] = data.slice!(0,1).unpack('C')[0]
if(col[:msg_len] and col[:msg_len] > 0)
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
end
info[:colnames] << (col[:name] || 'NULL')
end
end
#
# Parse a single row of a TDS reply
#
def mssql_parse_tds_row(data, info)
info[:rows] ||= []
row = []
info[:colinfos].each do |col|
if(data.length == 0)
row << "<EMPTY>"
next
end
case col[:id]
when :hex
str = ""
len = data.slice!(0,2).unpack('v')[0]
if(len > 0 and len < 65535)
str << data.slice!(0,len)
end
row << str.unpack("H*")[0]
when :string
str = ""
len = data.slice!(0,2).unpack('v')[0]
if(len > 0 and len < 65535)
str << data.slice!(0,len)
end
row << str.gsub("\x00", '')
when :datetime
row << data.slice!(0,8).unpack("H*")[0]
when :rawint
row << data.slice!(0,4).unpack('V')[0]
when :bigint
row << data.slice!(0,8).unpack("H*")[0]
when :smallint
row << data.slice!(0, 2).unpack("v")[0]
when :smallint3
row << [data.slice!(0, 3)].pack("Z4").unpack("V")[0]
when :tinyint
row << data.slice!(0, 1).unpack("C")[0]
when :image
str = ''
len = data.slice!(0,1).unpack('C')[0]
str = data.slice!(0,len) if (len and len > 0)
row << str.unpack("H*")[0]
when :int
len = data.slice!(0, 1).unpack("C")[0]
raw = data.slice!(0, len) if (len and len > 0)
case len
when 0,255
row << ''
when 1
row << raw.unpack("C")[0]
when 2
row << raw.unpack('v')[0]
when 4
row << raw.unpack('V')[0]
when 5
row << raw.unpack('V')[0] # XXX: missing high byte
when 8
row << raw.unpack('VV')[0] # XXX: missing high dword
else
info[:errors] << "invalid integer size: #{len} #{data[0,16].unpack("H*")[0]}"
end
else
info[:errors] << "unknown column type: #{col.inspect}"
end
end
info[:rows] << row
info
end
#
#this method send a prelogin packet and check if encryption is off
#
def mssql_prelogin(enc_error=false)
pkt = ""
pkt_hdr = ""
pkt_data_token = ""
pkt_data = ""
pkt_hdr = [
TYPE_PRE_LOGIN_MESSAGE, #type
STATUS_END_OF_MESSAGE, #status
0x0000, #length
0x0000, # SPID
0x00, # PacketID
0x00 #Window
]
version = [0x55010008,0x0000].pack("Vv")
encryption = ENCRYPT_NOT_SUP # off
instoptdata = "MSSQLServer\0"
threadid = "\0\0" + Rex::Text.rand_text(2)
idx = 21 # size of pkt_data_token
pkt_data_token << [
0x00, # Token 0 type Version
idx , # VersionOffset
version.length, # VersionLength
0x01, # Token 1 type Encryption
idx = idx + version.length, # EncryptionOffset
0x01, # EncryptionLength
0x02, # Token 2 type InstOpt
idx = idx + 1, # InstOptOffset
instoptdata.length, # InstOptLength
0x03, # Token 3 type Threadid
idx + instoptdata.length, # ThreadIdOffset
0x04, # ThreadIdLength
0xFF
].pack("CnnCnnCnnCnnC")
pkt_data << pkt_data_token
pkt_data << version
pkt_data << encryption
pkt_data << instoptdata
pkt_data << threadid
pkt_hdr[2] = pkt_data.length + 8
pkt = pkt_hdr.pack("CCnnCC") + pkt_data
resp = mssql_send_recv(pkt)
idx = 0
while resp and resp[0,1] != "\xff" and resp.length > 5
token = resp.slice!(0,5)
token = token.unpack("Cnn")
idx -= 5
if token[0] == 0x01
idx += token[1]
break
end
end
if idx > 0
encryption_mode = resp[idx,1].unpack("C")[0]
else
#force to ENCRYPT_NOT_SUP and hope for the best
encryption_mode = ENCRYPT_NOT_SUP
end
if encryption_mode != ENCRYPT_NOT_SUP and enc_error
raise RuntimeError,"Encryption is not supported"
end
encryption_mode
end
#
# Send and receive using TDS
#
def mssql_send_recv(req, timeout=15, check_status = true)
sock.put(req)
# Read the 8 byte header to get the length and status
# Read the length to get the data
# If the status is 0, read another header and more data
done = false
resp = ""
while(not done)
head = sock.get_once(8, timeout)
if !(head and head.length == 8)
return false
end
# Is this the last buffer?
if(head[1,1] == "\x01" or not check_status )
done = true
end
# Grab this block's length
rlen = head[2,2].unpack('n')[0] - 8
while(rlen > 0)
buff = sock.get_once(rlen, timeout)
return if not buff
resp << buff
rlen -= buff.length
end
end
resp
end
#
# Encrypt a password according to the TDS protocol (encode)
#
def mssql_tds_encrypt(pass)
# Convert to unicode, swap 4 bits both ways, xor with 0xa5
Rex::Text.to_unicode(pass).unpack('C*').map {|c| (((c & 0x0f) << 4) + ((c & 0xf0) >> 4)) ^ 0xa5 }.pack("C*")
end
protected
def windows_authentication
raise NotImplementedError
end
def use_ntlm2_session
raise NotImplementedError
end
def use_ntlmv2
raise NotImplementedError
end
def send_lm
raise NotImplementedError
end
def send_ntlm
raise NotImplementedError
end
def send_spn
raise NotImplementedError
end
end
end
end
end

View File

@ -0,0 +1,27 @@
#
# Gems
#
require 'active_support/dependencies/autoload'
# @note Must use the nested declaration of the
# {Metasploit::Framework::ParsedOptions} namespace because commands, which
# use parsed options, need to be able to be required directly without any
# other part of metasploit-framework besides config/boot so that the
# commands can parse arguments, setup RAILS_ENV, and load
# config/application.rb correctly.
module Metasploit
module Framework
# Namespace for parsed options for {Metasploit::Framework::Command
# commands}. The names of `Class`es in this namespace correspond to the
# name of the `Class` in the {Metasploit::Framework::Command} namespace
# for which this namespace's `Class` parses options.
module ParsedOptions
extend ActiveSupport::Autoload
autoload :Base
autoload :Console
end
end
end

View File

@ -0,0 +1,177 @@
#
# Standard Library
#
require 'optparse'
#
# Gems
#
require 'active_support/ordered_options'
#
# Project
#
require 'metasploit/framework/database'
require 'metasploit/framework/parsed_options'
# Options parsed from the command line that can be used to change the
# `Metasploit::Framework::Application.config` and `Rails.env`
class Metasploit::Framework::ParsedOptions::Base
#
# CONSTANTS
#
# msfconsole boots in production mode instead of the normal rails default of
# development.
DEFAULT_ENVIRONMENT = 'production'
#
# Attributes
#
attr_reader :positional
#
# Instance Methods
#
def initialize(arguments=ARGV)
@positional = option_parser.parse(arguments)
end
# Translates {#options} to the `application`'s config
#
# @param application [Rails::Application]
# @return [void]
def configure(application)
application.config['config/database'] = options.database.config
end
# Sets the `RAILS_ENV` environment variable.
#
# 1. If the -E/--environment option is given, then its value is used.
# 2. The default value, 'production', is used.
#
# @return [void]
def environment!
if defined?(Rails) && Rails.instance_variable_defined?(:@_env) && Rails.env != options.environment
raise "#{self.class}##{__method__} called too late to set RAILS_ENV: Rails.env already memoized"
end
ENV['RAILS_ENV'] = options.environment
end
# Options parsed from
#
# @return [ActiveSupport::OrderedOptions]
def options
unless @options
options = ActiveSupport::OrderedOptions.new
options.database = ActiveSupport::OrderedOptions.new
options.database.config = Metasploit::Framework::Database.configurations_pathname.try(:to_path)
options.database.disable = false
options.database.migrations_paths = []
# If RAILS_ENV is set, then it will be used, but if RAILS_ENV is set and the --environment option is given, then
# --environment value will be used to reset ENV[RAILS_ENV].
options.environment = ENV['RAILS_ENV'] || DEFAULT_ENVIRONMENT
options.framework = ActiveSupport::OrderedOptions.new
options.framework.config = nil
options.modules = ActiveSupport::OrderedOptions.new
options.modules.path = nil
@options = options
end
@options
end
private
# Parses arguments into {#options}.
#
# @return [OptionParser]
def option_parser
@option_parser ||= OptionParser.new { |option_parser|
option_parser.separator ''
option_parser.separator 'Common options'
option_parser.on(
'-E',
'--environment ENVIRONMENT',
%w{development production test},
"The Rails environment. Will use RAIL_ENV environment variable if that is set. " \
"Defaults to production if neither option not RAILS_ENV environment variable is set."
) do |environment|
options.environment = environment
end
option_parser.separator ''
option_parser.separator 'Database options'
option_parser.on(
'-M',
'--migration-path DIRECTORY',
'Specify a directory containing additional DB migrations'
) do |directory|
options.database.migrations_paths << directory
end
option_parser.on('-n', '--no-database', 'Disable database support') do
options.database.disable = true
end
option_parser.on(
'-y',
'--yaml PATH',
'Specify a YAML file containing database settings'
) do |path|
options.database.config = path
end
option_parser.separator ''
option_parser.separator 'Framework options'
option_parser.on('-c', '-c FILE', 'Load the specified configuration file') do |file|
options.framework.config = file
end
option_parser.on(
'-v',
'--version',
'Show version'
) do
options.subcommand = :version
end
option_parser.separator ''
option_parser.separator 'Module options'
option_parser.on(
'-m',
'--module-path DIRECTORY',
'An additional module path'
) do |directory|
options.modules.path = directory
end
#
# Tail
#
option_parser.separator ''
option_parser.on_tail('-h', '--help', 'Show this message') do
puts option_parser
exit
end
}
end
end

View File

@ -0,0 +1,79 @@
# Parsed options for {Metasploit::Framework::Command::Console}
class Metasploit::Framework::ParsedOptions::Console < Metasploit::Framework::ParsedOptions::Base
# Options parsed from msfconsole command-line.
#
# @return [ActiveSupport::OrderedOptions]
def options
unless @options
super.tap { |options|
options.console = ActiveSupport::OrderedOptions.new
options.console.commands = []
options.console.confirm_exit = false
options.console.defanged = false
options.console.local_output = nil
options.console.plugins = []
options.console.quiet = false
options.console.real_readline = false
options.console.resources = []
options.console.subcommand = :run
}
end
@options
end
private
# Parses msfconsole arguments into {#options}.
#
# @return [OptionParser]
def option_parser
unless @option_parser
super.tap { |option_parser|
option_parser.banner = "Usage: #{option_parser.program_name} [options]"
option_parser.separator ''
option_parser.separator 'Console options:'
option_parser.on('-a', '--ask', "Ask before exiting Metasploit or accept 'exit -y'") do
options.console.confirm_exit = true
end
option_parser.on('-d', '--defanged', 'Execute the console as defanged') do
options.console.defanged = true
end
option_parser.on('-L', '--real-readline', 'Use the system Readline library instead of RbReadline') do
options.console.real_readline = true
end
option_parser.on('-o', '--output FILE', 'Output to the specified file') do |file|
options.console.local_output = file
end
option_parser.on('-p', '--plugin PLUGIN', 'Load a plugin on startup') do |plugin|
options.console.plugins << plugin
end
option_parser.on('-q', '--quiet', 'Do not print the banner on start up') do
options.console.quiet = true
end
option_parser.on('-r', '--resource FILE', 'Execute the specified resource file') do |file|
options.console.resources << file
end
option_parser.on(
'-x',
'--execute-command COMMAND',
'Execute the specified string as console commands (use ; for multiples)'
) do |commands|
options.console.commands += commands.split(/\s*;\s*/)
end
}
end
@option_parser
end
end

View File

@ -0,0 +1,96 @@
# @note needs to use explicit nesting. so this file can be loaded directly without loading 'metasploit/framework', this
# file can be used prior to Bundler.require.
module Metasploit
module Framework
# Extension to `Kernel#require` behavior.
module Require
#
# Module Methods
#
# Tries to require `name`. If a `LoadError` occurs, then `without_warning` is printed to standard error using
# `Kernel#warn`, along with instructions for reinstalling the bundle. If a `LoadError` does not occur, then
# `with_block` is called.
#
# @param name [String] the name of the library to `Kernel#require`.
# @param without_warning [String] warning to print if `name` cannot be required.
# @yield block to run when `name` requires successfully
# @yieldreturn [void]
# @return [void]
def self.optionally(name, without_warning)
begin
require name
rescue LoadError
warn without_warning
warn "Bundle installed '--without #{Bundler.settings.without.join(' ')}'"
warn "To clear the without option do `bundle install --without ''` " \
"(the --without flag with an empty string) or " \
"`rm -rf .bundle` to remove the .bundle/config manually and " \
"then `bundle install`"
else
if block_given?
yield
end
end
end
# Tries to `require 'active_record/railtie'` to define the activerecord Rails initializers and rake tasks.
#
# @example Optionally requiring 'active_record/railtie'
# require 'metasploit/framework/require'
#
# class MyClass
# def setup
# if database_enabled
# Metasploit::Framework::Require.optionally_active_record_railtie
# end
# end
# end
#
# @return [void]
def self.optionally_active_record_railtie
if ::File.exist?(Rails.application.config.paths['config/database'].first)
optionally(
'active_record/railtie',
'activerecord not in the bundle, so database support will be disabled.'
)
else
warn 'Could not find database.yml, so database support will be disabled.'
end
end
# Tries to `require 'metasploit/credential/creation'` and include it in the `including_module`.
#
# @param including_module [Module] `Class` or `Module` that wants to `include Metasploit::Credential::Creation`.
# @return [void]
def self.optionally_include_metasploit_credential_creation(including_module)
optionally(
'metasploit/credential/creation',
"metasploit-credential not in the bundle, so Metasploit::Credential creation will fail for #{including_module.name}",
) do
including_module.send(:include, Metasploit::Credential::Creation)
end
end
#
# Instance Methods
#
# Tries to `require 'metasploit/credential/creation'` and include it in this `Class` or `Module`.
#
# @example Using in a `Module`
# require 'metasploit/framework/require'
#
# module MyModule
# extend Metasploit::Framework::Require
#
# optionally_include_metasploit_credential_creation
# end
#
# @return [void]
def optionally_include_metasploit_credential_creation
Metasploit::Framework::Require.optionally_include_metasploit_credential_creation(self)
end
end
end
end

View File

@ -0,0 +1,186 @@
module Metasploit
module Framework
module Tcp
module EvasiveTCP
attr_accessor :_send_size, :_send_delay, :evasive
def denagle
begin
setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
rescue ::Exception
end
end
def write(buf, opts={})
return super(buf, opts) if not @evasive
ret = 0
idx = 0
len = @_send_size || buf.length
while(idx < buf.length)
if(@_send_delay and idx > 0)
::IO.select(nil, nil, nil, @_send_delay)
end
pkt = buf[idx, len]
res = super(pkt, opts)
flush()
idx += len
ret += res if res
end
ret
end
end
module Client
#
# Establishes a TCP connection to the specified RHOST/RPORT
#
# @see Rex::Socket::Tcp
# @see Rex::Socket::Tcp.create
def connect(global = true, opts={})
dossl = false
if(opts.has_key?('SSL'))
dossl = opts['SSL']
else
dossl = ssl
end
nsock = Rex::Socket::Tcp.create(
'PeerHost' => opts['RHOST'] || rhost,
'PeerPort' => (opts['RPORT'] || rport).to_i,
'LocalHost' => opts['CHOST'] || chost || "0.0.0.0",
'LocalPort' => (opts['CPORT'] || cport || 0).to_i,
'SSL' => dossl,
'SSLVersion' => opts['SSLVersion'] || ssl_version,
'Proxies' => proxies,
'Timeout' => (opts['ConnectTimeout'] || connection_timeout || 10).to_i
)
# enable evasions on this socket
set_tcp_evasions(nsock)
# Set this socket to the global socket as necessary
self.sock = nsock if (global)
return nsock
end
# Enable evasions on a given client
def set_tcp_evasions(socket)
if( max_send_size.to_i == 0 and send_delay.to_i == 0)
return
end
return if socket.respond_to?('evasive')
socket.extend(EvasiveTCP)
if ( max_send_size.to_i > 0)
socket._send_size = max_send_size
socket.denagle
socket.evasive = true
end
if ( send_delay.to_i > 0)
socket._send_delay = send_delay
socket.evasive = true
end
end
#
# Closes the TCP connection
#
def disconnect(nsock = self.sock)
begin
if (nsock)
nsock.shutdown
nsock.close
end
rescue IOError
end
if (nsock == sock)
self.sock = nil
end
end
##
#
# Wrappers for getters
#
##
def max_send_size
raise NotImplementedError
end
def send_delay
raise NotImplementedError
end
#
# Returns the target host
#
def rhost
raise NotImplementedError
end
#
# Returns the remote port
#
def rport
raise NotImplementedError
end
#
# Returns the local host for outgoing connections
#
def chost
raise NotImplementedError
end
#
# Returns the local port for outgoing connections
#
def cport
raise NotImplementedError
end
#
# Returns the boolean indicating SSL
#
def ssl
raise NotImplementedError
end
#
# Returns the string indicating SSLVersion
#
def ssl_version
raise NotImplementedError
end
#
# Returns the proxy configuration
#
def proxies
raise NotImplementedError
end
attr_accessor :sock
end
end
end
end

View File

@ -0,0 +1,219 @@
require 'metasploit/framework/tcp/client'
module Metasploit
module Framework
module Telnet
module Client
include Metasploit::Framework::Tcp::Client
include Msf::Auxiliary::Login
attr_accessor :banner
#
# CONSTANTS
#
# Borrowing constants from Ruby's Net::Telnet class (ruby license)
IAC = 255.chr # "\377" # "\xff" # interpret as command
DONT = 254.chr # "\376" # "\xfe" # you are not to use option
DO = 253.chr # "\375" # "\xfd" # please, you use option
WONT = 252.chr # "\374" # "\xfc" # I won't use option
WILL = 251.chr # "\373" # "\xfb" # I will use option
SB = 250.chr # "\372" # "\xfa" # interpret as subnegotiation
GA = 249.chr # "\371" # "\xf9" # you may reverse the line
EL = 248.chr # "\370" # "\xf8" # erase the current line
EC = 247.chr # "\367" # "\xf7" # erase the current character
AYT = 246.chr # "\366" # "\xf6" # are you there
AO = 245.chr # "\365" # "\xf5" # abort output--but let prog finish
IP = 244.chr # "\364" # "\xf4" # interrupt process--permanently
BREAK = 243.chr # "\363" # "\xf3" # break
DM = 242.chr # "\362" # "\xf2" # data mark--for connect. cleaning
NOP = 241.chr # "\361" # "\xf1" # nop
SE = 240.chr # "\360" # "\xf0" # end sub negotiation
EOR = 239.chr # "\357" # "\xef" # end of record (transparent mode)
ABORT = 238.chr # "\356" # "\xee" # Abort process
SUSP = 237.chr # "\355" # "\xed" # Suspend process
EOF = 236.chr # "\354" # "\xec" # End of file
SYNCH = 242.chr # "\362" # "\xf2" # for telfunc calls
OPT_BINARY = 0.chr # "\000" # "\x00" # Binary Transmission
OPT_ECHO = 1.chr # "\001" # "\x01" # Echo
OPT_RCP = 2.chr # "\002" # "\x02" # Reconnection
OPT_SGA = 3.chr # "\003" # "\x03" # Suppress Go Ahead
OPT_NAMS = 4.chr # "\004" # "\x04" # Approx Message Size Negotiation
OPT_STATUS = 5.chr # "\005" # "\x05" # Status
OPT_TM = 6.chr # "\006" # "\x06" # Timing Mark
OPT_RCTE = 7.chr # "\a" # "\x07" # Remote Controlled Trans and Echo
OPT_NAOL = 8.chr # "\010" # "\x08" # Output Line Width
OPT_NAOP = 9.chr # "\t" # "\x09" # Output Page Size
OPT_NAOCRD = 10.chr # "\n" # "\x0a" # Output Carriage-Return Disposition
OPT_NAOHTS = 11.chr # "\v" # "\x0b" # Output Horizontal Tab Stops
OPT_NAOHTD = 12.chr # "\f" # "\x0c" # Output Horizontal Tab Disposition
OPT_NAOFFD = 13.chr # "\r" # "\x0d" # Output Formfeed Disposition
OPT_NAOVTS = 14.chr # "\016" # "\x0e" # Output Vertical Tabstops
OPT_NAOVTD = 15.chr # "\017" # "\x0f" # Output Vertical Tab Disposition
OPT_NAOLFD = 16.chr # "\020" # "\x10" # Output Linefeed Disposition
OPT_XASCII = 17.chr # "\021" # "\x11" # Extended ASCII
OPT_LOGOUT = 18.chr # "\022" # "\x12" # Logout
OPT_BM = 19.chr # "\023" # "\x13" # Byte Macro
OPT_DET = 20.chr # "\024" # "\x14" # Data Entry Terminal
OPT_SUPDUP = 21.chr # "\025" # "\x15" # SUPDUP
OPT_SUPDUPOUTPUT = 22.chr # "\026" # "\x16" # SUPDUP Output
OPT_SNDLOC = 23.chr # "\027" # "\x17" # Send Location
OPT_TTYPE = 24.chr # "\030" # "\x18" # Terminal Type
OPT_EOR = 25.chr # "\031" # "\x19" # End of Record
OPT_TUID = 26.chr # "\032" # "\x1a" # TACACS User Identification
OPT_OUTMRK = 27.chr # "\e" # "\x1b" # Output Marking
OPT_TTYLOC = 28.chr # "\034" # "\x1c" # Terminal Location Number
OPT_3270REGIME = 29.chr # "\035" # "\x1d" # Telnet 3270 Regime
OPT_X3PAD = 30.chr # "\036" # "\x1e" # X.3 PAD
OPT_NAWS = 31.chr # "\037" # "\x1f" # Negotiate About Window Size
OPT_TSPEED = 32.chr # " " # "\x20" # Terminal Speed
OPT_LFLOW = 33.chr # "!" # "\x21" # Remote Flow Control
OPT_LINEMODE = 34.chr # "\"" # "\x22" # Linemode
OPT_XDISPLOC = 35.chr # "#" # "\x23" # X Display Location
OPT_OLD_ENVIRON = 36.chr # "$" # "\x24" # Environment Option
OPT_AUTHENTICATION = 37.chr # "%" # "\x25" # Authentication Option
OPT_ENCRYPT = 38.chr # "&" # "\x26" # Encryption Option
OPT_NEW_ENVIRON = 39.chr # "'" # "\x27" # New Environment Option
OPT_EXOPL = 255.chr # "\377" # "\xff" # Extended-Options-List
#
# This method establishes an Telnet connection to host and port specified by
# the RHOST and RPORT options, respectively. After connecting, the banner
# message is read in and stored in the 'banner' attribute. This method has the
# benefit of handling telnet option negotiation.
#
def connect(global = true, verbose = true)
@trace = ''
@recvd = ''
fd = super(global)
self.banner = ''
# Wait for a banner to arrive...
begin
Timeout.timeout(banner_timeout) do
while(true)
buff = recv(fd)
self.banner << buff if buff
if(self.banner =~ @login_regex or self.banner =~ @password_regex)
break
elsif self.banner =~ @busy_regex
# It's about to drop connection anyway -- seen on HP JetDirect telnet server
break
end
end
end
rescue ::Timeout::Error
end
self.banner.strip!
# Return the file descriptor to the caller
fd
end
# Sometimes telnet servers start RSTing if you get them angry.
# This is a short term fix; the problem is that we don't know
# if it's going to reset forever, or just this time, or randomly.
# A better solution is to get the socket connect to try again
# with a little backoff.
def connect_reset_safe
begin
connect
rescue Rex::ConnectionRefused
return :refused
end
return :connected
end
def recv(fd=self.sock, timeout=telnet_timeout)
recv_telnet(fd, timeout.to_f)
end
#
# Handle telnet option negotiation
#
# Appends to the @recvd buffer which is used to tell us whether we're at a
# login prompt, a password prompt, or a working shell.
#
def recv_telnet(fd, timeout)
data = ''
begin
data = fd.get_once(-1, timeout)
return nil if not data or data.length == 0
# combine CR+NULL into CR
data.gsub!(/#{CR}#{NULL}/no, CR)
# combine EOL into "\n"
data.gsub!(/#{EOL}/no, "\n")
data.gsub!(/#{IAC}(
[#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]|[#{DO}#{DONT}#{WILL}#{WONT}]
[#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}]|#{SB}[^#{IAC}]*#{IAC}#{SE}
)/xno) do
m = $1
if m == IAC
IAC
elsif m == AYT
fd.write("YES" + EOL)
''
elsif m[0,1] == DO
if(m[1,1] == OPT_BINARY)
fd.write(IAC + WILL + OPT_BINARY)
else
fd.write(IAC + WONT + m[1,1])
end
''
elsif m[0,1] == DONT
fd.write(IAC + WONT + m[1,1])
''
elsif m[0,1] == WILL
if m[1,1] == OPT_BINARY
fd.write(IAC + DO + OPT_BINARY)
# Disable Echo
elsif m[1,1] == OPT_ECHO
fd.write(IAC + DONT + OPT_ECHO)
elsif m[1,1] == OPT_SGA
fd.write(IAC + DO + OPT_SGA)
else
fd.write(IAC + DONT + m[1,1])
end
''
elsif m[0,1] == WONT
fd.write(IAC + DONT + m[1,1])
''
else
''
end
end
@trace << data
@recvd << data
fd.flush
rescue ::EOFError, ::Errno::EPIPE
end
data
end
#
# Wrappers for getters
#
def banner_timeout
raise NotImplementedError
end
def telnet_timeout
raise NotImplementedError
end
end
end
end
end

View File

@ -0,0 +1,13 @@
module Metasploit
module Framework
module Version
MAJOR = 4
MINOR = 10
PATCH = 1
PRERELEASE = 'dev'
end
VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::PATCH}-#{Version::PRERELEASE}"
GEM_VERSION = VERSION.gsub('-', '.pre.')
end
end

View File

@ -1,6 +1,18 @@
# -*- coding: binary -*-
#
# Standard Library
#
require 'fileutils'
#
# Project
#
require 'metasploit/framework/version'
require 'rex/compat'
module Msf
# This class wraps interaction with global configuration that can be used as a
@ -25,16 +37,16 @@ class Config < Hash
['HOME', 'LOCALAPPDATA', 'APPDATA', 'USERPROFILE'].each do |dir|
val = Rex::Compat.getenv(dir)
if (val and File.directory?(val))
return File.join(val, ".msf#{Msf::Framework::Major}")
return File.join(val, ".msf#{Metasploit::Framework::Version::MAJOR}")
end
end
begin
# First we try $HOME/.msfx
File.expand_path("~#{FileSep}.msf#{Msf::Framework::Major}")
File.expand_path("~#{FileSep}.msf#{Metasploit::Framework::Version::MAJOR}")
rescue ::ArgumentError
# Give up and install root + ".msfx"
InstallRoot + ".msf#{Msf::Framework::Major}"
InstallRoot + ".msf#{Metasploit::Framework::Version::MAJOR}"
end
end

View File

@ -0,0 +1,33 @@
# -*- coding: binary -*-
require 'msf/base/sessions/meterpreter'
require 'msf/base/sessions/meterpreter_java'
require 'msf/base/sessions/meterpreter_options'
module Msf
module Sessions
###
#
# This class creates a platform-specific meterpreter session type
#
###
class Meterpreter_Java_Android < Msf::Sessions::Meterpreter_Java_Java
def initialize(rstream, opts={})
super
self.platform = 'java/android'
end
def load_android
original = console.disable_output
console.disable_output = true
console.run_single('load android')
console.disable_output = original
end
end
end
end

View File

@ -59,6 +59,12 @@ module MeterpreterOptions
end
end
if session.platform =~ /android/i
if datastore['AutoLoadAndroid']
session.load_android
end
end
[ 'InitialAutoRunScript', 'AutoRunScript' ].each do |key|
if (datastore[key].empty? == false)
args = Shellwords.shellwords( datastore[key] )

View File

@ -1,3 +1,4 @@
# -*- coding: binary -*-
module Msf
module Simple
module Framework
@ -9,9 +10,10 @@ module Msf
# Ensure the module cache is accurate
self.modules.refresh_cache_from_database
# Initialize the default module search paths
if (Msf::Config.module_directory)
self.modules.add_module_path(Msf::Config.module_directory, opts)
add_engine_module_paths(Rails.application, opts)
Rails.application.railties.engines.each do |engine|
add_engine_module_paths(engine, opts)
end
# Initialize the user module search path
@ -27,6 +29,25 @@ module Msf
}
end
end
private
# Add 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 [void]
def add_engine_module_paths(engine, options={})
modules_paths = engine.paths['modules']
if modules_paths
modules_directories = modules_paths.existent_directories
modules_directories.each do |modules_directory|
modules.add_module_path(modules_directory, options)
end
end
end
end
end
end

View File

@ -60,6 +60,7 @@ require 'msf/core/post'
# Custom HTTP Modules
require 'msf/http/wordpress'
require 'msf/http/typo3'
require 'msf/http/jboss'
# Drivers
require 'msf/core/exploit_driver'

View File

@ -0,0 +1,47 @@
# -*- coding: binary -*-
module Msf
###
#
# This module provides methods for Distributed Reflective Denial of Service (DRDoS) attacks
#
###
module Auxiliary::DRDoS
def prove_amplification(response_map)
vulnerable = false
proofs = []
response_map.each do |request, responses|
responses ||= []
this_proof = ''
# compute packet amplification
if responses.size > 1
vulnerable = true
this_proof += "#{responses.size}x packet amplification"
else
this_proof += 'No packet amplification'
end
this_proof += ' and '
# compute bandwidth amplification
total_size = responses.map(&:size).reduce(:+)
bandwidth_amplification = total_size - request.size
if bandwidth_amplification > 0
vulnerable = true
multiplier = total_size / request.size
this_proof += "a #{multiplier}x, #{bandwidth_amplification}-byte bandwidth amplification"
else
this_proof += 'no bandwidth amplification'
end
# TODO (maybe): show the request and responses in more detail?
proofs << this_proof
end
[ vulnerable, proofs.join(', ') ]
end
end
end

View File

@ -2,7 +2,8 @@
require 'open3'
require 'fileutils'
require 'rex/proto/ntlm/crypt'
require 'metasploit/framework/jtr/cracker'
require 'metasploit/framework/jtr/wordlist'
module Msf
@ -24,243 +25,28 @@ module Auxiliary::JohnTheRipper
register_options(
[
OptPath.new('JOHN_BASE', [false, 'The directory containing John the Ripper (src, run, doc)']),
OptPath.new('JOHN_PATH', [false, 'The absolute path to the John the Ripper executable']),
OptPath.new('Wordlist', [false, 'The path to an optional Wordlist']),
OptBool.new('Munge',[false, 'Munge the Wordlist (Slower)', false])
OptPath.new('CONFIG', [false, 'The path to a John config file to use instead of the default']),
OptPath.new('CUSTOM_WORDLIST', [false, 'The path to an optional custom wordlist']),
OptInt.new('ITERATION_TIMOUT', [false, 'The max-run-time for each iteration of cracking']),
OptPath.new('JOHN_PATH', [false, 'The absolute path to the John the Ripper executable']),
OptBool.new('MUTATE', [false, 'Apply common mutations to the Wordlist (SLOW)', false]),
OptPath.new('POT', [false, 'The path to a John POT file to use instead of the default']),
OptBool.new('USE_CREDS', [false, 'Use existing credential data saved in the database', true]),
OptBool.new('USE_DB_INFO', [false, 'Use looted database schema info to seed the wordlist', true]),
OptBool.new('USE_DEFAULT_WORDLIST', [false, 'Use the default metasploit wordlist', true]),
OptBool.new('USE_HOSTNAMES', [false, 'Seed the wordlist with hostnames from the workspace', true]),
OptBool.new('USE_ROOT_WORDS', [false, 'Use the Common Root Words Wordlist', true])
], Msf::Auxiliary::JohnTheRipper
)
@run_path = nil
@john_path = ::File.join(Msf::Config.data_directory, "john")
autodetect_platform
end
# @return [String] the run path instance variable if the platform is detectable, nil otherwise.
def autodetect_platform
return @run_path if @run_path
cpuinfo_base = ::File.join(Msf::Config.data_directory, "cpuinfo")
if File.directory?(cpuinfo_base)
data = nil
case ::RUBY_PLATFORM
when /mingw|cygwin|mswin/
fname = "#{cpuinfo_base}/cpuinfo.exe"
if File.exists?(fname) and File.executable?(fname)
data = %x{"#{fname}"} rescue nil
end
case data
when /sse2/
@run_path ||= "run.win32.sse2/john.exe"
when /mmx/
@run_path ||= "run.win32.mmx/john.exe"
else
@run_path ||= "run.win32.any/john.exe"
end
when /x86_64-linux/
fname = "#{cpuinfo_base}/cpuinfo.ia64.bin"
if File.exists? fname
::FileUtils.chmod(0755, fname) rescue nil
data = %x{"#{fname}"} rescue nil
end
case data
when /mmx/
@run_path ||= "run.linux.x64.mmx/john"
else
@run_path ||= "run.linux.x86.any/john"
end
when /i[\d]86-linux/
fname = "#{cpuinfo_base}/cpuinfo.ia32.bin"
if File.exists? fname
::FileUtils.chmod(0755, fname) rescue nil
data = %x{"#{fname}"} rescue nil
end
case data
when /sse2/
@run_path ||= "run.linux.x86.sse2/john"
when /mmx/
@run_path ||= "run.linux.x86.mmx/john"
else
@run_path ||= "run.linux.x86.any/john"
end
end
end
return @run_path
end
def john_session_id
@session_id ||= ::Rex::Text.rand_text_alphanumeric(8)
end
def john_pot_file
::File.join( ::Msf::Config.config_directory, "john.pot" )
end
def john_cracked_passwords
ret = {}
return ret if not ::File.exist?(john_pot_file)
::File.open(john_pot_file, "rb") do |fd|
fd.each_line do |line|
hash,clear = line.sub(/\r?\n$/, '').split(",", 2)
ret[hash] = clear
end
end
ret
end
def john_show_passwords(hfile, format=nil)
res = {:cracked => 0, :uncracked => 0, :users => {} }
john_command = john_binary_path
if john_command.nil?
print_error("John the Ripper executable not found")
return res
end
pot = john_pot_file
conf = ::File.join(john_base_path, "confs", "john.conf")
cmd = [ john_command, "--show", "--conf=#{conf}", "--pot=#{pot}", hfile]
if format
cmd << "--format=" + format
end
if RUBY_VERSION =~ /^1\.8\./
cmd = cmd.join(" ")
end
::IO.popen(cmd, "rb") do |fd|
fd.each_line do |line|
line.chomp!
print_status(line)
if line =~ /(\d+) password hash(es)* cracked, (\d+) left/m
res[:cracked] = $1.to_i
res[:uncracked] = $2.to_i
end
# XXX: If the password had : characters in it, we're screwed
bits = line.split(':', -1)
# Skip blank passwords
next if not bits[2]
if (format== 'lm' or format == 'nt')
res[ :users ][ bits[0] ] = bits[1]
else
bits.last.chomp!
res[ :users ][ bits[0] ] = bits.drop(1)
end
end
end
res
end
def john_unshadow(passwd_file,shadow_file)
retval=""
john_command = john_binary_path
if john_command.nil?
print_error("John the Ripper executable not found")
return nil
end
if File.exists?(passwd_file)
unless File.readable?(passwd_file)
print_error("We do not have permission to read #{passwd_file}")
return nil
end
else
print_error("File does not exist: #{passwd_file}")
return nil
end
if File.exists?(shadow_file)
unless File.readable?(shadow_file)
print_error("We do not have permission to read #{shadow_file}")
return nil
end
else
print_error("File does not exist: #{shadow_file}")
return nil
end
cmd = [ john_command.gsub(/john$/, "unshadow"), passwd_file , shadow_file ]
if RUBY_VERSION =~ /^1\.8\./
cmd = cmd.join(" ")
end
::IO.popen(cmd, "rb") do |fd|
fd.each_line do |line|
retval << line
end
end
return retval
end
def john_wordlist_path
# We ship it under wordlists/
path = ::File.join(john_base_path, "wordlists", "password.lst")
# magnumripper/JohnTheRipper repo keeps it under run/
unless ::File.file? path
path = ::File.join(john_base_path, "run", "password.lst")
end
path
end
def john_binary_path
path = nil
if datastore['JOHN_PATH'] and ::File.file?(datastore['JOHN_PATH'])
path = datastore['JOHN_PATH']
::FileUtils.chmod(0755, path) rescue nil
return path
end
if not @run_path
if ::RUBY_PLATFORM =~ /mingw|cygwin|mswin/
::File.join(john_base_path, "john.exe")
else
path = ::File.join(john_base_path, "john")
::FileUtils.chmod(0755, path) rescue nil
end
else
path = ::File.join(john_base_path, @run_path)
::FileUtils.chmod(0755, path) rescue nil
end
if path and ::File.exists?(path)
return path
end
path = Rex::FileUtils.find_full_path("john") ||
Rex::FileUtils.find_full_path("john.exe")
end
def john_base_path
if datastore['JOHN_BASE'] and ::File.directory?(datastore['JOHN_BASE'])
return datastore['JOHN_BASE']
end
if datastore['JOHN_PATH'] and ::File.file?(datastore['JOHN_PATH'])
return ::File.dirname( datastore['JOHN_PATH'] )
end
@john_path
end
def john_expand_word(str)
res = [str]
str.split(/\W+/) {|w| res << w }
res.uniq
end
# @param pwd [String] Password recovered from cracking an LM hash
# @param hash [String] NTLM hash for this password
# @return [String] `pwd` converted to the correct case to match the
# given NTLM hash
# @return [nil] if no case matches the NT hash. This can happen when
# `pwd` came from a john run that only cracked half of the LM hash
def john_lm_upper_to_ntlm(pwd, hash)
pwd = pwd.upcase
hash = hash.upcase
@ -273,179 +59,41 @@ module Auxiliary::JohnTheRipper
end
def john_crack(hfile, opts={})
res = {:cracked => 0, :uncracked => 0, :users => {} }
john_command = john_binary_path
if john_command.nil?
print_error("John the Ripper executable not found")
return nil
end
# Don't bother making a log file, we'd just have to rm it when we're
# done anyway.
cmd = [ john_command, "--session=" + john_session_id, "--nolog"]
if opts[:conf]
cmd << ( "--conf=" + opts[:conf] )
else
cmd << ( "--conf=" + ::File.join(john_base_path, "confs", "john.conf") )
end
if opts[:pot]
cmd << ( "--pot=" + opts[:pot] )
else
cmd << ( "--pot=" + john_pot_file )
end
if opts[:format]
cmd << ( "--format=" + opts[:format] )
end
if opts[:wordlist]
cmd << ( "--wordlist=" + opts[:wordlist] )
end
if opts[:incremental]
cmd << ( "--incremental=" + opts[:incremental] )
end
if opts[:single]
cmd << ( "--single=" + opts[:single] )
end
if opts[:rules]
cmd << ( "--rules=" + opts[:rules] )
end
cmd << hfile
if RUBY_VERSION =~ /^1\.8\./
cmd = cmd.join(" ")
end
::IO.popen(cmd, "rb") do |fd|
fd.each_line do |line|
print_status("Output: #{line.strip}")
end
end
res
# This method creates a new {Metasploit::Framework::JtR::Cracker} and populates
# some of the attributes based on the module datastore options.
#
# @return [nilClass] if there is no active framework db connection
# @return [Metasploit::Framework::JtR::Cracker] if it successfully creates a JtR Cracker object
def new_john_cracker
return nil unless framework.db.active
Metasploit::Framework::JtR::Cracker.new(
config: datastore['CONFIG'],
john_path: datastore['JOHN_PATH'],
max_runtime: datastore['ITERATION_TIMEOUT'],
pot: datastore['POT'],
wordlist: datastore['CUSTOM_WORDLIST']
)
end
def build_seed
seed = []
#Seed the wordlist with Database , Table, and Instance Names
count = 0
schemas = myworkspace.notes.where('ntype like ?', '%.schema%')
unless schemas.nil? or schemas.empty?
schemas.each do |anote|
seed << anote.data['DBName']
count += 1
anote.data['Tables'].each do |table|
seed << table['TableName']
count += 1
table['Columns'].each do |column|
seed << column['ColumnName']
count += 1
end
end
end
end
print_status "Seeding wordlist with DB schema info... #{count} words added"
count = 0
instances = myworkspace.notes.find(:all, :conditions => ['ntype=?', 'mssql.instancename'])
unless instances.nil? or instances.empty?
instances.each do |anote|
seed << anote.data['InstanceName']
count += 1
end
end
print_status "Seeding with MSSQL Instance Names....#{count} words added"
count = 0
# Seed the wordlist with usernames, passwords, and hostnames
myworkspace.hosts.find(:all).each do |o|
if o.name
seed << john_expand_word( o.name )
count += 1
end
end
print_status "Seeding with hostnames....#{count} words added"
count = 0
myworkspace.creds.each do |o|
if o.user
seed << john_expand_word( o.user )
count +=1
end
if (o.pass and o.ptype !~ /hash/)
seed << john_expand_word( o.pass )
count += 1
end
end
print_status "Seeding with found credentials....#{count} words added"
count = 0
# Grab any known passwords out of the john.pot file
john_cracked_passwords.values do |v|
seed << v
count += 1
end
print_status "Seeding with cracked passwords from John....#{count} words added"
count = 0
#Grab the default John Wordlist
john = File.open(john_wordlist_path, "rb")
john.each_line do |line|
seed << line.chomp
count += 1
end
print_status "Seeding with default John wordlist...#{count} words added"
count = 0
if datastore['Wordlist']
wordlist= File.open(datastore['Wordlist'], "rb")
wordlist.each_line do |line|
seed << line.chomp
count ==1
end
print_status "Seeding from user supplied wordlist...#{count} words added"
end
unless seed.empty?
seed.flatten!
seed.uniq!
if datastore['Munge']
mungedseed=[]
seed.each do |word|
munged = word.gsub(/[sS]/, "$").gsub(/[aA]/,"@").gsub(/[oO]/,"0")
mungedseed << munged
munged.gsub!(/[eE]/, "3")
munged.gsub!(/[tT]/, "7")
mungedseed << munged
end
print_status "Adding #{mungedseed.count} words from munging..."
seed << mungedseed
seed.flatten!
seed.uniq!
end
end
print_status "De-duping the wordlist...."
print_status("Wordlist Seeded with #{seed.length} words")
return seed
# This method instantiates a {Metasploit::Framework::JtR::Wordlist}, writes the data
# out to a file and returns the {rex::quickfile} object.
#
# @return [nilClass] if there is no active framework db connection
# @return [Rex::Quickfile] if it successfully wrote the wordlist to a file
def wordlist_file
return nil unless framework.db.active
wordlist = Metasploit::Framework::JtR::Wordlist.new(
custom_wordlist: datastore['CUSTOM_WORDLIST'],
mutate: datastore['MUTATE'],
use_creds: datastore['USE_CREDS'],
use_db_info: datastore['USE_DB_INFO'],
use_default_wordlist: datastore['USE_DEFAULT_WORDLIST'],
use_hostnames: datastore['USE_HOSTNAMES'],
use_common_root: datastore['USE_ROOT_WORDS'],
workspace: myworkspace
)
wordlist.to_file
end
end
end

View File

@ -21,6 +21,10 @@ module Auxiliary::Login
def initialize(info = {})
super
create_login_ivars
end
def create_login_ivars
# Appended to by each read and gets reset after each send. Doing it
# this way lets us deal with partial reads in the middle of expect
# strings, e.g., the first recv returns "Pa" and the second returns

View File

@ -5,6 +5,7 @@
#
require 'msf/core/auxiliary/auth_brute'
require 'msf/core/auxiliary/dos'
require 'msf/core/auxiliary/drdos'
require 'msf/core/auxiliary/fuzzer'
require 'msf/core/auxiliary/report'
require 'msf/core/auxiliary/scanner'
@ -19,6 +20,7 @@ require 'msf/core/auxiliary/login'
require 'msf/core/auxiliary/rservices'
require 'msf/core/auxiliary/cisco'
require 'msf/core/auxiliary/nmap'
require 'msf/core/auxiliary/jtr'
require 'msf/core/auxiliary/natpmp'
require 'msf/core/auxiliary/iax2'
require 'msf/core/auxiliary/ntp'
require 'msf/core/auxiliary/pii'

View File

@ -0,0 +1,27 @@
# -*- coding: binary -*-
require 'rex/proto/natpmp'
module Msf
###
#
# This module provides methods for working with NAT-PMP
#
###
module Auxiliary::NATPMP
include Auxiliary::Scanner
include Rex::Proto::NATPMP
def initialize(info = {})
super
register_options(
[
Opt::RPORT(Rex::Proto::NATPMP::DefaultPort),
Opt::CHOST
],
self.class
)
end
end
end

View File

@ -0,0 +1,33 @@
# -*- coding: binary -*-
require 'rex/proto/ntp'
module Msf
###
#
# This module provides methods for working with NTP
#
###
module Auxiliary::NTP
include Auxiliary::Scanner
#
# Initializes an instance of an auxiliary module that uses NTP
#
def initialize(info = {})
super
register_options(
[
Opt::RPORT(123),
], self.class)
register_advanced_options(
[
OptInt.new('VERSION', [true, 'Use this NTP version', 2]),
OptInt.new('IMPLEMENTATION', [true, 'Use this NTP mode 7 implementation', 3])
], self.class)
end
end
end

View File

@ -8,10 +8,13 @@ module Msf
###
module Auxiliary::Report
extend Metasploit::Framework::Require
optionally_include_metasploit_credential_creation
def initialize(info = {})
super
# This method overrides the method from Metasploit::Credential to check for an active db
def active_db?
framework.db.active
end
# Shortcut method for detecting when the DB is active
@ -23,6 +26,18 @@ module Auxiliary::Report
@myworkspace = framework.db.find_workspace(self.workspace)
end
# 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}
def myworkspace_id
if framework.db.active
myworkspace.id
else
nil
end
end
def mytask
if self[:task]
return self[:task].record
@ -215,7 +230,7 @@ module Auxiliary::Report
end
case ctype
when "text/plain"
when /^text\/[\w\.]+$/
ext = "txt"
end
# This method is available even if there is no database, don't bother checking
@ -387,6 +402,9 @@ module Auxiliary::Report
print_status "Collecting #{cred_opts[:user]}:#{cred_opts[:pass]}"
framework.db.report_auth_info(cred_opts)
end
end
end

View File

@ -1,3 +1,4 @@
# -*- coding: binary -*-
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit

View File

@ -1,3 +1,4 @@
# -*- coding: binary -*-
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit

Some files were not shown because too many files have changed in this diff Show More