Pass-the-Hash is Dead: Long Live Pass-the-Hash

[Edit 3/16/17] Many elements of this post, specifically the ones concerning KB2871997, are incorrect. I have an updated post titled “Pass-the-Hash Is Dead: Long Live LocalAccountTokenFilterPolicy” that contains the most up-to-date and accurate information.

[Edit 8/13/15] – Here is how the old version 1.9 cmdlets in this post translate to PowerView 2.0:

Invoke-EnumerateLocalAdmins -> Invoke-EnumerateLocalAdmin

-> Get-NetLocalGroups -> Get-NetLocalGroup -ListGroups

You may have heard the word recently about how a recent Microsoft patch has put all of us pentesters out of a job. Pass-the-hash is dead, attackers can no longer spread laterally, and Microsoft has finally secured its authentication mechanisms. Oh wait:

This is a fully-patched Windows 7 system in a fully-patched Windows 2012 domain. So what’s going on here, what has Microsoft claimed to do, what have they actually done, and what are the implications of all of this? The security advisory and associated knowledge base article we’re dealing with here is KB2871997 (aka the Mimikatz KB ;)

Besides backporting some of the Windows 8.1 protections that make extracting plaintext credentials from LSASS slightly more difficult, the advisory includes this ominous (to pentesters, at least) statement “Changes to this feature include: prevent network logon and remote interactive logon to domain-joined machine using local accounts…“. On the surface, this looks like it totally quashes the Windows pivoting vectors we’ve been taking advantage of for so long, [insert doom and gloom here]. Microsoft even originally titled their original advisory, “Update to fix the Pass-The-Hash Vulnerablity”, but quickly changed it to “Update to improve credentials protection and management”

It’s true, Microsoft has definitely raised the bar: accounts that are members of the localgroup “Administrators” are no longer able to execute code with WMI or PSEXEC, use schtasks or at, or even browse the open shares on the target machine. Oh, except (as pwnag3 reports and our experiences confirm) the RID 500 built-in Administrator account, even if it’s renamed. While Windows 7 installs will now disable this account by default and prompt for a user to set up another local administrator, many organizations used to standard advice and compliance still have loads of RID 500 accounts, enabled, all over their enterprise. Some organizations rely on this account for backwards compatibility reasons, and some use it as a way to perform vulnerability scans without passing around Domain Admin credentials.

If a domain is built using only modern Windows OSs and COTS products (which know how to operate within these new constraints), and configured correctly with no shortcuts taken, then these protections represent a big step forward. Microsoft has finally start to wise up to some of its inherent security issues, which I seriously applaud them for. However, the vast majority of organizations are a Frankensteinian amalgamation of security/management products, old (and sometimes unpatched) servers, heterogenous clients, lazy admins, backwards-compatibility-focused engineers, and usability-focused business units.

Regardless, accounts with this security identifier are almost certainly going to be enabled and around for a while. Additionally, Windows 2003 isn’t affected (which will surely linger around organizations significantly longer than Windows XP), and domain accounts which maintain Administrative access over a machine can still have their hashes passed to your heart’s content. Also, these local admin accounts should still work with psremoting if it’s enabled. Some organizations will leave the WinRM service still running as an artifact of deployment, and while you can’t use hashes for auth in this case, [edit – thanks to @mubix informing me] plaintext credentials can be specified for a remote PowerShell session [edit] as well as hashes through some existing Metasploit modules.

But let’s say everything’s set up fairly well, the default Administrator account is disabled, and you end up dumping the hash of another local admin user on a box. How is this going to look in the field when you try your normal pivoting, and what options are still open? Your favorite scanner will still likely flag the credentials as valid on machines with the same account reused, as the following examples with the local admin of ‘mike:password’ demonstrates:



However, when you try to use PSEXEC or WMIS to trigger agents or commands, or use Impacket’s functionality to browse the file shares, you’ll encounter something like this:

