Windows Security: Detecting malicious scheduled tasks

Hey there, fellow threat hunters! 👋 Today we're diving into the fascinating world of Windows Scheduled Tasks. While they're essential for system maintenance and automation, they're also a favorite playground for attackers. Let's explore how to spot the difference between legitimate tasks and malicious ones!

Windows Security: Detecting malicious scheduled tasks

Why Attackers Love Scheduled Tasks

Before we dive into detection, let's understand why attackers are so fond of scheduled tasks:
  • They persist through system reboots without requiring Run keys or services
  • They can run with SYSTEM privileges if configured that way
  • Many organizations don't regularly audit their scheduled tasks
  • They can blend in with legitimate maintenance tasks

Quick Task Enumeration

Let's start with a simple PowerShell command to list all scheduled tasks:
Get-ScheduledTask | Select-Object TaskName, TaskPath, State, Principal | Format-Table -AutoSize
But that's just scratching the surface. Here's a more detailed script to find potentially suspicious tasks:
# Get tasks with suspicious properties
Get-ScheduledTask | ForEach-Object {
    $task = $_
    $actions = $task | Get-ScheduledTaskInfo
    
    # Check for suspicious patterns
    if ($task.Actions.Execute -match '(powershell|cmd|wscript|cscript)' -or
        $task.TaskPath -notmatch '^\\Microsoft\\' -and
        $task.Principal.UserId -eq "SYSTEM") {
        
        [PSCustomObject]@{
            'TaskName' = $task.TaskName
            'Path' = $task.TaskPath
            'Command' = $task.Actions.Execute
            'Arguments' = $task.Actions.Arguments
            'User' = $task.Principal.UserId
            'LastRun' = $actions.LastRunTime
            'NextRun' = $actions.NextRunTime
        }
    }
} | Format-Table -AutoSize

Red Flags to Watch For

Here are some telltale signs that a scheduled task might be malicious:
  • Tasks that execute PowerShell with encoded commands
  • Tasks running from unusual locations (like Temp directories)
  • Tasks with generic or random-looking names
  • SYSTEM-level tasks that don't originate from Microsoft
  • Tasks that trigger on user login but run as SYSTEM

Deep Dive: Task XML Analysis

Sometimes the real dirt is in the task's XML definition. Here's how to check it:
$taskName = "SuspiciousTask"
$task = Get-ScheduledTask -TaskName $taskName
[xml]$taskXml = Export-ScheduledTask -TaskName $task.TaskName

Common Attack Patterns

Let's look at some real-world examples (obfuscated for safety):
# Malicious PowerShell execution
Action: powershell.exe
Arguments: -enc JiYoZW5jb2RlZF9wYXlsb2FkJiY=

# Credential theft via scheduled task
Action: cmd.exe
Arguments: /c net use \\server\share /user:domain\user password > %temp%\out.txt

# Persistence through task folder monitoring
Action: wscript.exe
Arguments: //B //NOLOGO C:\Windows\Temp\monitor.vbs

Hunting Script

Here's a more comprehensive hunting script to help you find suspicious tasks:
function Find-SuspiciousScheduledTasks {
    $suspiciousPatterns = @(
        '.*powershell.*-enc.*',
        '.*cmd.*/c.*',
        '.*wscript.*',
        '.*%temp%.*',
        '.*%appdata%.*'
    )

    Get-ScheduledTask | ForEach-Object {
        $task = $_
        $actions = $task | Get-ScheduledTaskInfo
        
        # Check command and arguments against patterns
        $suspicious = $false
        $matchedPattern = ""
        
        foreach ($pattern in $suspiciousPatterns) {
            if ($task.Actions.Execute -match $pattern -or 
                $task.Actions.Arguments -match $pattern) {
                $suspicious = $true
                $matchedPattern = $pattern
                break
            }
        }
        
        # Report suspicious tasks
        if ($suspicious) {
            [PSCustomObject]@{
                'TaskName' = $task.TaskName
                'Path' = $task.TaskPath
                'Command' = $task.Actions.Execute
                'Arguments' = $task.Actions.Arguments
                'User' = $task.Principal.UserId
                'LastRun' = $actions.LastRunTime
                'Pattern' = $matchedPattern
            }
        }
    }
}

Using Managed Service Accounts

Remember our discussion about MSAs and gMSAs in our Best practices for securing Windows services post? The same principles apply to scheduled tasks! Instead of using regular service accounts, you can (and should) use managed service accounts for your scheduled tasks. Here's how:
# Create a scheduled task with gMSA
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\Scripts\Maintenance.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 3am
$principal = New-ScheduledTaskPrincipal -UserID "domain\gMSA_MaintenanceTask$" -LogonType Password
Register-ScheduledTask -TaskName "MaintenanceTask" -Action $action -Trigger $trigger -Principal $principal
Benefits of using MSAs/gMSAs for scheduled tasks:
  • Automatic password management - no more expired passwords breaking your tasks
  • Enhanced security through complex, rotating passwords
  • Centralized management in Active Directory
  • Reduced attack surface compared to regular service accounts
  • Better audit trails for task execution

Pro Tips

  • Always baseline your known-good tasks first
  • Monitor for new task creation, especially outside of Microsoft paths
  • Look for tasks that execute from temporary locations
  • Pay special attention to SYSTEM-level tasks
  • Document your organization's legitimate maintenance tasks

Wrapping Up

Remember, not every suspicious-looking task is malicious, and not every malicious task looks suspicious! The key is understanding what's normal in your environment and investigating anything that stands out. Stay safe, and happy hunting! 🕵️‍♂️ P.S. Want to learn more about Windows security? Check out our other security-focused guides!

Windows Security: Best practices for securing Windows services

Hey there, fellow threat hunters! 👋 Today we're diving into Windows Service hardening. Sure, everyone knows you should "secure your services," but let's get into the nitty-gritty of what that actually means and how to do it properly.

Windows Security: Best practices for securing Windows services

Important Disclaimer

  • This blog post is intended for educational purposes only
  • In production environments, always use established and well-tested security tools such as:
    • Microsoft Security Configuration Manager (SCM)
    • Microsoft Defender for Endpoint
    • Enterprise service management platforms
    • Commercial security compliance tools
  • Always test service modifications in a non-production environment first
  • Follow your organization's change management procedures
  • Document all changes and maintain proper system backups

