Skip to main content

Lab 7.2: Windows Persistence Mechanisms

Learning Objectives​

By the end of this lab, you will be able to:

  • Understand Windows persistence mechanisms
  • Create and delete test services
  • Manage scheduled tasks
  • Detect malicious persistence

Estimated Duration​

50 minutes

Prerequisites​

  • Lab 7.1 completed
  • Windows 10/11 VM
  • Administrator rights

Professional Context​

Persistence is a technique used to:

  • Legitimately: Automatically start services
  • Maliciously: Maintain access after a reboot

Understanding these mechanisms is essential for defense.


WARNING​

This lab teaches persistence techniques for defensive purposes.

  • Execute only on test VMs
  • Clean up all creations at the end of the lab

Part 1: Persistence via Services​

Step 1.1: Create a test service​

# Create a service script
$scriptContent = @'
# Test service - DOES NOTHING
# This script simulates a service for the lab
Write-Output "Test service started" | Out-File C:\Temp\service_log.txt -Append
while ($true) {
Start-Sleep -Seconds 60
}
'@

# Save the script
New-Item -Path "C:\Temp" -ItemType Directory -Force | Out-Null
$scriptContent | Out-File "C:\Temp\test_service.ps1" -Encoding UTF8

Write-Host "Script created: C:\Temp\test_service.ps1" -ForegroundColor Green

Step 1.2: Register the service​

# Create the service with NSSM or sc.exe
# For this lab, we use sc.exe with cmd.exe

# Method 1: Simple service with cmd.exe (for demo)
sc.exe create "LabTestService" `
binPath= "cmd.exe /k echo Test service" `
DisplayName= "Lab Test Service" `
start= manual

# Verify
Get-Service "LabTestService"
sc.exe qc "LabTestService"

Step 1.3: Modify the service​

# Change the startup type
sc.exe config "LabTestService" start= auto

# Add a description
sc.exe description "LabTestService" "Service created for lab 7.2"

# Verify changes
Get-Service "LabTestService" | Select-Object *

Step 1.4: Detect our service​

# Our service should appear suspicious
Get-WmiObject Win32_Service | Where-Object {
$_.Name -eq "LabTestService"
} | Select-Object Name, DisplayName, PathName, StartMode, State

Question 1: Why might this service be considered suspicious?

Step 1.5: Delete the service​

# Stop and delete
Stop-Service "LabTestService" -Force -ErrorAction SilentlyContinue
sc.exe delete "LabTestService"

# Verify deletion
Get-Service "LabTestService" -ErrorAction SilentlyContinue

Part 2: Persistence via Scheduled Tasks​

Step 2.1: Create a simple task​

# Create a task that runs at startup
$action = New-ScheduledTaskAction `
-Execute "cmd.exe" `
-Argument "/c echo Test task > C:\Temp\task_log.txt"

$trigger = New-ScheduledTaskTrigger -AtStartup

$principal = New-ScheduledTaskPrincipal `
-UserId "SYSTEM" `
-RunLevel Highest

Register-ScheduledTask `
-TaskName "LabTestTask" `
-Action $action `
-Trigger $trigger `
-Principal $principal `
-Description "Task created for lab 7.2"

Write-Host "Task created: LabTestTask" -ForegroundColor Green

Step 2.2: Verify the task​

# View the task
Get-ScheduledTask -TaskName "LabTestTask"

# Full details
Get-ScheduledTask -TaskName "LabTestTask" |
Select-Object TaskName, State, @{N='Trigger';E={$_.Triggers.AtStartup}},
@{N='Action';E={$_.Actions.Execute}}

# Execution information
Get-ScheduledTaskInfo -TaskName "LabTestTask"

Step 2.3: Create a more realistic task​

# Task that mimics a legitimate service (for detection demo)
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-WindowStyle Hidden -Command Start-Sleep 3600"

$trigger = New-ScheduledTaskTrigger -AtLogOn

Register-ScheduledTask `
-TaskName "WindowsUpdateHelper" `
-Action $action `
-Trigger $trigger `
-Description "Windows Update (FAKE - LAB)"