The “pth-winexe” example above shows the difference between invalid credentials (NT_STATUS_LOGON_FAILURE) and the new patch behavior. If you happen to have the plaintext, through group policy preferences, some Mimikatz luck, or cracking the dumped NTLM hashes, you can still RDP to a target successfully with something like rdesktop -u mike -p password 192.168.52.151. But be careful: if you’re going after a Windows 7 machine and a domain user is currently logged on, it will politely ask them if they want to allow your remote session in, meaning this is probably best left for after-hours. Also, interesting note: if you have hashes for a domain user and are dealing with the new restricted admin mode, you might be able to pass-the-hash with rdp itself! The Kali linux guys have a great writeup on doing just that with xfreerdp.

So here we are, with the RID 500 local Administrator account, as well as any domain accounts granted administrative privileges over a machine, still being able to utilize Metasploit or the passing-the-hash toolkit to install agents or execute code on target systems. Seems like it would be useful to be able to enumerate what name the local RID 500 account is currently using, as well as any network users in the local Administrators group. Unfortunately, even if you get access to a basic user account on some target machine and get in a position to abuse Active Directory, you can’t query local administrators with WMI as you might like:

But all hope is not lost, with backwards-compatibility, the bane of Microsoft’s existence, once again coming to our aid. The Active Directory Service Interfaces [ADSI] WinNT provider, can be used to query information from domain-attached machines, even from non-privileged user accounts. A remnant of NT4 domain deployments, the WinNT provider in some cases can allow a non-privileged domain user to query information from target machines, including things like all the local groups and associated members (with SIDs and all). If we have Powershell access on a Windows domain machine, you can try enumerating all the local groups on a target machine with something like:

$computer = [ADSI]”WinNT://WINDOWS2,computer”

$computer.psbase.children | where { $_.psbase.schemaClassName -eq ‘group’ } | foreach { ($_.name)[0]}

If we want the members of a specific group, that’s not hard either:

$members = @($([ADSI]”WinNT://WINDOWS2/Administrators”).psbase.Invoke(“Members”))

$members | foreach { $_.GetType().InvokeMember(“ADspath”, ‘GetProperty’, $null, $_, $null) }

These functions have been integrated into PowerView, Get-NetLocalGroups and Get-NetLocalGroup respectively:

Another function, Invoke-EnumerateLocalAdmins, has also been built and integrated. This will query the domain for hosts, and enumerate every machine for members of a specific local group (default of ‘Administrators’). There are options for host lists, delay/jitter between host enumerations, and whether you want to pipe everything automatically to a csv file:

This gives you some nice, sortable .csv output with the server name, account, whether the name is a group or not, whether the account is enabled, and the full SID associated with the account. The built-in Administrator account is the one ending in *-500:

And again, this is all with an unprivileged Windows domain account. If you just have a hash and want the same information without having to use PowerShell, the Nmap scripts smb-enum-groups.nse and smb-enum-users.nse can accomplish the same thing using a valid account for the machine (even a member of local admins!) along a password or hash:

nmap -p U:137,T:139 –script-args ‘smbuser=mike,smbhash=8846f7eaee8fb117ad06bdd830b7586c’ –script=smb-enum-groups –script=smb-enum-users 192.168.52.151

If you want to use a domain account, set your flags to something like –script-args ‘smbdomain=DOMAIN,smbuser=USER,smbpass/smbhash=X’. You’ll be able to enumerate the RID 500 account name and whether it’s disabled, as well as all the members of the local Administrators group on the machine. If there’s a returned member of the Administrator group that doesn’t show up in the smb-enum-users list, like ‘Jason’ in this instance, it’s likely a domain account. This information can give you a better idea of what credentials will work where, and what systems/accounts you need to target.

If you have any issues or questions with PowerView, submit any issues to the official Github page, hit me up on Twitter at @harmj0y, email me at will [at] harmj0y.net, or find me on Freenode in #veil, #armitage, or #pssec under harmj0y. And if you’re doing the Blackhat/Defcon gauntlet this year, come check out the Veil-Framework BH Arsenal booth and/or my presentation on post-exploitation, as well as all the other awesome talks lined up this year!