Understanding the Basics: Why Services Are a Target

Before we jump into hardening, let's understand why attackers love targeting Windows services:
  • Often run with SYSTEM privileges
  • Start automatically with Windows
  • Can be used for persistence
  • Many organizations don't properly audit them
  • Provide access to system resources

1. Service Account Principle of Least Privilege

First, let's check for services running with excessive privileges:
# Get services running as SYSTEM
Get-WmiObject win32_service | 
    Where-Object {$_.StartName -eq "LocalSystem"} |
    Select-Object Name, DisplayName, StartName, PathName |
    Format-Table -AutoSize
Best practices for service accounts:
  • Use managed service accounts (MSAs) where possible
  • Create dedicated service accounts with minimal permissions
  • Never use domain admin accounts for services
  • Regularly audit service account permissions
  • Document service account dependencies and permissions

2. Understanding Service Accounts: MSA vs gMSA

Let's clear up some confusion about service accounts. I've seen too many environments where admins either don't know about or don't understand the difference between Managed Service Accounts (MSAs) and Group Managed Service Accounts (gMSAs).

Traditional Service Accounts - The Old Way

First, let's remember why we're moving away from traditional service accounts:
  • Manual password management
  • Password rotation headaches
  • Service interruptions during password changes
  • Passwords stored in scripts/documentation
  • No centralized management

Managed Service Accounts (MSAs)

MSAs are like your entry-level managed accounts:
  • Features:
    • Automatic password management
    • Simplified SPN management
    • Can only be used on one computer
    • Built-in password rotation
  • Best used for:
    • Single-server applications
    • Standalone services
    • Windows Server 2008 R2 environments
    • Simple service deployments
Here's how to create an MSA:
# Create a regular MSA
New-ADServiceAccount -Name "MSA_AppService" -RestrictToSingleComputer

# Install the MSA on the server
Add-ADComputerServiceAccount -Identity "ServerName" -ServiceAccount "MSA_AppService"

# Install the service account locally
Install-ADServiceAccount -Identity "MSA_AppService"

# Test the installation
Test-ADServiceAccount -Identity "MSA_AppService"

Group Managed Service Accounts (gMSAs) - The Modern Approach

gMSAs are like MSAs with superpowers:
  • Features:
    • Can be used across multiple servers
    • Automatic password management
    • Complex 240-character passwords
    • Centralized management
    • Supports Failover Clusters
    • Built-in SPN management
  • Best used for:
    • Farm deployments
    • Load-balanced services
    • Clustered services
    • Modern Windows deployments (2012 R2 and newer)
    • Enterprise applications
Here's how to set up a gMSA:
# First, ensure you have a KDS Root Key (only needed once in the domain)
Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10))

# Create a security group for servers that will use the gMSA
New-ADGroup -Name "gMSA_Servers" `
   -GroupScope Global `
   -Description "Servers that can use the gMSA account"

# Add servers to the group
Add-ADGroupMember -Identity "gMSA_Servers" -Members "Server1$","Server2$"

# Create the gMSA
New-ADServiceAccount -Name "gMSA_WebApp" `
   -DNSHostName "gMSA_WebApp.domain.com" `
   -PrincipalsAllowedToRetrieveManagedPassword "gMSA_Servers" `
   -ServicePrincipalNames "HTTP/webapp.domain.com"

# Install on each server
Invoke-Command -ComputerName "Server1", "Server2" -ScriptBlock {
   Install-ADServiceAccount -Identity "gMSA_WebApp"
   Test-ADServiceAccount -Identity "gMSA_WebApp"
}

When to Use What - Decision Matrix

ScenarioRecommended Account TypeReason
Single server application MSA Simpler to manage, sufficient for single-server deployments
Web farm gMSA Supports multiple servers, works with load balancing
Failover cluster gMSA Supports resource migration between nodes
Legacy application Traditional Service Account If application doesn't support managed accounts
Modern enterprise app gMSA Best security features and management capabilities

Common Pitfalls and Troubleshooting

Let's look at some common issues and how to resolve them:
# Issue 1: Service won't start with gMSA
# Check if the server can retrieve the password
Test-ADServiceAccount -Identity "gMSA_WebApp"

# Issue 2: SPNs not registering
# View current SPNs
Get-ADServiceAccount -Identity "gMSA_WebApp" -Properties ServicePrincipalNames

# Add missing SPN
Set-ADServiceAccount -Identity "gMSA_WebApp" `
   -ServicePrincipalNames @{Add="HTTP/newapp.domain.com"}

# Issue 3: Permission problems
# Check gMSA permissions
Get-ADServiceAccount -Identity "gMSA_WebApp" -Properties * |
   Select-Object Name, PrincipalsAllowedToRetrieveManagedPassword

3. Service Executable Security

Now that we have our service accounts sorted, let's lock down the service executables themselves:
# Comprehensive service executable audit script
function Audit-ServiceExecutables {
   Get-WmiObject win32_service | ForEach-Object {
       $service = $_
       $execPath = $service.PathName -replace '^"([^"]+)".*', '$1'
       
       try {
           $acl = Get-Acl $execPath -ErrorAction Stop
           $sig = Get-AuthenticodeSignature $execPath -ErrorAction Stop
           
           [PSCustomObject]@{
               ServiceName = $service.Name
               ExecutablePath = $execPath
               Permissions = $acl.Access | Where-Object {
                   $_.IdentityReference -notmatch 'NT SERVICE|NT AUTHORITY|BUILTIN'
               }
               IsSigned = $sig.Status -eq 'Valid'
               SignatureStatus = $sig.Status
               SignedBy = $sig.SignerCertificate.Subject
               LastWriteTime = (Get-Item $execPath).LastWriteTime
               FileHash = (Get-FileHash $execPath).Hash
           }
       } catch {
           Write-Warning "Could not check $($service.Name): $($_.Exception.Message)"
       }
   }
}
Best practices for service executables:
  • File System Security:
    • Place service executables in secure locations
    • Restrict permissions to SYSTEM and Administrators
    • Enable auditing on executable directories
    • Use file integrity monitoring
  • Signature Verification:
    • Only allow signed executables
    • Verify signature chain
    • Maintain an allowlist of trusted publishers
  • Path Security:
    • Use fully qualified paths
    • Avoid paths with spaces unless properly quoted
    • Restrict write access to service directories