Write-Host "Suspicious task created for demo" -ForegroundColor Yellow

Step 2.4: Perform detection​

# Script to detect suspicious tasks
$suspectTasks = @()

Get-ScheduledTask | Where-Object {
$_.TaskPath -notlike "\Microsoft\*"
} | ForEach-Object {
$task = $_

foreach ($action in $task.Actions) {
$isSuspect = $false
$reasons = @()

# Hidden PowerShell
if ($action.Arguments -match "-WindowStyle\s+Hidden") {
$isSuspect = $true
$reasons += "Hidden PowerShell"
}

# Encoded commands
if ($action.Arguments -match "-[Ee]ncoded") {
$isSuspect = $true
$reasons += "Encoded command"
}

# Our test tasks
if ($task.TaskName -match "^Lab" -or $task.TaskName -eq "WindowsUpdateHelper") {
$isSuspect = $true
$reasons += "Test/suspicious task"
}

if ($isSuspect) {
$suspectTasks += [PSCustomObject]@{
TaskName = $task.TaskName
Execute = $action.Execute
Arguments = $action.Arguments
Reasons = ($reasons -join ", ")
}
}
}
}

Write-Host "`n=== DETECTED SUSPICIOUS TASKS ===" -ForegroundColor Cyan
$suspectTasks | Format-Table -AutoSize

Question 2: Which tasks were detected as suspicious?

Step 2.5: Delete the tasks​

# Delete our test tasks
Unregister-ScheduledTask -TaskName "LabTestTask" -Confirm:$false
Unregister-ScheduledTask -TaskName "WindowsUpdateHelper" -Confirm:$false

Write-Host "Tasks deleted" -ForegroundColor Green

Part 3: Persistence via Registry​

Step 3.1: Run Keys (auto-start)​

Run keys execute programs at logon:

# View current Run keys
Write-Host "=== HKCU Run ===" -ForegroundColor Cyan
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue

Write-Host "`n=== HKLM Run ===" -ForegroundColor Cyan
Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue

Step 3.2: Add a test entry​

# Add a Run entry (current user)
Set-ItemProperty `
-Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" `
-Name "LabTestRun" `
-Value "cmd.exe /c echo Test > C:\Temp\run_test.txt"

Write-Host "Run entry added" -ForegroundColor Green

# Verify
Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" |
Select-Object LabTestRun

Step 3.3: Detect suspicious entries​

# Script to detect suspicious Run keys
$runKeys = @(
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKCU:\Software\Microsoft\Windows\CurrentVersion\RunOnce",
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\Software\Microsoft\Windows\CurrentVersion\RunOnce"
)

Write-Host "=== RUN KEYS ANALYSIS ===" -ForegroundColor Cyan

foreach ($key in $runKeys) {
if (Test-Path $key) {
Write-Host "`n$key" -ForegroundColor Yellow

$props = Get-ItemProperty $key -ErrorAction SilentlyContinue
$props.PSObject.Properties | Where-Object {
$_.Name -notmatch "^PS"
} | ForEach-Object {
$value = $_.Value

# Detect suspects
$suspect = $false
if ($value -match "\\Temp\\" -or
$value -match "\\AppData\\Local\\" -or
$value -match "-[Ee]ncoded" -or
$_.Name -match "^Lab") {
$suspect = $true
}

if ($suspect) {
Write-Host " [SUSPECT] $($_.Name): $value" -ForegroundColor Red
} else {
Write-Host " [OK] $($_.Name): $value" -ForegroundColor Green
}
}
}
}

Step 3.4: Delete the test entry​

Remove-ItemProperty `
-Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" `
-Name "LabTestRun"

Write-Host "Run entry deleted" -ForegroundColor Green

Part 4: Global Detection Script​

Step 4.1: Full persistence scan​

# Full persistence detection script
Write-Host @"
================================================================================
WINDOWS PERSISTENCE SCANNER
================================================================================
"@ -ForegroundColor Cyan

