2014-06-13 16:10:12 +00:00
|
|
|
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
|
2014-06-18 16:38:07 +00:00
|
|
|
# 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.
|
|
|
|
#
|
2014-06-13 16:10:12 +00:00
|
|
|
# @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
|
|
|
|
|
2014-06-14 22:54:37 +00:00
|
|
|
validates :config, :'Metasploit::Framework::File_path' => true, if: 'config.present?'
|
|
|
|
|
|
|
|
validates :hash_path, :'Metasploit::Framework::File_path' => true, if: 'hash_path.present?'
|
|
|
|
|
2014-06-14 23:01:24 +00:00
|
|
|
validates :john_path, :'Metasploit::Framework::Executable_path' => true, if: 'john_path.present?'
|
|
|
|
|
2014-06-14 22:54:37 +00:00
|
|
|
validates :pot, :'Metasploit::Framework::File_path' => true, if: 'pot.present?'
|
|
|
|
|
2014-06-13 16:10:12 +00:00
|
|
|
validates :max_runtime,
|
|
|
|
numericality: {
|
|
|
|
only_integer: true,
|
|
|
|
greater_than_or_equal_to: 0
|
2014-06-15 00:59:59 +00:00
|
|
|
}, if: 'max_runtime.present?'
|
2014-06-13 16:10:12 +00:00
|
|
|
|
2014-06-14 22:54:37 +00:00
|
|
|
validates :wordlist, :'Metasploit::Framework::File_path' => true, if: 'wordlist.present?'
|
|
|
|
|
2014-06-13 16:10:12 +00:00
|
|
|
# @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
|
2014-06-18 19:50:08 +00:00
|
|
|
if john_path && ::File.file?(john_path)
|
2014-06-13 16:10:12 +00:00
|
|
|
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")
|
|
|
|
|
2014-06-18 15:57:41 +00:00
|
|
|
if path && ::File.file?(path)
|
2014-06-13 16:10:12 +00:00
|
|
|
bin_path = path
|
|
|
|
else
|
|
|
|
# If we can't find john anywhere else, look at our precompiled binaries
|
|
|
|
bin_path = select_shipped_binary
|
|
|
|
end
|
|
|
|
end
|
2014-06-18 15:57:41 +00:00
|
|
|
raise JohnNotFoundError, 'No suitable John binary was found on the system' if bin_path.blank?
|
2014-06-13 16:10:12 +00:00
|
|
|
bin_path
|
|
|
|
end
|
|
|
|
|
2014-06-13 19:53:56 +00:00
|
|
|
# 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]
|
2014-06-13 16:10:12 +00:00
|
|
|
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
|
2014-06-19 18:08:43 +00:00
|
|
|
cmd = [ cmd_string, '--session=' + john_session_id, '--nolog' ]
|
2014-06-13 16:10:12 +00:00
|
|
|
|
|
|
|
if config.present?
|
2014-06-13 21:08:56 +00:00
|
|
|
cmd << ( "--config=" + config )
|
2014-07-21 23:26:50 +00:00
|
|
|
else
|
|
|
|
cmd << ( "--config=" + john_config_file )
|
2014-06-13 16:10:12 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if pot.present?
|
|
|
|
cmd << ( "--pot=" + pot )
|
|
|
|
else
|
2014-06-13 19:53:56 +00:00
|
|
|
cmd << ( "--pot=" + john_pot_file)
|
2014-06-13 16:10:12 +00:00
|
|
|
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
|
|
|
|
|
2014-06-15 01:03:56 +00:00
|
|
|
if max_runtime.present?
|
|
|
|
cmd << ( "--max-run-time=" + max_runtime.to_s)
|
|
|
|
end
|
|
|
|
|
2014-06-13 16:10:12 +00:00
|
|
|
cmd << hash_path
|
|
|
|
end
|
|
|
|
|
2014-06-18 16:24:55 +00:00
|
|
|
# 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
|
|
|
|
|
2014-07-21 23:26:50 +00:00
|
|
|
# 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
|
|
|
|
|
2014-06-13 16:10:12 +00:00
|
|
|
# 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
|
|
|
|
|
2014-06-14 17:28:17 +00:00
|
|
|
# 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}"
|
2014-07-21 23:26:50 +00:00
|
|
|
else
|
|
|
|
cmd << ( "--config=" + john_config_file )
|
2014-06-14 17:28:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
cmd << hash_path
|
|
|
|
end
|
|
|
|
|
2014-06-13 16:10:12 +00:00
|
|
|
private
|
|
|
|
|
|
|
|
# This method tries to identify the correct version of the pre-shipped
|
|
|
|
# JtR binaries to use based on the platform.
|
|
|
|
#
|
2014-07-21 22:58:27 +00:00
|
|
|
# @return [NilClass] if the correct binary could not be determined
|
2014-06-13 16:10:12 +00:00
|
|
|
# @return [String] the path to the selected binary
|
|
|
|
def select_shipped_binary
|
|
|
|
cpuinfo_base = ::File.join(Msf::Config.data_directory, "cpuinfo")
|
2014-07-21 22:58:27 +00:00
|
|
|
run_path = nil
|
2014-06-13 16:10:12 +00:00
|
|
|
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/
|
2014-07-21 22:58:27 +00:00
|
|
|
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.win32.sse2", "john.exe")
|
2014-06-13 16:10:12 +00:00
|
|
|
when /mmx/
|
2014-07-21 22:58:27 +00:00
|
|
|
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.win32.mmx", "john.exe")
|
2014-06-13 16:10:12 +00:00
|
|
|
else
|
2014-07-21 22:58:27 +00:00
|
|
|
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.win32.any", "john.exe")
|
2014-06-13 16:10:12 +00:00
|
|
|
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/
|
2014-07-21 22:58:27 +00:00
|
|
|
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x64.mmx", "john")
|
2014-06-13 16:10:12 +00:00
|
|
|
else
|
2014-07-21 22:58:27 +00:00
|
|
|
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.any", "john")
|
2014-06-13 16:10:12 +00:00
|
|
|
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/
|
2014-07-21 22:58:27 +00:00
|
|
|
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.sse2", "john")
|
2014-06-13 16:10:12 +00:00
|
|
|
when /mmx/
|
2014-07-21 22:58:27 +00:00
|
|
|
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.mmx", "john")
|
2014-06-13 16:10:12 +00:00
|
|
|
else
|
2014-07-21 22:58:27 +00:00
|
|
|
run_path ||= ::File.join(Msf::Config.data_directory, "john", "run.linux.x86.any", "john")
|
2014-06-13 16:10:12 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2014-07-21 22:58:27 +00:00
|
|
|
run_path
|
2014-06-13 16:10:12 +00:00
|
|
|
end
|
|
|
|
|
2014-06-14 20:48:25 +00:00
|
|
|
|
|
|
|
|
2014-06-13 16:10:12 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|