Here's a script to fix common service path issues:
# Fix unquoted service paths
Get-WmiObject win32_service | 
   Where-Object {$_.PathName -notmatch '^".*"$' -and $_.PathName -match '\s+'} |
   ForEach-Object {
       $newPath = '"' + $_.PathName + '"'
       Write-Host "Fixing path for $($_.Name)"
       $_ | Set-WmiInstance -Arguments @{PathName=$newPath}
   }

4. Service Dependencies and Attack Surface

Understanding service dependencies is crucial for security. One vulnerable dependency can compromise your entire service chain:
# Comprehensive dependency mapping function
function Get-ServiceDependencyMap {
   param(
       [string]$ServiceName,
       [int]$MaxDepth = 10
   )
   
   $dependencyMap = @{}
   $seenServices = @{}
   
   function Map-Dependencies {
       param(
           $ServiceName,
           $CurrentDepth = 0
       )
       
       if ($CurrentDepth -gt $MaxDepth -or $seenServices.ContainsKey($ServiceName)) {
           return
       }
       
       $seenServices[$ServiceName] = $true
       $service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
       
       if ($service) {
           $serviceInfo = @{
               Status = $service.Status
               StartType = $service.StartType
               Dependencies = @()
               DependentServices = @()
           }
           
           # Get dependencies
           $service.ServicesDependedOn | ForEach-Object {
               $serviceInfo.Dependencies += $_.Name
               Map-Dependencies -ServiceName $_.Name -CurrentDepth ($CurrentDepth + 1)
           }
           
           # Get dependent services
           $service.DependentServices | ForEach-Object {
               $serviceInfo.DependentServices += $_.Name
           }
           
           $dependencyMap[$ServiceName] = $serviceInfo
       }
   }
   
   Map-Dependencies -ServiceName $ServiceName
   return $dependencyMap
}

5. Advanced Hardening Techniques

Beyond basic configuration, let's implement advanced hardening:

5.1 Service Isolation

# Configure service isolation
$serviceName = "YourService"
$sddl = "D:(A;;CCLCSWRPWPDTLOCRRC;;;SU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)"

# Apply restricted security descriptor
sc.exe sdset $serviceName $sddl

# Verify the change
$service = Get-WmiObject win32_service -Filter "Name='$serviceName'"
$service.GetSecurityDescriptor().Descriptor

5.2 Resource Access Restrictions

# Create a restricted token for service
$policy = @'
{
   "Windows": {
       "SecurityFilters": [
           {
               "Services": {
                   "IsolationType": "Limited",
                   "RestrictedServices": ["YourService"],
                   "Capabilities": ["EnumerateUsers"]
               }
           }
       ]
   }
}
'@

Set-Content -Path "C:\ServicePolicy.json" -Value $policy

# Apply policy using Windows Security
New-ServiceSecurityPolicy -Path "C:\ServicePolicy.json"

6. Monitoring and Incident Response

Set up proper monitoring for your hardened services:
# Create an event monitor for critical service changes
$query = @"
    <QueryList>
        <Query Id="0" Path="System">
            <Select Path="System">
                *[System[(EventID=7030 or EventID=7031 or EventID=7032 or 
                  EventID=7034 or EventID=7035 or EventID=7036 or EventID=7040)]]
            </Select>
        </Query>
    </QueryList>
"@

# Create the event subscription
$params = @{
    Query = $query
    SourceIdentifier = "ServiceMonitor"
    Action = {
        $event = $Event.SourceEventArgs.NewEvent
        $message = "Service Event Detected: $($event.Message)"
        Send-MailMessage -To "admin@domain.com" -Subject "Service Alert" -Body $message
    }
}

Register-CimIndicationEvent @params

7. Common Attack Scenarios and Mitigations

Let's look at real-world attacks and how to prevent them:

7.1 DLL Hijacking Prevention

# Audit service DLL load paths
function Audit-ServiceDLLPaths {
   Get-WmiObject win32_service | ForEach-Object {
       $service = $_
       if ($service.PathName -like "*svchost.exe*") {
           $regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$($service.Name)\Parameters"
           $dllPath = Get-ItemProperty -Path $regPath -Name "ServiceDll" -ErrorAction SilentlyContinue
           
           if ($dllPath) {
               [PSCustomObject]@{
                   ServiceName = $service.Name
                   DLLPath = $dllPath.ServiceDll
                   Exists = Test-Path $dllPath.ServiceDll
                   IsSecurePath = $dllPath.ServiceDll -like "$env:SystemRoot\System32\*"
               }
           }
       }
   }
}

7.2 Privilege Escalation Prevention

  • Service Configuration:
    • Restrict service account permissions
    • Remove unnecessary privileges
    • Implement service isolation
    • Monitor for privilege changes
  • File System Security:
    • Lock down service directories
    • Implement access controls
    • Monitor for unauthorized changes

8. Quick Reference - Hardening Checklist

CategoryActionPriority
Account Security Implement gMSA High
File System Secure service paths High
Dependencies Map and secure dependencies Medium
Monitoring Implement change detection High
Isolation Configure service isolation Medium

References

Wrapping Up

Remember, service hardening is not a one-time task but an ongoing process. Keep your configurations updated, monitor for changes, and regularly audit your service security posture. Stay tuned for our next post where we'll dive into Windows Scheduled Task security! Until then, keep hunting! 🕵️‍♂️

Windows Security: Registry Forensics - Where Attackers Hide

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!

Windows Security: Registry Forensics - Where Attackers Hide

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"
Excerpt Script Output

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"

Essential Windows Event IDs for Security Monitoring: The Complete Guide

Hey there, fellow threat hunters! 👋 Today we're diving deep into the world of Windows Event IDs. And when I say deep, I mean it - grab your coffee, because we're going to explore everything from basic authentication events to advanced Active Directory monitoring. Whether you're securing a small business or a large enterprise, understanding these events is crucial for detecting threats in your environment.

