Files
metasploit-gs/lib/metasploit/framework/jtr/wordlist.rb
T
2015-12-30 11:00:45 -06:00

430 lines
14 KiB
Ruby

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.where(['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, '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, '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