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/keytab.rb
Views: 11783
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6def initialize(info = {})7super(8update_info(9info,10'Name' => 'Kerberos keytab utilities',11'Description' => %q{12Utilities for interacting with keytab files, which can store the hashed passwords of one or13more principals.1415Discovered keytab files can be used to generate Kerberos Ticket Granting Tickets, or bruteforced16offline.1718Keytab files can be also useful for decrypting Kerberos traffic using Wireshark dissectors,19including the krbtgt encrypted blobs if the AES password hash is used.20},21'Author' => [22'alanfoster' # Metasploit Module23],24'References' => [25],26'License' => MSF_LICENSE,27'Notes' => {28'Stability' => [],29'SideEffects' => [],30'Reliability' => []31},32'Actions' => [33['LIST', { 'Description' => 'List the entries in the keytab file' }],34['ADD', { 'Description' => 'Add a new entry to the keytab file' }],35['EXPORT', { 'Description' => 'Export the current database creds to the keytab file' }]36],37'DefaultAction' => 'LIST',38'DefaultOptions' => {39'VERBOSE' => true40}41)42)4344supported_encryption_names = ['ALL']45supported_encryption_names += Rex::Proto::Kerberos::Crypto::Encryption::SUPPORTED_ENCRYPTIONS46.map { |id| Rex::Proto::Kerberos::Crypto::Encryption.const_name(id) }4748register_options(49[50OptString.new('KEYTAB_FILE', [true, 'The keytab file to manipulate']),51OptString.new('PRINCIPAL', [false, 'The kerberos principal name']),52OptString.new('REALM', [false, 'The kerberos realm']),53OptEnum.new('ENCTYPE', [false, 'The enctype to use. If a password is specified this can set to \'ALL\'', supported_encryption_names[0], supported_encryption_names]),54OptString.new('KEY', [false, 'The key to use. If not specified, the key will be generated from the password']),55OptString.new('PASSWORD', [false, 'The password. If not specified, the KEY option will be used']),56OptString.new('SALT', [false, 'The salt to use when creating a key from the password. If not specified, this will be generated from the principal name']),57OptInt.new('KVNO', [true, 'The kerberos key version number', 1]),58OptEnum.new('OUTPUT_FORMAT', [true, 'The output format to use for listing keytab entries', 'table', %w[csv table]]),59]60)61end6263def run64if datastore['KEYTAB_FILE'].blank?65fail_with(Failure::BadConfig, 'KEYTAB_FILE must be set to a non-empty string')66end6768case action.name69when 'LIST'70list_keytab_entries71when 'ADD'72add_keytab_entry73when 'EXPORT'74export_keytab_entries75end76end7778# Export the keytab entries from the database into the given keytab file. The keytab file will be created if it did not previously exist.79def export_keytab_entries80unless framework.db.active81print_error('export not available, because the database is not active.')82return83end8485keytab_path = datastore['KEYTAB_FILE']86keytab = read_or_initialize_keytab(keytab_path)8788# Kerberos encryption keys, most likely extracted from running secrets dump89kerberos_key_creds = framework.db.creds(type: 'Metasploit::Credential::KrbEncKey')90keytab_entries = kerberos_key_creds.map do |cred|91[92cred.id,93{94realm: cred.realm.value,95components: cred.public.username.split('/'),96name_type: Rex::Proto::Kerberos::Model::NameType::NT_PRINCIPAL,97timestamp: Time.at(0).utc,98vno8: datastore['KVNO'],99vno: datastore['KVNO'],100keyblock: {101enctype: cred.private.enctype,102data: cred.private.key103}104}105]106end107108# Additionally append NTHASH values, which don't require a salt109nthash_creds = framework.db.creds(type: 'Metasploit::Credential::NTLMHash')110keytab_entries += nthash_creds.map do |cred|111nthash = cred.private.to_s.split(':').last112[113cred.id,114{115realm: cred.realm&.value.to_s,116components: cred.public.username.split('/'),117name_type: Rex::Proto::Kerberos::Model::NameType::NT_PRINCIPAL,118timestamp: Time.at(0).utc,119vno8: datastore['KVNO'],120vno: datastore['KVNO'],121keyblock: {122enctype: Rex::Proto::Kerberos::Crypto::Encryption::RC4_HMAC,123data: [nthash].pack('H*')124}125}126]127end128129if keytab_entries.empty?130print_status('No entries to export')131end132133keytab.key_entries.concat(keytab_entries.sort_by { |id, _entry| id }.to_h.values)134write_keytab(keytab_path, keytab)135end136137# Add keytab entries into the given keytab file. The keytab file will be created if it did not previously exist.138def add_keytab_entry139keytab_path = datastore['KEYTAB_FILE']140keytab = read_or_initialize_keytab(keytab_path)141142principal = datastore['PRINCIPAL']143fail_with(Failure::BadConfig, 'PRINCIPAL must be set to a non-empty string') if principal.blank?144145realm = datastore['REALM']146fail_with(Failure::BadConfig, 'REALM must be set to a non-empty string') if realm.blank?147148if /[[:lower:]]/.match(realm)149print_warning("REALM option has lowercase letters present - this may not work as expected for Window's Active Directory environments which uses a uppercase domain")150end151152keyblocks = []153if datastore['KEY'].present?154fail_with(Failure::BadConfig, 'enctype ALL not supported when KEY is set') if datastore['ENCTYPE'] == 'ALL'155156keyblocks << {157enctype: Rex::Proto::Kerberos::Crypto::Encryption.value_for(datastore['ENCTYPE']),158data: [datastore['KEY']].pack('H*')159}160elsif datastore['PASSWORD'].present?161password = datastore['PASSWORD']162salt = datastore['SALT']163if salt.blank?164salt = "#{realm}#{principal.split('/')[0]}"165vprint_status("Generating key with salt: #{salt}. The SALT option can be set manually")166end167168if datastore['ENCTYPE'] == 'ALL'169enctypes = Rex::Proto::Kerberos::Crypto::Encryption::SUPPORTED_ENCRYPTIONS170else171enctypes = [Rex::Proto::Kerberos::Crypto::Encryption.value_for(datastore['ENCTYPE'])]172end173174enctypes.each do |enctype|175encryptor = Rex::Proto::Kerberos::Crypto::Encryption.from_etype(enctype)176keyblocks << {177enctype: enctype,178data: encryptor.string_to_key(password, salt)179}180end181else182fail_with(Failure::BadConfig, 'KEY or PASSWORD required to add a new entry')183end184185keytab_entries = keyblocks.map do |keyblock|186{187realm: realm,188components: principal.split('/'),189name_type: Rex::Proto::Kerberos::Model::NameType::NT_PRINCIPAL,190timestamp: Time.at(0).utc,191vno8: datastore['KVNO'],192vno: datastore['KVNO'],193keyblock: keyblock194}195end196keytab.key_entries.concat(keytab_entries)197write_keytab(keytab_path, keytab)198end199200# List the keytab entries within the keytab file201def list_keytab_entries202if datastore['KEYTAB_FILE'].blank? || !File.exist?(datastore['KEYTAB_FILE'])203fail_with(Failure::BadConfig, 'Invalid key tab file')204end205206tbl = Rex::Text::Table.new(207'Header' => 'Keytab entries',208'Indent' => 1,209'WordWrap' => false,210'Columns' => %w[211kvno212type213principal214hash215date216]217)218219keytab = File.binread(datastore['KEYTAB_FILE'])220keytab = Rex::Proto::Kerberos::Keytab::Krb5Keytab.read(keytab)221keytab.key_entries.each do |entry|222keyblock = entry.keyblock223tbl << [224entry.vno,225enctype_name(keyblock.enctype),226entry.principal,227keyblock.data.unpack1('H*'),228entry.timestamp,229]230end231232case datastore['OUTPUT_FORMAT']233when 'table'234print_line(tbl.to_s)235when 'csv'236print_line(tbl.to_csv)237else238print_line(tbl.to_s)239end240end241242# @param [Object] id243# @see Rex::Proto::Kerberos::Crypto::Encryption244def enctype_name(id)245name = Rex::Proto::Kerberos::Crypto::Encryption.const_name(id)246name ? "#{id.to_s.ljust(2)} (#{name})" : id.to_s247end248249private250251# @param [String] keytab_path the keytab path252# @return [Rex::Proto::Kerberos::Keytab::Keytab]253def read_or_initialize_keytab(keytab_path)254return Rex::Proto::Kerberos::Keytab::Krb5Keytab.read(File.binread(keytab_path)) if File.exist?(keytab_path)255256Rex::Proto::Kerberos::Keytab::Krb5Keytab.new257end258259# @param [String] keytab_path the keytab path260# @param [Rex::Proto::Kerberos::Keytab::Keytab] keytab261def write_keytab(keytab_path, keytab)262File.binwrite(keytab_path, keytab.to_binary_s)263print_good "keytab saved to #{keytab_path}"264265if datastore['VERBOSE']266list_keytab_entries267end268end269end270271272