Hey there, fellow threat hunters! 👋 Today we're diving into everyone's favorite Windows feature - the Registry. This isn't just another basic "check your Run keys" tutorial. We're going deep into the attacker's playbook to understand not just where they hide, but why they choose these locations. Grab your coffee, because this is going to be a good one!

Important Disclaimer
Before we dive in, there are a few important points to consider:
- This blog post is intended for educational purposes only
- In production environments, always use established and well-tested security tools such as:
- Sysmon - For comprehensive system monitoring
- Windows Defender Advanced Threat Protection (ATP)
- Commercial EDR (Endpoint Detection and Response) solutions
- SIEM (Security Information and Event Management) systems
- The scripts provided here are meant to demonstrate concepts and should not be used as primary security tools
- Always follow your organization's security policies and procedures
- When investigating potential security incidents:
- Use forensically sound tools and methods
- Maintain proper chain of custody
- Document all actions taken
- Engage your security team or qualified security professionals
Remember: Real-world threat hunting and incident response require a comprehensive approach using properly validated tools and established procedures. The information and scripts in this blog post are meant to help you understand the concepts, not to replace professional security tools.
1. The Run Keys Family - More Than Meets the Eye
Let's start with the classics. Yes, everyone knows about Run keys, but there's more to them than you might think:
# Primary Run Keys
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce
# Less Known Run Keys
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices
HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\Load
HKCU\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\Run
Why attackers love them:
- Survive system reboots
- Execute with user/system privileges
- No special permissions needed for HKCU keys
- RunOnce keys automatically delete after execution (great for staging payloads)
Real-world example: The Emotet malware used Run keys with randomly generated names, storing encoded PowerShell commands for persistence [1]. Recent variants even implemented a technique to monitor and restore these registry keys if removed [2].
2. The Windows Services Registry - Attacker's VIP Lounge
Services are a goldmine for persistence. The main registry location:
HKLM\SYSTEM\CurrentControlSet\Services
Why attackers choose services:
- Run with SYSTEM privileges by default
- Can specify custom service accounts
- Support for multiple trigger methods:
- Boot-time execution
- On-demand activation
- Delayed start
- Can masquerade as legitimate Windows services
- Harder to detect than Run keys
Key attributes attackers modify:
- ImagePath: Points to the malicious executable
- ServiceDLL: For shared service processes
- ObjectName: Service account to run under
- Type: Kernel drivers (1) vs user-mode services (16)
- Start: Boot (0), System (1), Auto (2), Manual (3), Disabled (4)
Real-world example: The BlackByte ransomware creates a service named "BConfig" with Type=16 and Start=2 to ensure persistence across reboots [3].
3. The COM Object Hijacking - Advanced Persistence
COM objects are like a five-star hotel for malware:
HKLM\SOFTWARE\Classes\CLSID
HKCU\SOFTWARE\Classes\CLSID
HKLM\SOFTWARE\Classes\Wow6432Node\CLSID # 64-bit systems
Why attackers love COM hijacking:
- Triggers when legitimate applications load COM objects
- Can intercept calls to legitimate DLLs
- Often overlooked in security scans
- Persists through system updates
- Can achieve DLL search order hijacking
Real-world example: The Astaroth trojan hijacked the CLSID "{62BE5D10-60EB-11D0-BD3B-00A0C911CE86}" used by various Windows processes to maintain persistence and intercept system calls [4].
4. Shell Extensions - The Silent Watchers
Shell extensions can monitor and intercept user actions:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved
# Context Menu Handlers
HKCR\*\shellex\ContextMenuHandlers
HKCR\Directory\shellex\ContextMenuHandlers
Why attackers use shell extensions:
- Load whenever Explorer.exe runs
- Can monitor file system activities
- Intercept right-click context menus
- Execute when specific file types are opened
- Often trusted by Windows
Real-world example: The HookExplorer malware used shell extensions to monitor file operations and capture banking credentials, persisting through Windows Explorer's trusted execution.
5. Explorer Load Points - The Sneaky Ones
These keys load when Explorer.exe starts:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell
HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows\Load
Why attackers choose these:
- AppInit_DLLs inject into every process that loads User32.dll
- Winlogon Shell modifications affect the login process
- Windows\Load runs whenever a user logs in
- Often survive system updates
Real-world example: The Ursnif banking trojan utilized AppInit_DLLs for system-wide code injection, making it particularly difficult to detect and remove.
6. File Association Hijacking - The Subtle Approach
Let's look at how attackers change Windows file type handling:
# File Association Registry Locations
HKCR\.exe
HKCR\.dll
HKCR\.cmd
HKLM\SOFTWARE\Classes\.exe
Why it's effective:
- Executes whenever specific file types are opened
- Can intercept execution of legitimate files
- Often overlooked in security audits
- Can affect all users on the system
Here's a script to check for file association hijacking:
# Check for modified file associations
$suspectExtensions = @('.exe', '.dll', '.cmd', '.bat', '.ps1')
foreach ($ext in $suspectExtensions) {
$defaultHandler = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Classes\$ext" -ErrorAction SilentlyContinue)."(Default)"
$userHandler = (Get-ItemProperty -Path "HKCU:\SOFTWARE\Classes\$ext" -ErrorAction SilentlyContinue)."(Default)"
if ($defaultHandler -ne $null -or $userHandler -ne $null) {
[PSCustomObject]@{
Extension = $ext
SystemHandler = $defaultHandler
UserHandler = $userHandler
Suspicious = ($userHandler -ne $null -and $userHandler -ne $defaultHandler)
}
}
}
Real-world example: The Carbanak group used file association hijacking to intercept .doc file openings, enabling them to monitor banking documents [5].
7. LSA Providers - The Crown Jewels
LSA (Local Security Authority) providers handle authentication:
HKLM\SYSTEM\CurrentControlSet\Control\Lsa
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\OSConfig
Why attackers target LSA:
- Can intercept authentication processes
- Access to plain-text credentials
- High-privilege execution
- Difficult to detect without specialized monitoring
Real-world example: APT29 utilized LSA provider manipulation as part of their credential theft toolkit, demonstrating the sophisticated abuse of this registry location [6].
Detection Strategies
Key areas to focus your monitoring efforts:
- Registry Monitoring:
- Use Process Monitor to track registry modifications
- Set up alerts for modifications to critical keys
- Monitor for new registry values in known persistence locations
- Baseline Comparison:
- Maintain a known-good baseline of registry keys
- Regularly compare current state against baseline
- Document legitimate changes during software installations
- Suspicious Indicators:
- Encoded PowerShell commands
- Paths to temporary directories
- References to common attack tools
- Unsigned executables in system locations
Comprehensive Detection Script
Here's a thorough script that checks all the persistence locations we discussed:
# Comprehensive Registry Persistence Hunter
# Use with caution and only on systems you own/have permission to scan
function Write-ColorOutput {
param(
[string]$Message,
[string]$Color = "White"
)
Write-Host $Message -ForegroundColor $Color
}
# Initialize suspicious patterns
$suspiciousPatterns = @{
Commands = @(
"*powershell*", "*cmd.exe*", "*wscript*", "*cscript*",
"*rundll32*", "*regsvr32*", "*mshta*", "*certutil*"
)
Paths = @(
"*temp*", "*appdata*", "*downloads*",
"*/windows/fonts/*", "*/recycle*", "*/perflogs/*"
)
Extensions = @(
"*.ps1", "*.vbs", "*.js", "*.hta", "*.exe", "*.dll",
"*.cmd", "*.bat", "*.scr", "*.tmp"
)
Encodings = @(
"*base64*", "*utf-8*", "*utf-16*", "*unicode*",
"*-enc*", "*-encodedcommand*", "*-e *", "*-ec *"
)
Keywords = @(
"*bypass*", "*hidden*", "*encrypt*", "*http*", "*https*",
"*ftp*", "*ssh*", "*admin*", "*password*", "*credential*"
)
}
function Test-SuspiciousValue {
param(
[string]$Value
)
foreach ($category in $suspiciousPatterns.Keys) {
foreach ($pattern in $suspiciousPatterns[$category]) {
if ($Value -like $pattern) {
return @{
IsSuspicious = $true
Category = $category
Pattern = $pattern
}
}
}
}
return @{
IsSuspicious = $false
Category = $null
Pattern = $null
}
}
function Get-RegistryKeyLastWriteTime {
param(
[string]$Path
)
try {
$key = Get-Item -LiteralPath $Path -ErrorAction Stop
return $key.GetValue("LastWriteTime")
}
catch {
return $null
}
}
function Check-RegistryLocation {
param(
[string]$Path,
[string]$Description
)
Write-ColorOutput "`nChecking $Description" "Yellow"
Write-ColorOutput "Location: $Path" "Gray"
$findings = @()
try {
# Get all properties
$items = Get-ItemProperty -Path $Path -ErrorAction Stop
# Get subkeys if they exist
$subKeys = Get-ChildItem -Path $Path -ErrorAction SilentlyContinue
# Check properties
foreach ($prop in ($items.PSObject.Properties |
Where-Object { $_.Name -notin @('PSPath','PSParentPath','PSChildName','PSProvider')})) {
$suspiciousCheck = Test-SuspiciousValue -Value $prop.Value
if ($suspiciousCheck.IsSuspicious) {
$findings += [PSCustomObject]@{
Location = $Path
ItemName = $prop.Name
ItemValue = $prop.Value
Type = "Property"
SuspiciousCategory = $suspiciousCheck.Category
MatchedPattern = $suspiciousCheck.Pattern
LastWriteTime = Get-RegistryKeyLastWriteTime -Path $Path
}
}
}
# Check subkeys if they exist
if ($subKeys) {
foreach ($key in $subKeys) {
$suspiciousCheck = Test-SuspiciousValue -Value $key.Name
if ($suspiciousCheck.IsSuspicious) {
$findings += [PSCustomObject]@{
Location = $key.PSPath
ItemName = $key.Name
ItemValue = "(Registry Key)"
Type = "Key"
SuspiciousCategory = $suspiciousCheck.Category
MatchedPattern = $suspiciousCheck.Pattern
LastWriteTime = Get-RegistryKeyLastWriteTime -Path $key.PSPath
}
}
}
}
}
catch {
Write-ColorOutput "Error accessing $Path : $($_.Exception.Message)" "Red"
return $null
}
return $findings
}
# Define registry locations to check
$locationsToCheck = @{
# Run Keys
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" = "Run Keys (HKLM)"
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" = "RunOnce Keys (HKLM)"
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" = "Run Keys (HKCU)"
"HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" = "RunOnce Keys (HKCU)"
# Services
"HKLM:\SYSTEM\CurrentControlSet\Services" = "Services"
# COM Objects
"HKLM:\SOFTWARE\Classes\CLSID" = "COM Objects (HKLM)"
"HKCU:\SOFTWARE\Classes\CLSID" = "COM Objects (HKCU)"
# Shell Extensions
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved" = "Shell Extensions"
# Explorer Load Points
"HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows" = "Windows Load Points"
"HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" = "Winlogon"
# LSA Providers
"HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" = "LSA Providers"
}
# Main execution
$allFindings = @()
$startTime = Get-Date
Write-ColorOutput "Starting Registry Persistence Hunter" "Green"
Write-ColorOutput "Start Time: $startTime" "Gray"
Write-ColorOutput "Scanning registry locations for suspicious entries..." "Yellow"
foreach ($location in $locationsToCheck.GetEnumerator()) {
$findings = Check-RegistryLocation -Path $location.Key -Description $location.Value
if ($findings) {
$allFindings += $findings
}
}
# Output findings
if ($allFindings.Count -gt 0) {
Write-ColorOutput "`nSuspicious Registry Entries Found:" "Red"
$allFindings | ForEach-Object {
Write-ColorOutput "`nLocation: $($_.Location)" "Yellow"
Write-ColorOutput "Item Name: $($_.ItemName)" "White"
Write-ColorOutput "Value: $($_.ItemValue)" "White"
Write-ColorOutput "Type: $($_.Type)" "Gray"
Write-ColorOutput "Category: $($_.SuspiciousCategory)" "Magenta"
Write-ColorOutput "Matched Pattern: $($_.MatchedPattern)" "Gray"
if ($_.LastWriteTime) {
Write-ColorOutput "Last Write Time: $($_.LastWriteTime)" "Gray"
}
Write-ColorOutput "-----------------" "Gray"
}
# Export to CSV
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$exportPath = ".\RegistryFindings_$timestamp.csv"
$allFindings | Export-Csv -Path $exportPath -NoTypeInformation
Write-ColorOutput "`nFindings exported to: $exportPath" "Green"
}
else {
Write-ColorOutput "`nNo suspicious registry entries found." "Green"
}
$endTime = Get-Date
$duration = $endTime - $startTime
Write-ColorOutput "`nScan completed in $($duration.TotalSeconds) seconds" "Green"
Usage Notes
- Run this script with administrative privileges for full access to registry locations
- Results are both displayed on screen and exported to a CSV file
- The script includes color-coded output for easier reading
- Suspicious patterns are customizable in the $suspiciousPatterns variable
- Each finding includes:
- Location of the suspicious entry
- Item name and value
- Type (Key or Property)
- Category of suspicious pattern matched
- Last write time (when available)
Important Cautions
- This script may generate false positives - always verify findings
- Some registry keys require specific permissions to access
- Running comprehensive registry scans may impact system performance
- Always test security scripts in a controlled environment first
This detection script complements our earlier examination of attacker persistence techniques and provides a practical tool for identifying suspicious registry entries.
Wrapping Up
The Windows Registry is an attacker's playground with countless hiding spots. While we've covered the most common locations, remember that creative attackers are always finding new places to hide. The key is understanding not just where to look, but why attackers choose specific locations.
Stay tuned for our next post where we'll dive into Windows Service hardening! Until then, keep hunting! 🕵️♂️
References
Additional Resources
- SANS Institute: "Windows Registry Forensics in-depth"
- Microsoft Security baselines
- Sysinternals Documentation: "Process Monitor for Registry Analysis"