├── .github └── workflows │ └── main.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── PersistenceSniper ├── PersistenceSniper.psd1 └── PersistenceSniper.psm1 ├── README.md ├── SECURITY.md └── persistencesnipernew4.png /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the "main" branch 8 | push: 9 | branches: [ "main" ] 10 | pull_request: 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 16 | jobs: 17 | # This workflow contains a single job called "build" 18 | publish_on_PSGallery: 19 | # The type of runner that the job will run on 20 | runs-on: ubuntu-latest 21 | 22 | # Steps represent a sequence of tasks that will be executed as part of the job 23 | steps: 24 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 25 | - uses: actions/checkout@v3 26 | 27 | - name: Publish PowerShell Module 28 | uses: pcgeek86/publish-powershell-module-action@v20 29 | with: 30 | # The NuGet API Key for PowerShell Gallery, with permission to push this module. 31 | NuGetApiKey: ${{ secrets.PS_GALLERY_KEY }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | PersistenceSniper.zip 3 | PersistenceSniper.7z 4 | .vs -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | ## 1.17.1 3 | Features: 4 | - Detection for SetupExecuteNoPnpSync 5 | - Enhanced detection for techniques implemented in 1.17.1 (expanded search outside of System32 - credit @sixtyvividtails) 6 | 7 | ## 1.17.0 8 | Features: 9 | - Detection for BootExecute and BootExecuteNoPnpSync 10 | - Detection for PlatformExecute 11 | - Detection for SetupExecute 12 | - Detection for Netsh Helper DLL 13 | 14 | ## 1.16.3 15 | Fixes: 16 | - Fixed a bug in the remote computer execution which, under certain circumstances, prevented the proper execution of the module 17 | 18 | ## 1.16.2 19 | Fixes: 20 | - Fixed a bug in the remote computer execution which, under certain circumstances, prevented the proper execution of the module 21 | - Fixed a bug in the handling of the LSA Notification Package detection (see PR #27) 22 | 23 | ## 1.16.1 24 | Fixes: 25 | - Fixed a bug in the Ghost Task function which under certain circumstances prevented the detection of the technique 26 | 27 | ## 1.16.0 28 | Features: 29 | - Detection for the BootVerificationProgram hijacking 30 | - Detection for the AppInit DLLs injection 31 | Fixes: 32 | - Fixed a false positive in the detection of the Suborner Attack caused by a faulty implementation of the Parse-NetUser internal function 33 | 34 | ## 1.15.1 35 | Fixes: 36 | - Fixed a gap in the detection of the techniques which relied on Get-IfSafeExecutable function which would prevent Powershell persistences from showing up 37 | 38 | ## 1.15.0 39 | Features: 40 | - Detection for the GhostTask technique 41 | Fixes: 42 | - Fixed some minor bugs 43 | 44 | ## 1.14.0 45 | Features: 46 | - Detection for the DSRM backdoor 47 | Fixes: 48 | - Fixed a bug regarding the Parse-NetUser internal function (see issue #20). 49 | 50 | ## 1.13.0 51 | Features: 52 | - Detection for RID hijacking 53 | - Detection for the Suborner technique 54 | Fixes: 55 | - Fixed a bug regarding module-wide string comparisons (see issue #19). 56 | 57 | ## 1.12.1 58 | Fixes: 59 | - Fixed a bug which prevented the detection of the Utilman.exe hijacking in the Accessibility Tools persistence detection. 60 | 61 | ## 1.12.0 62 | Features: 63 | - Save results to the local Windows Event Log 64 | Fixes: 65 | - Fixed a bug which saw OutputCSV contain the techniques that should have been filtered out by DiffCSV. 66 | 67 | ## 1.11.0 68 | Features: 69 | - Detection for RunEx registry key added 70 | - Detection for RunOnceEx registry key added 71 | - Detection for .NET startup hooks added 72 | Fixes: 73 | - Fixed a bug which prevented the detection of CmdAutoRun from working as intended. 74 | 75 | ## 1.10.1 76 | Fixes: 77 | - Fixed a bug which prevented -DiffCSV from working as intended. 78 | 79 | ## 1.10.0 80 | Features: 81 | - Detection for Office AI.exe hijacking 82 | - Detection for Service Control Manager Security Descriptor tampering 83 | - Detection for Explorer Context Menu hijacking 84 | Fixes: 85 | - Fixed handling of system environment variables in the registry 86 | - Fixed the bug in which the script blocked if one of the remote computers was not reachable 87 | 88 | 89 | ## 1.9.3 90 | Features: 91 | - Added the possibility of passing a Virustotal API key and check if the hash of the detected file is known. 92 | - Malicious Office Templates are now detected 93 | - New license has been implemented. 94 | 95 | ## 1.9.2 96 | Fixes: 97 | - Fixed 3 lines of code dealing with minor bugs 98 | 99 | ## 1.9.1 100 | Features: 101 | - Added the following persistence techniques: 102 | - Screensaver 103 | - BITS JOb NotifyCmdLine 104 | - Power Automate 105 | 106 | ## 1.8.0 107 | Features: 108 | - Added the following persistence techniques: 109 | - AMSI Providers 110 | - Powershell Profiles 111 | - Silent Exit Monitor 112 | - Telemetry Controller Commands 113 | - RDP WDS Startup Programs 114 | - Scheduled Tasks 115 | Fixes: 116 | - Fixed minor typos here and there 117 | 118 | ## 1.7.1 119 | Fixes: 120 | - the PSM1 is now also signed (it was not in v1.7.0) 121 | 122 | ## 1.7.0 123 | Features: 124 | - add support for accessibility tools backdoor detection 125 | 126 | ## 1.6.0 127 | Features: 128 | - add support for RDP InitialProgram detection 129 | 130 | ## 1.5.0 131 | Features: 132 | - added the `PersistenceMethod` parameter in order to selectively check for one persistence technique at a time 133 | 134 | ## 1.4.0 135 | Features: 136 | - the module is now digitally signed with a valid code signing certificate 137 | 138 | ## 1.3.1 139 | Features: 140 | - a number of new persistence checks have been implemented 141 | 142 | ## 1.0 143 | Features: 144 | - WMI event subscriptions persistence check has been implemented 145 | ## 0.9 146 | Beta release 147 | 148 | 149 | 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/last-byte/PersistenceSniper/13006aa8aa311cb593bb5cf43aa7b3cff8ece1b0/LICENSE -------------------------------------------------------------------------------- /PersistenceSniper/PersistenceSniper.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/last-byte/PersistenceSniper/13006aa8aa311cb593bb5cf43aa7b3cff8ece1b0/PersistenceSniper/PersistenceSniper.psd1 -------------------------------------------------------------------------------- /PersistenceSniper/PersistenceSniper.psm1: -------------------------------------------------------------------------------- 1 | <#PSScriptInfo 2 | 3 | .VERSION 1.17.1 4 | 5 | .GUID 3ce01128-01f1-4503-8f7f-2e50deb56ebc 6 | 7 | .AUTHOR Federico @last0x00 Lagrasta 8 | 9 | .DESCRIPTION This module tries to enumerate all the persistence techniques implanted on a compromised machine. 10 | 11 | .COPYRIGHT Commons Clause 12 | 13 | .TAGS Windows Registry Persistence Detection Blue Purple Red Team Incident Response DFIR IR Forensics AMSI Powershell Ghosttask Run RunOnce Key Hive Property Cmd 14 | 15 | .LICENSEURI https://github.com/last-byte/PersistenceSniper/blob/main/LICENSE 16 | 17 | .PROJECTURI https://github.com/last-byte/PersistenceSniper 18 | 19 | .ICONURI https://blog.notso.pro/img/persistencesnipernew4.png 20 | 21 | .EXTERNALMODULEDEPENDENCIES 22 | 23 | .REQUIREDSCRIPTS 24 | 25 | .EXTERNALSCRIPTDEPENDENCIES 26 | 27 | .RELEASENOTES Check the CHANGELOG on the Github Repository. 28 | 29 | .PRIVATEDATA 30 | 31 | #> 32 | 33 | #Requires -RunAsAdministrator 34 | 35 | function Find-AllPersistence { 36 | <# 37 | .SYNOPSIS 38 | Find-AllPersistence is PersistenceSniper's main function. All the other functions defined in it are used by Find-AllPersistence to gather information on potential persistence techniques implanted on the machines PersistenceSniper is run on. 39 | 40 | .DESCRIPTION 41 | Enumerate all the persistence methods found on a machine and print them for the user to see. 42 | 43 | .PARAMETER PersistenceMethod 44 | Optional, choose a single persistence method to check for. Default value is All. 45 | 46 | .PARAMETER ComputerName 47 | Optional, an array of computernames to run the script on. 48 | 49 | .PARAMETER DiffCSV 50 | Optional, a CSV file to be taken as input and used to exclude from the output all the local persistences which match the ones in the CSV file itself. 51 | 52 | .PARAMETER IncludeHighFalsePositivesChecks 53 | Optional, a switch which forces PersistenceSniper to also call a number of functions with checks which are more difficult to filter and in turn cause a lot of false positives. 54 | 55 | .PARAMETER OutputCSV 56 | Optional, a CSV file to be used as output which will contain all the findings in a CSV format. 57 | 58 | .EXAMPLE 59 | Find-AllPersistence 60 | Enumerate low false positive persistence techniques implanted on the local machine. 61 | 62 | .EXAMPLE 63 | Find-AllPersistence -PersistenceMethod RunAndRunOnce 64 | Enumerate only persistence techniques implanted on the local machine relying on the Run and RunOnce registry keys. 65 | 66 | .EXAMPLE 67 | $Persistences = Find-AllPersistence 68 | Enumerate low false positive persistence techniques implanted on the local machine and save them in Powershell variable for later processing. 69 | 70 | .EXAMPLE 71 | Find-AllPersistence -OutputCSV .\persistences.csv 72 | Enumerate low false positive persistence techniques implanted on the local machine and output to a CSV. 73 | 74 | .EXAMPLE 75 | Find-AllPersistence -LogFindings 76 | Enumerate low false positive persistence techniques implanted on the local machine and log results to the Windows Event Log. 77 | 78 | .EXAMPLE 79 | Find-AllPersistence -DiffCSV .\persistences.csv 80 | Enumerate low false positive persistence techniques implanted on the local machine but show us only the persistences which are not in an input CSV. 81 | 82 | .EXAMPLE 83 | Find-AllPersistence -DiffCSV .\persistences.csv -OutputCSV .\newPersistences.csv 84 | Enumerate low false positive persistence techniques implanted on the local machine but show us only the persistences which are not in an input CSV named persistences.csv and output the results on another CSV named newPersistences.csv. 85 | 86 | .EXAMPLE 87 | Find-AllPersistence -ComputerName @('dc1.macrohard.lol', 'dc2.macrohard.lol') -IncludeHighFalsePositivesChecks -DiffCSV .\persistences.csv -OutputCSV .\findings.csv 88 | Enumerate all persistence techniques implanted on an array of remote machines but show only the persistences which are not in an input CSV and output the findings on a CSV. 89 | 90 | .EXAMPLE 91 | Find-AllPersistence -ComputerName (Get-Content computers.txt) -IncludeHighFalsePositivesChecks -DiffCSV .\persistences.csv -OutputCSV .\findings.csv 92 | Enumerate all persistence techniques implanted on an array of remote machines retrieved from a file containing one hostname per line but show only the persistences which are not in an input CSV and output the findings on a CSV. 93 | 94 | .EXAMPLE 95 | Find-AllPersistence -DiffCSV .\persistences.csv -OutputCSV .\findings.csv | Where-Object Classification -Like "MITRE ATT&CK T*" 96 | Enumerate all persistence techniques implanted on the local machine, filter out the ones in the persistences.csv file, save the results in findings.csv and output to console only the persistences which are classified under the MITRE ATT&CK framework. 97 | #> 98 | 99 | Param( 100 | [Parameter(Position = 0)] 101 | [ValidateSet( 102 | 'All', 103 | 'RunAndRunOnce', 104 | 'ImageFileExecutionOptions', 105 | 'NLDPDllOverridePath', 106 | 'AeDebug', 107 | 'WerFaultHangs', 108 | 'CmdAutoRun', 109 | 'ExplorerLoad', 110 | 'WinlogonUserinit', 111 | 'WinlogonShell', 112 | 'TerminalProfileStartOnUserLogin', 113 | 'AppCertDlls', 114 | 'ServiceDlls', 115 | 'GPExtensionDlls', 116 | 'WinlogonMPNotify', 117 | 'CHMHelperDll', 118 | 'HHCtrlHijacking', 119 | 'StartupPrograms', 120 | 'UserInitMprScript', 121 | 'AutodialDLL', 122 | 'LsaExtensions', 123 | 'ServerLevelPluginDll', 124 | 'LsaPasswordFilter', 125 | 'LsaAuthenticationPackages', 126 | 'LsaSecurityPackages', 127 | 'WinlogonNotificationPackages', 128 | 'ExplorerTools', 129 | 'DotNetDebugger', 130 | 'ErrorHandlerCmd', 131 | 'WMIEventsSubscrition', 132 | 'WindowsServices', 133 | 'AppPaths', 134 | 'TerminalServicesInitialProgram', 135 | 'AccessibilityTools', 136 | 'AMSIProviders', 137 | 'PowershellProfiles', 138 | 'SilentExitMonitor', 139 | 'TelemetryController', 140 | 'RDPWDSStartupPrograms', 141 | 'ScheduledTasks', 142 | 'BitsJobsNotify', 143 | 'Screensaver', 144 | 'PowerAutomate', 145 | 'OfficeAddinsAndTemplates', 146 | 'Services', 147 | 'ExplorerContextMenu', 148 | 'ServiceControlManagerSD', 149 | 'OfficeAiHijacking', 150 | 'RunExAndRunOnceEx', 151 | 'DotNetStartupHooks', 152 | 'RIDHijacking', 153 | 'SubornerAttack', 154 | 'DSRMBackdoor', 155 | 'GhostTask', 156 | 'BootVerificationProgram', 157 | 'AppInitDLLs', 158 | 'BootExecute', 159 | 'NetshHelperDLL', 160 | 'SetupExecute', 161 | 'PlatformExecute' 162 | )] 163 | $PersistenceMethod = 'All', 164 | 165 | [Parameter(Position = 1)] 166 | [String[]] 167 | $ComputerName = $null, 168 | 169 | [Parameter(Position = 2)] 170 | [String] 171 | $DiffCSV = $null, 172 | 173 | [Parameter(Position = 3)] 174 | [Switch] 175 | $IncludeHighFalsePositivesChecks, 176 | 177 | [Parameter(Position = 4)] 178 | [String] 179 | $OutputCSV = $null, 180 | 181 | [Parameter(Position = 5)] 182 | [String] 183 | $VTApiKey = $null, 184 | 185 | [Parameter(Position = 6)] 186 | [Switch] 187 | $LogFindings 188 | ) 189 | 190 | # This array will hold all the persistence techniques found on all the machines the module is run on 191 | $globalPersistenceObjectArray = [Collections.ArrayList]::new() 192 | 193 | $ScriptBlock = 194 | { 195 | if ($PSSenderInfo) { 196 | $PersistenceMethod = $Using:PersistenceMethod 197 | $VerbosePreference = $Using:VerbosePreference 198 | } 199 | 200 | $ErrorActionPreference = 'SilentlyContinue' 201 | $hostname = ([Net.Dns]::GetHostByName($env:computerName)).HostName 202 | $psProperties = @('PSChildName', 'PSDrive', 'PSParentPath', 'PSPath', 'PSProvider') 203 | $persistenceObjectArray = [Collections.ArrayList]::new() 204 | $systemAndUsersHives = [Collections.ArrayList]::new() 205 | $systemHive = (Get-Item Registry::HKEY_LOCAL_MACHINE).PSpath 206 | $null = $systemAndUsersHives.Add($systemHive) 207 | $sids = Get-ChildItem Registry::HKEY_USERS 208 | foreach ($sid in $sids) { 209 | $null = $systemAndUsersHives.Add($sid.PSpath) 210 | } 211 | function New-PersistenceObject { 212 | param( 213 | [String] 214 | $Hostname = $null, 215 | 216 | [String] 217 | $Technique = $null, 218 | 219 | [String] 220 | $Classification = $null, 221 | 222 | [String] 223 | $Path = $null, 224 | 225 | [String] 226 | $Value = $null, 227 | 228 | [String] 229 | $AccessGained = $null, 230 | 231 | [String] 232 | $Note = $null, 233 | 234 | [String] 235 | $Reference = $null, 236 | 237 | [String] 238 | $Signature = $null , 239 | 240 | [Bool] 241 | $IsBuiltinBinary = $false, 242 | 243 | [Bool] 244 | $IsLolbin = $false, 245 | 246 | [String] 247 | $VTEntries = $null 248 | ) 249 | 250 | $Executable = Get-ExecutableFromCommandLine $Value 251 | 252 | $PersistenceObject = [PSCustomObject]@{ 253 | 'Hostname' = $Hostname 254 | 'Technique' = $Technique 255 | 'Classification' = $Classification 256 | 'Path' = $Path 257 | 'Value' = $Value 258 | 'Access Gained' = $AccessGained 259 | 'Note' = $Note 260 | 'Reference' = $Reference 261 | 'Signature' = Find-CertificateInfo $Executable 262 | 'IsBuiltinBinary' = Get-IfBuiltinBinary $Executable 263 | 'IsLolbin' = Get-IfLolBin $Executable 264 | 'VTEntries' = Get-IfHashIsMalicious $Executable 265 | } 266 | return $PersistenceObject 267 | } 268 | 269 | function Get-IfHashIsMalicious($executable) { 270 | $authenticode = Get-AuthenticodeSignature($executable) 271 | if ($authenticode.IsOSBinary -eq $false) { 272 | if ($VTApiKey) { 273 | $headers = @{ 274 | 'x-apikey' = $VTApiKey 275 | } 276 | $hash = (Get-FileHash $executable).Hash 277 | $result = Invoke-RestMethod -Headers $headers "https://www.virustotal.com/api/v3/search?query=$hash" 278 | Sleep 1 279 | if ($result.data) { 280 | $result.data.attributes.last_analysis_stats.malicious 281 | } 282 | else { 283 | return "0" 284 | } 285 | } 286 | else { 287 | return "N/A" 288 | } 289 | } 290 | else { 291 | return "N/A" 292 | } 293 | 294 | } 295 | 296 | 297 | function Get-IfLolBin { 298 | param( 299 | [String] 300 | $executable 301 | ) 302 | # To get an updated list of lolbins 303 | # curl https://lolbas-project.github.io/# | grep -E "bin-name\">(.*)\.exe<" -o | cut -d ">" -f 2 | cut -d "<" -f 1 304 | [String[]]$lolbins = "APPINSTALLER.EXE", "ASPNET_COMPILER.EXE", "AT.EXE", "ATBROKER.EXE", "BASH.EXE", "BITSADMIN.EXE", "CERTOC.EXE", "CERTREQ.EXE", "CERTUTIL.EXE", "CMD.EXE", "CMDKEY.EXE", "CMDL32.EXE", "CMSTP.EXE", "CONFIGSECURITYPOLICY.EXE", "CONHOST.EXE", "CONTROL.EXE", "CSC.EXE", "CSCRIPT.EXE", "DATASVCUTIL.EXE", "DESKTOPIMGDOWNLDR.EXE", "DFSVC.EXE", "DIANTZ.EXE", "DISKSHADOW.EXE", "DNSCMD.EXE", "ESENTUTL.EXE", "EVENTVWR.EXE", "EXPAND.EXE", "EXPLORER.EXE", "EXTEXPORT.EXE", "EXTRAC32.EXE", "FINDSTR.EXE", "FINGER.EXE", "FLTMC.EXE", "FORFILES.EXE", "FTP.EXE", "GFXDOWNLOADWRAPPER.EXE", "GPSCRIPT.EXE", "HH.EXE", "IMEWDBLD.EXE", "IE4UINIT.EXE", "IEEXEC.EXE", "ILASM.EXE", "INFDEFAULTINSTALL.EXE", "INSTALLUTIL.EXE", "JSC.EXE", "MAKECAB.EXE", "MAVINJECT.EXE", "MICROSOFT.WORKFLOW.COMPILER.EXE", "MMC.EXE", "MPCMDRUN.EXE", "MSBUILD.EXE", "MSCONFIG.EXE", "MSDT.EXE", "MSHTA.EXE", "MSIEXEC.EXE", "NETSH.EXE", "ODBCCONF.EXE", "OFFLINESCANNERSHELL.EXE", "ONEDRIVESTANDALONEUPDATER.EXE", "PCALUA.EXE", "PCWRUN.EXE", "PKTMON.EXE", "PNPUTIL.EXE", "PRESENTATIONHOST.EXE", "PRINT.EXE", "PRINTBRM.EXE", "PSR.EXE", "RASAUTOU.EXE", "RDRLEAKDIAG.EXE", "REG.EXE", "REGASM.EXE", "REGEDIT.EXE", "REGINI.EXE", "REGISTER-CIMPROVIDER.EXE", "REGSVCS.EXE", "REGSVR32.EXE", "REPLACE.EXE", "RPCPING.EXE", "RUNDLL32.EXE", "RUNONCE.EXE", "RUNSCRIPTHELPER.EXE", "SC.EXE", "SCHTASKS.EXE", "SCRIPTRUNNER.EXE", "SETTINGSYNCHOST.EXE", "STORDIAG.EXE", "SYNCAPPVPUBLISHINGSERVER.EXE", "TTDINJECT.EXE", "TTTRACER.EXE", "VBC.EXE", "VERCLSID.EXE", "WAB.EXE", "WLRMDR.EXE", "WMIC.EXE", "WORKFOLDERS.EXE", "WSCRIPT.EXE", "WSRESET.EXE", "WUAUCLT.EXE", "XWIZARD.EXE", "ACCCHECKCONSOLE.EXE", "ADPLUS.EXE", "AGENTEXECUTOR.EXE", "APPVLP.EXE", "BGINFO.EXE", "CDB.EXE", "COREGEN.EXE", "CSI.EXE", "DEVTOOLSLAUNCHER.EXE", "DNX.EXE", "DOTNET.EXE", "DUMP64.EXE", "DXCAP.EXE", "EXCEL.EXE", "FSI.EXE", "FSIANYCPU.EXE", "MFTRACE.EXE", "MSDEPLOY.EXE", "MSXSL.EXE", "NTDSUTIL.EXE", "POWERPNT.EXE", "POWERSHELL.EXE", "PROCDUMP(64).EXE", "RCSI.EXE", "REMOTE.EXE", "SQLDUMPER.EXE", "SQLPS.EXE", "SQLTOOLSPS.EXE", "SQUIRREL.EXE", "TE.EXE", "TRACKER.EXE", "UPDATE.EXE", "VSIISEXELAUNCHER.EXE", "VISUALUIAVERIFYNATIVE.EXE", "VSJITDEBUGGER.EXE", "WFC.EXE", "WINWORD.EXE", "WSL.EXE" 305 | foreach ($lolbin in $lolbins) { 306 | $exe = Split-Path -path $executable -Leaf 307 | if (($exe.ToUpper()) -eq $lolbin) { 308 | return $true 309 | } 310 | } 311 | return $false 312 | } 313 | 314 | function Get-IfBuiltinBinary { 315 | param( 316 | [String] 317 | $executable 318 | ) 319 | try { 320 | $authenticode = Get-AuthenticodeSignature $executable 321 | if ($authenticode.IsOsBinary) { 322 | return $true 323 | } 324 | else { 325 | return $false 326 | } 327 | } 328 | catch { 329 | return $false 330 | } 331 | } 332 | 333 | function Find-CertificateInfo { 334 | param( 335 | [String] 336 | $executable 337 | ) 338 | try { 339 | $authenticode = Get-AuthenticodeSignature $executable 340 | $formattedString = [string]::Format("Status = {0}, Subject = {1}", $authenticode.Status, $authenticode.SignerCertificate.Subject) 341 | return $formattedString 342 | } 343 | catch { 344 | return "Unknown error occurred" 345 | } 346 | } 347 | function Get-ExecutableFromCommandLine { 348 | param( 349 | [String] 350 | $pathName 351 | ) 352 | $pathName = [System.Environment]::ExpandEnvironmentVariables($pathName) -Replace '"' 353 | 354 | $match = [regex]::Match($pathName, '[A-Za-z0-9\s]+\.(exe|dll|ocx|cmd|bat|ps1)', [Text.RegularExpressions.RegexOptions]::IgnoreCase) 355 | if ($match.Success) { 356 | # Grab Index from the [regex]::Match() result 357 | $Index = $Match.Index 358 | 359 | # Substring using the index we obtained above 360 | $ThingsBeforeMatch = $pathName.Substring(0, $Index) 361 | $path = "$ThingsBeforeMatch$match" 362 | } 363 | else { 364 | $path = $null 365 | } 366 | 367 | if (([System.IO.Path]::IsPathRooted($path)) -eq $false) { 368 | $path = (Get-Command $path).Source 369 | } 370 | return $path 371 | } 372 | function Get-IfSafeExecutable { 373 | param( 374 | [String] 375 | $executable 376 | ) 377 | 378 | $exePath = Get-ExecutableFromCommandLine $executable 379 | if ((Get-IfBuiltinBinary $exePath) -and -not (Get-IfLolBin $exePath) ) { 380 | return $true 381 | } 382 | else { 383 | return $false 384 | } 385 | } 386 | 387 | function Get-IfSafeLibrary { 388 | param( 389 | [String] 390 | $dllFullPath 391 | ) 392 | 393 | if ((Get-IfBuiltinBinary $dllFullPath) -eq $true) { 394 | return $true 395 | } 396 | else { 397 | return $false 398 | } 399 | } 400 | 401 | function Parse-NetUser { 402 | <# 403 | .SYNOPSIS 404 | Parses the net user command output into a single list. 405 | Author: Jake Miller (@LaconicWolf) 406 | 407 | .DESCRIPTION 408 | Accepts the output of net user via the pipeline and parses into a 409 | single list. 410 | 411 | .EXAMPLE 412 | PS C:\> net user | Parse-NetUser 413 | 414 | Name 415 | ------ 416 | Administrator 417 | DefaultAccount 418 | Dwight 419 | Guest 420 | Jake 421 | WDAGUtilityAccount 422 | #> 423 | 424 | $outputStart = 0 425 | foreach ($item in $input) { 426 | if ($item -match '----') { 427 | $outputStart = 1 428 | continue 429 | } 430 | elseif ($outputStart -eq 0) { 431 | continue 432 | } 433 | if ($item -eq "") { 434 | continue 435 | } 436 | if ($item -match '.*\.$') { 437 | continue 438 | } 439 | 440 | $contentArray = @() 441 | foreach ($line in $item -split '\s{2,}') { 442 | if ($line -ne '') { 443 | $contentArray += $line 444 | } 445 | } 446 | 447 | foreach ($content in $contentArray) { 448 | $content = $content -replace '"', '' 449 | if ($content.Length -ne 0) { 450 | New-Object -TypeName PSObject -Property @{"Name" = $content.Trim() } 451 | } 452 | } 453 | } 454 | } 455 | 456 | 457 | function ElevateTo-System { 458 | 459 | $signature = @" 460 | [StructLayout(LayoutKind.Sequential, Pack = 1)] 461 | public struct TokPriv1Luid 462 | { 463 | public int Count; 464 | public long Luid; 465 | public int Attr; 466 | } 467 | 468 | public const int SE_PRIVILEGE_ENABLED = 0x00000002; 469 | public const int TOKEN_QUERY = 0x00000008; 470 | public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; 471 | public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000; 472 | 473 | public const UInt32 STANDARD_RIGHTS_READ = 0x00020000; 474 | public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x0001; 475 | public const UInt32 TOKEN_DUPLICATE = 0x0002; 476 | public const UInt32 TOKEN_IMPERSONATE = 0x0004; 477 | public const UInt32 TOKEN_QUERY_SOURCE = 0x0010; 478 | public const UInt32 TOKEN_ADJUST_GROUPS = 0x0040; 479 | public const UInt32 TOKEN_ADJUST_DEFAULT = 0x0080; 480 | public const UInt32 TOKEN_ADJUST_SESSIONID = 0x0100; 481 | public const UInt32 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY); 482 | public const UInt32 TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | 483 | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | 484 | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | 485 | TOKEN_ADJUST_SESSIONID); 486 | 487 | public const string SE_TIME_ZONE_NAMETEXT = "SeTimeZonePrivilege"; 488 | public const int ANYSIZE_ARRAY = 1; 489 | 490 | [StructLayout(LayoutKind.Sequential)] 491 | public struct LUID 492 | { 493 | public UInt32 LowPart; 494 | public UInt32 HighPart; 495 | } 496 | 497 | [StructLayout(LayoutKind.Sequential)] 498 | public struct LUID_AND_ATTRIBUTES { 499 | public LUID Luid; 500 | public UInt32 Attributes; 501 | } 502 | 503 | 504 | public struct TOKEN_PRIVILEGES { 505 | public UInt32 PrivilegeCount; 506 | [MarshalAs(UnmanagedType.ByValArray, SizeConst=ANYSIZE_ARRAY)] 507 | public LUID_AND_ATTRIBUTES [] Privileges; 508 | } 509 | 510 | [DllImport("advapi32.dll", SetLastError=true)] 511 | public extern static bool DuplicateToken(IntPtr ExistingTokenHandle, int 512 | SECURITY_IMPERSONATION_LEVEL, out IntPtr DuplicateTokenHandle); 513 | 514 | 515 | [DllImport("advapi32.dll", SetLastError=true)] 516 | [return: MarshalAs(UnmanagedType.Bool)] 517 | public static extern bool SetThreadToken( 518 | IntPtr PHThread, 519 | IntPtr Token 520 | ); 521 | 522 | [DllImport("advapi32.dll", SetLastError=true)] 523 | [return: MarshalAs(UnmanagedType.Bool)] 524 | public static extern bool OpenProcessToken(IntPtr ProcessHandle, 525 | UInt32 DesiredAccess, out IntPtr TokenHandle); 526 | 527 | [DllImport("advapi32.dll", SetLastError = true)] 528 | public static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); 529 | 530 | [DllImport("kernel32.dll", ExactSpelling = true)] 531 | public static extern IntPtr GetCurrentProcess(); 532 | 533 | [DllImport("advapi32.dll", ExactSpelling = true)] 534 | public static extern IntPtr RevertToSelf(); 535 | 536 | [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] 537 | public static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, 538 | ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); 539 | "@ 540 | 541 | $currentPrincipal = New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent()) 542 | if ($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -ne $true) { 543 | throw "Run the Command as an Administrator" 544 | break 545 | } 546 | 547 | Add-Type -MemberDefinition $signature -Name AdjPriv -Namespace AdjPriv 548 | $adjPriv = [AdjPriv.AdjPriv] 549 | [long]$luid = 0 550 | 551 | $tokPriv1Luid = New-Object AdjPriv.AdjPriv+TokPriv1Luid 552 | $tokPriv1Luid.Count = 1 553 | $tokPriv1Luid.Luid = $luid 554 | $tokPriv1Luid.Attr = [AdjPriv.AdjPriv]::SE_PRIVILEGE_ENABLED 555 | 556 | $retVal = $adjPriv::LookupPrivilegeValue($null, "SeDebugPrivilege", [ref]$tokPriv1Luid.Luid) 557 | 558 | [IntPtr]$htoken = [IntPtr]::Zero 559 | $retVal = $adjPriv::OpenProcessToken($adjPriv::GetCurrentProcess(), [AdjPriv.AdjPriv]::TOKEN_ALL_ACCESS, [ref]$htoken) 560 | 561 | 562 | $tokenPrivileges = New-Object AdjPriv.AdjPriv+TOKEN_PRIVILEGES 563 | $retVal = $adjPriv::AdjustTokenPrivileges($htoken, $false, [ref]$tokPriv1Luid, 12, [IntPtr]::Zero, [IntPtr]::Zero) 564 | 565 | if (-not ($retVal)) { 566 | [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 567 | break 568 | } 569 | 570 | $process = (Get-Process -Name winlogon) 571 | [IntPtr]$hwinlogontoken = [IntPtr]::Zero 572 | $retVal = $adjPriv::OpenProcessToken($process.Handle, ([AdjPriv.AdjPriv]::TOKEN_IMPERSONATE -bor [AdjPriv.AdjPriv]::TOKEN_DUPLICATE), [ref]$hwinlogontoken) 573 | 574 | [IntPtr]$dulicateTokenHandle = [IntPtr]::Zero 575 | $retVal = $adjPriv::DuplicateToken($hwinlogontoken, 2, [ref]$dulicateTokenHandle) 576 | 577 | $retval = $adjPriv::SetThreadToken([IntPtr]::Zero, $dulicateTokenHandle) 578 | if (-not ($retVal)) { 579 | [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 580 | } 581 | return 0 582 | } 583 | 584 | function RevertTo-Self { 585 | 586 | $signature = @" 587 | [DllImport("advapi32.dll", ExactSpelling = true)] 588 | public static extern IntPtr RevertToSelf(); 589 | "@ 590 | $adjPriv = [AdjPriv.AdjPriv] 591 | $retval = $adjPriv::RevertToSelf() 592 | if (-not ($retVal)) { 593 | [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 594 | } 595 | return 0 596 | } 597 | 598 | function Get-RunAndRunOnce { 599 | Write-Verbose -Message "$hostname - Getting Run properties..." 600 | foreach ($hive in $systemAndUsersHives) { 601 | 602 | $runProps = Get-ItemProperty -Path "$hive\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" 603 | if ($runProps) { 604 | Write-Verbose -Message "$hostname - [!] Found properties under $(Convert-Path -Path $hive)'s Run key which deserve investigation!" 605 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $runProps)) { 606 | if ($psProperties.Contains($prop.Name)) { 607 | continue 608 | } # skip the property if it's powershell built-in property 609 | $propPath = Convert-Path -Path $runProps.PSPath 610 | $propPath += '\' + $prop.Name 611 | $currentHive = Convert-Path -Path $hive 612 | if (($currentHive -eq 'HKEY_LOCAL_MACHINE') -or ($currentHive -eq 'HKEY_USERS\S-1-5-18') -or ($currentHive -eq 'HKEY_USERS\S-1-5-19') -or ($currentHive -eq 'HKEY_USERS\S-1-5-20')) { 613 | $access = 'System' 614 | } 615 | else { 616 | $access = 'User' 617 | } 618 | 619 | if (Get-IfSafeExecutable $runProps.($prop.Name)) { 620 | continue 621 | } 622 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Registry Run Key' -Classification 'MITRE ATT&CK T1547.001' -Path $propPath -Value $runProps.($prop.Name) -AccessGained $access -Note 'Executables in properties of the key (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows\CurrentVersion\Run are run when the user logs in or when the machine boots up (in the case of the HKLM hive).' -Reference 'https://attack.mitre.org/techniques/T1547/001/' 623 | $null = $persistenceObjectArray.Add($PersistenceObject) 624 | } 625 | } 626 | } 627 | 628 | Write-Verbose -Message '' 629 | Write-Verbose -Message "$hostname - Getting RunOnce properties..." 630 | foreach ($hive in $systemAndUsersHives) { 631 | $runOnceProps = Get-ItemProperty -Path "$hive\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" 632 | if ($runOnceProps) { 633 | Write-Verbose -Message "$hostname - [!] Found properties under $(Convert-Path -Path $hive)'s RunOnce key which deserve investigation!" 634 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $runOnceProps)) { 635 | if ($psProperties.Contains($prop.Name)) { 636 | continue 637 | } # skip the property if it's powershell built-in property 638 | $propPath = Convert-Path -Path $runOnceProps.PSPath 639 | $propPath += '\' + $prop.Name 640 | $currentHive = Convert-Path -Path $hive 641 | if (($currentHive -eq 'HKEY_LOCAL_MACHINE') -or ($currentHive -eq 'HKEY_USERS\S-1-5-18') -or ($currentHive -eq 'HKEY_USERS\S-1-5-19') -or ($currentHive -eq 'HKEY_USERS\S-1-5-20')) { 642 | $access = 'System' 643 | } 644 | else { 645 | $access = 'User' 646 | } 647 | if (Get-IfSafeExecutable $runOnceProps.($prop.Name)) { 648 | continue 649 | } 650 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Registry RunOnce Key' -Classification 'MITRE ATT&CK T1547.001' -Path $propPath -Value $runOnceProps.($prop.Name) -AccessGained $access -Note 'Executables in properties of the key (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce are run once when the user logs in, or when the machine boots up (in the case of the HKLM hive), and then deleted.' -Reference 'https://attack.mitre.org/techniques/T1547/001/' 651 | $null = $persistenceObjectArray.Add($PersistenceObject) 652 | } 653 | } 654 | } 655 | Write-Verbose -Message '' 656 | } 657 | 658 | function Get-ImageFileExecutionOptions { 659 | $IFEOptsDebuggers = New-Object -TypeName System.Collections.ArrayList 660 | $foundDangerousIFEOpts = $false 661 | Write-Verbose -Message "$hostname - Getting Image File Execution Options..." 662 | foreach ($hive in $systemAndUsersHives) { 663 | $ifeOpts = Get-ChildItem -Path "$hive\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" 664 | if ($ifeOpts) { 665 | foreach ($key in $ifeOpts) { 666 | $debugger = Get-ItemProperty -Path Registry::$key -Name Debugger 667 | if ($debugger) { 668 | $foundDangerousIFEOpts = $true 669 | $null = $IFEOptsDebuggers.Add($key) 670 | } 671 | } 672 | 673 | if ($foundDangerousIFEOpts) { 674 | Write-Verbose -Message "$hostname - [!] Found subkeys under the Image File Execution Options key of $(Convert-Path -Path $hive) which deserve investigation!" 675 | foreach ($key in $IFEOptsDebuggers) { 676 | $ifeProps = Get-ItemProperty -Path Registry::$key -Name Debugger 677 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $ifeProps)) { 678 | if ($psProperties.Contains($prop.Name)) { 679 | continue 680 | } # skip the property if it's powershell built-in property 681 | $propPath = Convert-Path -Path $ifeProps.PSPath 682 | $propPath += '\' + $prop.Name 683 | if (Get-IfSafeExecutable $ifeProps.($prop.Name)) { 684 | continue 685 | } 686 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Image File Execution Options' -Classification 'MITRE ATT&CK T1546.012' -Path $propPath -Value $ifeProps.($prop.Name) -AccessGained 'System/User' -Note 'Executables in the Debugger property of a subkey of (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ are run instead of the program corresponding to the subkey. Gained access depends on whose context the debugged process runs in.' -Reference 'https://attack.mitre.org/techniques/T1546/012/' 687 | $null = $persistenceObjectArray.Add($PersistenceObject) 688 | } 689 | } 690 | } 691 | } 692 | } 693 | Write-Verbose -Message '' 694 | } 695 | 696 | function Get-NLDPDllOverridePath { 697 | $KeysWithDllOverridePath = New-Object -TypeName System.Collections.ArrayList 698 | $foundDllOverridePath = $false 699 | Write-Verbose -Message "$hostname - Getting Natural Language Development Platform DLL path override properties..." 700 | foreach ($hive in $systemAndUsersHives) { 701 | $NLDPLanguages = Get-ChildItem -Path "$hive\SYSTEM\CurrentControlSet\Control\ContentIndex\Language" 702 | if ($NLDPLanguages) { 703 | foreach ($key in $NLDPLanguages) { 704 | $DllOverridePath = Get-ItemProperty -Path Registry::$key -Name *DLLPathOverride 705 | if ($DllOverridePath) { 706 | $foundDllOverridePath = $true 707 | $null = $KeysWithDllOverridePath.Add($key) 708 | } 709 | } 710 | 711 | if ($foundDllOverridePath) { 712 | Write-Verbose -Message "$hostname - [!] Found subkeys under $(Convert-Path -Path $hive)\SYSTEM\CurrentControlSet\Control\ContentIndex\Language which deserve investigation!" 713 | foreach ($key in $KeysWithDllOverridePath) { 714 | $properties = Get-ItemProperty -Path Registry::$key | Select-Object -Property *DLLPathOverride, PS* 715 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $properties)) { 716 | if ($psProperties.Contains($prop.Name)) { 717 | continue 718 | } # skip the property if it's powershell built-in property 719 | $propPath = Convert-Path -Path $properties.PSPath 720 | $propPath += '\' + $prop.Name 721 | $currentHive = Convert-Path -Path $hive 722 | if (($currentHive -eq 'HKEY_LOCAL_MACHINE') -or ($currentHive -eq 'HKEY_USERS\S-1-5-18') -or ($currentHive -eq 'HKEY_USERS\S-1-5-19') -or ($currentHive -eq 'HKEY_USERS\S-1-5-20')) { 723 | $access = 'System' 724 | } 725 | else { 726 | $access = 'User' 727 | } 728 | if (Get-IfSafeLibrary $properties.($prop.Name)) { 729 | continue 730 | } 731 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Natural Language Development Platform 6 DLL Override Path' -Classification 'Hexacorn Technique N.98' -Path $propPath -Value $properties.($prop.Name) -AccessGained $access -Note 'DLLs listed in properties of subkeys of (HKLM|HKEY_USERS\)\SYSTEM\CurrentControlSet\Control\ContentIndex\Language are loaded via LoadLibrary executed by SearchIndexer.exe' -Reference 'https://www.hexacorn.com/blog/2018/12/30/beyond-good-ol-run-key-part-98/' 732 | $null = $persistenceObjectArray.Add($PersistenceObject) 733 | } 734 | } 735 | } 736 | } 737 | } 738 | Write-Verbose -Message '' 739 | } 740 | 741 | function Get-AeDebug { 742 | Write-Verbose -Message "$hostname - Getting AeDebug properties..." 743 | foreach ($hive in $systemAndUsersHives) { 744 | $aeDebugger = Get-ItemProperty -Path "$hive\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug" -Name Debugger 745 | if ($aeDebugger) { 746 | Write-Verbose -Message "$hostname - [!] Found properties under the $(Convert-Path -Path $hive) AeDebug key which deserve investigation!" 747 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $aeDebugger)) { 748 | if ($psProperties.Contains($prop.Name)) { 749 | continue 750 | } # skip the property if it's powershell built-in property 751 | $propPath = Convert-Path -Path $aeDebugger.PSPath 752 | $propPath += '\' + $prop.Name 753 | if (Get-IfSafeExecutable $aeDebugger.($prop.Name)) { 754 | continue 755 | } 756 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'AEDebug Custom Debugger' -Classification 'Hexacorn Technique N.4' -Path $propPath -Value $aeDebugger.($prop.Name) -AccessGained 'System/User' -Note "The executable in the Debugger property of (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug is run when a process crashes. Gained access depends on whose context the debugged process runs in; if the Auto property of the same registry key is set to 1, the debugger starts without user interaction. A value of 'C:\Windows\system32\vsjitdebugger.exe' might be a false positive if you have Visual Studio Community installed." -Reference 'https://www.hexacorn.com/blog/2013/09/19/beyond-good-ol-run-key-part-4/' 757 | $null = $persistenceObjectArray.Add($PersistenceObject) 758 | } 759 | } 760 | 761 | $aeDebugger = Get-ItemProperty -Path "$hive\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug" -Name Debugger 762 | if ($aeDebugger) { 763 | Write-Verbose -Message "$hostname - [!] Found properties under the $(Convert-Path -Path $hive) Wow6432Node AeDebug key which deserve investigation!" 764 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $aeDebugger)) { 765 | if ($psProperties.Contains($prop.Name)) { 766 | continue 767 | } # skip the property if it's powershell built-in property 768 | $propPath = Convert-Path -Path $aeDebugger.PSPath 769 | $propPath += '\' + $prop.Name 770 | if (Get-IfSafeExecutable $aeDebugger.($prop.Name)) { 771 | continue 772 | } 773 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Wow6432Node AEDebug Custom Debugger' -Classification 'Hexacorn Technique N.4' -Path $propPath -Value $aeDebugger.($prop.Name) -AccessGained 'System/User' -Note "The executable in the Debugger property of (HKLM|HKEY_USERS\)\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug is run when a 32 bit process on a 64 bit system crashes. Gained access depends on whose context the debugged process runs in; if the Auto property of the same registry key is set to 1, the debugger starts without user interaction. A value of 'C:\Windows\system32\vsjitdebugger.exe' might be a false positive if you have Visual Studio Community installed." -Reference 'https://www.hexacorn.com/blog/2013/09/19/beyond-good-ol-run-key-part-4/' 774 | $null = $persistenceObjectArray.Add($PersistenceObject) 775 | } 776 | } 777 | } 778 | Write-Verbose -Message '' 779 | } 780 | 781 | function Get-WerFaultHangs { 782 | Write-Verbose -Message "$hostname - Getting WerFault Hangs registry key Debug property..." 783 | foreach ($hive in $systemAndUsersHives) { 784 | $werfaultDebugger = Get-ItemProperty -Path "$hive\SOFTWARE\Microsoft\Windows\Windows Error Reporting\Hangs" -Name Debugger 785 | if ($werfaultDebugger) { 786 | Write-Verbose -Message "$hostname - [!] Found a Debugger property under the $(Convert-Path -Path $hive) WerFault Hangs key which deserve investigation!" 787 | $werfaultDebugger | Select-Object -Property Debugger, PS* 788 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $werfaultDebugger)) { 789 | if ($psProperties.Contains($prop.Name)) { 790 | continue 791 | } # skip the property if it's powershell built-in property 792 | $propPath = Convert-Path -Path $werfaultDebugger.PSPath 793 | $propPath += '\' + $prop.Name 794 | if (Get-IfSafeExecutable $werfaultDebugger.($prop.Name)) { 795 | continue 796 | } 797 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Windows Error Reporting Debugger' -Classification 'Hexacorn Technique N.116' -Path $propPath -Value $werfaultDebugger.($prop.Name) -AccessGained 'System' -Note 'The executable in the Debugger property of (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows\Windows Error Reporting\Hangs is spawned by WerFault.exe when a process crashes.' -Reference 'https://www.hexacorn.com/blog/2019/09/20/beyond-good-ol-run-key-part-116/' 798 | $null = $persistenceObjectArray.Add($PersistenceObject) 799 | } 800 | } 801 | } 802 | 803 | Write-Verbose -Message '' 804 | Write-Verbose -Message "$hostname - Getting WerFault Hangs registry key ReflectDebug property..." 805 | foreach ($hive in $systemAndUsersHives) { 806 | $werfaultReflectDebugger = Get-ItemProperty -Path "$hive\SOFTWARE\Microsoft\Windows\Windows Error Reporting\Hangs" -Name ReflectDebugger 807 | if ($werfaultReflectDebugger) { 808 | Write-Verbose -Message "$hostname - [!] Found a ReflectDebugger property under the $(Convert-Path -Path $hive) WerFault Hangs key which deserve investigation!" 809 | $werfaultReflectDebugger | Select-Object -Property ReflectDebugger, PS* 810 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $werfaultReflectDebugger)) { 811 | if ($psProperties.Contains($prop.Name)) { 812 | continue 813 | } # skip the property if it's powershell built-in property 814 | $propPath = Convert-Path -Path $werfaultReflectDebugger.PSPath 815 | $propPath += '\' + $prop.Name 816 | if (Get-IfSafeExecutable $werfaultReflectDebugger.($prop.Name)) { 817 | continue 818 | } 819 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Windows Error Reporting ReflectDebugger' -Classification 'Hexacorn Technique N.85' -Path $propPath -Value $werfaultReflectDebugger.($prop.Name) -AccessGained 'System' -Note 'The executable in the ReflectDebugger property of (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows\Windows Error Reporting\Hangs is spawned by WerFault.exe when called with the -pr argument.' -Reference 'https://www.hexacorn.com/blog/2018/08/31/beyond-good-ol-run-key-part-85/' 820 | $null = $persistenceObjectArray.Add($PersistenceObject) 821 | } 822 | } 823 | } 824 | Write-Verbose -Message '' 825 | } 826 | 827 | function Get-CmdAutoRun { 828 | Write-Verbose -Message "$hostname - Getting Command Processor's AutoRun property..." 829 | foreach ($hive in $systemAndUsersHives) { 830 | $autorun = (Get-ItemProperty -Path "$hive\Software\Microsoft\Command Processor" -Name AutoRun).AutoRun 831 | if ($autorun) { 832 | Write-Verbose -Message "$hostname - [!] $(Convert-Path -Path $hive) Command Processor's AutoRun property is set and deserves investigation!" 833 | $propPath = Convert-Path -Path $hive 834 | $propPath += "\Software\Microsoft\Command Processor\AutoRun" 835 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Command Processor AutoRun key' -Classification 'Uncatalogued Technique N.1' -Path $propPath -Value $autorun -AccessGained 'User' -Note 'The executable in the AutoRun property of (HKLM|HKEY_USERS\)\Software\Microsoft\Command Processor\AutoRun is run when cmd.exe is spawned without the /D argument.' -Reference 'https://persistence-info.github.io/Data/cmdautorun.html' 836 | $null = $persistenceObjectArray.Add($PersistenceObject) 837 | 838 | } 839 | } 840 | Write-Verbose -Message '' 841 | } 842 | function Get-ExplorerLoad { 843 | Write-Verbose -Message "$hostname - Getting Explorer's Load property..." 844 | foreach ($hive in $systemAndUsersHives) { 845 | $loadKey = Get-ItemProperty -Path "$hive\Software\Microsoft\Windows NT\CurrentVersion\Windows" -Name Load 846 | if ($loadKey) { 847 | Write-Verbose -Message "$hostname - [!] $(Convert-Path -Path $hive) Load property is set and deserves investigation!" 848 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $loadKey)) { 849 | if ($psProperties.Contains($prop.Name)) { 850 | continue 851 | } # skip the property if it's powershell built-in property 852 | $propPath = Convert-Path -Path $loadKey.PSPath 853 | $propPath += '\' + $prop.Name 854 | $currentHive = Convert-Path -Path $hive 855 | if (($currentHive -eq 'HKEY_LOCAL_MACHINE') -or ($currentHive -eq 'HKEY_USERS\S-1-5-18') -or ($currentHive -eq 'HKEY_USERS\S-1-5-19') -or ($currentHive -eq 'HKEY_USERS\S-1-5-20')) { 856 | $access = 'System' 857 | } 858 | else { 859 | $access = 'User' 860 | } 861 | if (Get-IfSafeExecutable $loadKey.($prop.Name)) { 862 | continue 863 | } 864 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Explorer Load Property' -Classification 'Uncatalogued Technique N.2' -Path $propPath -Value $loadKey.($prop.Name) -AccessGained $access -Note 'The executable in the Load property of (HKLM|HKEY_USERS\)\Software\Microsoft\Windows NT\CurrentVersion\Windows is run by explorer.exe at login time.' -Reference 'https://persistence-info.github.io/Data/windowsload.html' 865 | $null = $persistenceObjectArray.Add($PersistenceObject) 866 | } 867 | } 868 | } 869 | Write-Verbose -Message '' 870 | } 871 | 872 | function Get-WinlogonUserinit { 873 | Write-Verbose -Message "$hostname - Getting Winlogon's Userinit property..." 874 | foreach ($hive in $systemAndUsersHives) { 875 | $userinit = Get-ItemProperty -Path "$hive\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name Userinit 876 | if ($userinit) { 877 | if ($userinit.Userinit -ne 'C:\Windows\system32\userinit.exe,') { 878 | Write-Verbose -Message "$hostname - [!] $(Convert-Path -Path $hive) Winlogon's Userinit property is set to a non-standard value and deserves investigation!" 879 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $userinit)) { 880 | if ($psProperties.Contains($prop.Name)) { 881 | continue 882 | } # skip the property if it's powershell built-in property 883 | $propPath = Convert-Path -Path $userinit.PSPath 884 | $propPath += '\' + $prop.Name 885 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Winlogon Userinit Property' -Classification 'MITRE ATT&CK T1547.004' -Path $propPath -Value $userinit.($prop.Name) -AccessGained 'System' -Note "The executables in the Userinit property of (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon are run at login time by any user. Normally this property should be set to 'C:\Windows\system32\userinit.exe,' without any further executables appended." -Reference 'https://attack.mitre.org/techniques/T1547/004/' 886 | $null = $persistenceObjectArray.Add($PersistenceObject) 887 | } 888 | } 889 | } 890 | } 891 | Write-Verbose -Message '' 892 | } 893 | 894 | function Get-WinlogonShell { 895 | Write-Verbose -Message "$hostname - Getting Winlogon's Shell property..." 896 | foreach ($hive in $systemAndUsersHives) { 897 | 898 | $shell = Get-ItemProperty -Path "$hive\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name Shell 899 | if ($shell) { 900 | if ($shell.Shell -ne 'explorer.exe') { 901 | Write-Verbose -Message "$hostname - [!] $(Convert-Path -Path $hive) Winlogon's Shell property is set to a non-standard value and deserves investigation!" 902 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $shell)) { 903 | if ($psProperties.Contains($prop.Name)) { 904 | continue 905 | } # skip the property if it's a powershell built-in property 906 | $propPath = Convert-Path -Path $shell.PSPath 907 | $propPath += '\' + $prop.Name 908 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Winlogon Shell Property' -Classification 'MITRE ATT&CK T1547.004' -Path $propPath -Value $shell.($prop.Name) -AccessGained 'User' -Note "The executables in the Shell property of (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon are run as the default shells for any users. Normally this property should be set to 'explorer.exe' without any further executables appended." -Reference 'https://attack.mitre.org/techniques/T1547/004/' 909 | $null = $persistenceObjectArray.Add($PersistenceObject) 910 | } 911 | } 912 | } 913 | } 914 | Write-Verbose -Message '' 915 | } 916 | 917 | function Get-TerminalProfileStartOnUserLogin { 918 | Write-Verbose -Message "$hostname - Checking if users' Windows Terminal Profile's settings.json contains a startOnUserLogin value..." 919 | $userDirectories = Get-ChildItem -Path 'C:\Users\' 920 | foreach ($directory in $userDirectories) { 921 | $terminalDirectories = Get-ChildItem -Path "$($directory.FullName)\Appdata\Local\Packages\Microsoft.WindowsTerminal_*" 922 | foreach ($terminalDirectory in $terminalDirectories) { 923 | $settingsFile = Get-Content -Raw -Path "$($terminalDirectory.FullName)\LocalState\settings.json" | ConvertFrom-Json 924 | if ($settingsFile.startOnUserLogin -ne 'true') { 925 | # skip to the next profile if startOnUserLogin is not present 926 | break 927 | } 928 | $defaultProfileGuid = $settingsFile.defaultProfile 929 | $found = $false 930 | foreach ($profileList in $settingsFile.profiles) { 931 | foreach ($profile in $profileList.list) { 932 | if ($profile.guid -eq $defaultProfileGuid) { 933 | Write-Verbose -Message "$hostname - [!] The file $($terminalDirectory.FullName)\LocalState\settings.json has the startOnUserLogin key set, the default profile has GUID $($profile.guid)!" 934 | if ($profile.commandline) { 935 | $executable = $profile.commandline 936 | } 937 | else { 938 | $executable = $profile.name 939 | } 940 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Windows Terminal startOnUserLogin' -Classification 'Uncatalogued Technique N.3' -Path "$($terminalDirectory.FullName)\LocalState\settings.json" -Value "$executable" -AccessGained 'User' -Note "The executable specified as value of the key `"commandline`" of a profile which has the `"startOnUserLogin`" key set to `"true`" in the Windows Terminal's settings.json of a user is run every time that user logs in." -Reference 'https://twitter.com/nas_bench/status/1550836225652686848' 941 | $null = $persistenceObjectArray.Add($PersistenceObject) 942 | $found = $true 943 | break 944 | } 945 | } 946 | if ($found) { 947 | break 948 | } 949 | } 950 | } 951 | } 952 | Write-Verbose -Message '' 953 | } 954 | 955 | function Get-AppCertDlls { 956 | Write-Verbose -Message "$hostname - Getting AppCertDlls properties..." 957 | foreach ($hive in $systemAndUsersHives) { 958 | $appCertDllsProps = Get-ItemProperty -Path "$hive\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls" 959 | if ($appCertDllsProps) { 960 | Write-Verbose -Message "$hostname - [!] Found properties under $(Convert-Path -Path $hive) AppCertDlls key which deserve investigation!" 961 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $appCertDllsProps)) { 962 | if ($psProperties.Contains($prop.Name)) { continue } # skip the property if it's powershell built-in property 963 | $propPath = Convert-Path -Path $appCertDllsProps.PSPath 964 | $propPath += '\' + $prop.Name 965 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'AppCertDlls' -Classification 'MITRE ATT&CK T1546.009' -Path $propPath -Value $appCertDllsProps.($prop.Name) -AccessGained 'System' -Note 'DLLs in properties of the key (HKLM|HKEY_USERS\)\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls are loaded by every process that loads the Win32 API at process creation.' -Reference 'https://attack.mitre.org/techniques/T1546/009/' 966 | $null = $persistenceObjectArray.Add($PersistenceObject) 967 | } 968 | } 969 | } 970 | Write-Verbose -Message '' 971 | } 972 | 973 | function Get-AppPaths { 974 | Write-Verbose -Message "$hostname - Getting App Paths inside the registry..." 975 | foreach ($hive in $systemAndUsersHives) { 976 | $appPathsKeys = Get-ChildItem -Path "$hive\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths" 977 | foreach ($key in $appPathsKeys) { 978 | $appPath = Get-ItemProperty -Path Registry::$key -Name '(Default)' 979 | 980 | 981 | $exePath = $appPath.'(Default)' 982 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($exePath))) -eq $false) { 983 | $exePath = "C:\Windows\System32\$exePath".ToLower() 984 | } 985 | if ($exePath.Contains('powershell') -or $exePath.Contains('cmd') -or -not (Get-AuthenticodeSignature -FilePath $exePath ).IsOSBinary) { 986 | Write-Verbose -Message "$hostname - [!] Found subkeys under the $(Convert-Path -Path $hive) App Paths key which deserve investigation!" 987 | $propPath = Convert-Path -Path $key.PSPath 988 | $propPath += '\' + $appPath.Name 989 | if (Get-IfSafeExecutable $appPath.'(Default)') { 990 | continue 991 | } 992 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'App Paths' -Classification 'Hexacorn Technique N.3' -Path "$propPath(Default)" -Value $appPath.'(Default)' -AccessGained 'System/User' -Note 'Executables in the (Default) property of a subkey of (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\ are run instead of the program corresponding to the subkey. Gained access depends on whose context the process runs in. Be aware this might be a false positive.' -Reference 'https://www.hexacorn.com/blog/2013/01/19/beyond-good-ol-run-key-part-3/' 993 | $null = $persistenceObjectArray.Add($PersistenceObject) 994 | } 995 | } 996 | } 997 | Write-Verbose -Message '' 998 | } 999 | 1000 | function Get-ServiceDlls { 1001 | Write-Verbose -Message "$hostname - Getting Service DLLs inside the registry..." 1002 | foreach ($hive in $systemAndUsersHives) { 1003 | $keys = Get-ChildItem -Path "$hive\SYSTEM\CurrentControlSet\Services\" 1004 | foreach ($key in $keys) { 1005 | $ImagePath = (Get-ItemProperty -Path ($key.pspath)).ImagePath 1006 | $ImagePath = $ImagePath.ToLower() 1007 | if ($null -ne $ImagePath) { 1008 | if ($ImagePath.Contains('\svchost.exe')) { 1009 | if (Test-Path -Path ($key.pspath + '\Parameters')) { 1010 | $ServiceDll = (Get-ItemProperty -Path ($key.pspath + '\Parameters')).ServiceDll 1011 | } 1012 | else { 1013 | $ServiceDll = (Get-ItemProperty -Path ($key.pspath)).ServiceDll 1014 | } 1015 | if ($null -ne $ServiceDll) { 1016 | $dllPath = $ServiceDll 1017 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($dllPath))) -eq $false) { 1018 | $dllPath = "C:\Windows\System32\$dllPath" 1019 | } 1020 | if ((Get-IfSafeLibrary $dllPath) -EQ $false) { 1021 | Write-Verbose -Message "$hostname - [!] Found subkeys under the $(Convert-Path -Path $hive) Services key which deserve investigation!" 1022 | $propPath = (Convert-Path -Path "$($key.pspath)") + '\Parameters\ServiceDll' 1023 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'ServiceDll Hijacking' -Classification 'Hexacorn Technique N.4' -Path $propPath -Value "$ServiceDll" -AccessGained 'System' -Note "DLLs in the ServiceDll property of (HKLM|HKEY_USERS\)\SYSTEM\CurrentControlSet\Services\\Parameters are loaded by the corresponding service's svchost.exe. If an attacker modifies said entry, the malicious DLL will be loaded in place of the legitimate one." -Reference 'https://www.hexacorn.com/blog/2013/09/19/beyond-good-ol-run-key-part-4/' 1024 | $null = $persistenceObjectArray.Add($PersistenceObject) 1025 | } 1026 | } 1027 | } 1028 | } 1029 | } 1030 | } 1031 | Write-Verbose -Message '' 1032 | } 1033 | 1034 | function Get-GPExtensionDlls { 1035 | Write-Verbose -Message "$hostname - Getting Group Policy Extension DLLs inside the registry..." 1036 | foreach ($hive in $systemAndUsersHives) { 1037 | $keys = Get-ChildItem -Path "$hive\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions" 1038 | foreach ($key in $keys) { 1039 | $DllName = (Get-ItemProperty -Path ($key.pspath)).DllName 1040 | if ($null -ne $DllName) { 1041 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($DllName))) -eq $false) { 1042 | $DllName = "C:\Windows\System32\$DllName" 1043 | } 1044 | if ((Get-IfSafeLibrary $DllName) -EQ $false) { 1045 | Write-Verbose -Message "$hostname - [!] Found DllName property under a subkey of the $(Convert-Path -Path $hive) GPExtensions key which deserve investigation!" 1046 | $propPath = (Convert-Path -Path "$($key.pspath)") + '\DllName' 1047 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Group Policy Extension DLL' -Classification 'Uncatalogued Technique N.4' -Path $propPath -Value "$DllName" -AccessGained 'System' -Note 'DLLs in the DllName property of (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions\\ are loaded by the gpsvc process. If an attacker modifies said entry, the malicious DLL will be loaded in place of the legitimate one.' -Reference 'https://persistence-info.github.io/Data/gpoextension.html' 1048 | $null = $persistenceObjectArray.Add($PersistenceObject) 1049 | } 1050 | } 1051 | } 1052 | } 1053 | Write-Verbose -Message '' 1054 | } 1055 | 1056 | function Get-WinlogonMPNotify { 1057 | Write-Verbose -Message "$hostname - Getting Winlogon MPNotify property..." 1058 | foreach ($hive in $systemAndUsersHives) { 1059 | $mpnotify = Get-ItemProperty -Path "$hive\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name mpnotify 1060 | if ($mpnotify) { 1061 | Write-Verbose -Message "$hostname - [!] Found MPnotify property under $(Convert-Path -Path $hive) Winlogon key!" 1062 | $propPath = (Convert-Path -Path $mpnotify.PSPath) + '\mpnotify' 1063 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Winlogon MPNotify Executable' -Classification 'Uncatalogued Technique N.5' -Path $propPath -Value $mpnotify.mpnotify -AccessGained 'System' -Note 'The executable specified in the "mpnotify" property of the (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon key is run by Winlogon when a user logs on. After the timeout (30s) the process and its child processes are terminated.' -Reference 'https://persistence-info.github.io/Data/mpnotify.html' 1064 | $null = $persistenceObjectArray.Add($PersistenceObject) 1065 | } 1066 | } 1067 | Write-Verbose -Message '' 1068 | } 1069 | 1070 | function Get-CHMHelperDll { 1071 | Write-Verbose -Message "$hostname - Getting CHM Helper DLL inside the registry..." 1072 | foreach ($hive in $systemAndUsersHives) { 1073 | $dllLocation = Get-ItemProperty -Path "$hive\Software\Microsoft\HtmlHelp Author" -Name Location 1074 | if ($dllLocation) { 1075 | $dllPath = $dllLocation.Location 1076 | if ($null -ne $dllPath) { 1077 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($dllPath))) -eq $false) { 1078 | $dllPath = "C:\Windows\System32\$dllPath" 1079 | } 1080 | if ((Get-IfSafeLibrary $dllPath) -EQ $false) { 1081 | Write-Verbose -Message "$hostname - [!] Found Location property under $(Convert-Path -Path $hive)\Software\Microsoft\HtmlHelp Author\ which deserve investigation!" 1082 | $propPath = (Convert-Path -Path "$($dllLocation.pspath)") + '\Location' 1083 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'CHM Helper DLL' -Classification 'Hexacorn Technique N.76' -Path $propPath -Value "$($dllLocation.Location)" -AccessGained 'User' -Note 'DLLs in the Location property of (HKLM|HKEY_USERS\)\Software\Microsoft\HtmlHelp Author\ are loaded when a CHM help file is parsed. If an attacker adds said entry, the malicious DLL will be loaded.' -Reference 'https://www.hexacorn.com/blog/2018/04/22/beyond-good-ol-run-key-part-76/' 1084 | $null = $persistenceObjectArray.Add($PersistenceObject) 1085 | } 1086 | } 1087 | } 1088 | } 1089 | Write-Verbose -Message '' 1090 | } 1091 | 1092 | function Get-HHCtrlHijacking { 1093 | Write-Verbose -Message "$hostname - Getting the hhctrl.ocx library inside the registry..." 1094 | $hive = (Get-Item Registry::HKEY_CLASSES_ROOT).PSpath 1095 | $dllLocation = Get-ItemProperty -Path "$hive\CLSID\{52A2AAAE-085D-4187-97EA-8C30DB990436}\InprocServer32" -Name '(Default)' 1096 | $dllPath = $dllLocation.'(Default)' 1097 | if ($null -ne $dllPath) { 1098 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($dllPath))) -eq $false) { 1099 | $dllPath = "C:\Windows\System32\$dllPath" 1100 | } 1101 | if (-not (Get-AuthenticodeSignature -FilePath $dllPath ).IsOSBinary) { 1102 | Write-Verbose -Message "$hostname - [!] The DLL at $(Convert-Path -Path $hive)\CLSID\{52A2AAAE-085D-4187-97EA-8C30DB990436}\InprocServer32\(Default) is not an OS binary and deserves investigation!" 1103 | $propPath = (Convert-Path -Path "$($dllLocation.pspath)") + '\(Default)' 1104 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Hijacking of hhctrl.ocx' -Classification 'Hexacorn Technique N.77' -Path $propPath -Value "$($dllLocation.'(Default)')" -AccessGained 'User' -Note 'The DLL in the (Default) property of HKEY_CLASSES_ROOT\CLSID\{52A2AAAE-085D-4187-97EA-8C30DB990436}\InprocServer32 is loaded when a CHM help file is parsed or when hh.exe is started. If an attacker modifies said entry, the malicious DLL will be loaded. In case the loading fails for any reason, C:\Windows\hhctrl.ocx is loaded.' -Reference 'https://www.hexacorn.com/blog/2018/04/23/beyond-good-ol-run-key-part-77/' 1105 | $null = $persistenceObjectArray.Add($PersistenceObject) 1106 | } 1107 | } 1108 | else { 1109 | $dllPath = "C:\Windows\System32\hhctrl.ocx" 1110 | if (-not (Get-AuthenticodeSignature -FilePath $dllPath ).IsOSBinary) { 1111 | Write-Verbose -Message "$hostname - [!] The DLL at $dllPath is not an OS binary and deserves investigation!" 1112 | $propPath = (Convert-Path -Path "$($dllLocation.pspath)") + '\(Default)' 1113 | $PersistenceObject = New-PersistenceObject -Hostname "$hostname" -Technique 'Hijacking of hhctrl.ocx' -Classification 'Hexacorn Technique N.77' -Path "$dllPath" -Value "Not an OS binary" -AccessGained 'User' -Note 'The DLL in the (Default) property of HKEY_CLASSES_ROOT\CLSID\{52A2AAAE-085D-4187-97EA-8C30DB990436}\InprocServer32 is loaded when a CHM help file is parsed or when hh.exe is started. If an attacker modifies said entry, the malicious DLL will be loaded. In case the loading fails for any reason, C:\Windows\hhctrl.ocx is loaded.' -Reference 'https://www.hexacorn.com/blog/2018/04/23/beyond-good-ol-run-key-part-77/' 1114 | $null = $persistenceObjectArray.Add($PersistenceObject) 1115 | } 1116 | } 1117 | Write-Verbose -Message '' 1118 | } 1119 | 1120 | function Get-StartupPrograms { 1121 | Write-Verbose -Message "$hostname - Checking if users' Startup folder contains interesting artifacts..." 1122 | $userDirectories = Get-ChildItem -Path 'C:\Users\' 1123 | foreach ($directory in $userDirectories) { 1124 | $fullPath = $directory.FullName 1125 | $startupDirectory = Get-ChildItem -Path "$fullPath\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\" 1126 | foreach ($file in $startupDirectory) { 1127 | $relPath = $file.Name 1128 | Write-Verbose -Message "$hostname - [!] Found a file under $fullPath\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\ folder!" 1129 | $safeCheck = Get-IfSafeExecutable "$fullPath\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\$relPath" 1130 | if ($safeCheck) { 1131 | continue 1132 | } 1133 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Startup Folder' -Classification 'MITRE ATT&CK T1547.001' -Path "$fullPath\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\" -Value "$fullPath\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\$relPath" -AccessGained 'User' -Note "The executables under the .\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\ of a user's folder are run every time that user logs in." -Reference 'https://attack.mitre.org/techniques/T1547/001/' 1134 | $null = $persistenceObjectArray.Add($PersistenceObject) 1135 | break 1136 | } 1137 | } 1138 | Write-Verbose -Message '' 1139 | } 1140 | 1141 | function Get-UserInitMprScript { 1142 | Write-Verbose -Message "$hostname - Getting users' UserInitMprLogonScript property..." 1143 | foreach ($hive in $systemAndUsersHives) { 1144 | $mprlogonscript = Get-ItemProperty -Path "$hive\Environment" -Name UserInitMprLogonScript 1145 | if ($mprlogonscript) { 1146 | Write-Verbose -Message "$hostname - [!] Found UserInitMprLogonScript property under $(Convert-Path -Path $hive)\Environment\ key!" 1147 | $propPath = (Convert-Path -Path $mprlogonscript.PSPath) + '\UserInitMprLogonScript' 1148 | $currentHive = Convert-Path -Path $hive 1149 | if (($currentHive -eq 'HKEY_LOCAL_MACHINE') -or ($currentHive -eq 'HKEY_USERS\S-1-5-18') -or ($currentHive -eq 'HKEY_USERS\S-1-5-19') -or ($currentHive -eq 'HKEY_USERS\S-1-5-20')) { 1150 | $access = 'System' 1151 | } 1152 | else { 1153 | $access = 'User' 1154 | } 1155 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'User Init Mpr Logon Script' -Classification 'MITRE ATT&CK T1037.001' -Path $propPath -Value $mprlogonscript.UserInitMprLogonScript -AccessGained $access -Note 'The executable specified in the "UserInitMprLogonScript" property of the HKEY_USERS\\Environment key is run when the user logs on.' -Reference 'https://attack.mitre.org/techniques/T1037/001/' 1156 | $null = $persistenceObjectArray.Add($PersistenceObject) 1157 | } 1158 | } 1159 | Write-Verbose -Message '' 1160 | } 1161 | 1162 | function Get-AutodialDLL { 1163 | Write-Verbose -Message "$hostname - Getting the AutodialDLL property..." 1164 | foreach ($hive in $systemAndUsersHives) { 1165 | $autodialDll = Get-ItemProperty -Path "$hive\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters" -Name AutodialDLL 1166 | if ($autodialDll) { 1167 | $dllPath = $autodialDll.AutodialDLL 1168 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($dllPath))) -eq $false) { 1169 | $dllPath = "C:\Windows\System32\$dllPath" 1170 | } 1171 | if ((Get-IfSafeLibrary $dllPath) -EQ $false) { 1172 | Write-Verbose -Message "$hostname - [!] Found AutodialDLL property under $(Convert-Path -Path $hive)\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters\ key which points to a non-OS DLL!" 1173 | $propPath = (Convert-Path -Path $autodialDll.PSPath) + '\AutodialDLL' 1174 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'AutodialDLL Winsock Injection' -Classification 'Hexacorn Technique N.24' -Path $propPath -Value $autodialDll.AutodialDLL -AccessGained 'System' -Note 'The DLL specified in the "AutodialDLL" property of the (HKLM|HKEY_USERS\)\SYSTEM\CurrentControlSet\Services\WinSock2\Parameters key is loaded by the Winsock library everytime it connects to the internet.' -Reference 'https://www.hexacorn.com/blog/2015/01/13/beyond-good-ol-run-key-part-24/' 1175 | $null = $persistenceObjectArray.Add($PersistenceObject) 1176 | } 1177 | } 1178 | } 1179 | Write-Verbose -Message '' 1180 | } 1181 | 1182 | function Get-LsaExtensions { 1183 | Write-Verbose -Message "$hostname - Getting LSA's extensions..." 1184 | foreach ($hive in $systemAndUsersHives) { 1185 | $lsaExtensions = Get-ItemProperty -Path "$hive\SYSTEM\CurrentControlSet\Control\LsaExtensionConfig\LsaSrv" -Name Extensions 1186 | if ($lsaExtensions) { 1187 | $dlls = $lsaExtensions.Extensions -split '\s+' 1188 | foreach ($dll in $dlls) { 1189 | $dllPath = $dll 1190 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($dllPath))) -eq $false) { 1191 | $dllPath = "C:\Windows\System32\$dllPath" 1192 | } 1193 | if ((Get-IfSafeLibrary $dllPath) -EQ $false) { 1194 | Write-Verbose -Message "$hostname - [!] Found LSA Extension DLL under the $(Convert-Path -Path $hive)\SYSTEM\CurrentControlSet\Control\LsaExtensionConfig\LsaSrv\Extensions property which points to a non-OS DLL!" 1195 | $propPath = (Convert-Path -Path $lsaExtensions.PSPath) + '\Extensions' 1196 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'LSA Extensions DLL' -Classification 'Uncatalogued Technique N.6' -Path $propPath -Value $dll -AccessGained 'System' -Note 'The DLLs specified in the "Extensions" property of the (HKLM|HKEY_USERS\)\SYSTEM\CurrentControlSet\Control\LsaExtensionConfig\LsaSrv\ key are loaded by LSASS at machine boot.' -Reference 'https://persistence-info.github.io/Data/lsaaextension.html' 1197 | $null = $persistenceObjectArray.Add($PersistenceObject) 1198 | } 1199 | } 1200 | } 1201 | } 1202 | Write-Verbose -Message '' 1203 | } 1204 | 1205 | function Get-ServerLevelPluginDll { 1206 | Write-Verbose -Message "$hostname - Getting the ServerLevelPluginDll property..." 1207 | foreach ($hive in $systemAndUsersHives) { 1208 | $pluginDll = Get-ItemProperty -Path "$hive\SYSTEM\CurrentControlSet\Services\DNS\Parameters" -Name ServerLevelPluginDll 1209 | if ($pluginDll) { 1210 | $dllPath = $pluginDll.ServerLevelPluginDll 1211 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($dllPath))) -eq $false) { 1212 | $dllPath = "C:\Windows\System32\$dllPath" 1213 | } 1214 | if ((Get-IfSafeLibrary $dllPath) -EQ $false) { 1215 | Write-Verbose -Message "$hostname - [!] Found ServerLevelPluginDll property under $(Convert-Path -Path $hive)\SYSTEM\CurrentControlSet\Services\DNS\Parameters key which points to a non-OS DLL!" 1216 | $propPath = (Convert-Path -Path $pluginDll.PSPath) + '\ServerLevelPluginDll' 1217 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'ServerLevelPluginDll DNS Server DLL Hijacking' -Classification 'Uncatalogued Technique N.7' -Path $propPath -Value $pluginDll.ServerLevelPluginDll -AccessGained 'System' -Note 'The DLL specified in the "ServerLevelPluginDll" property of the (HKLM|HKEY_USERS\)\SYSTEM\CurrentControlSet\Services\DNS\Parameters key is loaded by the DNS service on systems with the "DNS Server" role enabled.' -Reference 'https://persistence-info.github.io/Data/serverlevelplugindll.html' 1218 | $null = $persistenceObjectArray.Add($PersistenceObject) 1219 | } 1220 | } 1221 | } 1222 | Write-Verbose -Message '' 1223 | } 1224 | 1225 | function Get-LsaPasswordFilter { 1226 | Write-Verbose -Message "$hostname - Getting LSA's password filters..." 1227 | foreach ($hive in $systemAndUsersHives) { 1228 | $passwordFilters = Get-ItemProperty -Path "$hive\SYSTEM\CurrentControlSet\Control\Lsa" -Name 'Notification Packages' 1229 | if ($passwordFilters) { 1230 | $dlls = $passwordFilters.'Notification Packages' -split '\s+' 1231 | foreach ($dll in $dlls) { 1232 | if ($dll -like "*.dll") { 1233 | $dllPath = "C:\Windows\System32\$dll" 1234 | } 1235 | else { 1236 | $dllPath = "C:\Windows\System32\$dll.dll" 1237 | } 1238 | 1239 | if ((Get-IfSafeLibrary $dllPath) -EQ $false) { 1240 | Write-Verbose -Message "$hostname - [!] Found a LSA password filter DLL under the $(Convert-Path -Path $hive)\SYSTEM\CurrentControlSet\Control\Lsa\Notification Packages property which points to a non-OS DLL!" 1241 | $propPath = (Convert-Path -Path $passwordFilters.PSPath) + '\Notification Packages' 1242 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'LSA Password Filter DLL' -Classification 'MITRE ATT&CK T1556.002' -Path $propPath -Value $dllPath -AccessGained 'System' -Note 'The DLLs specified in the "Notification Packages" property of the (HKLM|HKEY_USERS\)\SYSTEM\CurrentControlSet\Control\Lsa\ key are loaded by LSASS at machine boot.' -Reference 'https://attack.mitre.org/techniques/T1556/002/' 1243 | $null = $persistenceObjectArray.Add($PersistenceObject) 1244 | } 1245 | } 1246 | } 1247 | } 1248 | Write-Verbose -Message '' 1249 | } 1250 | 1251 | function Get-LsaAuthenticationPackages { 1252 | Write-Verbose -Message "$hostname - Getting LSA's authentication packages..." 1253 | foreach ($hive in $systemAndUsersHives) { 1254 | $authPackages = Get-ItemProperty -Path "$hive\SYSTEM\CurrentControlSet\Control\Lsa" -Name 'Authentication Packages' 1255 | if ($authPackages) { 1256 | $dlls = $authPackages.'Authentication Packages' -split '\s+' 1257 | foreach ($dll in $dlls) { 1258 | $dllPath = "C:\Windows\System32\$dll.dll" 1259 | if ((Get-IfSafeLibrary $dllPath) -EQ $false) { 1260 | Write-Verbose -Message "$hostname - [!] Found a LSA authentication package DLL under the $(Convert-Path -Path $hive)\SYSTEM\CurrentControlSet\Control\Lsa\Authentication Packages property which points to a non-OS DLL!" 1261 | $propPath = (Convert-Path -Path $authPackages.PSPath) + '\Authentication Packages' 1262 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'LSA Authentication Package DLL' -Classification 'MITRE ATT&CK T1547.002' -Path $propPath -Value $dllPath -AccessGained 'System' -Note 'The DLLs specified in the "Authentication Packages" property of the (HKLM|HKEY_USERS\)\SYSTEM\CurrentControlSet\Control\Lsa\ key are loaded by LSASS at machine boot.' -Reference 'https://attack.mitre.org/techniques/T1547/002/' 1263 | $null = $persistenceObjectArray.Add($PersistenceObject) 1264 | } 1265 | } 1266 | } 1267 | } 1268 | Write-Verbose -Message '' 1269 | } 1270 | 1271 | function Get-LsaSecurityPackages { 1272 | Write-Verbose -Message "$hostname - Getting LSA's security packages..." 1273 | foreach ($hive in $systemAndUsersHives) { 1274 | $secPackages = Get-ItemProperty -Path "$hive\SYSTEM\CurrentControlSet\Control\Lsa" -Name 'Security Packages' 1275 | if ($secPackages) { 1276 | $packageString = $secPackages.'Security Packages' -replace '"', '' 1277 | $dlls = $packageString -split '\s+' 1278 | foreach ($dll in $dlls) { 1279 | if ($dll -eq "") { 1280 | continue 1281 | } 1282 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($dll))) -eq $false) { 1283 | $dll = "C:\Windows\System32\$dll.dll" 1284 | } 1285 | if ((Get-IfSafeLibrary $dllPath) -EQ $false) { 1286 | Write-Verbose -Message "$hostname - [!] Found a LSA security package DLL under the $(Convert-Path -Path $hive)\SYSTEM\CurrentControlSet\Control\Lsa\Security Packages property which points to a non-OS DLL!" 1287 | $propPath = (Convert-Path -Path $secPackages.PSPath) + '\Security Packages' 1288 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'LSA Security Package DLL' -Classification 'MITRE ATT&CK T1547.005' -Path $propPath -Value $dll -AccessGained 'System' -Note 'The DLLs specified in the "Security Packages" property of the (HKLM|HKEY_USERS\)\SYSTEM\CurrentControlSet\Control\Lsa\ key are loaded by LSASS at machine boot.' -Reference 'https://attack.mitre.org/techniques/T1547/005/' 1289 | $null = $persistenceObjectArray.Add($PersistenceObject) 1290 | } 1291 | } 1292 | } 1293 | } 1294 | Write-Verbose -Message '' 1295 | } 1296 | 1297 | function Get-WinlogonNotificationPackages { 1298 | Write-Verbose -Message "$hostname - Getting Winlogon Notification packages..." 1299 | foreach ($hive in $systemAndUsersHives) { 1300 | 1301 | $notificationPackages = Get-ItemProperty -Path "$hive\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify" 1302 | if ($notificationPackages) { 1303 | Write-Verbose -Message "$hostname - [!] Found properties under $(Convert-Path -Path $hive)\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify key which deserve investigation!" 1304 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $notificationPackages)) { 1305 | if ($psProperties.Contains($prop.Name)) { 1306 | continue 1307 | } # skip the property if it's powershell built-in property 1308 | $propPath = Convert-Path -Path $notificationPackages.PSPath 1309 | $propPath += '\' + $prop.Name 1310 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Winlogon Notification Package' -Classification 'MITRE ATT&CK T1547.004' -Path $propPath -Value $notificationPackages.($prop.Name) -AccessGained 'System' -Note 'DLLs in the properties of the (HKLM|HKEY_USERS\)\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify key are loaded by the system when it boots.' -Reference 'https://attack.mitre.org/techniques/T1547/004/' 1311 | $null = $persistenceObjectArray.Add($PersistenceObject) 1312 | } 1313 | } 1314 | } 1315 | Write-Verbose -Message '' 1316 | } 1317 | 1318 | function Get-ExplorerTools { 1319 | Write-Verbose -Message "$hostname - Getting Explorer Tools..." 1320 | foreach ($hive in $systemAndUsersHives) { 1321 | $explorerTools = Get-ChildItem -Path "$hive\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer" 1322 | foreach ($key in $explorerTools) { 1323 | $path = ((Get-ItemProperty -Path Registry::$key -Name '(Default)').'(Default)' -split '\s+')[0] # split the path and take only the executable in case there are arguments 1324 | if (('' -ne $path) -and ([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($path)) -eq $false)) { 1325 | $path = "C:\Windows\System32\$path.dll" 1326 | } 1327 | if (-not (Get-AuthenticodeSignature -FilePath $path ).IsOSBinary) { 1328 | Write-Verbose -Message "$hostname - [!] Found an executable under a subkey of $(Convert-Path -Path $hive)\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer key which deserve investigation!" 1329 | $propPath = Convert-Path -Path $key.PSPath 1330 | $propPath += '\(Default)' 1331 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Explorer Tools Hijacking' -Classification 'Hexacorn Technique N.55' -Path $propPath -Value $path -AccessGained 'System' -Note 'Executables in the (Default) property of a subkey of (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer are run when the corresponding event is triggered.' -Reference 'https://www.hexacorn.com/blog/2017/01/18/beyond-good-ol-run-key-part-55/' 1332 | $null = $persistenceObjectArray.Add($PersistenceObject) 1333 | } 1334 | } 1335 | } 1336 | Write-Verbose -Message '' 1337 | } 1338 | 1339 | function Get-DotNetDebugger { 1340 | Write-Verbose -Message "$hostname - Getting .NET Debugger properties..." 1341 | foreach ($hive in $systemAndUsersHives) { 1342 | $dotNetDebugger = Get-ItemProperty -Path "$hive\SOFTWARE\Microsoft\.NETFramework" -Name DbgManagedDebugger 1343 | if ($dotNetDebugger.DbgManagedDebugger) { 1344 | if (Get-IfSafeExecutable $dotNetDebugger.DbgManagedDebugger) { 1345 | continue 1346 | } 1347 | Write-Verbose -Message "$hostname - [!] Found DbgManagedDebugger under the $(Convert-Path -Path $hive)\SOFTWARE\Microsoft\.NETFramework key which deserve investigation!" 1348 | $propPath = Convert-Path -Path $dotNetDebugger.PSPath 1349 | $propPath += '\DbgManagedDebugger' 1350 | 1351 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'DbgManagedDebugger Custom Debugger' -Classification 'Hexacorn Technique N.4' -Path $propPath -Value $dotNetDebugger.DbgManagedDebugger -AccessGained 'System/User' -Note "The executable in the DbgManagedDebugger property of (HKLM|HKEY_USERS\)\SOFTWARE\Wow6432Node\Microsoft\.NETFramework is run when a .NET process crashes. Gained access depends on whose context the debugged process runs in." -Reference 'https://www.hexacorn.com/blog/2013/09/19/beyond-good-ol-run-key-part-4/' 1352 | $null = $persistenceObjectArray.Add($PersistenceObject) 1353 | } 1354 | 1355 | $dotNetDebugger = Get-ItemProperty -Path "$hive\SOFTWARE\Wow6432Node\Microsoft\.NETFramework" -Name DbgManagedDebugger 1356 | if ($dotNetDebugger.DbgManagedDebugger) { 1357 | if (Get-IfSafeExecutable $dotNetDebugger.DbgManagedDebugger) { 1358 | continue 1359 | } 1360 | Write-Verbose -Message "$hostname - [!] Found DbgManagedDebugger under the $(Convert-Path -Path $hive)\SOFTWARE\Wow6432Node\Microsoft\.NETFramework key which deserve investigation!" 1361 | $propPath = Convert-Path -Path $dotNetDebugger.PSPath 1362 | $propPath += '\DbgManagedDebugger' 1363 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Wow6432Node DbgManagedDebugger Custom Debugger' -Classification 'Hexacorn Technique N.4' -Path $propPath -Value $dotNetDebugger.DbgManagedDebugger -AccessGained 'System/User' -Note "The executable in the DbgManagedDebugger property of (HKLM|HKEY_USERS\)\SOFTWARE\Wow6432Node\Microsoft\.NETFramework is run when a .NET 32 bit process on a 64 bit system crashes. Gained access depends on whose context the debugged process runs in." -Reference 'https://www.hexacorn.com/blog/2013/09/19/beyond-good-ol-run-key-part-4/' 1364 | $null = $persistenceObjectArray.Add($PersistenceObject) 1365 | } 1366 | } 1367 | Write-Verbose -Message '' 1368 | } 1369 | 1370 | function Get-ErrorHandlerCmd { 1371 | Write-Verbose -Message "$hostname - Checking if C:\WINDOWS\Setup\Scripts\ contains a file called ErrorHandler.cmd..." 1372 | $errorHandlerCmd = Get-ChildItem -Path 'C:\WINDOWS\Setup\Scripts\ErrorHandler.cmd' 1373 | if ($errorHandlerCmd) { 1374 | Write-Verbose -Message "$hostname - [!] Found C:\WINDOWS\Setup\Scripts\ErrorHandler.cmd!" 1375 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'ErrorHandler.cmd Hijacking' -Classification 'Hexacorn Technique N.135' -Path "C:\WINDOWS\Setup\Scripts\" -Value "ErrorHandler.cmd" -AccessGained 'User' -Note "The content of C:\WINDOWS\Setup\Scripts\ErrorHandler.cmd is read whenever some tools under C:\WINDOWS\System32\oobe\ (e.g. Setup.exe) fail to run for any reason." -Reference 'https://www.hexacorn.com/blog/2022/01/16/beyond-good-ol-run-key-part-135/' 1376 | $null = $persistenceObjectArray.Add($PersistenceObject) 1377 | } 1378 | Write-Verbose -Message '' 1379 | } 1380 | 1381 | function Get-WMIEventsSubscrition { 1382 | Write-Verbose -Message "$hostname - Checking WMI Subscriptions..." 1383 | $cmdEventConsumer = Get-WMIObject -Namespace root\Subscription -Class CommandLineEventConsumer 1384 | if ($cmdEventConsumer) { 1385 | foreach ( $cmdEntry in ($cmdEventConsumer)) { 1386 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'WMI Event Subscription' -Classification 'MITRE ATT&CK T1546.003' -Path $cmdEntry.__PATH -Value "CommandLineTemplate: $($cmdEntry.CommandLineTemplate) / ExecutablePath: $($cmdEntry.ExecutablePath)" -AccessGained 'System' -Note "WMI Events subscriptions can be used to link script/command executions to specific events. Here we list the active consumer events, but you may want to review also existing Filters (with Get-WMIObject -Namespace root\Subscription -Class __EventFilter) and Bindings (with Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding)" -Reference 'https://attack.mitre.org/techniques/T1546/003/' 1387 | $null = $persistenceObjectArray.Add($PersistenceObject) 1388 | } 1389 | } 1390 | 1391 | $scriptEventConsumer = Get-WMIObject -Namespace root\Subscription -Class ActiveScriptEventConsumer 1392 | if ($scriptEventConsumer) { 1393 | foreach ( $scriptEntry in ($scriptEventConsumer)) { 1394 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'WMI Event Subscription' -Classification 'MITRE ATT&CK T1546.003' -Path $scriptEntry.__PATH -Value "ScriptingEngine: $($scriptEntry.ScriptingEngine) / ScriptFileName: $($scriptEntry.ScriptFileName) / ScriptText: $($scriptEntry.ScriptText)" -AccessGained 'System' -Note "WMI Events subscriptions can be used to link script/command executions to specific events. Here we list the active consumer events, but you may want to review also existing Filters (with Get-WMIObject -Namespace root\Subscription -Class __EventFilter) and Bindings (with Get-WMIObject -Namespace root\Subscription -Class __FilterToConsumerBinding)" -Reference 'https://attack.mitre.org/techniques/T1546/003/' 1395 | $null = $persistenceObjectArray.Add($PersistenceObject) 1396 | } 1397 | } 1398 | Write-Verbose -Message '' 1399 | } 1400 | 1401 | function Get-WindowsServices { 1402 | Write-Verbose -Message "$hostname - Checking Windows Services..." 1403 | $services = Get-CimInstance -ClassName win32_service | Select-Object Name, DisplayName, State, PathName 1404 | foreach ( $service in $services) { 1405 | $path = Get-ExecutableFromCommandLine $service.PathName 1406 | if ((Get-IfSafeExecutable $path) -EQ $false) { 1407 | Write-Verbose -Message "$hostname - [!] Found Windows Services which may deserve investigation..." 1408 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Windows Service' -Classification 'MITRE ATT&CK T1543.003' -Path $service.Name -Value $service.PathName -AccessGained 'System' -Note "Adversaries may create or modify Windows services to repeatedly execute malicious payloads as part of persistence. When Windows boots up, it starts programs or applications called services that perform background system functions." -Reference 'https://attack.mitre.org/techniques/T1543/003/' 1409 | $null = $persistenceObjectArray.Add($PersistenceObject) 1410 | } 1411 | } 1412 | Write-Verbose -Message '' 1413 | } 1414 | 1415 | function Get-PowerAutomate { 1416 | Write-Verbose -Message "$hostname - Checking Power Automate presence..." 1417 | 1418 | $PADFolder = "$env:ProgramData\Microsoft\Power Automate\Logs" 1419 | $LastPALog = Get-ChildItem -Path $PADFolder | Sort-Object LastWriteTime -Descending | Select-Object -First 1 1420 | 1421 | if ($LastPALog) { 1422 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Power Automate' -Classification 'Uncatalogued Technique N.12' -Path $PADFolder -Value $LastPALog -AccessGained 'System/User' -Note "'Power Automate' is an RPA (Robotic Process Automation) made available by Microsoft. It can runs on standalone system or through Azure Tenants. Given the high number of functions available and the 'legit source' of these executables and processes, it could be used for malicious intent as well. The presence of the logs means that the system is in some way running these flows. Review if they are legit or not (last log is shown in Value)." -Reference 'https://github.com/mbrg/defcon30/tree/main/No_Code_Malware' 1423 | $null = $persistenceObjectArray.Add($PersistenceObject) 1424 | } 1425 | 1426 | Write-Verbose -Message '' 1427 | } 1428 | 1429 | function Get-TSInitialProgram { 1430 | Write-Verbose -Message "$hostname - Getting Terminal Services InitialProgram properties..." 1431 | foreach ($hive in $systemAndUsersHives) { 1432 | $InitialProgram = Get-ItemProperty -Path "$hive\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" -Name InitialProgram 1433 | $fInheritInitialProgram = Get-ItemProperty -Path "$hive\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" -Name fInheritInitialProgram 1434 | if ($null -ne $InitialProgram.InitialProgram -and $InitialProgram.InitialProgram.Length -ne 0 -and $fInheritInitialProgram -eq 1) { 1435 | Write-Verbose -Message "$hostname - [!] Found InitialProgram property under the $(Convert-Path -Path $hive) Terminal Services key which deserve investigation!" 1436 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $InitialProgram)) { 1437 | if ($psProperties.Contains($prop.Name)) { 1438 | continue 1439 | } # skip the property if it's powershell built-in property 1440 | $propPath = Convert-Path -Path $InitialProgram.PSPath 1441 | $propPath += '\' + $prop.Name 1442 | if (Get-IfSafeExecutable $InitialProgram.($prop.Name)) { 1443 | continue 1444 | } 1445 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Terminal Services InitialProgram' -Classification 'Uncatalogued Technique N.8' -Path $propPath -Value $InitialProgram.($prop.Name) -AccessGained 'System/User' -Note "The executable in the InitialProgram property of (HKLM|HKEY_USERS\)\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services is run when a Remote Desktop Connection is made to the target machine. Gained access depends on whether the key is in the system hive or a user's hive. For this technique to work, the fInheritInitialProgram property of the same key must also be set to 1." -Reference 'https://persistence-info.github.io/Data/tsinitialprogram.html' 1446 | $null = $persistenceObjectArray.Add($PersistenceObject) 1447 | } 1448 | } 1449 | 1450 | $InitialProgram = Get-ItemProperty -Path "$hive\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name InitialProgram 1451 | $fInheritInitialProgram = Get-ItemProperty -Path "$hive\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name fInheritInitialProgram 1452 | if ($null -ne $InitialProgram.InitialProgram -and $InitialProgram.InitialProgram.Length -ne 0 -and $fInheritInitialProgram -eq 1) { 1453 | Write-Verbose -Message "$hostname - [!] Found InitialProgram property under the $(Convert-Path -Path $hive) Terminal Services key which deserve investigation!" 1454 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $InitialProgram)) { 1455 | if ($psProperties.Contains($prop.Name)) { 1456 | continue 1457 | } # skip the property if it's powershell built-in property 1458 | $propPath = Convert-Path -Path $InitialProgram.PSPath 1459 | $propPath += '\' + $prop.Name 1460 | if (Get-IfSafeExecutable $InitialProgram.($prop.Name)) { 1461 | continue 1462 | } 1463 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Terminal Services InitialProgram' -Classification 'Uncatalogued Technique N.8' -Path $propPath -Value $InitialProgram.($prop.Name) -AccessGained 'System/User' -Note "The executable in the InitialProgram property of (HKLM|HKEY_USERS\)\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services is run when a Remote Desktop Connection is made to the target machine. Gained access depends on whether the key is in the system hive or a user's hive. For this technique to work, the fInheritInitialProgram property of the same key must also be set to 1." -Reference 'https://persistence-info.github.io/Data/tsinitialprogram.html' 1464 | $null = $persistenceObjectArray.Add($PersistenceObject) 1465 | } 1466 | } 1467 | } 1468 | Write-Verbose -Message '' 1469 | } 1470 | 1471 | function Get-AccessibilityTools { 1472 | Write-Verbose -Message "$hostname - Looking for accessibility tools backdoors..." 1473 | 1474 | $accessibilityTools = @( 1475 | "$env:windir\System32\sethc.exe", 1476 | "$env:windir\System32\osk.exe", 1477 | "$env:windir\System32\Narrator.exe", 1478 | "$env:windir\System32\Magnify.exe", 1479 | "$env:windir\System32\DisplaySwitch.exe", 1480 | "$env:windir\System32\Utilman.exe" 1481 | ) 1482 | 1483 | $cmdHash = Get-FileHash -LiteralPath $env:windir\System32\cmd.exe 1484 | $psHash = Get-FileHash -LiteralPath $env:windir\System32\WindowsPowerShell\v1.0\powershell.exe 1485 | $explorerHash = Get-FileHash -LiteralPath $env:windir\explorer.exe 1486 | 1487 | $backdoorHashes = [ordered]@{ 1488 | $cmdHash.Hash = "$env:windir\System32\cmd.exe"; 1489 | $psHash.Hash = "$env:windir\System32\WindowsPowerShell\v1.0\powershell.exe"; 1490 | $explorerHash.Hash = "$env:windir\explorer.exe" 1491 | } 1492 | 1493 | foreach ($tool in $accessibilityTools) { 1494 | if ((Get-AuthenticodeSignature -FilePath $tool).IsOSBinary -ne $true) { 1495 | Write-Verbose -Message "$hostname - [!] Found a suspicious executable in place of of the accessibility tool $tool" 1496 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Accessibility Tools Backdoor' -Classification 'MITRE ATT&CK T1546.008' -Path $tool -Value $tool -AccessGained 'System' -Note "Accessibility tools are executables that can be run from the lock screen of a Windows machine and are supposed to enable accessibility features like text to speech or zooming in on the screen. If an attacker replaces them with malicious or LOLBIN executables they can execute code with SYSTEM permission from a lock screen, effectively bypassing authentication. In this case, the accessibility tool in the Path field is not an OS executable, so it may have been replaced with a malicious, non-Microsoft executable." -Reference 'https://attack.mitre.org/techniques/T1546/008/' 1497 | $null = $persistenceObjectArray.Add($PersistenceObject) 1498 | } 1499 | else { 1500 | $toolHash = Get-FileHash -LiteralPath $tool 1501 | foreach ($hash in $backdoorHashes.Keys) { 1502 | if ($toolHash.Hash -eq $hash) { 1503 | Write-Verbose -Message "$hostname - [!] Found a suspicious executable in place of of the accessibility tool $tool" 1504 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Accessibility Tools Backdoor' -Classification 'MITRE ATT&CK T1546.008' -Path $tool -Value $backdoorHashes[$hash] -AccessGained 'System' -Note "Accessibility tools are executables that can be run from the lock screen of a Windows machine and are supposed to enable accessibility features like text to speech or zooming in on the screen. If an attacker replaces them with malicious or LOLBIN executables they can execute code with SYSTEM permission from a lock screen, effectively bypassing authentication. In this case, the accessibility tool in the Path field has been replaced with the binary in the Value field." -Reference 'https://attack.mitre.org/techniques/T1546/008/' 1505 | $null = $persistenceObjectArray.Add($PersistenceObject) 1506 | } 1507 | } 1508 | } 1509 | } 1510 | Write-Verbose -Message '' 1511 | } 1512 | 1513 | function Get-AMSIProviders { 1514 | Write-Verbose -Message "$hostname - Getting AMSI providers..." 1515 | $legitAMSIGUID = '{2781761E-28E0-4109-99FE-B9D127C57AFE}' # this is the GUID of Microsoft's legitimate AMSI provider 1516 | $amsiProviders = Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\AMSI\Providers\" 1517 | foreach ($key in $amsiProviders) { 1518 | $keyGUID = $key.PSChildName 1519 | if ($keyGUID -eq $legitAMSIGUID) { 1520 | continue 1521 | } 1522 | Write-Verbose -Message "$hostname - [!] Found an unknown AMSI provider under the key HKLM\SOFTWARE\Microsoft\AMSI\Providers\$keyGUID which deserves investigation!" 1523 | $path = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Classes\CLSID\$keyGUID\InprocServer32" -Name '(Default)').'(Default)' 1524 | if (-not ($path -like '*.dll')) { 1525 | # if the DLL is specified without a .dll, append it 1526 | $path = $path + '.dll' 1527 | } 1528 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($path))) -eq $false) { 1529 | # if the DLL is specified without a path, assume it's under System32 1530 | $path = "C:\Windows\System32\$path" 1531 | } 1532 | $propPath = "HKLM:\SOFTWARE\Classes\CLSID\$keyGUID\InprocServer32\(Default)" 1533 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Fake AMSI Provider' -Classification 'Uncatalogued Technique N.9' -Path $propPath -Value $path -AccessGained 'System/User' -Note 'DLLs in the (Default) property of HKLM:\SOFTWARE\Classes\CLSID\$keyGUID\InprocServer32 where $keyGUID is a GUID listed under HKLM:\SOFTWARE\Microsoft\AMSI\Providers\ are considered AMSI providers and loaded by all processes also loading the .NET CLR.' -Reference 'https://b4rtik.github.io/posts/antimalware-scan-interface-provider-for-persistence/' 1534 | $null = $persistenceObjectArray.Add($PersistenceObject) 1535 | } 1536 | 1537 | Write-Verbose -Message '' 1538 | } 1539 | 1540 | function Get-PowershellProfiles { 1541 | Write-Verbose -Message "$hostname - Getting Powershell profiles..." 1542 | $script:powershellProfilesArray = [Collections.ArrayList]::new() 1543 | $systemProfile = Get-ChildItem 'C:\Windows\System32\WindowsPowerShell\v1.0\Profile.ps1' 1544 | $microsoftSystemProfile = Get-ChildItem 'C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1' 1545 | if ($systemProfile) { 1546 | $null = $powershellProfilesArray.Add($systemProfile) 1547 | } 1548 | if ($microsoftSystemProfile) { 1549 | $null = $powershellProfilesArray.Add($microsoftSystemProfile) 1550 | } 1551 | $userDirectories = Get-ChildItem -Path 'C:\Users\' 1552 | foreach ($directory in $userDirectories) { 1553 | $userProfile = Get-ChildItem -Path "$($directory.FullName)\Documents\WindowsPowerShell\Profile.ps1" 1554 | if ($userProfile) { 1555 | $null = $powershellProfilesArray.Add($userProfile) 1556 | } 1557 | $userProfile = Get-ChildItem -Path "$($directory.FullName)\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1" 1558 | if ($userProfile) { 1559 | $null = $powershellProfilesArray.Add($userProfile) 1560 | } 1561 | } 1562 | foreach ($profile in $powershellProfilesArray) { 1563 | Write-Verbose -Message "$hostname - [!] Found a Powershell profile under $($profile.FullName) which deserves investigation!" 1564 | $path = $profile 1565 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Powershell Profile' -Classification 'MITRE ATT&CK T1546.013' -Path $path.DirectoryName -Value $path.FullName -AccessGained 'User' -Note "Files named 'Profile.ps1' or 'Microsoft.PowerShell_profile.ps1' under System32's Powershell directory or a user's Documents\WindowsPowerShell folder are loaded whenever a user launches Powershell." -Reference 'https://attack.mitre.org/techniques/T1546/013/' 1566 | $null = $persistenceObjectArray.Add($PersistenceObject) 1567 | } 1568 | Write-Verbose -Message '' 1569 | } 1570 | 1571 | function Get-SilentExitMonitor { 1572 | Write-Verbose -Message "$hostname - Getting Silent exit monitors..." 1573 | $exitMonitors = Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\" 1574 | foreach ($key in $exitMonitors) { 1575 | $monitoredApp = $key.PSPath 1576 | $monitoringApp = (Get-ItemProperty $monitoredApp).MonitorProcess 1577 | Write-Verbose -Message "$hostname - [!] Found a silently monitored process under HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\ which deserves investigation!" 1578 | $propPath = Convert-Path -Path $monitoredApp 1579 | $propPath += '\MonitorProcess' 1580 | $path = $monitoringApp 1581 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($path))) -eq $false) { 1582 | # if the exe is specified without a path, try to get it with Get-Command 1583 | $path = (Get-Command $path).Source 1584 | } 1585 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Silent Process Exit Monitor' -Classification 'MITRE ATT&CK T1546.012' -Path $propPath -Value $path -AccessGained 'System/User' -Note 'Executables specified under subkeys of HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\ are run when the process associated with the subkey is terminated by another process.' -Reference 'https://attack.mitre.org/techniques/T1546/012/' 1586 | $null = $persistenceObjectArray.Add($PersistenceObject) 1587 | } 1588 | 1589 | Write-Verbose -Message '' 1590 | } 1591 | 1592 | function Get-TelemetryController { 1593 | Write-Verbose -Message "$hostname - Getting Telemetry controllers..." 1594 | $telemetryController = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\TelemetryController").Command 1595 | if ($telemetryController) { 1596 | $propPath = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\TelemetryController\Command' 1597 | $path = $telemetryController 1598 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($path))) -eq $false) { 1599 | # if the exe is specified without a path, try to get it with Get-Command 1600 | $path = (Get-Command $path).Source 1601 | } 1602 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Telemetry Controller Command' -Classification 'Uncatalogued Technique N.10' -Path $propPath -Value $path -AccessGained 'System' -Note "Executables specified under the Command property of HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\TelemetryController\ are run by the Windows Compatibility Telemetry's binary named CompatTelRunner.exe" -Reference 'https://www.trustedsec.com/blog/abusing-windows-telemetry-for-persistence/' 1603 | $null = $persistenceObjectArray.Add($PersistenceObject) 1604 | } 1605 | Write-Verbose -Message '' 1606 | } 1607 | 1608 | function Get-RDPWDSStartupPrograms { 1609 | Write-Verbose -Message "$hostname - Getting RDP WDS startup programs" 1610 | $startupPrograms = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd").StartupPrograms 1611 | if ($startupPrograms) { 1612 | $executables = $startupPrograms.split(',') 1613 | foreach ($exe in $executables) { 1614 | if ($exe -eq 'rdpclip') { 1615 | continue 1616 | } 1617 | $propPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\StartupPrograms' 1618 | $path = $exe 1619 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($path))) -eq $false) { 1620 | # if the exe is specified without a path, try to get it with Get-Command 1621 | $path = (Get-Command $path).Source 1622 | } 1623 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'RDP WDS Startup Programs' -Classification 'Uncatalogued Technique N.11' -Path $propPath -Value $path -AccessGained 'System' -Note "Executables specified under the StartupPrograms property of HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd are run whenever a user logs on the machine through remote desktop." -Reference 'https://persistence-info.github.io/Data/rdpwdstartupprograms.html' 1624 | $null = $persistenceObjectArray.Add($PersistenceObject) 1625 | } 1626 | } 1627 | Write-Verbose -Message '' 1628 | } 1629 | 1630 | function Get-ScheduledTasks { 1631 | Write-Verbose -Message "$hostname - Getting scheduled tasks" 1632 | $tasks = Get-ScheduledTask 1633 | if ($tasks) { 1634 | foreach ($task in $tasks) { 1635 | $propPath = $task.TaskPath 1636 | $propPath += $task.TaskName 1637 | $path = ($task.Actions).Execute + " " + ($task.Actions).Arguments 1638 | if ($task.UserId -eq 'SYSTEM') { 1639 | $access = 'System' 1640 | } 1641 | else { 1642 | $access = 'User' 1643 | } 1644 | 1645 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Scheduled Task' -Classification 'MITRE ATT&CK T1053.005' -Path $propPath -Value $path -AccessGained $access -Note "Scheduled tasks run executables or actions when certain conditions, such as user log in or machine boot up, are met." -Reference 'https://attack.mitre.org/techniques/T1053/005/' 1646 | $null = $persistenceObjectArray.Add($PersistenceObject) 1647 | } 1648 | } 1649 | Write-Verbose -Message '' 1650 | } 1651 | 1652 | function Get-BitsJobsNotifyCmdLine { 1653 | Write-Verbose -Message "$hostname - Getting BITS Jobs" 1654 | $jobs = Get-BitsTransfer -AllUsers | Where-Object { $_.JobState -eq "Error" } | Where-Object { $_.NotifyCmdLine.Length -gt 0 } 1655 | if ($jobs) { 1656 | foreach ($job in $jobs) { 1657 | $propPath += $job.JobId 1658 | $path = $job.NotifyCmdLine 1659 | if ($job.OwnerAccount -eq 'NT AUTHORITY\SYSTEM') { 1660 | $access = 'System' 1661 | } 1662 | else { 1663 | $access = 'User' 1664 | } 1665 | 1666 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'BITS Job NotifyCmdLine' -Classification 'MITRE ATT&CK T1197.003' -Path $propPath -Value $path -AccessGained $access -Note "Windows Background Intelligent Transfer Service (BITS) can be used to persistently execute code by creating long-standing jobs. Specifically, if an attacker sets the SetNotifyCmdLine when creating a job which will error, the executable specified will be run everytime the BITS job fails." -Reference 'https://attack.mitre.org/techniques/T1197/' 1667 | $null = $persistenceObjectArray.Add($PersistenceObject) 1668 | } 1669 | } 1670 | Write-Verbose -Message '' 1671 | } 1672 | 1673 | function Get-Screensaver { 1674 | Write-Verbose -Message "$hostname - Getting Screensaver programs" 1675 | foreach ($sid in $systemAndUsersHives) { 1676 | $legitimatePrograms = "C:\Windows\system32\Mystify.scr", "C:\Windows\system32\Ribbons.scr", "C:\Windows\system32\Bubbles.scr", "C:\Windows\system32\ssText3d.scr", "C:\Windows\system32\scrnsave.scr", "C:\Windows\system32\PhotoScreensaver.scr" 1677 | $screenSaverProgram = (Get-ItemProperty -ErrorAction SilentlyContinue -Path "$sid\Control Panel\Desktop\" -Name "SCRNSAVE.exe") 1678 | if (($screenSaverProgram) -and ($screenSaverProgram."SCRNSAVE.EXE" -ne "")) { 1679 | $executable = $screenSaverProgram."SCRNSAVE.EXE" 1680 | if ($legitimatePrograms.Contains($Executable)) { 1681 | continue 1682 | } 1683 | $propPath = Convert-Path -Path $screenSaverProgram.PSPath 1684 | $propPath = $propPath + "SCRNSAVE.EXE" 1685 | 1686 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Suspicious Screensaver Program' -Classification 'MITRE ATT&CK T1546.002' -Path $propPath -Value $executable -AccessGained 'User' -Note "Executables specified under the SCRNSAVE.EXE property of the HKEY_USERS\\Control Panel\Desktop key will be run in place of the legitimate screensaver, thus achieving persistence on a compromised machine. " -Reference 'https://attack.mitre.org/techniques/T1546/002/' 1687 | $null = $persistenceObjectArray.Add($PersistenceObject) 1688 | } 1689 | } 1690 | Write-Verbose -Message '' 1691 | } 1692 | 1693 | function Get-OfficeTemplates { 1694 | Write-Verbose -Message "$hostname - Checking if users' Office folders contains interesting templates..." 1695 | $userDirectories = Get-ChildItem -Path 'C:\Users\' 1696 | foreach ($directory in $userDirectories) { 1697 | $addins = Get-ChildItem -Path "$($directory.FullName)\AppData\Roaming\Microsoft\Word\STARTUP\" 1698 | $addins += Get-ChildItem -Path "$($directory.FullName)\AppData\Roaming\Microsoft\Templates\" -Filter *.dotm 1699 | $addins += Get-ChildItem -Path "$($directory.FullName)\AppData\Roaming\Microsoft\Excel\XLSTART\" 1700 | $addins += Get-ChildItem -Path "$($directory.FullName)\AppData\Roaming\Microsoft\AddIns\" 1701 | $addins += Get-ChildItem -Path "$($directory.FullName)\AppData\Roaming\Microsoft\Outlook\" -Filter *.OTM 1702 | foreach ($file in $addins) { 1703 | $fullname = $file.FullName 1704 | $path = Split-Path -Path $fullname 1705 | Write-Verbose -Message "$hostname - Found $fullname" 1706 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Office Application Startup' -Classification 'MITRE ATT&CK T1137.001' -Path "$path\" -Value "$fullname" -AccessGained 'User' -Note "Attackers can drop macro-enabled files in specific folders to trigger their execution every time the victim user opens an Office application." -Reference 'https://attack.mitre.org/techniques/T1137/' 1707 | $null = $persistenceObjectArray.Add($PersistenceObject) 1708 | } 1709 | } 1710 | Write-Verbose -Message '' 1711 | } 1712 | 1713 | function Get-ExplorerContextMenu { 1714 | Write-Verbose -Message "$hostname - Checking for Explorer Context Menu hijacking..." 1715 | $path = (Get-ItemProperty -Path "Registry::HKEY_CLASSES_ROOT\AllFilesystemObjects\shellex\ContextMenuHandlers\{B7CDF620-DB73-44C0-8611-832B261A0107}" -Name '(Default)').'(Default)' 1716 | if ($null -ne $path) { 1717 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($path))) -eq $false) { 1718 | $path = "C:\Windows\System32\$path" 1719 | } 1720 | 1721 | Write-Verbose -Message "$hostname - [!] Found a DLL in the (Default) property of the 'HKCR:\AllFilesystemObjects\shellex\ContextMenuHandlers\{B7CDF620-DB73-44C0-8611-832B261A0107}' key which deserve investigation!" 1722 | $propPath = 'HKEY_CLASSES_ROOT\AllFilesystemObjects\shellex\ContextMenuHandlers\{B7CDF620-DB73-44C0-8611-832B261A0107}\(Default)' 1723 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Explorer Tools Hijacking' -Classification 'Uncatalogued Technique N.13' -Path $propPath -Value $path -AccessGained 'User' -Note 'DLLs in the (Default) property of the "HKEY_CLASSES_ROOT\AllFilesystemObjects\shellex\ContextMenuHandlers\{B7CDF620-DB73-44C0-8611-832B261A0107}" key are run when the user right clicks any explorer Window.' -Reference 'https://ristbs.github.io/2023/02/15/hijack-explorer-context-menu-for-persistence-and-fun.html' 1724 | $null = $persistenceObjectArray.Add($PersistenceObject) 1725 | } 1726 | Write-Verbose -Message '' 1727 | } 1728 | 1729 | function Get-ServiceControlManagerSecurityDescriptor { 1730 | Write-Verbose -Message "$hostname - Checking for manipulation of the security descriptor of the Service Control Manager..." 1731 | 1732 | $currentSDDL = (sc.exe sdshow scmanager) -join '' 1733 | $defaultSDDL = 'D:(A;;CC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)(A;;CC;;;S-1-15-3-1024-528118966-3876874398-709513571-1907873084-3598227634-3698730060-278077788-3990600205)S:(AU;FA;KA;;;WD)(AU;OIIOFA;GA;;;WD)' 1734 | 1735 | if ($defaultSDDL -eq $currentSDDL) { 1736 | return 1737 | } 1738 | 1739 | Write-Verbose -Message "$hostname - [!] It looks like the Security Descriptor of the Service Control Manager is not set to the default value and should be investigated." 1740 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Service Control Manager Security Descriptor Manipulation' -Classification 'Uncatalogued Technique N.14' -Path 'N/A' -Value $currentSDDL -AccessGained 'System' -Note 'The Service Control Manager is the software responsible for starting and stopping services in the Windows OS. If its ACL is loosely set, it would be possible for a non administrative process to start administrative processes by creating a service running with high or SYSTEM privileges.' -Reference 'https://pentestlab.blog/2023/03/20/persistence-service-control-manager/' 1741 | $null = $persistenceObjectArray.Add($PersistenceObject) 1742 | Write-Verbose -Message '' 1743 | } 1744 | 1745 | function Get-MicrosoftOfficeAIHijacking { 1746 | Write-Verbose -Message "$hostname - Checking for the hijacking of the Microsoft Office AI.exe executable..." 1747 | 1748 | $officex64Dir = [System.Environment]::ExpandEnvironmentVariables('%ProgramFiles%\Microsoft Office\root\') 1749 | 1750 | $officex86Dir = [System.Environment]::ExpandEnvironmentVariables('%PROGRAMFILES(X86)%\Microsoft Office\root\') 1751 | 1752 | $paths = @(Get-ChildItem $officex64Dir) 1753 | $paths += @(Get-ChildItem $officex86Dir) 1754 | 1755 | foreach ($path in $paths) { 1756 | if ((Test-Path -Path "$($path.FullName)\ai.exe") -eq $false) { 1757 | continue 1758 | } 1759 | 1760 | Write-Verbose -Message "$hostname - [!] Found AI.exe under an Office path which deserve investigation!" 1761 | $propPath = $path.FullName 1762 | $exePath = "$($path.FullName)\ai.exe" 1763 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Microsoft Office AI.exe Hijacking' -Classification 'Uncatalogued Technique N.15' -Path $propPath -Value $exePath -AccessGained 'User' -Note 'Office executables like WINWORD.exe look for AI.exe under the %ProgramFiles%\Microsoft Office\root\ and %ProgramFiles(x86)%\Microsoft Office\root\ directories. An attacker may place a malicious AI.exe there in order to have persistence whenever a user interacts with the Microsoft Office Suite.' -Reference 'https://twitter.com/laughing_mantis/status/1645268114966470662' 1764 | $null = $persistenceObjectArray.Add($PersistenceObject) 1765 | } 1766 | Write-Verbose -Message '' 1767 | } 1768 | 1769 | function Get-RunExAndRunOnceEx { 1770 | Write-Verbose -Message "$hostname - Getting Run properties..." 1771 | foreach ($hive in $systemAndUsersHives) { 1772 | $runKeys = Get-ChildItem -Path "$hive\SOFTWARE\Microsoft\Windows\CurrentVersion\RunEx" 1773 | foreach ($key in $runKeys) { 1774 | Write-Verbose -Message "$hostname - [!] Found keys under $(Convert-Path -Path $hive)'s RunEx key which deserve investigation!" 1775 | $runProps = Get-ItemProperty -Path $key.PSPath 1776 | if ($runProps) { 1777 | Write-Verbose -Message "$hostname - [!] Found properties under a key in $(Convert-Path -Path $hive)'s RunEx key which deserve investigation!" 1778 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $runProps)) { 1779 | if ($psProperties.Contains($prop.Name)) { 1780 | continue 1781 | } # skip the property if it's powershell built-in property 1782 | $propPath = Convert-Path -Path $runProps.PSPath 1783 | $propPath += '\' + $prop.Name 1784 | $currentHive = Convert-Path -Path $hive 1785 | if (($currentHive -eq 'HKEY_LOCAL_MACHINE') -or ($currentHive -eq 'HKEY_USERS\S-1-5-18') -or ($currentHive -eq 'HKEY_USERS\S-1-5-19') -or ($currentHive -eq 'HKEY_USERS\S-1-5-20')) { 1786 | $access = 'System' 1787 | } 1788 | else { 1789 | $access = 'User' 1790 | } 1791 | 1792 | if (Get-IfSafeExecutable $runProps.($prop.Name)) { 1793 | continue 1794 | } 1795 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Registry RunEx Key' -Classification 'MITRE ATT&CK T1547.001' -Path $propPath -Value $runProps.($prop.Name) -AccessGained $access -Note 'Executables in properties of any key under the (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows\CurrentVersion\RunEx key are run when the user logs in or when the machine boots up (in the case of the HKLM hive).' -Reference 'https://attack.mitre.org/techniques/T1547/001/' 1796 | $null = $persistenceObjectArray.Add($PersistenceObject) 1797 | } 1798 | } 1799 | } 1800 | } 1801 | 1802 | Write-Verbose -Message '' 1803 | Write-Verbose -Message "$hostname - Getting RunOnce properties..." 1804 | foreach ($hive in $systemAndUsersHives) { 1805 | $runOnceKeys = Get-ChildItem -Path "$hive\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx" 1806 | foreach ($key in $runOnceKeys) { 1807 | Write-Verbose -Message "$hostname - [!] Found keys under $(Convert-Path -Path $hive)'s RunOnceEx key which deserve investigation!" 1808 | $runOnceProps = (Get-ItemProperty -Path $key).PSPath 1809 | 1810 | if ($runOnceProps) { 1811 | Write-Verbose -Message "$hostname - [!] Found properties under a key in $(Convert-Path -Path $hive)'s RunOnceEx key which deserve investigation!" 1812 | foreach ($prop in (Get-Member -MemberType NoteProperty -InputObject $runOnceProps)) { 1813 | if ($psProperties.Contains($prop.Name)) { 1814 | continue 1815 | } # skip the property if it's powershell built-in property 1816 | $propPath = Convert-Path -Path $runOnceProps.PSPath 1817 | $propPath += '\' + $prop.Name 1818 | $currentHive = Convert-Path -Path $hive 1819 | if (($currentHive -eq 'HKEY_LOCAL_MACHINE') -or ($currentHive -eq 'HKEY_USERS\S-1-5-18') -or ($currentHive -eq 'HKEY_USERS\S-1-5-19') -or ($currentHive -eq 'HKEY_USERS\S-1-5-20')) { 1820 | $access = 'System' 1821 | } 1822 | else { 1823 | $access = 'User' 1824 | } 1825 | if (Get-IfSafeExecutable $runOnceProps.($prop.Name)) { 1826 | continue 1827 | } 1828 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Registry RunOnceEx Key' -Classification 'MITRE ATT&CK T1547.001' -Path $propPath -Value $runOnceProps.($prop.Name) -AccessGained $access -Note 'Executables in properties of any key under the (HKLM|HKEY_USERS\)\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx key are run when the user logs in or when the machine boots up (in the case of the HKLM hive), and then deleted.' -Reference 'https://attack.mitre.org/techniques/T1547/001/' 1829 | $null = $persistenceObjectArray.Add($PersistenceObject) 1830 | } 1831 | } 1832 | } 1833 | } 1834 | Write-Verbose -Message '' 1835 | } 1836 | 1837 | function Get-DotNetStartupHooks { 1838 | Write-Verbose -Message "$hostname - Getting DotNet Startup Hooks..." 1839 | foreach ($hive in $systemAndUsersHives) { 1840 | $dotnetHooks = (Get-ItemProperty -Path "$hive\Environment" -Name DOTNET_STARTUP_HOOKS).DOTNET_STARTUP_HOOKS 1841 | if ($dotnetHooks) { 1842 | $dotnetHooks = $dotnetHooks -split ';' 1843 | } 1844 | foreach ($hook in $dotnetHooks) { 1845 | Write-Verbose -Message "$hostname - [!] Found a .NET hook in the DOTNET_STARTUP_HOOKS property in the $(Convert-Path -Path $hive)\Environment key!" 1846 | $propPath = Convert-Path -Path $hive 1847 | $propPath += "\Environment\DOTNET_STARTUP_HOOKS" 1848 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique '.NET Startup Hooks DLL Sideloading' -Classification 'MITRE ATT&CK T1574.002' -Path $propPath -Value $hook -AccessGained 'User/System' -Note 'The .NET DLLs listed in the DOTNET_STARTUP_HOOKS environment variable are loaded into .NET processes at runtime.' -Reference 'https://persistence-info.github.io/Data/dotnetstartuphooks.html' 1849 | $null = $persistenceObjectArray.Add($PersistenceObject) 1850 | } 1851 | } 1852 | 1853 | $systemHooks = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" -Name DOTNET_STARTUP_HOOKS).DOTNET_STARTUP_HOOKS 1854 | if ($systemHooks) { 1855 | $systemHooks = $systemHooks -split ';' 1856 | } 1857 | foreach ($hook in $systemHooks) { 1858 | Write-Verbose -Message "$hostname - [!] Found a .NET hook in the DOTNET_STARTUP_HOOKS property in the $(Convert-Path -Path $hive)\Environment key!" 1859 | $propPath = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" 1860 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique '.NET Startup Hooks DLL Sideloading' -Classification 'MITRE ATT&CK T1574.002' -Path $propPath -Value $hook -AccessGained 'User/System' -Note 'The .NET DLLs listed in the DOTNET_STARTUP_HOOKS environment variable are loaded into .NET processes at runtime.' -Reference 'https://persistence-info.github.io/Data/dotnetstartuphooks.html' 1861 | $null = $persistenceObjectArray.Add($PersistenceObject) 1862 | } 1863 | Write-Verbose -Message '' 1864 | } 1865 | 1866 | function Get-SubornerAttack { 1867 | $netUsers = net.exe users | Parse-NetUser 1868 | $poshUsers = Get-LocalUser | Select-Object Name 1869 | $diffUsers = Compare-Object -ReferenceObject $poshUsers -DifferenceObject $netUsers -Property Name 1870 | foreach ($user in $diffUsers) { 1871 | Write-Verbose -Message "$hostname - Found hidden account with username $($user.Name)" 1872 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Suborner Attack' -Classification 'Uncatalogued Technique N.16' -Path 'N/A' -Value $user.Name -AccessGained 'User/System' -Note 'The Suborner attack involves creating hidden users which are not shown using the "net user" command. They do not appear in the login screen or in lusrmgr.msc and can be found using the Get-LocalUser powershell cmdlet. This technique is usually paired with RID hijacking to achieve stealthy, admin level persistence.' -Reference 'https://r4wsec.com/notes/the_suborner_attack/' 1873 | $null = $persistenceObjectArray.Add($PersistenceObject) 1874 | } 1875 | Write-Verbose -Message '' 1876 | } 1877 | 1878 | function Get-RidHijacking { 1879 | 1880 | Write-Verbose -Message "$hostname - Checking for RID Hijacking" 1881 | $success = ElevateTo-System 1882 | if ($success -ne 0) { 1883 | Write-Verbose -Message "$hostname - Failed to elevate privileges to SYSTEM, RID hijacking checks will be disabled as they require SYSTEM privileges." 1884 | return 1885 | } 1886 | 1887 | $users = Get-LocalUser | Select-Object Name, Sid 1888 | foreach ($user in $users) { 1889 | $userRid = ($user.Sid).ToString().Split('-')[-1] 1890 | $registryRid = '00000{0:X}' -f [int]$userRid 1891 | $byte1 = [int](Get-ItemPropertyValue -Path "HKLM:\SAM\SAM\Domains\Account\Users\$registryRid" -Name F)[0x30] 1892 | $byte2 = [int](Get-ItemPropertyValue -Path "HKLM:\SAM\SAM\Domains\Account\Users\$registryRid" -Name F)[0x31] 1893 | $hexRid = '0x{0:X}{1:X}' -f $byte2, $byte1 1894 | $decRid = [int32]$hexRid 1895 | if ($decRid.ToString() -ne $userRid.ToString()) { 1896 | Write-Verbose -Message "$hostname - Found username $($user.Name) with hijacked RID $decRid" 1897 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'RID Hijacking' -Classification 'Uncatalogued Technique N.17' -Path "$user" -Value $decRid -AccessGained 'User/System' -Note 'RID hijacking allows an attacker to covertly replace the RID of a user with the RID of another user, effectively giving the first user all of the privileges of the second user. The second user is usually an Administrator, which allows the first user to gain administrator level privileges while using a non-administrator account.' -Reference 'https://pentestlab.blog/2020/02/12/persistence-rid-hijacking/' 1898 | $null = $persistenceObjectArray.Add($PersistenceObject) 1899 | } 1900 | } 1901 | $success = RevertTo-Self 1902 | if ($success -ne 0) { 1903 | Write-Verbose -Message "$hostname - Failed to revert the token to normal administrator." 1904 | } 1905 | 1906 | Write-Verbose -Message '' 1907 | } 1908 | 1909 | function Get-GhostTask { 1910 | $SizeOfAuthorOffset = 2 1911 | $success = ElevateTo-System 1912 | if ($success -ne 0) { 1913 | Write-Verbose -Message "$hostname - Failed to elevate privileges to SYSTEM, the GhostTask check may miss potential persistences only visible to SYSTEM." 1914 | } 1915 | Write-Verbose -Message "$hostname - Checking for GhostTask technique" 1916 | $taskKeys = Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree\" 1917 | foreach ($key in $taskKeys) { 1918 | $taskSD = (Get-ItemProperty -Path $key.PSPath).SD 1919 | if ($null -eq $taskSD) { 1920 | Write-Verbose -Message "$hostname - [!] Found a key in HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree without the SD property!" 1921 | } 1922 | else { 1923 | $encodedMaliciousSD = 'MQAgADAAIAA0ACAAMQAyADgAIAAyADgAIAAwACAAMAAgADAAIAA0ADQAIAAwACAAMAAgADAAIAAwACAAMAAgADAAIAAwACAAMgAwACAAMAAgADAAIAAwACAAMgAgADAAIAA4ACAAMAAgADAAIAAwACAAMAAgADAAIAAxACAAMgAgADAAIAAwACAAMAAgADAAIAAwACAANQAgADMAMgAgADAAIAAwACAAMAAgADMAMgAgADIAIAAwACAAMAAgADEAIAAxACAAMAAgADAAIAAwACAAMAAgADAAIAA1ACAAMQA4ACAAMAAgADAAIAAwAA==' 1924 | $maliciousSD = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($encodedMaliciousSD)) 1925 | $SDBytes = [System.Text.Encoding]::Unicode.GetBytes($taskSD) 1926 | $EncodedSDBytes = [Convert]::ToBase64String($SDBytes) 1927 | $DecodedSD = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($EncodedSDBytes)) 1928 | if ($maliciousSD -ne $DecodedSD) { 1929 | continue 1930 | } 1931 | Write-Verbose -Message "$hostname - [!] Found a key in HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree with a GhostTask SD property!" 1932 | } 1933 | 1934 | # Parsing the Actions structure requires some offset arithmethics. Check the GhostTask.h header file on the linked Github repo you can find in the reference of this technique 1935 | $taskGUID = (Get-ItemProperty -Path $key.PSPath).Id 1936 | $taskActions = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\$taskGUID").Actions 1937 | $SizeOfAuthor = [bitconverter]::ToInt32($taskActions[$SizeOfAuthorOffset..($SizeOfAuthorOffset + 4)], 0) 1938 | $SizeOfCmdOffset = $SizeOfAuthorOffset + 4 + $SizeOfAuthor + 6 1939 | $SizeOfCmd = [bitconverter]::ToInt32($taskActions[$SizeOfCmdOffset..($SizeOfCmdOffset + 3)], 0) 1940 | $CmdOffset = $SizeOfCmdOffset + 4 1941 | $SizeOfArgumentOffset = $CmdOffset + $SizeOfCmd 1942 | $SizeOfArgument = [bitconverter]::ToInt32($taskActions[$SizeOfArgumentOffset..($SizeOfArgumentOffset + 3)], 0) 1943 | $ArgumentOffset = $SizeOfArgumentOffset + 4 1944 | 1945 | $CmdByteArray = $taskActions[$CmdOffset.. ($CmdOffset + ($SizeOfCmd - 1))] 1946 | $ArgumentByteArray = $taskActions[$ArgumentOffset.. ($ArgumentOffset + ($SizeOfArgument - 1))] 1947 | 1948 | $enc = [System.Text.Encoding]::UTF8 1949 | $Cmd = $enc.GetString($CmdByteArray) 1950 | $Argument = $enc.GetString($ArgumentByteArray) 1951 | 1952 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'GhostTask' -Classification 'Uncatalogued Technique N.18' -Path $key.Name -Value "$Cmd $Argument" -AccessGained 'System' -Note 'Malicious scheduled tasks can be created manually by properly modifying some registry keys. Tasks created in this way and without the SD property do not show up in the Task Scheduler utility or in the Event Log.' -Reference 'https://github.com/netero1010/GhostTask' 1953 | $null = $persistenceObjectArray.Add($PersistenceObject) 1954 | } 1955 | Write-Verbose -Message '' 1956 | } 1957 | 1958 | function Get-DSRMBackdoor { 1959 | Write-Verbose -Message "$hostname - Checking for Directory Services Restore Mode (DSRM) backdoor..." 1960 | $dsrmAdminLogonBehavior = (Get-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Lsa").DsrmAdminLogonBehavior 1961 | if ($dsrmAdminLogonBehavior -EQ 2) { 1962 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'DSRM Backdoor' -Classification 'MITRE ATT&CK T1003.003' -Path 'HKLM:\System\CurrentControlSet\Control\Lsa\DsrmAdminLogonBehavior' -Value $dsrmAdminLogonBehavior -AccessGained 'System' -Note "The password used to enter Directory Services Restore Mode (DSRM) is the password set to the local administrator of a Domain Controller during DCPROMO. If the DsrmAdminLogonBehavior property of the HKLM:\System\CurrentControlSet\Control\Lsa key is set to 2, this password can be used to access the Domain Controller with the local administrator account." -Reference 'https://adsecurity.org/?p=1785' 1963 | $null = $persistenceObjectArray.Add($PersistenceObject) 1964 | } 1965 | Write-Verbose -Message '' 1966 | } 1967 | 1968 | function Get-BootVerificationProgram { 1969 | Write-Verbose -Message "$hostname - Checking for Boot Verification Program hijacking..." 1970 | $bootVerificationProgram = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\BootVerificationProgram").ImagePath 1971 | if ($bootVerificationProgram) { 1972 | Write-Verbose -Message "$hostname - [!] Found custom Boot Verification Program at ImagePath property of the HKLM:\SYSTEM\CurrentControlSet\Control\BootVerificationProgram key!" 1973 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Boot Verification Program Hijacking' -Classification 'Uncatalogued Technique N.19' -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\BootVerificationProgram\ImagePath' -Value $bootVerificationProgram -AccessGained 'System' -Note "The executable pointed to by the ImagePath property of the HKLM:\SYSTEM\CurrentControlSet\Control\BootVerificationProgram key is run by the Windows Service Manager at boot time in place of the legitimate Bootvrfy.exe" -Reference 'https://persistence-info.github.io/Data/bootverificationprogram.html' 1974 | $null = $persistenceObjectArray.Add($PersistenceObject) 1975 | } 1976 | Write-Verbose -Message '' 1977 | } 1978 | 1979 | function Get-AppInitDLLs { 1980 | Write-Verbose -Message "$hostname - Getting AppInit DLLs..." 1981 | $appInitDLL = (Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Windows").AppInit_DLLs 1982 | if ($appInitDLL) { 1983 | Write-Verbose -Message "$hostname - [!] AppInit_DLLs property under the HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Windows key is populated!" 1984 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'AppInit DLL injection' -Classification 'MITRE ATT&CK T1546.010' -Path 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Windows' -Value $appInitDLL -AccessGained 'System/User' -Note "The DLLs specified in the AppInit_DLLs property of the HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Windows key are loaded by user32.dll whenever a new process starts." -Reference 'https://attack.mitre.org/techniques/T1546/010/' 1985 | $null = $persistenceObjectArray.Add($PersistenceObject) 1986 | } 1987 | 1988 | $appInitDLL = (Get-ItemProperty -Path "HKLM:\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows").AppInit_DLLs 1989 | if ($appInitDLL) { 1990 | Write-Verbose -Message "$hostname - [!] AppInit_DLLs property under the HKLM:\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows key is populated!" 1991 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'AppInit DLL injection' -Classification 'MITRE ATT&CK T1546.010' -Path 'HKLM:\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows' -Value $appInitDLL -AccessGained 'System/User' -Note "The DLLs specified in the AppInit_DLLs property of the HKLM:\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows key are loaded by user32.dll whenever a new process starts." -Reference 'https://attack.mitre.org/techniques/T1546/010/' 1992 | $null = $persistenceObjectArray.Add($PersistenceObject) 1993 | } 1994 | 1995 | Write-Verbose -Message '' 1996 | } 1997 | 1998 | function Get-BootExecute { 1999 | Write-Verbose -Message "$hostname - Getting BootExecute and BootExecuteNoPnpSync executables" 2000 | $exesProp = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name 'BootExecute' 2001 | if ($exesProp) { 2002 | $exes = $exesProp.'BootExecute' -split '\s+' 2003 | foreach ($exe in $exes) { 2004 | if ($exe -eq "autocheck") { 2005 | continue 2006 | } 2007 | 2008 | if ($exe -eq "autochk") { 2009 | continue 2010 | } 2011 | 2012 | if ($exe -eq "*") { 2013 | continue 2014 | } 2015 | 2016 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($exe))) -eq $false) { 2017 | $exePath = "C:\Windows\System32\$exe" 2018 | } 2019 | else 2020 | { 2021 | $exePath = $exe 2022 | } 2023 | 2024 | if ((Get-IfSafeExecutable $exePath) -EQ $false) { 2025 | Write-Verbose -Message "$hostname - [!] Found a potentially malicious entry in the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecute property" 2026 | $propPath = (Convert-Path -Path $exesProp.PSPath) + '\BootExecute' 2027 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'BootExecute Binary' -Classification 'MITRE ATT&CK T1547.001' -Path $propPath -Value $exePath -AccessGained 'System' -Note 'The executables specified in the "BootExecute" property of the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager key are loaded by the OS before any other process, including EDRs.' -Reference 'https://attack.mitre.org/techniques/T1547/001/' 2028 | $null = $persistenceObjectArray.Add($PersistenceObject) 2029 | } 2030 | } 2031 | } 2032 | 2033 | Write-Verbose -Message '' 2034 | $exesProp = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name 'BootExecuteNoPnpSync' 2035 | if ($exesProp) { 2036 | $exes = $exesProp.'BootExecuteNoPnpSync' -split '\s+' 2037 | foreach ($exe in $exes) { 2038 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($exe))) -eq $false) { 2039 | $exePath = "C:\Windows\System32\$exe" 2040 | } 2041 | else 2042 | { 2043 | $exePath = $exe 2044 | } 2045 | if ((Get-IfSafeExecutable $exePath) -EQ $false) { 2046 | Write-Verbose -Message "$hostname - [!] Found a potentially malicious entry in the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecuteNoPnpSync property" 2047 | $propPath = (Convert-Path -Path $exesProp.PSPath) + '\BootExecuteNoPnpSync' 2048 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'BootExecuteNoPnpSync Binary' -Classification 'MITRE ATT&CK T1547.001' -Path $propPath -Value $exePath -AccessGained 'System' -Note 'The executables specified in the "BootExecuteNoPnpSync" property of the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager key are loaded by the OS before any other process, including EDRs.' -Reference 'https://attack.mitre.org/techniques/T1547/001/' 2049 | $null = $persistenceObjectArray.Add($PersistenceObject) 2050 | } 2051 | } 2052 | } 2053 | Write-Verbose -Message '' 2054 | } 2055 | 2056 | function Get-NetshHelperDLL { 2057 | Write-Verbose -Message "$hostname - Getting Netsh Helper DLLs" 2058 | $props = Get-Item 'HKLM:\SOFTWARE\Microsoft\NetSh' | Select-Object -ExpandProperty Property 2059 | foreach ($prop in $props) { 2060 | $dll = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\NetSh')."$prop" 2061 | $dllProp = "C:\Windows\System32\$dll" 2062 | 2063 | if ((Get-IfSafeLibrary $dllProp) -EQ $false) { 2064 | Write-Verbose -Message "$hostname - [!] Found a potentially malicious entry in the HKLM\SOFTWARE\Microsoft\NetSh\$prop property" 2065 | $propPath = "HKLM\SOFTWARE\Microsoft\NetSh\$prop" 2066 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'Netsh Helper DLL' -Classification 'MITRE ATT&CK T1546.007' -Path $propPath -Value $dllProp -AccessGained 'System/User' -Note 'The DLLs specified in the properties of the HKLM\SOFTWARE\Microsoft\NetSh key are loaded by netsh.exe whenever it is started.' -Reference 'https://attack.mitre.org/techniques/T1546/007/' 2067 | $null = $persistenceObjectArray.Add($PersistenceObject) 2068 | } 2069 | } 2070 | Write-Verbose -Message '' 2071 | } 2072 | 2073 | function Get-SetupExecute { 2074 | Write-Verbose -Message "$hostname - Getting SetupExecute and SetupExecuteNoPnpSync executables" 2075 | $exesProp = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name 'SetupExecute' 2076 | if ($exesProp) { 2077 | $exes = $exesProp.'SetupExecute' -split '\s+' 2078 | foreach ($exe in $exes) { 2079 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($exe))) -eq $false) { 2080 | $exePath = "C:\Windows\System32\$exe" 2081 | } 2082 | else 2083 | { 2084 | $exePath = $exe 2085 | } 2086 | 2087 | if ((Get-IfSafeExecutable $exePath) -EQ $false) { 2088 | Write-Verbose -Message "$hostname - [!] Found a potentially malicious entry in the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SetupExecute property" 2089 | $propPath = (Convert-Path -Path $exesProp.PSPath) + '\SetupExecute' 2090 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'SetupExecute Binary' -Classification 'Uncatalogued Technique N.20' -Path $propPath -Value $exePath -AccessGained 'System' -Note 'The executables specified in the "SetupExecute" property of the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager key are loaded by the OS before any other process, including EDRs.' -Reference 'https://github.com/rad9800/BootExecuteEDR' 2091 | $null = $persistenceObjectArray.Add($PersistenceObject) 2092 | } 2093 | } 2094 | } 2095 | Write-Verbose -Message '' 2096 | 2097 | $exesProp = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name 'SetupExecuteNoPnpSync' 2098 | if ($exesProp) { 2099 | $exes = $exesProp.'SetupExecuteNoPnpSync' -split '\s+' 2100 | foreach ($exe in $exes) { 2101 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($exe))) -eq $false) { 2102 | $exePath = "C:\Windows\System32\$exe" 2103 | } 2104 | else 2105 | { 2106 | $exePath = $exe 2107 | } 2108 | 2109 | if ((Get-IfSafeExecutable $exePath) -EQ $false) { 2110 | Write-Verbose -Message "$hostname - [!] Found a potentially malicious entry in the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SetupExecuteNoPnpSync property" 2111 | $propPath = (Convert-Path -Path $exesProp.PSPath) + '\SetupExecuteNoPnpSync' 2112 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'SetupExecuteNoPnpSync Binary' -Classification 'Uncatalogued Technique N.20' -Path $propPath -Value $exePath -AccessGained 'System' -Note 'The executables specified in the "SetupExecuteNoPnpSync" property of the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager key are loaded by the OS before any other process, including EDRs.' -Reference 'https://github.com/rad9800/BootExecuteEDR' 2113 | $null = $persistenceObjectArray.Add($PersistenceObject) 2114 | } 2115 | } 2116 | } 2117 | } 2118 | 2119 | function Get-PlatformExecute { 2120 | Write-Verbose -Message "$hostname - Getting PlatformExecute executables" 2121 | $exesProp = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name 'PlatformExecute' 2122 | if ($exesProp) { 2123 | $exes = $exesProp.'PlatformExecute' -split '\s+' 2124 | foreach ($exe in $exes) { 2125 | if (([System.IO.Path]::IsPathRooted([System.Environment]::ExpandEnvironmentVariables($exe))) -eq $false) { 2126 | $exePath = "C:\Windows\System32\$exe" 2127 | } 2128 | else 2129 | { 2130 | $exePath = $exe 2131 | } 2132 | 2133 | if ((Get-IfSafeExecutable $exePath) -EQ $false) { 2134 | Write-Verbose -Message "$hostname - [!] Found a potentially malicious entry in the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PlatformExecute property" 2135 | $propPath = (Convert-Path -Path $exesProp.PSPath) + '\PlatformExecute' 2136 | $PersistenceObject = New-PersistenceObject -Hostname $hostname -Technique 'PlatformExecute Binary' -Classification 'Uncatalogued Technique N.21' -Path $propPath -Value $exePath -AccessGained 'System' -Note 'The executables specified in the "PlatformExecute" property of the HKLM\SYSTEM\CurrentControlSet\Control\Session Manager key are loaded by the OS before any other process, including EDRs.' -Reference 'https://github.com/rad9800/BootExecuteEDR' 2137 | $null = $persistenceObjectArray.Add($PersistenceObject) 2138 | } 2139 | } 2140 | } 2141 | Write-Verbose -Message '' 2142 | } 2143 | 2144 | 2145 | function Out-EventLog { 2146 | 2147 | Param ( 2148 | 2149 | [Parameter(Mandatory = $true)] 2150 | [Collections.ArrayList]$Findings 2151 | 2152 | ) 2153 | 2154 | Begin { 2155 | $EventIDMapping = @{ 2156 | 'Registry Run Key' = $null 2157 | 'Registry RunOnce Key' = $null 2158 | 'Image File Execution Options' = $null 2159 | 'Natural Language Development Platform 6 DLL Override Path' = $null 2160 | 'AEDebug Custom Debugger' = $null 2161 | 'Wow6432Node AEDebug Custom Debugger' = $null 2162 | 'Windows Error Reporting Debugger' = $null 2163 | 'Windows Error Reporting ReflectDebugger' = $null 2164 | 'Command Processor AutoRun key' = $null 2165 | 'Explorer Load Property' = $null 2166 | 'Winlogon Userinit Property' = $null 2167 | 'Winlogon Shell Property' = $null 2168 | 'Windows Terminal startOnUserLogin' = $null 2169 | 'AppCertDlls' = $null 2170 | 'App Paths' = $null 2171 | 'ServiceDll Hijacking' = $null 2172 | 'Group Policy Extension DLL' = $null 2173 | 'Winlogon MPNotify Executable' = $null 2174 | 'CHM Helper DLL' = $null 2175 | 'Hijacking of hhctrl.ocx' = $null 2176 | 'Startup Folder' = $null 2177 | 'User Init Mpr Logon Script' = $null 2178 | 'AutodialDLL Winsock Injection' = $null 2179 | 'LSA Extensions DLL' = $null 2180 | 'ServerLevelPluginDll DNS Server DLL Hijacking' = $null 2181 | 'LSA Password Filter DLL' = $null 2182 | 'LSA Authentication Package DLL' = $null 2183 | 'LSA Security Package DLL' = $null 2184 | 'Winlogon Notification Package' = $null 2185 | 'Explorer Tools Hijacking' = $null 2186 | 'DbgManagedDebugger Custom Debugger' = $null 2187 | 'Wow6432Node DbgManagedDebugger Custom Debugger' = $null 2188 | 'ErrorHandler.cmd Hijacking' = $null 2189 | 'WMI Event Subscription' = $null 2190 | 'Windows Service' = $null 2191 | 'Power Automate' = $null 2192 | 'Terminal Services InitialProgram' = $null 2193 | 'Accessibility Tools Backdoor' = $null 2194 | 'Fake AMSI Provider' = $null 2195 | 'Powershell Profile' = $null 2196 | 'Silent Process Exit Monitor' = $null 2197 | 'Telemetry Controller Command' = $null 2198 | 'RDP WDS Startup Programs' = $null 2199 | 'Scheduled Task' = $null 2200 | 'BITS Job NotifyCmdLine' = $null 2201 | 'Suspicious Screensaver Program' = $null 2202 | 'Office Application Startup' = $null 2203 | 'Service Control Manager Security Descriptor Manipulation' = $null 2204 | 'Microsoft Office AI.exe Hijacking' = $null 2205 | 'RunEx And RunOnceEx' = $null 2206 | 'DotNet Startup Hooks' = $null 2207 | 'RID Hijacking' = $null 2208 | 'Suborner Attack' = $null 2209 | 'DSRM Backdoor' = $null 2210 | 'GhostTask' = $null 2211 | 'BootVerificationProgram' = $null 2212 | 'AppInitDLLs' = $null 2213 | 'BootExecute' = $null 2214 | 'NetshHelperDLL' = $null 2215 | 'SetupExecute' = $null 2216 | 'PlatformExecute' = $null 2217 | } 2218 | 2219 | # Collect the keys in a separate list 2220 | $keys = $EventIDMapping.Keys | ForEach-Object { $_ } 2221 | 2222 | $i = 1000 2223 | foreach ($key in $keys) { 2224 | $EventIDMapping[$key] = $i 2225 | $i++ 2226 | } 2227 | } 2228 | 2229 | Process { 2230 | $evtlog = "Application" 2231 | $source = "PersistenceSniper" 2232 | 2233 | 2234 | if ([System.Diagnostics.EventLog]::SourceExists($source) -eq $false) { 2235 | [System.Diagnostics.EventLog]::CreateEventSource($source, $evtlog) 2236 | } 2237 | 2238 | foreach ($finding in $Findings) { 2239 | $evtID = $EventIDMapping[$finding.technique] 2240 | $id = New-Object System.Diagnostics.EventInstance($evtID, 1) # Info Event 2241 | $propertiesValue = $finding.PSObject.Properties | Select-Object -ExpandProperty Value 2242 | $evtObject = New-Object System.Diagnostics.EventLog 2243 | $evtObject.Log = $evtlog 2244 | $evtObject.Source = $source 2245 | $evtObject.WriteEvent($id, $propertiesValue) 2246 | } 2247 | } 2248 | } 2249 | 2250 | Write-Verbose -Message "$hostname - Starting execution..." 2251 | 2252 | if ($PersistenceMethod -eq 'All') { 2253 | Get-RunAndRunOnce 2254 | Get-ImageFileExecutionOptions 2255 | Get-NLDPDllOverridePath 2256 | Get-AeDebug 2257 | Get-WerFaultHangs 2258 | Get-CmdAutoRun 2259 | Get-ExplorerLoad 2260 | Get-WinlogonUserinit 2261 | Get-WinlogonShell 2262 | Get-TerminalProfileStartOnUserLogin 2263 | Get-AppCertDlls 2264 | Get-ServiceDlls 2265 | Get-GPExtensionDlls 2266 | Get-WinlogonMPNotify 2267 | Get-CHMHelperDll 2268 | Get-HHCtrlHijacking 2269 | Get-StartupPrograms 2270 | Get-UserInitMprScript 2271 | Get-AutodialDLL 2272 | Get-LsaExtensions 2273 | Get-ServerLevelPluginDll 2274 | Get-LsaPasswordFilter 2275 | Get-LsaAuthenticationPackages 2276 | Get-LsaSecurityPackages 2277 | Get-WinlogonNotificationPackages 2278 | Get-ExplorerTools 2279 | Get-DotNetDebugger 2280 | Get-ErrorHandlerCmd 2281 | Get-WMIEventsSubscrition 2282 | Get-TSInitialProgram 2283 | Get-AccessibilityTools 2284 | Get-AMSIProviders 2285 | Get-PowershellProfiles 2286 | Get-SilentExitMonitor 2287 | Get-TelemetryController 2288 | Get-RDPWDSStartupPrograms 2289 | Get-BitsJobsNotifyCmdLine 2290 | Get-Screensaver 2291 | Get-PowerAutomate 2292 | Get-OfficeTemplates 2293 | Get-ExplorerContextMenu 2294 | Get-ServiceControlManagerSecurityDescriptor 2295 | Get-MicrosoftOfficeAIHijacking 2296 | Get-RunExAndRunOnceEx 2297 | Get-DotNetStartupHooks 2298 | Get-SubornerAttack 2299 | Get-RidHijacking 2300 | Get-DSRMBackdoor 2301 | Get-GhostTask 2302 | Get-BootVerificationProgram 2303 | Get-AppInitDLLs 2304 | Get-BootExecute 2305 | Get-NetshHelperDLL 2306 | Get-SetupExecute 2307 | Get-PlatformExecute 2308 | 2309 | if ($IncludeHighFalsePositivesChecks.IsPresent) { 2310 | Write-Verbose -Message "$hostname - You have used the -IncludeHighFalsePositivesChecks switch, this may generate a lot of false positives since it includes checks with results which are difficult to filter programmatically..." 2311 | Get-AppPaths 2312 | Get-WindowsServices 2313 | Get-ScheduledTasks 2314 | } 2315 | } 2316 | 2317 | else { 2318 | switch ($PersistenceMethod) { 2319 | 'RunAndRunOnce' { 2320 | Get-RunAndRunOnce 2321 | break 2322 | } 2323 | 'ImageFileExecutionOptions' { 2324 | Get-ImageFileExecutionOptions 2325 | break 2326 | } 2327 | 'NLDPDllOverridePath' { 2328 | Get-NLDPDllOverridePath 2329 | break 2330 | } 2331 | 'AeDebug' { 2332 | Get-AeDebug 2333 | break 2334 | } 2335 | 'WerFaultHangs' { 2336 | Get-WerFaultHangs 2337 | break 2338 | } 2339 | 'CmdAutoRun' { 2340 | Get-CmdAutoRun 2341 | break 2342 | } 2343 | 'ExplorerLoad' { 2344 | Get-ExplorerLoad 2345 | break 2346 | } 2347 | 'WinlogonUserinit' { 2348 | Get-WinlogonUserinit 2349 | break 2350 | } 2351 | 'WinlogonShell' { 2352 | Get-WinlogonShell 2353 | break 2354 | } 2355 | 'TerminalProfileStartOnUserLogin' { 2356 | Get-TerminalProfileStartOnUserLogin 2357 | break 2358 | } 2359 | 'AppCertDlls' { 2360 | Get-AppCertDlls 2361 | break 2362 | } 2363 | 'ServiceDlls' { 2364 | Get-ServiceDlls 2365 | break 2366 | } 2367 | 'GPExtensionDlls' { 2368 | Get-GPExtensionDlls 2369 | break 2370 | } 2371 | 'WinlogonMPNotify' { 2372 | Get-WinlogonMPNotify 2373 | break 2374 | } 2375 | 'CHMHelperDll' { 2376 | Get-CHMHelperDll 2377 | break 2378 | } 2379 | 'HHCtrlHijacking' { 2380 | Get-HHCtrlHijacking 2381 | break 2382 | } 2383 | 'StartupPrograms' { 2384 | Get-StartupPrograms 2385 | break 2386 | } 2387 | 'UserInitMprScript' { 2388 | Get-UserInitMprScript 2389 | break 2390 | } 2391 | 'AutodialDLL' { 2392 | Get-AutodialDLL 2393 | break 2394 | } 2395 | 'LsaExtensions' { 2396 | Get-LsaExtensions 2397 | break 2398 | } 2399 | 'ServerLevelPluginDll' { 2400 | Get-ServerLevelPluginDll 2401 | break 2402 | } 2403 | 'LsaPasswordFilter' { 2404 | Get-LsaPasswordFilter 2405 | break 2406 | } 2407 | 'LsaAuthenticationPackages' { 2408 | Get-LsaAuthenticationPackages 2409 | break 2410 | } 2411 | 'LsaSecurityPackages' { 2412 | Get-LsaSecurityPackages 2413 | break 2414 | } 2415 | 'WinlogonNotificationPackages' { 2416 | Get-WinlogonNotificationPackages 2417 | break 2418 | } 2419 | 'ExplorerTools' { 2420 | Get-ExplorerTools 2421 | break 2422 | } 2423 | 'DotNetDebugger' { 2424 | Get-DotNetDebugger 2425 | break 2426 | } 2427 | 'ErrorHandlerCmd' { 2428 | Get-ErrorHandlerCmd 2429 | break 2430 | } 2431 | 'WMIEventsSubscrition' { 2432 | Get-WMIEventsSubscrition 2433 | break 2434 | } 2435 | 'WindowsServices' { 2436 | Get-WindowsServices 2437 | break 2438 | } 2439 | 'AppPaths' { 2440 | Get-AppPaths 2441 | break 2442 | } 2443 | 'TerminalServicesInitialProgram' { 2444 | Get-TSInitialProgram 2445 | break 2446 | } 2447 | 'AccessibilityTools' { 2448 | Get-AccessibilityTools 2449 | break 2450 | } 2451 | 'AMSIProviders' { 2452 | Get-AMSIProviders 2453 | break 2454 | } 2455 | 'PowershellProfiles' { 2456 | Get-PowershellProfiles 2457 | break 2458 | } 2459 | 'SilentExitMonitor' { 2460 | Get-SilentExitMonitor 2461 | break 2462 | } 2463 | 'TelemetryController' { 2464 | Get-TelemetryController 2465 | break 2466 | } 2467 | 'RDPWDSStartupPrograms' { 2468 | Get-RDPWDSStartupPrograms 2469 | break 2470 | } 2471 | 'ScheduledTasks' { 2472 | Get-ScheduledTasks 2473 | break 2474 | } 2475 | 2476 | 'Screensaver' { 2477 | Get-Screensaver 2478 | break 2479 | } 2480 | 2481 | 'BitsJobsNotify' { 2482 | Get-BitsJobsNotifyCmdLine 2483 | break 2484 | } 2485 | 'PowerAutomate' { 2486 | Get-PowerAutomate 2487 | break 2488 | } 2489 | 'Services' { 2490 | Get-WindowsServices 2491 | break 2492 | } 2493 | 'ScheduledTasks' { 2494 | Get-ScheduledTasks 2495 | break 2496 | } 2497 | 'OfficeAddinsAndTemplates' { 2498 | Get-OfficeTemplates 2499 | break 2500 | } 2501 | 'ExplorerContextMenu' { 2502 | Get-ExplorerContextMenu 2503 | break 2504 | } 2505 | 'ServiceControlManagerSD' { 2506 | Get-ServiceControlManagerSecurityDescriptor 2507 | break 2508 | } 2509 | 'OfficeAiHijacking' { 2510 | Get-MicrosoftOfficeAIHijacking 2511 | break 2512 | } 2513 | 'RunExAndRunOnceEx' { 2514 | Get-RunExAndRunOnceEx 2515 | break 2516 | } 2517 | 'DotNetStartupHooks' { 2518 | Get-DotNetStartupHooks 2519 | break 2520 | } 2521 | 'RIDHijacking' { 2522 | Get-RidHijacking 2523 | break 2524 | } 2525 | 'SubornerAttack' { 2526 | Get-SubornerAttack 2527 | break 2528 | } 2529 | 'DSRMBackdoor' { 2530 | Get-DSRMBackdoor 2531 | break 2532 | } 2533 | 'GhostTask' { 2534 | Get-GhostTask 2535 | break 2536 | } 2537 | 'BootVerificationProgram' { 2538 | Get-BootVerificationProgram 2539 | break 2540 | } 2541 | 'AppInitDLLs' { 2542 | Get-AppInitDLLs 2543 | break 2544 | } 2545 | 'BootExecute' { 2546 | Get-BootExecute 2547 | break 2548 | } 2549 | 'NetshHelperDLL' { 2550 | Get-NetshHelperDLL 2551 | break 2552 | } 2553 | 'SetupExecute' { 2554 | Get-SetupExecute 2555 | break 2556 | } 2557 | 'PlatformExecute' { 2558 | Get-PlatformExecute 2559 | break 2560 | } 2561 | } 2562 | } 2563 | 2564 | 2565 | if ($LogFindings.IsPresent) { 2566 | Write-Verbose -Message "$hostname - You have used the -LogFindings switch, the results will be saved in the Event Log." 2567 | Out-EventLog $persistenceObjectArray 2568 | } 2569 | 2570 | Write-Verbose -Message "$hostname - Execution finished, outputting results..." 2571 | $persistenceObjectArray 2572 | } 2573 | 2574 | if ($ComputerName) { 2575 | $returnedArray = Invoke-Command -ComputerName $ComputerName -ScriptBlock $ScriptBlock -ErrorAction Continue 2576 | # Save all the techniques found on the remote machines in the global array. 2577 | foreach ($finding in $returnedArray) { 2578 | $null = $globalPersistenceObjectArray.Add($finding) 2579 | } 2580 | } 2581 | else { 2582 | $returnedArray = Invoke-Command -ScriptBlock $ScriptBlock 2583 | # Save all the techniques found on this machine in the global array. 2584 | foreach ($finding in $returnedArray) { 2585 | $null = $globalPersistenceObjectArray.Add($finding) 2586 | } 2587 | } 2588 | 2589 | # Use input CSV to make a diff of the results and only show us the persistences implanted on the machine which are not in the CSV 2590 | if ($DiffCSV) { 2591 | Write-Verbose -Message 'Diffing found persistences with the ones in the input CSV...' 2592 | $importedPersistenceObjectArray = Import-Csv -Path $DiffCSV -ErrorAction Stop 2593 | $newPersistenceObjectArray = New-Object -TypeName System.Collections.ArrayList 2594 | foreach ($localPersistence in $globalPersistenceObjectArray) { 2595 | $found = $false 2596 | foreach ($importedPersistence in $importedPersistenceObjectArray) { 2597 | if (($importedPersistence.Technique -eq $localPersistence.Technique) -and ($importedPersistence.Path -eq $localPersistence.Path) -and ($importedPersistence.Value -eq $localPersistence.Value)) { 2598 | $found = $true 2599 | break 2600 | } 2601 | } 2602 | if ($found -eq $false) { 2603 | $null = $newPersistenceObjectArray.Add($localPersistence) 2604 | } 2605 | } 2606 | $globalPersistenceObjectArray = $newPersistenceObjectArray.Clone() 2607 | } 2608 | 2609 | if ($OutputCSV) { 2610 | $globalPersistenceObjectArray | 2611 | ConvertTo-Csv -NoTypeInformation | 2612 | Out-File -FilePath $OutputCSV -ErrorAction Stop 2613 | } 2614 | else { 2615 | # Output the final result to stdin 2616 | $globalPersistenceObjectArray 2617 | } 2618 | 2619 | Write-Verbose -Message 'Module execution finished.' 2620 | } 2621 | # SIG # Begin signature block 2622 | # MIIVlQYJKoZIhvcNAQcCoIIVhjCCFYICAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB 2623 | # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR 2624 | # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUAd5By56s4SERLveuQihdaRrn 2625 | # hlmgghH1MIIFbzCCBFegAwIBAgIQSPyTtGBVlI02p8mKidaUFjANBgkqhkiG9w0B 2626 | # AQwFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVy 2627 | # MRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEh 2628 | # MB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTIxMDUyNTAwMDAw 2629 | # MFoXDTI4MTIzMTIzNTk1OVowVjELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3Rp 2630 | # Z28gTGltaXRlZDEtMCsGA1UEAxMkU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5n 2631 | # IFJvb3QgUjQ2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjeeUEiIE 2632 | # JHQu/xYjApKKtq42haxH1CORKz7cfeIxoFFvrISR41KKteKW3tCHYySJiv/vEpM7 2633 | # fbu2ir29BX8nm2tl06UMabG8STma8W1uquSggyfamg0rUOlLW7O4ZDakfko9qXGr 2634 | # YbNzszwLDO/bM1flvjQ345cbXf0fEj2CA3bm+z9m0pQxafptszSswXp43JJQ8mTH 2635 | # qi0Eq8Nq6uAvp6fcbtfo/9ohq0C/ue4NnsbZnpnvxt4fqQx2sycgoda6/YDnAdLv 2636 | # 64IplXCN/7sVz/7RDzaiLk8ykHRGa0c1E3cFM09jLrgt4b9lpwRrGNhx+swI8m2J 2637 | # mRCxrds+LOSqGLDGBwF1Z95t6WNjHjZ/aYm+qkU+blpfj6Fby50whjDoA7NAxg0P 2638 | # OM1nqFOI+rgwZfpvx+cdsYN0aT6sxGg7seZnM5q2COCABUhA7vaCZEao9XOwBpXy 2639 | # bGWfv1VbHJxXGsd4RnxwqpQbghesh+m2yQ6BHEDWFhcp/FycGCvqRfXvvdVnTyhe 2640 | # Be6QTHrnxvTQ/PrNPjJGEyA2igTqt6oHRpwNkzoJZplYXCmjuQymMDg80EY2NXyc 2641 | # uu7D1fkKdvp+BRtAypI16dV60bV/AK6pkKrFfwGcELEW/MxuGNxvYv6mUKe4e7id 2642 | # FT/+IAx1yCJaE5UZkADpGtXChvHjjuxf9OUCAwEAAaOCARIwggEOMB8GA1UdIwQY 2643 | # MBaAFKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQy65Ka/zWWSC8oQEJw 2644 | # IDaRXBeF5jAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zATBgNVHSUE 2645 | # DDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEMGA1Ud 2646 | # HwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0FBQUNlcnRpZmlj 2647 | # YXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0 2648 | # cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBDAUAA4IBAQASv6Hvi3Sa 2649 | # mES4aUa1qyQKDKSKZ7g6gb9Fin1SB6iNH04hhTmja14tIIa/ELiueTtTzbT72ES+ 2650 | # BtlcY2fUQBaHRIZyKtYyFfUSg8L54V0RQGf2QidyxSPiAjgaTCDi2wH3zUZPJqJ8 2651 | # ZsBRNraJAlTH/Fj7bADu/pimLpWhDFMpH2/YGaZPnvesCepdgsaLr4CnvYFIUoQx 2652 | # 2jLsFeSmTD1sOXPUC4U5IOCFGmjhp0g4qdE2JXfBjRkWxYhMZn0vY86Y6GnfrDyo 2653 | # XZ3JHFuu2PMvdM+4fvbXg50RlmKarkUT2n/cR/vfw1Kf5gZV6Z2M8jpiUbzsJA8p 2654 | # 1FiAhORFe1rYMIIGGjCCBAKgAwIBAgIQYh1tDFIBnjuQeRUgiSEcCjANBgkqhkiG 2655 | # 9w0BAQwFADBWMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVk 2656 | # MS0wKwYDVQQDEyRTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYw 2657 | # HhcNMjEwMzIyMDAwMDAwWhcNMzYwMzIxMjM1OTU5WjBUMQswCQYDVQQGEwJHQjEY 2658 | # MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJTZWN0aWdvIFB1Ymxp 2659 | # YyBDb2RlIFNpZ25pbmcgQ0EgUjM2MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB 2660 | # igKCAYEAmyudU/o1P45gBkNqwM/1f/bIU1MYyM7TbH78WAeVF3llMwsRHgBGRmxD 2661 | # eEDIArCS2VCoVk4Y/8j6stIkmYV5Gej4NgNjVQ4BYoDjGMwdjioXan1hlaGFt4Wk 2662 | # 9vT0k2oWJMJjL9G//N523hAm4jF4UjrW2pvv9+hdPX8tbbAfI3v0VdJiJPFy/7Xw 2663 | # iunD7mBxNtecM6ytIdUlh08T2z7mJEXZD9OWcJkZk5wDuf2q52PN43jc4T9OkoXZ 2664 | # 0arWZVeffvMr/iiIROSCzKoDmWABDRzV/UiQ5vqsaeFaqQdzFf4ed8peNWh1OaZX 2665 | # nYvZQgWx/SXiJDRSAolRzZEZquE6cbcH747FHncs/Kzcn0Ccv2jrOW+LPmnOyB+t 2666 | # AfiWu01TPhCr9VrkxsHC5qFNxaThTG5j4/Kc+ODD2dX/fmBECELcvzUHf9shoFvr 2667 | # n35XGf2RPaNTO2uSZ6n9otv7jElspkfK9qEATHZcodp+R4q2OIypxR//YEb3fkDn 2668 | # 3UayWW9bAgMBAAGjggFkMIIBYDAfBgNVHSMEGDAWgBQy65Ka/zWWSC8oQEJwIDaR 2669 | # XBeF5jAdBgNVHQ4EFgQUDyrLIIcouOxvSK4rVKYpqhekzQwwDgYDVR0PAQH/BAQD 2670 | # AgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwGwYD 2671 | # VR0gBBQwEjAGBgRVHSAAMAgGBmeBDAEEATBLBgNVHR8ERDBCMECgPqA8hjpodHRw 2672 | # Oi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ1Jvb3RS 2673 | # NDYuY3JsMHsGCCsGAQUFBwEBBG8wbTBGBggrBgEFBQcwAoY6aHR0cDovL2NydC5z 2674 | # ZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdSb290UjQ2LnA3YzAj 2675 | # BggrBgEFBQcwAYYXaHR0cDovL29jc3Auc2VjdGlnby5jb20wDQYJKoZIhvcNAQEM 2676 | # BQADggIBAAb/guF3YzZue6EVIJsT/wT+mHVEYcNWlXHRkT+FoetAQLHI1uBy/YXK 2677 | # ZDk8+Y1LoNqHrp22AKMGxQtgCivnDHFyAQ9GXTmlk7MjcgQbDCx6mn7yIawsppWk 2678 | # vfPkKaAQsiqaT9DnMWBHVNIabGqgQSGTrQWo43MOfsPynhbz2Hyxf5XWKZpRvr3d 2679 | # MapandPfYgoZ8iDL2OR3sYztgJrbG6VZ9DoTXFm1g0Rf97Aaen1l4c+w3DC+IkwF 2680 | # kvjFV3jS49ZSc4lShKK6BrPTJYs4NG1DGzmpToTnwoqZ8fAmi2XlZnuchC4NPSZa 2681 | # PATHvNIzt+z1PHo35D/f7j2pO1S8BCysQDHCbM5Mnomnq5aYcKCsdbh0czchOm8b 2682 | # kinLrYrKpii+Tk7pwL7TjRKLXkomm5D1Umds++pip8wH2cQpf93at3VDcOK4N7Ew 2683 | # oIJB0kak6pSzEu4I64U6gZs7tS/dGNSljf2OSSnRr7KWzq03zl8l75jy+hOds9TW 2684 | # SenLbjBQUGR96cFr6lEUfAIEHVC1L68Y1GGxx4/eRI82ut83axHMViw1+sVpbPxg 2685 | # 51Tbnio1lB93079WPFnYaOvfGAA0e0zcfF/M9gXr+korwQTh2Prqooq2bYNMvUoU 2686 | # KD85gnJ+t0smrWrb8dee2CvYZXD5laGtaAxOfy/VKNmwuWuAh9kcMIIGYDCCBMig 2687 | # AwIBAgIRANqGcyslm0jf1LAmu7gf13AwDQYJKoZIhvcNAQEMBQAwVDELMAkGA1UE 2688 | # BhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDErMCkGA1UEAxMiU2VjdGln 2689 | # byBQdWJsaWMgQ29kZSBTaWduaW5nIENBIFIzNjAeFw0yMjA4MzAwMDAwMDBaFw0y 2690 | # NTA4MjkyMzU5NTlaMFQxCzAJBgNVBAYTAklUMQ0wCwYDVQQIDARSb21hMRowGAYD 2691 | # VQQKDBFGZWRlcmljbyBMYWdyYXN0YTEaMBgGA1UEAwwRRmVkZXJpY28gTGFncmFz 2692 | # dGEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCWUdcGbyUbCFFFZ6Mj 2693 | # e/e1M0dGv9oWUKwB6O1XNQGLG5wMpsiJZRc1w6uV9iYsqIb2K5MyrbL7YNhMMgSv 2694 | # JGM51OCphdX4MN2JKyG8oZs0CGnMfKckJfNw0rukD513VlL9s34Y1A+4xyfdgJ8q 2695 | # pKz455vUM5PA3emF6ydRwdAa7vPRATqKqa/E6jUABluW0juMsMwNLucJeudD4lvL 2696 | # INY8WBxdb7U6a9XmMqW67DdrrE93nenuDF1VYL3R4s0c9bYXvnLF45im/NjMnK+F 2697 | # MJZfZq1OuE7DsTKNQ2KLru5i5luZAYnrFEP9U2oGZI1G149beOuzGBVju5TS5yqr 2698 | # L9uVOaoRxvHpFUuZXE9Wxn7eNTAuA1NBfSqvwlJuL9xLStCR+Ep20euMihqKyROV 2699 | # Jy/UbXbA9haB9D4xnGWPhdMbzh62og2taeCyUSR/ITznssDa8gj2Zz2dqdKI985M 2700 | # BWlb+rIcnhTvfguBLo5aGvOcTepcxjcgs7WRq9AoL+tmXsFlHbnenmOXeyypfS1B 2701 | # /L3WVND4sKU4RImFw1DHRUdUhtzv/OzXWn1MyTH/W1v0L8AMe/5YBmexHlcOaB05 2702 | # xZJNxy3BQVXg/DEWAgIdZlatw7vrTPzROV4VPUkU1IPe/ZCJNe9Ij2ICa2kmb2I1 2703 | # 8dVZ3m1o8T8P6Lq1rB/+d8yTMQIDAQABo4IBqzCCAacwHwYDVR0jBBgwFoAUDyrL 2704 | # IIcouOxvSK4rVKYpqhekzQwwHQYDVR0OBBYEFD9Q0l33XgRE0bG3DGVYiX6xoX03 2705 | # MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUF 2706 | # BwMDMEoGA1UdIARDMEEwNQYMKwYBBAGyMQECAQMCMCUwIwYIKwYBBQUHAgEWF2h0 2707 | # dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgGBmeBDAEEATBJBgNVHR8EQjBAMD6gPKA6 2708 | # hjhodHRwOi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmlu 2709 | # Z0NBUjM2LmNybDB5BggrBgEFBQcBAQRtMGswRAYIKwYBBQUHMAKGOGh0dHA6Ly9j 2710 | # cnQuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3J0 2711 | # MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTAgBgNVHREEGTAX 2712 | # gRVmZWQubGFnQHByb3Rvbm1haWwuY2gwDQYJKoZIhvcNAQEMBQADggGBAJj2JGru 2713 | # B4OuFoVRe64Tj83gGZbenpMtVVzLsSXzqoYv9Xy/+DdgQpBCksbCM7lL+BXbjlrO 2714 | # aRAhtshMcPXyKC0LyUK/97fmuZSEd0uJv+5bA+8J+syr/Bm7mfy+Wp0y3vN/rH0Y 2715 | # 6OuUm8YVnEsh3dN5LkYBtht0E4uOMhaAY8FvQ+UqoVO64IEYGZvfeIQxpeoOFcZ6 2716 | # LXNTEPwUsXT6aBwrdzXoTthdzYPG1OZscG5t1A+Q4FzjPgye0asKDEcL6nIiLsgn 2717 | # KFwVxJoOvSg+xpj4urUbQ5K5STKvy6FeN05JjN00w91pauOXowy+sWKsA2tk0sEj 2718 | # 7GyXN5xpdmmpS9syU0Piom/9stGkGJurdoUPNcCCagSQ6+6lDVDhxSnMroR75hIS 2719 | # lYKhmhoGgn1vWQqUwx7CywDXxMGY7GT+ufXCssa6xZT+Nn+CIaHpb4EJyrNdKN/m 2720 | # uFkQgQqZUeeV0/azIa5L9T1IaEn1xhe2ETNqZCHeGzmpmvifXW/N9+/HZDGCAwow 2721 | # ggMGAgEBMGkwVDELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRl 2722 | # ZDErMCkGA1UEAxMiU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWduaW5nIENBIFIzNgIR 2723 | # ANqGcyslm0jf1LAmu7gf13AwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAI 2724 | # oAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIB 2725 | # CzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFNx6PcNOyGT7bxM73lF+ 2726 | # buEOIc73MA0GCSqGSIb3DQEBAQUABIICADIOHpYaO7YIe6IZavtE8/X0vu3KLt9L 2727 | # HRcUu4YUcf7Zs9nIHHP82myxWWxXqEI+MU9h4+FNgzvFp5X2gF+NUpyEii17VCW1 2728 | # +HOc6LO5NCglBUwlO6LmqneibEYgNrNKMMTrHe6M8Ulx3PI3SETUg6bYhKm0R/G4 2729 | # hMqOXZ8NGQiSni5H1dTQR/PwK43TSem+dC1KzHjnzR/S5vMmqZyd8KsPD9XqnGTy 2730 | # gXgOoj5TgnODnBDAw/vcTx0mmdTsIriPX40OMlKgzfS7hiB5JtmlAg7Jc+ycb+ti 2731 | # uHx/6Qdxlh08Z6hbR8KwaEUvgZmcZSszlZeo3peeQu0nmQYT2DEcn3sN0c8yl+gT 2732 | # LV67OYtTy+vI9TfY8a57NmU07Sn/uDC044nmXbQ+AGR1EVnpqgVetswxIldj5GIx 2733 | # m+p0qC236EqTMbcHZ37RdAaEmUbN+6TFgmwAiupAMkoEDlEfI2mu4eQ+abNGt/uC 2734 | # 7Z3NnFNCKZLgDr1MA8sbOG8Q3aGQZxCIYKu+IJr7YnHqyXAQ063EKuRs1tAguojn 2735 | # dDtrPeRiK3O19tfnKFbNemsunTOvQpEvyrR2JvBmZCtRqyekcDEUphTglVmPRjlC 2736 | # BKgwtdHkxihniBrCzbrQ8pM+2ElrsdvDLLyMxBLfl21WFYbfT6F2s21j8i2pz8i9 2737 | # 5kJ5Pcls70on 2738 | # SIG # End signature block 2739 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |

PersistenceSniper

10 | 11 |

language version shield logo number of techniques implemented workflow gallery downloads twitter twitter twitter_rick buy me a coffee

12 |

PersistenceSniper is a Powershell module that can be used by Blue Teams, Incident Responders and System Administrators to hunt persistences implanted in Windows machines. It is also available on Powershell Gallery and it is digitally signed with a valid code signing certificate. The tool is under active development with new releases coming out by the week, so make sure to use the up-to-date version. Official Twitter/X account @PersistSniper.

13 |
17 | 18 | ## The Why 19 | Why writing such a tool, you might ask. Well, for starters, I tried looking around and I did not find a tool which suited my particular use case, which was looking for known persistence techniques, automatically, across multiple machines, while also being able to quickly and easily parse and compare results. Sure, [Sysinternals' Autoruns](https://docs.microsoft.com/en-us/sysinternals/downloads/autoruns) is an amazing tool and it's definitely worth using, but, given it outputs results in non-standard formats and can't be run remotely unless you do some shenanigans with its command line equivalent, I did not find it a good fit for me. Plus, some of the techniques I implemented so far in PersistenceSniper have not been implemented into Autoruns yet, as far as I know. Anyway, if what you need is an easy to use, GUI based tool with lots of already implemented features, Autoruns is the way to go, otherwise let PersistenceSniper have a shot, it won't miss it 😉 20 | 21 | ## The How 22 | To learn how to use PersistenceSniper properly, head to the [Project's Wiki](https://github.com/last-byte/PersistenceSniper/wiki). 23 | 24 | __TL;DR__ 25 | If you are too lazy to read the [Wiki](https://github.com/last-byte/PersistenceSniper/wiki) (which I highly recommend you do) you can install, import, and fire PersistenceSniper with the following three commands. 26 | ```powershell 27 | PS> Install-Module PersistenceSniper 28 | PS> Import-Module PersistenceSniper 29 | PS> Find-AllPersistence 30 | ``` 31 | 32 | ## Persistence techniques implemented so far 33 | The persistence techniques implemented so far are detailed in the [Detections Page](https://github.com/last-byte/PersistenceSniper/wiki/3-%E2%80%90-Detections) of PersistenceSniper's Wiki. 34 | 35 | ## Credits 36 | Most of this tool is based on the work of other skilled researchers, so it's right to give credit where credit's due. This project wouldn't be around if it weren't for: 37 | - [Hexacorn](https://www.hexacorn.com/) and his never-ending [Beyond good ol' Run key series](https://www.hexacorn.com/blog/2017/01/28/beyond-good-ol-run-key-all-parts/); 38 | - [Grzegorz Tworek](https://twitter.com/0gtweet/) and his amazing [persistence-info.github.io website](https://persistence-info.github.io/); 39 | - All the other researchers who disclosed cool and unknown persistence techniques. 40 | 41 | Furthermore, these people contributed to the project: 42 | - [Riccardo Ancarani](https://x.com/dottor_morte) 43 | - [Cecio](https://x.com/red5heep) 44 | - [Vadim](https://x.com/D3F7A5105) 45 | - [fkadibs](https://x.com/fkadibs) 46 | - [suinswofi](https://github.com/suinswofi) 47 | - [Antonio Blescia](https://github.com/ablescia) 48 | - [Strassi](https://x.com/strassi7) 49 | - [sixtyvividtails](https://x.com/sixtyvividtails) 50 | 51 | I'd also like to give credits to my fellow mates at [@APTortellini](https://aptw.tf/about/) for the flood of ideas that helped it grow from a puny text-oriented script to a full-fledged Powershell module. 52 | 53 | ## License 54 | This project is under the [Commons Clause version of the MIT License](https://github.com/last-byte/PersistenceSniper/blob/main/LICENSE) license. TL;DR: you can copy, modify, distribute and perform the work for whatever reason, __excluding__ commercial purposes, all without asking permission. 55 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Only the latest release of PersistenceSniper is covered by this security policy. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | To report a vulnerability in PersistenceSniper write an email to fed DOT lag AT protonmail DOT ch stating what the vulnerability is and attaching pictures of a POC should there be a need for it. You should get a reply within a couple of days. 10 | -------------------------------------------------------------------------------- /persistencesnipernew4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/last-byte/PersistenceSniper/13006aa8aa311cb593bb5cf43aa7b3cff8ece1b0/persistencesnipernew4.png --------------------------------------------------------------------------------