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/spec/modules/auxiliary/admin/kerberos/keytab_spec.rb
Views: 11789
require 'rspec'12RSpec.describe 'kerberos keytab' do3include_context 'Msf::UIDriver'4include_context 'Msf::DBManager'5include_context 'Msf::Simple::Framework#modules loading'67let(:subject) do8load_and_create_module(9module_type: 'auxiliary',10reference_name: 'admin/kerberos/keytab'11)12end1314=begin15Generated with heimdal ktutil; which has two additional bytes at the end for a 32-bit kvno and flags which are16not present in the mit format1718rm -f heimdal.keytab19ktutil --keytab=./heimdal.keytab --verbose add --password=password [email protected] --enctype=aes256-cts-hmac-sha1-96 --kvno=120ktutil --keytab=./heimdal.keytab --verbose add --password=password [email protected] --enctype=aes128-cts-hmac-sha1-96 --kvno=121ktutil --keytab=./heimdal.keytab --verbose add --password=password [email protected] --enctype=arcfour-hmac-md5 --kvno=122ktutil --keytab=./heimdal.keytab --verbose list2324ruby -r 'active_support/core_ext/array' -e 'puts File.binread("./heimdal.keytab").bytes.map { |x| "\\x#{x.to_s(16).rjust(2, "0")}" }.in_groups_of(16).map { |row| "\"#{row.join("")}\"" }.join(" \\ \n")'25=end26let(:valid_keytab) do27"\x05\x02\x00\x00\x00\x54\x00\x01\x00\x0c\x44\x4f\x4d\x41\x49\x4e" \28"\x2e\x4c\x4f\x43\x41\x4c\x00\x0d\x41\x64\x6d\x69\x6e\x69\x73\x74" \29"\x72\x61\x74\x6f\x72\x00\x00\x00\x01\x63\x38\x7e\x21\x01\x00\x12" \30"\x00\x20\xc4\xa3\xf3\x1d\x64\xaf\xa6\x48\xa6\xd0\x8d\x07\x76\x56" \31"\x3e\x12\x38\xb9\x76\xd0\xb9\x0f\x79\xea\x07\x21\x94\x36\x82\x94" \32"\xe9\x29\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x44\x00\x01" \33"\x00\x0c\x44\x4f\x4d\x41\x49\x4e\x2e\x4c\x4f\x43\x41\x4c\x00\x0d" \34"\x41\x64\x6d\x69\x6e\x69\x73\x74\x72\x61\x74\x6f\x72\x00\x00\x00" \35"\x01\x63\x38\x7e\x21\x01\x00\x11\x00\x10\xba\xba\x43\xa8\xb9\x7b" \36"\xac\xa1\x53\xbd\x54\xb2\xf0\x77\x4a\xd7\x00\x00\x00\x01\x00\x00" \37"\x00\x00\x00\x00\x00\x44\x00\x01\x00\x0c\x44\x4f\x4d\x41\x49\x4e" \38"\x2e\x4c\x4f\x43\x41\x4c\x00\x0d\x41\x64\x6d\x69\x6e\x69\x73\x74" \39"\x72\x61\x74\x6f\x72\x00\x00\x00\x01\x63\x38\x7e\x21\x01\x00\x17" \40"\x00\x10\x88\x46\xf7\xea\xee\x8f\xb1\x17\xad\x06\xbd\xd8\x30\xb7" \41"\x58\x6c\x00\x00\x00\x01\x00\x00\x00\x00"42end43let(:keytab_file) { Tempfile.new('keytab') }4445before(:each) do46Timecop.freeze(Time.parse('Jul 15, 2022 12:33:40.000000000 GMT'))47subject.datastore['VERBOSE'] = false48allow(driver).to receive(:input).and_return(driver_input)49allow(driver).to receive(:output).and_return(driver_output)50subject.init_ui(driver_input, driver_output)51end5253after(:each) do54Timecop.return55end5657describe '#add_keytab_entry' do58context 'when the keytab file does not exist' do59before(:each) do60File.delete(keytab_file.path)61subject.datastore['KEYTAB_FILE'] = keytab_file.path62end6364context 'when supplying a key with aes126 encryption type' do65it 'creates a new keytab' do66subject.datastore['PRINCIPAL'] = 'Administrator'67subject.datastore['REALM'] = 'DOMAIN.LOCAL'68subject.datastore['KVNO'] = 169subject.datastore['ENCTYPE'] = 'AES256'70subject.datastore['KEY'] = 'c4a3f31d64afa648a6d08d0776563e1238b976d0b90f79ea072194368294e929'71subject.add_keytab_entry7273subject.list_keytab_entries74expect(@combined_output.join("\n")).to match_table <<~TABLE75keytab saved to #{keytab_file.path}76Keytab entries77==============7879kvno type principal hash date80---- ---- --------- ---- ----811 18 (AES256) [email protected] c4a3f31d64afa648a6d08d0776563e1238b976d0b90f79ea072194368294e929 #{Time.parse('1970-01-01 00:00:00 +0000').to_time}82TABLE83end84end8586context 'when supplying a password with the ALL encryption type specified' do87it 'creates a new keytab' do88subject.datastore['PRINCIPAL'] = 'Administrator'89subject.datastore['REALM'] = 'DOMAIN.LOCAL'90subject.datastore['KVNO'] = 191subject.datastore['ENCTYPE'] = 'ALL'92subject.datastore['PASSWORD'] = 'password'93subject.add_keytab_entry9495subject.list_keytab_entries96expect(@combined_output.join("\n")).to match_table <<~TABLE97keytab saved to #{keytab_file.path}98Keytab entries99==============100101kvno type principal hash date102---- ---- --------- ---- ----1031 3 (DES_CBC_MD5) [email protected] 89d3b923d6a7195e #{Time.parse('1970-01-01 00:00:00 +0000').to_time}1041 16 (DES3_CBC_SHA1) [email protected] 341994e0ba5b1a20d640911cda23c137b637d51a6416d6cb #{Time.parse('1970-01-01 00:00:00 +0000').to_time}1051 23 (RC4_HMAC) [email protected] 8846f7eaee8fb117ad06bdd830b7586c #{Time.parse('1970-01-01 00:00:00 +0000').to_time}1061 17 (AES128) [email protected] baba43a8b97baca153bd54b2f0774ad7 #{Time.parse('1970-01-01 00:00:00 +0000').to_time}1071 18 (AES256) [email protected] c4a3f31d64afa648a6d08d0776563e1238b976d0b90f79ea072194368294e929 #{Time.parse('1970-01-01 00:00:00 +0000').to_time}108109TABLE110end111end112113context 'when supplying a password with aes256 encryption type' do114it 'creates a new keytab' do115subject.datastore['PRINCIPAL'] = 'Administrator'116subject.datastore['REALM'] = 'DOMAIN.LOCAL'117subject.datastore['KVNO'] = 1118subject.datastore['ENCTYPE'] = 'AES256'119subject.datastore['PASSWORD'] = 'password'120subject.add_keytab_entry121122subject.list_keytab_entries123expect(@combined_output.join("\n")).to match_table <<~TABLE124keytab saved to #{keytab_file.path}125Keytab entries126==============127128kvno type principal hash date129---- ---- --------- ---- ----1301 18 (AES256) [email protected] c4a3f31d64afa648a6d08d0776563e1238b976d0b90f79ea072194368294e929 #{Time.parse('1970-01-01 00:00:00 +0000').to_time}131TABLE132end133end134end135136context 'when the keytab file exists' do137before(:each) do138File.binwrite(keytab_file.path, valid_keytab)139subject.datastore['KEYTAB_FILE'] = keytab_file.path140end141142context 'when supplying a password with aes256 encryption type' do143it 'updates the existing keytab' do144subject.datastore['PRINCIPAL'] = 'Administrator'145subject.datastore['REALM'] = 'DOMAIN.LOCAL'146subject.datastore['KVNO'] = 1147subject.datastore['ENCTYPE'] = 'AES256'148subject.datastore['PASSWORD'] = 'password'149subject.add_keytab_entry150151subject.list_keytab_entries152expect(@combined_output.join("\n")).to match_table <<~TABLE153keytab saved to #{keytab_file.path}154Keytab entries155==============156157kvno type principal hash date158---- ---- --------- ---- ----1591 18 (AES256) [email protected] c4a3f31d64afa648a6d08d0776563e1238b976d0b90f79ea072194368294e929 #{Time.parse('2022-10-01 17:51:29 +0000').to_time}1601 17 (AES128) [email protected] baba43a8b97baca153bd54b2f0774ad7 #{Time.parse('2022-10-01 17:51:29 +0000').to_time}1611 23 (RC4_HMAC) [email protected] 8846f7eaee8fb117ad06bdd830b7586c #{Time.parse('2022-10-01 17:51:29 +0000').to_time}1621 18 (AES256) [email protected] c4a3f31d64afa648a6d08d0776563e1238b976d0b90f79ea072194368294e929 #{Time.parse('1970-01-01 00:00:00 +0000').to_time}163TABLE164end165end166end167end168169describe '#list_keytab_entries' do170context 'when the keytab file does not exist' do171it 'raises a config error' do172expect { subject.list_keytab_entries }.to raise_error Msf::Auxiliary::Failed, /Invalid key tab file/173end174end175176context 'when the keytab file exists' do177before(:each) do178File.binwrite(keytab_file.path, valid_keytab)179subject.datastore['KEYTAB_FILE'] = keytab_file.path180end181182it 'lists the available keytab entries' do183subject.list_keytab_entries184expect(@combined_output.join("\n")).to match_table <<~TABLE185Keytab entries186==============187188kvno type principal hash date189---- ---- --------- ---- ----1901 18 (AES256) [email protected] c4a3f31d64afa648a6d08d0776563e1238b976d0b90f79ea072194368294e929 #{Time.parse('2022-10-01 17:51:29 +0000').to_time}1911 17 (AES128) [email protected] baba43a8b97baca153bd54b2f0774ad7 #{Time.parse('2022-10-01 17:51:29 +0000').to_time}1921 23 (RC4_HMAC) [email protected] 8846f7eaee8fb117ad06bdd830b7586c #{Time.parse('2022-10-01 17:51:29 +0000').to_time}193194TABLE195end196end197end198199describe '#export_keytab_entries' do200context 'when the keytab file does not exist' do201before(:each) do202File.delete(keytab_file.path)203subject.datastore['KEYTAB_FILE'] = keytab_file.path204framework.db.delete_credentials(ids: (framework.db.creds || []).map(&:id))205end206207after(:each) do208framework.db.delete_credentials(ids: (framework.db.creds || []).map(&:id))209end210211context 'when there is no database active' do212before(:each) do213allow(subject.framework.db).to receive(:active).and_return(false)214end215216it 'notifies the user that there is no database active' do217subject.export_keytab_entries218219expect(@combined_output.join("\n")).to match_table <<~TABLE220export not available, because the database is not active.221TABLE222end223end224225context 'when there are no kerberos or ntlm creds present in the database' do226it 'notifies the user that there are no entries to export' do227subject.export_keytab_entries228229expect(@combined_output.join("\n")).to match_table <<~TABLE230No entries to export231keytab saved to #{keytab_file.path}232233TABLE234end235end236237context 'when there are kerberos and ntlm creds present in the database' do238def report_creds(239user, hash, type: :ntlm_hash, jtr_format: '', realm_key: nil, realm_value: nil,240rhost: '192.0.2.2', rport: '445', myworkspace_id: nil, module_fullname: nil241)242service_data = {243address: rhost,244port: rport,245service_name: 'smb',246protocol: 'tcp',247workspace_id: myworkspace_id248}249credential_data = {250module_fullname: module_fullname,251origin_type: :service,252private_data: hash,253private_type: type,254jtr_format: jtr_format,255username: user256}.merge(service_data)257credential_data[:realm_key] = realm_key if realm_key258credential_data[:realm_value] = realm_value if realm_value259260cl = framework.db.create_credential_and_login(credential_data)261cl.respond_to?(:core_id) ? cl.core_id : nil262end263264before(:each) do265report_creds(266'user_without_realm', 'aad3b435b51404eeaad3b435b51404ee:e02bc503339d51f71d913c245d35b50b',267type: :ntlm_hash, module_fullname: subject.fullname, myworkspace_id: framework.db.default_workspace.id268)269report_creds(270'user_with_realm', 'aad3b435b51404eeaad3b435b51404ee:32ede47af254546a82b1743953cc4950',271type: :ntlm_hash, module_fullname: subject.fullname, myworkspace_id: framework.db.default_workspace.id,272realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN, realm_value: 'example.local'273)274krb_key = {275enctype: Rex::Proto::Kerberos::Crypto::Encryption::AES256,276salt: "DEMO.LOCALuser_with_krbkey".b,277key: 'c4a3f31d64afa648a6d08d0776563e1238b976d0b90f79ea072194368294e929'278}279report_creds(280'user_with_krbkey', Metasploit::Credential::KrbEncKey.build_data(**krb_key),281type: :krb_enc_key, module_fullname: subject.fullname, myworkspace_id: framework.db.default_workspace.id,282realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN, realm_value: 'demo.local'283)284end285286it 'exports the creds' do287subject.export_keytab_entries288subject.list_keytab_entries289290expect(@combined_output.join("\n")).to match_table <<~TABLE291keytab saved to #{keytab_file.path}292Keytab entries293==============294295kvno type principal hash date296---- ---- --------- ---- ----2971 23 (RC4_HMAC) user_without_realm@ e02bc503339d51f71d913c245d35b50b #{Time.parse('1970-01-01 01:00:00 +0100').to_time}2981 23 (RC4_HMAC) [email protected] 32ede47af254546a82b1743953cc4950 #{Time.parse('1970-01-01 01:00:00 +0100').to_time}2991 18 (AES256) [email protected] 63346133663331643634616661363438613664303864303737363536336531323338623937366430623930663739656130373231393433363832393465393239 #{Time.parse('1970-01-01 01:00:00 +0100').to_time}300301TABLE302end303end304end305end306end307308309