Table of Contents

  • Prerequisites and Setup
  • Basic Security Events
  • Domain Controller & Active Directory Monitoring
  • User Behavior Analytics
  • Policy Changes and Their Impact
  • Compliance and ISO 27001
  • Setting Up Proper Monitoring
  • Analysis Techniques and Scripts

Prerequisites and Setup - Before We Dive In

Before we start hunting through Event IDs, let's make sure we have everything configured correctly. Trust me, there's nothing worse than missing critical events because of improper setup! 🔧

1. System Requirements

First, let's ensure our systems meet these basic requirements:

Minimum Event Log Sizes:

  • Security: 4GB (crucial for security monitoring)
  • Application: 1GB
  • System: 1GB
  • PowerShell Operational: 1GB (for script block logging)
Operating System Requirements:
  • Windows Server 2016 or later (for Domain Controllers)
  • Windows 10/11 Enterprise (for workstations)
  • Latest Windows Updates installed
  • PowerShell 5.1 or later

2. Audit Policy Setup

Let's configure our audit policies properly. Here's what you'll need to do:

Via Group Policy (Recommended Method): 

1. Open Group Policy Management Console

2. Create a new GPO or edit an existing one

3. Navigate to: Computer Configuration → Policies → Windows Settings → Security Settings → Advanced Audit Policy Configuration

Advanced Audit Policy Configuration

Essential Policy Settings to Enable:

# PowerShell command to verify current settings
auditpol /get /category:*

Required Categories (enable both Success and Failure):
  • Account Logon
  • Account Management
  • Detailed Tracking
  • DS Access Logon/Logoff
  • Object Access
  • Policy Change
  • Privilege Use
  • System

Group Policy Settings Walkthrough: 

1. Account Logon Auditing:

Computer Configuration →
    Windows Settings →
        Security Settings →
            Advanced Audit Policy →
                Account Logon
                    √ Audit Credential Validation (Success and Failure)
                    √ Audit Kerberos Authentication Service (Success and Failure)
                    √ Audit Kerberos Service Ticket Operations (Success and Failure)
2. Object Access Auditing:
Computer Configuration →
    Windows Settings →
        Security Settings →
            Advanced Audit Policy →
                Object Access
                    √ Audit File System (Success and Failure)
                    √ Audit Registry (Success and Failure)
                    √ Audit Handle Manipulation (Success and Failure)

3. Initial Setup Steps

Let's configure everything step by step:

1. Configure Log Sizes:

# Run PowerShell as Administrator
# Security Log (4GB)
wevtutil sl Security /ms:4294967296

# Application Log (1GB)
wevtutil sl Application /ms:1073741824

# System Log (1GB)
wevtutil sl System /ms:1073741824

# PowerShell Operational Log (1GB)
wevtutil sl "Microsoft-Windows-PowerShell/Operational" /ms:1073741824
2. Enable PowerShell Logging:
# Enable Script Block Logging
$regPath = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
New-Item -Path $regPath -Force
Set-ItemProperty -Path $regPath -Name "EnableScriptBlockLogging" -Value 1

# Enable Module Logging
$regPath = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ModuleLogging"
New-Item -Path $regPath -Force
Set-ItemProperty -Path $regPath -Name "EnableModuleLogging" -Value 1

3. Configure Group Policy Templates: 

Download and install the latest Administrative Templates (.admx) for your Windows version from Microsoft. Then:

  1. Copy the .admx files to: `C:\Windows\PolicyDefinitions`
  2. Copy the .adml files to: `C:\Windows\PolicyDefinitions\en-US`
  3. Configure via Group Policy Management Editor:
Computer Configuration →
    Administrative Templates →
        Windows Components →
            Windows PowerShell
                √ Turn on Module Logging
                √ Turn on PowerShell Script Block Logging
                √ Turn on PowerShell Transcription

4. Verification Steps

After setup, verify everything is working correctly:

1. Check Audit Policy Configuration:

# Check all audit policies
auditpol /get /category:*

# Check specific category
auditpol /get /category:"Object Access"
2. Verify Log Sizes:
# Check all log sizes
Get-WinEvent -ListLog * | Where-Object {$_.RecordCount -gt 0} |
    Select-Object LogName, MaximumSizeInBytes, RecordCount |
    Sort-Object MaximumSizeInBytes -Descending |
    Format-Table -AutoSize
3. Test Event Generation:
# Try to generate a test event
Write-EventLog -LogName Application -Source "Application" -EventId 1000 -Message "Test event"

# Verify PowerShell logging
$testScript = "Write-Host 'Testing script block logging'"
Invoke-Expression $testScript

5. Troubleshooting Common Issues

If you run into problems, check these common issues:

Issue: Events Not Being Logged 

  • Verify audit policies are applied: `gpresult /H report.html`
  • Check event log service is running: `Get-Service EventLog`
  • Verify log size limits aren't reached

Issue: Policy Not Applying 

  • Run: `gpupdate /force`
  • Check Group Policy inheritance
  • Verify security filtering

⚠️ Important Notes:

1. Performance Impact

  • Large log sizes can impact system performance
  • Heavy auditing may affect system resources
  • Consider storage requirements for long-term logging

2. Security Considerations

  • Don't disable logs during maintenance
  • Monitor log clearing events (Event ID 1102)
  • Regular backup of event logs is recommended

3. Maintenance

  • Regularly archive old logs
  • Monitor available disk space
  • Review and adjust log sizes as needed

Once you've completed all these steps, you're ready to start monitoring those Event IDs we'll discuss next! 👍

1. Basic Security Events - The Foundation

Before we dive into the advanced stuff, let's establish our baseline. These are the events you absolutely must monitor:

Authentication Events:

  • 4624 - Successful logon
  • 4625 - Failed logon
  • 4634 - Logoff
  • 4647 - User initiated logoff
  • 4648 - Explicit credential logon
  • 4672 - Special privileges assigned