$findings = @()

# 1. Suspicious services
Write-Host "`n[1/3] Analyzing services..." -ForegroundColor Yellow
Get-WmiObject Win32_Service | Where-Object {
$_.PathName -match "\\(Temp|AppData|Downloads)\\" -or
$_.Name -match "^(Lab|Test)"
} | ForEach-Object {
$findings += [PSCustomObject]@{
Type = "Service"
Name = $_.Name
Details = $_.PathName
Risk = "Medium"
}
}

# 2. Suspicious scheduled tasks
Write-Host "[2/3] Analyzing scheduled tasks..." -ForegroundColor Yellow
Get-ScheduledTask | Where-Object {
$_.TaskPath -notlike "\Microsoft\*"
} | ForEach-Object {
$task = $_
$action = $task.Actions | Select-Object -First 1

if ($action.Arguments -match "-Hidden|-[Ee]ncoded" -or
$task.TaskName -match "^(Lab|Test|WindowsUpdate[A-Z])") {
$findings += [PSCustomObject]@{
Type = "ScheduledTask"
Name = $task.TaskName
Details = "$($action.Execute) $($action.Arguments)"
Risk = "High"
}
}
}

# 3. Run Keys
Write-Host "[3/3] Analyzing Run keys..." -ForegroundColor Yellow
$runKeys = @(
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Run"
)

foreach ($key in $runKeys) {
if (Test-Path $key) {
$props = Get-ItemProperty $key -ErrorAction SilentlyContinue
$props.PSObject.Properties | Where-Object {
$_.Name -notmatch "^PS" -and
($_.Value -match "\\Temp\\" -or $_.Name -match "^Lab")
} | ForEach-Object {
$findings += [PSCustomObject]@{
Type = "RunKey"
Name = $_.Name
Details = $_.Value
Risk = "High"
}
}
}
}

# Report
Write-Host "`n================================================================================`n" -ForegroundColor Cyan

if ($findings.Count -eq 0) {
Write-Host "[OK] No suspicious persistence detected" -ForegroundColor Green
} else {
Write-Host "[!] $($findings.Count) suspicious persistence(s) detected:" -ForegroundColor Red
$findings | Format-Table -AutoSize
}

Write-Host "================================================================================" -ForegroundColor Cyan

Part 5: Cleanup​

Step 5.1: Final verification and cleanup​

# Verify nothing remains
Write-Host "=== CLEANUP VERIFICATION ===" -ForegroundColor Cyan

# Services
$testService = Get-Service "LabTestService" -ErrorAction SilentlyContinue
if ($testService) {
Write-Host "Service LabTestService still present - deleting..." -ForegroundColor Yellow
sc.exe delete "LabTestService"
}

# Tasks
$testTasks = Get-ScheduledTask | Where-Object {$_.TaskName -match "^Lab|WindowsUpdateHelper"}
foreach ($task in $testTasks) {
Write-Host "Task $($task.TaskName) still present - deleting..." -ForegroundColor Yellow
Unregister-ScheduledTask -TaskName $task.TaskName -Confirm:$false
}

# Run keys
$runKey = Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "LabTestRun" -ErrorAction SilentlyContinue
if ($runKey) {
Write-Host "Run key LabTestRun still present - deleting..." -ForegroundColor Yellow
Remove-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name "LabTestRun"
}

# Files
if (Test-Path "C:\Temp\test_service.ps1") {
Remove-Item "C:\Temp\test_service.ps1" -Force
}

Write-Host "`nCleanup completed!" -ForegroundColor Green

Deliverables​

  1. Answers to the 2 questions

  2. Screenshots:

    • Created service
    • Created scheduled task
    • Persistence scanner result
  3. Report (1-2 pages):

    • Tested persistence mechanisms
    • How to detect them
    • Defense recommendations

Evaluation Criteria​

CriteriaPoints
Persistence via service25%
Persistence via tasks30%
Persistence via registry20%
Detection script25%

Lab developed as part of the Operating Systems course - MODULE 7