CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/lib/metasploit/framework/password_crackers/cracker.rb
Views: 1904
module Metasploit1module Framework2module PasswordCracker3class PasswordCrackerNotFoundError < StandardError4end56class Cracker7include ActiveModel::Validations89# @!attribute attack10# @return [String] The attack mode for hashcat to use (not applicable to John)11attr_accessor :attack1213# @!attribute config14# @return [String] The path to an optional config file for John to use15attr_accessor :config1617# @!attribute cracker18# @return [String] Which cracker to use. 'john' and 'hashcat' are valid19attr_accessor :cracker2021# @!attribute cracker_path22# This attribute allows the user to specify a cracker binary to use.23# If not supplied, the Cracker will search the PATH for a suitable john or hashcat binary24# and finally fall back to the pre-compiled john versions shipped with Metasploit.25#26# @return [String] The file path to an alternative cracker binary to use27attr_accessor :cracker_path2829# @!attribute format30# If the cracker type is john, this format will automatically be translated31# to the hashcat equivalent via jtr_format_to_hashcat_format32#33# @return [String] The hash format to try.34attr_accessor :format3536# @!attribute fork37# If the cracker type is john, the amount of forks to specify38#39# @return [String] The hash format to try.40attr_accessor :fork4142# @!attribute hash_path43# @return [String] The path to the file containing the hashes44attr_accessor :hash_path4546# @!attribute incremental47# @return [String] The incremental mode to use48attr_accessor :incremental4950# @!attribute increment_length51# @return [Array] The incremental min and max to use52attr_accessor :increment_length5354# @!attribute mask55# If the cracker type is hashcat, If set, the mask to use. Should consist of the character sets56# pre-defined by hashcat, such as ?d ?s ?l etc57#58# @return [String] The mask to use59attr_accessor :mask6061# @!attribute max_runtime62# @return [Integer] An optional maximum duration of the cracking attempt in seconds63attr_accessor :max_runtime6465# @!attribute max_length66# @return [Integer] An optional maximum length of password to attempt cracking67attr_accessor :max_length6869# @!attribute optimize70# @return [Boolean] If the Optimize flag should be given to Hashcat71attr_accessor :optimize7273# @!attribute pot74# @return [String] The file path to an alternative John pot file to use75attr_accessor :pot7677# @!attribute rules78# @return [String] The wordlist mangling rules to use inside John/Hashcat79attr_accessor :rules8081# @!attribute wordlist82# @return [String] The file path to the wordlist to use83attr_accessor :wordlist8485validates :config, 'Metasploit::Framework::File_path': true, if: -> { config.present? }8687validates :cracker, inclusion: { in: %w[john hashcat] }8889validates :cracker_path, 'Metasploit::Framework::Executable_path': true, if: -> { cracker_path.present? }9091validates :fork,92numericality: {93only_integer: true,94greater_than_or_equal_to: 195}, if: -> { fork.present? }9697validates :hash_path, 'Metasploit::Framework::File_path': true, if: -> { hash_path.present? }9899validates :pot, 'Metasploit::Framework::File_path': true, if: -> { pot.present? }100101validates :max_runtime,102numericality: {103only_integer: true,104greater_than_or_equal_to: 0105}, if: -> { max_runtime.present? }106107validates :max_length,108numericality: {109only_integer: true,110greater_than_or_equal_to: 0111}, if: -> { max_length.present? }112113validates :wordlist, 'Metasploit::Framework::File_path': true, if: -> { wordlist.present? }114115# @param attributes [Hash{Symbol => String,nil}]116def initialize(attributes = {})117attributes.each do |attribute, value|118public_send("#{attribute}=", value)119end120end121122# This method takes a {framework.db.cred.private.jtr_format} (string), and123# returns the string number associated to the hashcat format124#125# @param format [String] A jtr_format string126# @return [String] The format number for Hashcat127def jtr_format_to_hashcat_format(format)128case format129# nix130when 'md5crypt'131'500'132when 'descrypt'133'1500'134when 'bsdicrypt'135'12400'136when 'sha256crypt'137'7400'138when 'sha512crypt'139'1800'140when 'bcrypt'141'3200'142# windows143when 'lm', 'lanman'144'3000'145when 'nt', 'ntlm'146'1000'147when 'mscash'148'1100'149when 'mscash2'150'2100'151when 'netntlm'152'5500'153when 'netntlmv2'154'5600'155# dbs156when 'mssql'157'131'158when 'mssql05'159'132'160when 'mssql12'161'1731'162# hashcat requires a format we dont have all the data for163# in the current dumper, so this is disabled in module and lib164# when 'oracle', 'des,oracle'165# return '3100'166when 'oracle11', 'raw-sha1,oracle'167'112'168when 'oracle12c', 'pbkdf2,oracle12c'169'12300'170when 'postgres', 'dynamic_1034', 'raw-md5,postgres'171'12'172when 'mysql'173'200'174when 'mysql-sha1'175'300'176when 'PBKDF2-HMAC-SHA512' # osx 10.8+177'7100'178# osx179when 'xsha' # osx 10.4-6180'122'181when 'xsha512' # osx 10.7182'1722'183# webapps184when 'PBKDF2-HMAC-SHA1' # Atlassian185'12001'186when 'phpass' # Wordpress/PHPass, Joomla, phpBB3187'400'188when 'mediawiki' # mediawiki b type189'3711'190# mobile191when 'android-samsung-sha1'192'5800'193when 'android-sha1'194'110'195when 'android-md5'196'10'197when 'hmac-md5'198'10200'199when 'dynamic_82'200'1710'201when 'ssha'202'111'203when 'raw-sha512'204'1700'205when 'raw-sha256'206'1400'207when 'raw-sha1'208'100'209when 'raw-md5'210'0'211when 'smd5'212'6300'213when 'ssha256'214'1411'215when 'ssha512'216'1711'217when 'Raw-MD5u'218'30'219when 'pbkdf2-sha256'220'10900'221end222end223224# This method sets the appropriate parameters to run a cracker in incremental mode225def mode_incremental226self.increment_length = nil227self.wordlist = nil228self.mask = nil229self.max_runtime = nil230if cracker == 'john'231self.rules = nil232self.incremental = 'Digits'233elsif cracker == 'hashcat'234self.attack = '3'235self.incremental = true236end237end238239# This method sets the appropriate parameters to run a cracker in wordlist mode240#241# @param file [String] A file location of the wordlist to use242def mode_wordlist(file)243self.increment_length = nil244self.incremental = nil245self.max_runtime = nil246self.mask = nil247if cracker == 'john'248self.wordlist = file249self.rules = 'wordlist'250elsif cracker == 'hashcat'251self.wordlist = file252self.attack = '0'253end254end255256# This method sets the appropriate parameters to run a cracker in a pin mode (4-8 digits) on hashcat257def mode_pin258self.rules = nil259if cracker == 'hashcat'260self.attack = '3'261self.mask = '?d' * 8262self.incremental = true263self.increment_length = [4, 8]264self.max_runtime = 300 # 5min on an i7 got through 4-7 digits. 8digit was 32min more265end266end267268# This method sets the john to 'normal' mode269def mode_normal270if cracker == 'john'271self.max_runtime = nil272self.mask = nil273self.wordlist = nil274self.rules = nil275self.incremental = nil276self.increment_length = nil277end278end279280# This method sets the john to single mode281#282# @param file [String] A file location of the wordlist to use283def mode_single(file)284if cracker == 'john'285self.wordlist = file286self.rules = 'single'287self.incremental = nil288self.increment_length = nil289self.mask = nil290end291end292293# This method follows a decision tree to determine the path294# to the cracker binary we should use.295#296# @return [String, NilClass] Returns Nil if a binary path could not be found, or a String containing the path to the selected JTR binary on success.297def binary_path298# Always prefer a manually entered path299if cracker_path && ::File.file?(cracker_path)300return cracker_path301else302# Look in the Environment PATH for the john binary303if cracker == 'john'304path = Rex::FileUtils.find_full_path('john') ||305Rex::FileUtils.find_full_path('john.exe')306elsif cracker == 'hashcat'307path = Rex::FileUtils.find_full_path('hashcat') ||308Rex::FileUtils.find_full_path('hashcat.exe')309else310raise PasswordCrackerNotFoundError, 'No suitable Cracker was selected, so a binary could not be found on the system'311end312313if path && ::File.file?(path)314return path315end316317raise PasswordCrackerNotFoundError, 'No suitable john/hashcat binary was found on the system'318end319end320321# This method runs the command from {#crack_command} and yields each line of output.322#323# @yield [String] a line of output from the cracker command324# @return [void]325def crack(&block)326if cracker == 'john'327results = john_crack_command328elsif cracker == 'hashcat'329results = hashcat_crack_command330end331::IO.popen(results, 'rb') do |fd|332fd.each_line(&block)333end334end335336# This method returns the version of John the Ripper or Hashcat being used.337#338# @raise [PasswordCrackerNotFoundError] if a suitable cracker binary was never found339# @return [String] the version detected340def cracker_version341if cracker == 'john'342cmd = binary_path343elsif cracker == 'hashcat'344cmd = binary_path345cmd << (' -V')346end347::IO.popen(cmd, 'rb') do |fd|348fd.each_line do |line|349if cracker == 'john'350# John the Ripper 1.8.0.13-jumbo-1-bleeding-973a245b96 2018-12-17 20:12:51 +0100 OMP [linux-gnu 64-bit x86_64 AVX2 AC]351# John the Ripper 1.9.0-jumbo-1 OMP [linux-gnu 64-bit x86_64 AVX2 AC]352# John the Ripper password cracker, version 1.8.0.2-bleeding-jumbo_omp [64-bit AVX-autoconf]353# John the Ripper password cracker, version 1.8.0354return Regexp.last_match(1).strip if line =~ /John the Ripper(?: password cracker, version)? ([^\[]+)/355elsif cracker == 'hashcat'356# v5.1.0357return Regexp.last_match(1) if line =~ /(v[\d.]+)/358end359end360end361nil362end363364# This method is used to determine which format of the no log option should be used365# --no-log vs --nolog https://github.com/openwall/john/commit/8982e4f7a2e874aab29807a05b421373015c9b61366# We base this either on a date being in the version, or running the command and checking the output367#368# @return [String] The nolog format to use369def john_nolog_format370if /(\d{4}-\d{2}-\d{2})/ =~ cracker_version371# we lucked out and theres a date, we'll check its older than the commit that changed the nolog372if Date.parse(Regexp.last_match(1)) < Date.parse('2020-11-27')373return '--nolog'374end375376return '--no-log'377end378379# no date, so lets give it a run with the old format and check if we raise an error380# on *nix 'unknown option' goes to stderr381::IO.popen([binary_path, '--nolog', { err: %i[child out] }], 'rb') do |fd|382return '--nolog' unless fd.read.include? 'Unknown option'383end384'--no-log'385end386387# This method builds an array for the command to actually run the cracker.388# It builds the command from all of the attributes on the class.389#390# @raise [PasswordCrackerNotFoundError] if a suitable John binary was never found391# @return [Array] An array set up for {::IO.popen} to use392def john_crack_command393cmd_string = binary_path394395cmd = [cmd_string, '--session=' + cracker_session_id, john_nolog_format]396397if config.present?398cmd << ('--config=' + config)399else400cmd << ('--config=' + john_config_file)401end402403if pot.present?404cmd << ('--pot=' + pot)405else406cmd << ('--pot=' + john_pot_file)407end408409if fork.present? && fork > 1410cmd << ('--fork=' + fork.to_s)411end412413if format.present?414cmd << ('--format=' + format)415end416417if wordlist.present?418cmd << ('--wordlist=' + wordlist)419end420421if incremental.present?422cmd << ('--incremental=' + incremental)423end424425if rules.present?426cmd << ('--rules=' + rules)427end428429if max_runtime.present?430cmd << ('--max-run-time=' + max_runtime.to_s)431end432433if max_length.present?434cmd << ('--max-len=' + max_length.to_s)435end436437cmd << hash_path438end439440# This method builds an array for the command to actually run the cracker.441# It builds the command from all of the attributes on the class.442#443# @raise [PasswordCrackerNotFoundError] if a suitable Hashcat binary was never found444# @return [Array] An array set up for {::IO.popen} to use445def hashcat_crack_command446cmd_string = binary_path447cmd = [cmd_string, '--session=' + cracker_session_id, '--logfile-disable', '--quiet', '--username']448449if pot.present?450cmd << ('--potfile-path=' + pot)451else452cmd << ('--potfile-path=' + john_pot_file)453end454455if format.present?456cmd << ('--hash-type=' + jtr_format_to_hashcat_format(format))457end458459if optimize.present?460# https://hashcat.net/wiki/doku.php?id=frequently_asked_questions#what_is_the_maximum_supported_password_length_for_optimized_kernels461# Optimized Kernels has a large impact on speed. Here are some stats from Hashcat 5.1.0:462463# Kali Linux on Dell Precision M3800464## hashcat -b -w 2 -m 0465# * Device #1: Quadro K1100M, 500/2002 MB allocatable, 2MCU466# Speed.#1.........: 185.9 MH/s (11.15ms) @ Accel:64 Loops:16 Thr:1024 Vec:1467468## hashcat -b -w 2 -O -m 0469# * Device #1: Quadro K1100M, 500/2002 MB allocatable, 2MCU470# Speed.#1.........: 463.6 MH/s (8.92ms) @ Accel:64 Loops:32 Thr:1024 Vec:1471472# Windows 10473# PS C:\hashcat-5.1.0> .\hashcat64.exe -b -O -w 2 -m 0474# * Device #1: GeForce RTX 2070 SUPER, 2048/8192 MB allocatable, 40MCU475# Speed.#1.........: 13914.0 MH/s (5.77ms) @ Accel:128 Loops:64 Thr:256 Vec:1476477# PS C:\hashcat-5.1.0> .\hashcat64.exe -b -O -w 2 -m 0478# * Device #1: GeForce RTX 2070 SUPER, 2048/8192 MB allocatable, 40MCU479# Speed.#1.........: 31545.6 MH/s (10.36ms) @ Accel:256 Loops:128 Thr:256 Vec:1480481# This change should result in 225%-250% speed boost at the sacrifice of some password length, which most likely482# wouldn't be tested inside of MSF since most users are using the MSF modules for word list and easy cracks.483# Anything of length where this would cut off is most likely being done independently (outside MSF)484485cmd << ('-O')486end487488if incremental.present?489cmd << ('--increment')490if increment_length.present?491cmd << ('--increment-min=' + increment_length[0].to_s)492cmd << ('--increment-max=' + increment_length[1].to_s)493else494# anything more than max 4 on even des took 8+min on an i7.495# maybe in the future this can be adjusted or made a variable496# but current time, we'll leave it as this seems like reasonable497# time expectation for a module to run498cmd << ('--increment-max=4')499end500end501502if rules.present?503cmd << ('--rules-file=' + rules)504end505506if attack.present?507cmd << ('--attack-mode=' + attack)508end509510if max_runtime.present?511cmd << ('--runtime=' + max_runtime.to_s)512end513514cmd << hash_path515516if mask.present?517cmd << mask.to_s518end519520# must be last521if wordlist.present?522cmd << (wordlist)523end524cmd525end526527# This runs the show command in john and yields cracked passwords.528#529# @return [Array] the output from the command split on newlines530def each_cracked_password531::IO.popen(show_command, 'rb').readlines532end533534# This method returns the path to a default john.conf file.535#536# @return [String] the path to the default john.conf file537def john_config_file538::File.join(::Msf::Config.data_directory, 'jtr', 'john.conf')539end540541# This method returns the path to a default john.pot file.542#543# @return [String] the path to the default john.pot file544def john_pot_file545::File.join(::Msf::Config.config_directory, 'john.pot')546end547548# This method is a getter for a random Session ID for the cracker.549# It allows us to dinstiguish between cracking sessions.550#551# @ return [String] the Session ID to use552def cracker_session_id553@session_id ||= ::Rex::Text.rand_text_alphanumeric(8)554end555556# This method builds the command to show the cracked passwords.557#558# @raise [JohnNotFoundError] if a suitable John binary was never found559# @return [Array] An array set up for {::IO.popen} to use560def show_command561cmd_string = binary_path562563pot_file = pot || john_pot_file564if cracker == 'hashcat'565cmd = [cmd_string, '--show', '--username', "--potfile-path=#{pot_file}", "--hash-type=#{jtr_format_to_hashcat_format(format)}"]566elsif cracker == 'john'567cmd = [cmd_string, '--show', "--pot=#{pot_file}", "--format=#{format}"]568569if config570cmd << "--config=#{config}"571else572cmd << ('--config=' + john_config_file)573end574end575cmd << hash_path576end577578end579end580end581end582583584