Quick Detection Script:
$startTime = (Get-Date).AddHours(-1)
$failedLogons = Get-WinEvent -FilterHashtable @{
    LogName = 'Security'
    ID = 4625
    StartTime = $startTime
} -ErrorAction SilentlyContinue | 
Select-Object TimeCreated,
    @{N='Username';E={$_.Properties[5].Value}},
    @{N='Source';E={$_.Properties[2].Value}},
    @{N='Status';E={$_.Properties[7].Value}},
    @{N='SubStatus';E={$_.Properties[9].Value}}

$failedLogons | Group-Object Source | 
    Where-Object {$_.Count -gt 5} |
    Select-Object Name,Count

2. Domain Controller & Active Directory Events - The Crown Jewels

Your Domain Controllers are the heart of your network. Here's what to watch:

Directory Service Events:

  • 4662 - An operation was performed on an object
  • 4741 - A computer account was created
  • 4742 - A computer account was changed
  • 4743 - A computer account was deleted
  • 5136 - A directory service object was modified
  • 5137 - A directory service object was created
  • 5138 - A directory service object was undeleted
  • 5139 - A directory service object was moved
  • 5141 - A directory service object was deleted
Critical AD Changes to Monitor:
  • Schema modifications (Event ID 5137 with specific object classes)
  • SYSVOL changes
  • Domain Trust modifications
  • AD database changes
  • FSMO role changes
AD Monitoring Script:
# Monitor for sensitive AD changes
$criticalADEvents = Get-WinEvent -FilterHashtable @{
    LogName = 'Security'
    ID = @(5136,5137,5138,5139,5141)
    StartTime = (Get-Date).AddDays(-1)
} | Select-Object TimeCreated,ID,
    @{N='ObjectDN';E={$_.Properties[5].Value}},
    @{N='ObjectClass';E={$_.Properties[6].Value}},
    @{N='Modifier';E={$_.Properties[1].Value}}

# Look for modifications to sensitive groups
$sensitiveGroups = @('Domain Admins','Enterprise Admins','Schema Admins')
$criticalADEvents | Where-Object {
    $sensitiveGroups | ForEach-Object {
        $group = $_
        $_.ObjectDN -like "*$group*"
    }
}

3. Group Policy Changes - The Silent Configuration Manager

Group Policy changes can have wide-reaching effects. Monitor these events:

Group Policy Events:

  • 4706 - A new trust was created to a domain
  • 4707 - A trust to a domain was removed
  • 4739 - Domain Policy was changed
  • 5136 - GPO modification (when targeting GP containers)
  • 5137 - GPO creation
  • 5141 - GPO deletion
GPO Change Monitoring:
# Monitor GPO Changes
$gpoEvents = Get-WinEvent -FilterHashtable @{
    LogName = 'Security'
    ID = @(5136,5137,5141)
    StartTime = (Get-Date).AddHours(-24)
} | Where-Object {
    $_.Properties[5].Value -like "*CN=Policies,CN=System*"
} | Select-Object TimeCreated,ID,
    @{N='GPO';E={$_.Properties[5].Value}},
    @{N='User';E={$_.Properties[1].Value}}

4. User Behavior Analytics - The Human Element

Understanding normal vs. abnormal user behavior is crucial.

Here's what to track: User Activity Events:

  • 4688 - Process creation
  • 4689 - Process termination
  • 4663 - File system/registry access attempt
  • 4657 - Registry value modification
  • 4698 - Scheduled task creation
  • 4699 - Scheduled task deletion
  • 4702 - Scheduled task updated
PowerShell Activity:
  • 4103 - Module logging
  • 4104 - Script block logging
  • 4105 - Command start
  • 4106 - Command stop
Behavior Analysis Script:
# Track unusual process creation patterns
$processes = Get-WinEvent -FilterHashtable @{
    LogName = 'Security'
    ID = 4688
    StartTime = (Get-Date).AddHours(-4)
} | Select-Object TimeCreated,
    @{N='User';E={$_.Properties[1].Value}},
    @{N='Process';E={$_.Properties[5].Value}},
    @{N='CommandLine';E={$_.Properties[8].Value}}

# Group by user and process to find unusual patterns
$processes | Group-Object User | ForEach-Object {
    $user = $_.Name
    $userProcesses = $_.Group | Group-Object Process
    
    $unusualCount = $userProcesses | Where-Object {
        $_.Count -gt 10
    }
    
    if ($unusualCount) {
        Write-Warning "User $user has suspicious process patterns:"
        $unusualCount | Format-Table Name,Count
    }
}

5. Application Execution Monitoring - Know What's Running

Monitor application execution with these events:

Application Events:

  • 4688 - Process creation (with command line tracking)
  • 3001 - AppLocker policy applied
  • 8002 - AppLocker exe/dll allowed
  • 8003 - AppLocker exe/dll denied
  • 8004 - AppLocker script allowed
  • 8005 - AppLocker script denied
Advanced Process Tracking:
# Track processes with command line arguments
$suspiciousCommands = @(
    'powershell.exe.*encode',
    'cmd.exe.*/c',
    '.*bypass.*',
    'certutil.*-urlcache',
    'bitsadmin.*transfer'
)

Get-WinEvent -FilterHashtable @{
    LogName = 'Security'
    ID = 4688
    StartTime = (Get-Date).AddHours(-12)
} | Where-Object {
    $cmdLine = $_.Properties[8].Value
    $suspiciousCommands | ForEach-Object {
        $pattern = $_
        if ($cmdLine -match $pattern) { return $true }
    }
} | Select-Object TimeCreated,
    @{N='User';E={$_.Properties[1].Value}},
    @{N='CommandLine';E={$_.Properties[8].Value}}

6. Local Policy Changes - The System Configuration

Track changes to local security policies:

Policy Change Events:

  • 4719 - System audit policy was changed
  • 4907 - Auditing settings on object were changed
  • 4912 - Per user audit policy was changed
  • 4713 - Kerberos policy was changed
  • 4717 - System security access was granted to an account
  • 4718 - System security access was removed from an account
  • 4739 - Domain Policy was changed
Policy Monitoring Script:
# Monitor security policy changes
$policyChanges = Get-WinEvent -FilterHashtable @{
    LogName = 'Security'
    ID = @(4719,4907,4912,4713)
    StartTime = (Get-Date).AddDays(-1)
} | Select-Object TimeCreated,ID,
    @{N='User';E={$_.Properties[1].Value}},
    @{N='Category';E={$_.Properties[3].Value}},
    @{N='Change';E={$_.Properties[4].Value}}

# Alert on critical changes
$policyChanges | Where-Object {
    $_.Category -match 'Audit|Privilege|Token'
} | Format-Table -AutoSize

7. ISO 27001 Alignment - Meeting Compliance

ISO 27001 requires monitoring of specific activities. Here's how to align your monitoring:

A.9 Access Control: 

  • Monitor events 4624, 4625 (authentication)
  • Track 4720, 4722, 4723 (account management)
  • Log 4728, 4732, 4756 (group membership changes)

A.12 Operations Security: 

  • Monitor events 4688, 4689 (process execution)
  • Track 4663, 4656 (file access)
  • Log 5140 (network share access)

A.12.4 Logging Requirements:

  • User activities (4624, 4634)
  • Privileged operations (4672, 4673)
  • System exceptions (1000, 1001)
  • Security events (4625, 4648)
ISO 27001 Compliance Check Script:
# Basic ISO 27001 compliance check
$isoEvents = @{
    'Authentication' = @(4624,4625)
    'Account_Management' = @(4720,4722,4723)
    'Privileged_Access' = @(4672,4673)
    'System_Changes' = @(4688,4689)
}

$report = foreach ($category in $isoEvents.Keys) {
    $events = Get-WinEvent -FilterHashtable @{
        LogName = 'Security'
        ID = $isoEvents[$category]
        StartTime = (Get-Date).AddDays(-7)
    } -ErrorAction SilentlyContinue
    
    [PSCustomObject]@{
        Category = $category
        EventCount = $events.Count
        LastEvent = ($events | Select-Object -First 1).TimeCreated
        Status = if($events.Count -gt 0){'Logging Active'}else{'No Logs Found'}
    }
}

$report | Format-Table -AutoSize

Best Practices for Implementation

1. Log Size Management:
# Increase log size to 4GB
wevtutil sl Security /ms:4294967296
wevtutil sl "Microsoft-Windows-PowerShell/Operational" /ms:1073741824
2. Audit Policy Configuration:
# Configure advanced audit policy
auditpol /set /category:"Account Logon" /success:enable /failure:enable
auditpol /set /category:"Account Management" /success:enable /failure:enable
auditpol /set /category:"DS Access" /success:enable /failure:enable
auditpol /set /category:"Object Access" /success:enable /failure:enable
auditpol /set /category:"Policy Change" /success:enable /failure:enable
auditpol /set /category:"Privilege Use" /success:enable /failure:enable
auditpol /set /category:"System" /success:enable /failure:enable
3. PowerShell Logging:
# Enable via Registry
$regPath = "HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"
New-Item -Path $regPath -Force
Set-ItemProperty -Path $regPath -Name "EnableScriptBlockLogging" -Value 1

Common Attack Patterns to Watch

1. Golden Ticket Attacks: 

  • Multiple 4624 events with NTLM authentication
  • Service account authentication outside business hours
  • Unusual TGT request patterns

2. DCSync Attacks: 

  • Event ID 4662 with specific GUID {1131f6aa-9c07-11d1-f79f-00c04fc2dcd2}
  • Replication requests from non-DC machines

3. Kerberoasting: 

  • Multiple 4769 events with RC4 encryption
  • Service ticket requests for multiple services from single account

4. Living Off The Land: 

  • PowerShell/WMI events outside normal patterns
  • LOLBAS tool execution (certutil.exe, bitsadmin.exe, etc.)

Pro Tips

1. Performance Considerations: 

  • Use targeted event collection
  • Implement log rotation
  • Consider event forwarding for centralized collection

2. False Positive Management: 

  • Baseline normal activity first
  • Create allow lists for known good behavior
  • Document maintenance windows

3. Incident Response Integration: 

  • Create automated alerts for critical events
  • Maintain event correlation rules
  • Document investigation procedures

Wrapping Up

Remember, effective event monitoring is more than just collecting logs - it's about understanding what's normal in your environment and quickly spotting what isn't. Start with the basics and gradually expand your monitoring as you better understand your environment's patterns. Stay safe, and happy hunting! 🕵️‍♂️ P.S. Want to learn about integrating these events with an ELK stack? Stay tuned for our next post!

Python Quick Guide: Building a Simple Port Scanner

Python Quick Guide: Building a Simple Port Scanner

Hey there, fellow threat hunters! 👋 Today we're going to build something fun and educational - a basic port scanner in Python. Because sometimes you need to think like the attackers to better defend your systems. (Just remember: only scan systems you own or have explicit permission to test!)

Our Scanner vs. Nmap

While building your own tools is educational, let's be honest - in production environments, you'll probably want to use Nmap. Here's why:

1. Feature Comparison

Our Scanner:

  • Basic TCP connect scanning
  • Simple service detection
  • Multi-threading
  • ~100 lines of code

Nmap:

  • Multiple scan types (SYN, FIN, NULL, XMAS, etc.)
  • OS detection
  • NSE (Nmap Scripting Engine)
  • Version detection
  • Timing templates
  • Proven reliability

2. Why Learn Both?

Understanding how to build a basic scanner helps you:

  • Better understand what Nmap is doing under the hood
  • Debug network issues more effectively
  • Customize scanning behavior for specific needs
  • Create specialized tools when Nmap isn't available

3. Equivalent Nmap Commands

Our scanner's functionality can be replicated in Nmap like this:

# Basic scan (like our default)
nmap -p1-1024 target.com

# Fast scan with threads (like our threaded version)
nmap -p1-1024 -T4 target.com

# With service detection (better than our service detection)
nmap -p1-1024 -sV target.com

4. When to Use What

Use our scanner when:

  • Learning about network programming
  • Need a very specific, custom scanning behavior
  • Want to integrate scanning into a larger Python application
  • Can't install Nmap on the system

Use Nmap when:

  • Need reliable, production-ready scanning
  • Want advanced features like OS detection
  • Need to run complex scanning scripts (NSE)
  • Time is critical (Nmap is much faster!)

Remember: Nmap took years of development and community testing to become what it is. Our scanner is a learning tool - think of it as "Nmap 101" rather than a replacement! 😉

What We're Building

We'll create a simple but effective port scanner that can: - Scan a range of ports on a target host - Identify open ports and their potential services - Handle both TCP and UDP scanning - Provide clear, formatted output - Support multiple threads for faster scanning

The Basic Scanner

Let's start with a simple version and then build it up:
import socket
import threading
from queue import Queue
import time
import argparse
from typing import List, Tuple

class PortScanner:
    def __init__(self, target: str, start_port: int = 1, end_port: int = 1024, threads: int = 50):
        self.target = target
        self.start_port = start_port
        self.end_port = end_port
        self.threads = threads
        self.queue = Queue()
        self.results = []
        
    def _is_port_open(self, port: int) -> Tuple[int, bool, str]:
        """Test if a port is open."""
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.settimeout(1)
                result = s.connect_ex((self.target, port))
                if result == 0:
                    try:
                        service = socket.getservbyport(port)
                    except:
                        service = "unknown"
                    return port, True, service
            return port, False, ""
        except:
            return port, False, ""
            
    def _worker(self):
        """Worker thread to process the port queue."""
        while True:
            port = self.queue.get()
            if port is None:
                break
            result = self._is_port_open(port)
            if result[1]:  # if port is open
                self.results.append(result)
            self.queue.task_done()
            
    def scan(self) -> List[Tuple[int, bool, str]]:
        """Start the port scanning process."""
        start_time = time.time()
        
        # Fill the queue with ports
        for port in range(self.start_port, self.end_port + 1):
            self.queue.put(port)
            
        # Start worker threads
        thread_list = []
        for _ in range(self.threads):
            t = threading.Thread(target=self._worker)
            t.start()
            thread_list.append(t)
            
        # Add sentinel values to stop threads
        for _ in range(self.threads):
            self.queue.put(None)
            
        # Wait for all threads to complete
        for t in threading.Thread:
            t.join()
            
        end_time = time.time()
        self.scan_time = end_time - start_time
        return sorted(self.results)

Making It Usable

Now let's add a nice command-line interface:
def main():
    parser = argparse.ArgumentParser(description="Simple Python Port Scanner")
    parser.add_argument("target", help="Target host to scan")
    parser.add_argument("-s", "--start", type=int, default=1, help="Start port (default: 1)")
    parser.add_argument("-e", "--end", type=int, default=1024, help="End port (default: 1024)")
    parser.add_argument("-t", "--threads", type=int, default=50, help="Number of threads (default: 50)")
    args = parser.parse_args()

    # Create and run scanner
    scanner = PortScanner(args.target, args.start, args.end, args.threads)
    print(f"\nStarting scan on host {args.target}")
    
    try:
        results = scanner.scan()
        
        # Print results
        print(f"\nScan completed in {scanner.scan_time:.2f} seconds")
        print("\nOpen ports:")
        print("PORT\tSTATE\tSERVICE")
        print("-" * 30)
        
        for port, is_open, service in results:
            if is_open:
                print(f"{port}\topen\t{service}")
                
        print(f"\nScanned {args.end - args.start + 1} ports")
        print(f"Found {len(results)} open ports")
        
    except KeyboardInterrupt:
        print("\nScan interrupted by user")
    except socket.gaierror:
        print("\nHostname could not be resolved")
    except socket.error:
        print("\nCouldn't connect to server")

if __name__ == "__main__":
    main()

Using the Scanner

Save this code as `port_scanner.py` and run it like this:
python port_scanner.py localhost  # Scan default ports
python port_scanner.py example.com -s 20 -e 100  # Scan specific port range
python port_scanner.py 192.168.1.1 -t 100  # Use more threads
Scanner usage example
Scanner usage example

How It Works

Let's break down the key components:

  1. Socket Connection: We use Python's socket library to attempt TCP connections to each port.
  2. Threading: Multiple threads process the port queue concurrently for faster scanning.
  3. Service Identification: We try to identify common services using `socket.getservbyport()`.
  4. Queue Management: A queue is used to safely distribute work among threads.

Making It Better

Here are some ways you could enhance this scanner: 1. Add UDP scanning:
def _udp_scan(self, port: int) -> Tuple[int, bool, str]:
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
            s.settimeout(1)
            s.sendto(bytes(0), (self.target, port))
            try:
                s.recvfrom(1024)
                return port, True, "udp"
            except socket.timeout:
                return port, False, ""
    except:
        return port, False, ""
2. Add banner grabbing:
def _grab_banner(self, ip: str, port: int) -> str:
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(2)
            s.connect((ip, port))
            banner = s.recv(1024).decode().strip()
            return banner
    except:
        return ""
3. Add service fingerprinting with common ports dictionary:
COMMON_PORTS = {
    20: "FTP-DATA", 21: "FTP", 22: "SSH", 23: "TELNET",
    25: "SMTP", 53: "DNS", 80: "HTTP", 443: "HTTPS",
    3306: "MYSQL", 3389: "RDP"
}

Security Considerations

Remember: - Port scanning without permission can be illegal - Some networks/firewalls may block scanning attempts - Aggressive scanning can trigger security alerts - Always test in authorized environments first

Pro Tips

1. Adjust the timeout value based on network conditions 2. Use fewer threads on less powerful systems 3. Be careful with large port ranges - they take longer to scan 4. Consider adding logging for scan results 5. Think about adding IP range support for network scanning

Wrapping Up

There you have it - a basic but functional port scanner in Python! It's a great starting point for learning about network programming and security tools. Remember to use this knowledge responsibly! Stay safe, and happy hunting! 🕵️‍♂️ P.S. Want to learn more about Python security tools? Let me know in the comments!

PowerShell Quick Guide: Creating Your First Security Audit Script

PowerShell Quick Guide: Creating Your First Security Audit Script
Hey there, fellow threat hunters! 👋 Today we're going to create your first PowerShell security audit script. Tired of running individual commands every time you need to check a system? Let's build something reusable that will make your life easier! You can find the full repository here:
SecurityScriptographer/PowerShell-Quick-Guide

Setting Up Our Script

First, let's create our basic script structure. We'll build this step by step:
param(
    [Parameter(Mandatory=$false)]
    [string]$OutputPath = ".\SecurityAudit_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt",
    
    [Parameter(Mandatory=$false)]
    [switch]$ExportToCSV
)

# Initialize our report
$report = @()
$startTime = Get-Date
Write-Host "Starting security audit at $startTime" -ForegroundColor Green

System Information Module

Let's start with basic system information - always good to know what you're dealing with:
function Get-SystemInfo {
    Write-Host "Collecting system information..." -ForegroundColor Yellow
    
    $os = Get-WmiObject -Class Win32_OperatingSystem
    $computerSystem = Get-WmiObject -Class Win32_ComputerSystem
    
    return @{
        'Hostname' = $env:COMPUTERNAME
        'OS Version' = $os.Caption
        'OS Architecture' = $os.OSArchitecture
        'Last Boot Time' = $os.ConvertToDateTime($os.LastBootUpTime)
        'Total Memory (GB)' = [math]::Round($computerSystem.TotalPhysicalMemory/1GB, 2)
        'Domain' = $computerSystem.Domain
    }
}

Security Settings Check

Now, let's add a function to check important security settings:
function Get-SecuritySettings {
    Write-Host "Checking security settings..." -ForegroundColor Yellow
    
    $firewallStatus = Get-NetFirewallProfile | Select-Object Name, Enabled
    $antivirusProduct = Get-WmiObject -Namespace root\SecurityCenter2 -Class AntiVirusProduct -ErrorAction SilentlyContinue
    
    return @{
        'Firewall Status' = ($firewallStatus | ForEach-Object { "$($_.Name): $($_.Enabled)" }) -join '; '
        'Antivirus Status' = if ($antivirusProduct) { $antivirusProduct.displayName } else { "No AV detected" }
        'PowerShell Version' = $PSVersionTable.PSVersion.ToString()
        'UAC Enabled' = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System).EnableLUA -eq 1
        'SecureBoot Enabled' = Confirm-SecureBootUEFI -ErrorAction SilentlyContinue
    }
}

User and Group Analysis

Let's add some user account auditing:
function Get-UserAudit {
    Write-Host "Auditing user accounts..." -ForegroundColor Yellow
    
    $localUsers = Get-LocalUser | Where-Object Enabled -eq $true
    $adminGroup = Get-LocalGroupMember -Group "Administrators" -ErrorAction SilentlyContinue
    
    return @{
        'Enabled Local Users' = ($localUsers | Select-Object Name).Name -join ', '
        'Local Admins' = ($adminGroup | Select-Object Name).Name -join ', '
        'Last 5 Created Users' = (Get-LocalUser | Sort-Object Created -Descending | 
            Select-Object -First 5 Name, Created | ForEach-Object { 
                "$($_.Name) (Created: $($_.Created))" 
            }) -join '; '
    }
}

Running Services and Network Connections

Time to check what's running and connecting:
function Get-RunningServices {
    Write-Host "Analyzing services and connections..." -ForegroundColor Yellow
    
    $suspiciousServices = Get-WmiObject win32_service | 
        Where-Object {$_.PathName -like "*temp*" -or $_.PathName -like "*appdata*"}
    
    $externalConnections = Get-NetTCPConnection -State Established |
        Where-Object {$_.RemoteAddress -notmatch "^10\.|^172\.|^192\.168\."}
    
    return @{
        'Suspicious Service Paths' = if ($suspiciousServices) {
            ($suspiciousServices | Select-Object DisplayName, PathName).DisplayName -join ', '
        } else { "None found" }
        'External Connections' = ($externalConnections | Measure-Object).Count
    }
}

Putting It All Together

Now for our main execution block:
try {
    # Create our report object
    $report = [ordered]@{
        'ScanTime' = Get-Date
        'SystemInformation' = Get-SystemInfo
        'SecuritySettings' = Get-SecuritySettings
        'UserAudit' = Get-UserAudit
        'ServiceAnalysis' = Get-RunningServices
    }
    
    # Export results based on format preference
    if ($ExportToCSV) {
        $reportPath = $OutputPath -replace '\.json$', '.csv'
        $report | ConvertTo-Json | 
            ConvertFrom-Json | 
            Export-Csv -Path $reportPath -NoTypeInformation
        Write-Host "Audit complete! Results saved to $reportPath" -ForegroundColor Green
    } else {
        $report | ConvertTo-Json -Depth 10 | 
            Out-File -FilePath $OutputPath -Encoding UTF8
        Write-Host "Audit complete! Results saved to $OutputPath" -ForegroundColor Green
        
        # Optionally display a summary in console
        Write-Host "`nQuick Summary:" -ForegroundColor Yellow
        Write-Host "- Hostname: $($report.SystemInformation.Hostname)"
        Write-Host "- OS Version: $($report.SystemInformation.OSVersion)"
        Write-Host "- External Connections Found: $($report.ServiceAnalysis.'External Connections')"
        Write-Host "- Enabled Local Users: $($report.UserAudit.'Enabled Local Users'.Split(',').Count)"
    }
} catch {
    Write-Host "Error during audit: $($_.Exception.Message)" -ForegroundColor Red
}

Running the Script

Save this as `Security-Audit.ps1` and run it like this:
.\Security-Audit.ps1 -ExportToCSV
Or for JSON output:
.\Security-Audit.ps1

Pro Tips

  1. Always run this with administrative privileges - many checks require elevated access.
  2. Consider adding error handling for each function to prevent one failure from stopping the entire audit.
  3. Feel free to customize the checks based on your organization's security requirements.
  4. Remember to sanitize any output before sharing - you don't want to accidentally leak sensitive information!

Room for Improvement

This is a basic script, but you could enhance it by:

  • Adding checks for installed software and patches
  • Implementing compliance checks against a baseline
  • Adding remote system scanning capabilities
  • Including scheduled task analysis
  • Adding registry checks for common malware persistence locations

Wrapping Up

There you have it - your first security audit script! It's not perfect (what is?), but it's a solid foundation that you can build upon. Remember, automation is your friend in security, but always verify your results. A script can only tell you what you programmed it to look for! Stay safe, and happy hunting! 🕵️‍♂️ P.S. Want to learn more about PowerShell security tools? Check out the other PowerShell Quick Guides.