# T1490 - Inhibit System Recovery

## SOC Recommendation
Investigate Inhibit System Recovery activity in the context of Impact: confirm scope, affected host/identity, and whether it matches expected administrative behaviour before deciding this is benign.

## D3FEND Mappings
| D3FEND Technique | Relationship | Practical SOC Action |
|---|---|---|
| Software Update | Harden | Apply Software Update to reduce this technique's viability before an incident occurs. |
| Restore Software | Restore | Use Restore Software to recover affected systems or data after containment. |
| Software Inventory | Model | Use Software Inventory to establish a baseline that makes this technique's deviations easier to spot. |

## Investigation Steps
1. Isolate the device immediately — do not wait for confirmation
2. Alert the entire SOC team — this is an all-hands event
3. Notify the SOC Manager by phone — who notifies the customer CISO
4. Activate IR-RANSOMWARE-01 playbook in the SOAR platform
5. Record the time of first alert — this is the start of the incident timeline
6. 1 device: Isolated incident — still treat as active ransomware
7. 2–5 devices: Active spread — consider emergency network segmentation at the switch level
8. 5+ devices: Outbreak — activate BCP immediately, contact customer operations team

## Evidence to Collect
- Host or resource affected
- Account or identity involved
- Timestamp of the activity
- Related process, file, or network artifact
- Any preceding or follow-on alerts

## Response Actions
| Action | Risk | Automation Safe | Approval Required |
|---|---|---|---|
| Isolate ALL affected hosts | Medium | No | Yes |
| Disconnect NAS and network shares | Low | No | Yes |
| Notify: | Low | Yes | No |
| Activate BCP | Low | No | Yes |
| Do NOT reboot | Low | No | Yes |
| Do NOT pay the ransom | Low | No | Yes |
| Confirm backup integrity — are backups clean and accessible? | Low | Yes | No |
| Identify patient zero and begin root cause analysis | Low | No | Yes |

## KQL
```kql
let lookback = 1h;
let recoveryInhibitCommands = dynamic([
    "vssadmin delete shadows",
    "vssadmin.exe delete shadows",
    "wmic shadowcopy delete",
    "wbadmin delete catalog",
    "wbadmin delete systemstatebackup",
    "get-wmiobject win32_shadowcopy",
    "get-ciminstance win32_shadowcopy",
    "bcdedit /set",
    "recoveryenabled no",
    "bootstatuspolicy ignoreallfailures",
    "schtasks.exe /change /tn \"\\microsoft\\windows\\systemrestore\\sr\" /disable",
    "reg add \"hklm\\software\\policies\\microsoft\\windows nt\\systemrestore\"",
    "disablesr"
]);
DeviceProcessEvents
| where TimeGenerated >= ago(lookback)
| extend CommandLine = tolower(tostring(ProcessCommandLine))
| where CommandLine has_any (recoveryInhibitCommands)
| summarize
    FirstSeen = min(TimeGenerated),
    LastSeen = max(TimeGenerated),
    CommandCount = dcount(CommandLine),
    CommandSamples = make_set(ProcessCommandLine, 20),
    Accounts = make_set(AccountName, 10),
    ParentProcesses = make_set(InitiatingProcessFileName, 10)
    by DeviceId, DeviceName
| project TimeGenerated = LastSeen, FirstSeen, LastSeen, DeviceId, DeviceName, CommandCount, CommandSamples, Accounts, ParentProcesses
| extend timestamp = TimeGenerated,
         HostCustomEntity = DeviceName
| order by TimeGenerated desc
```
```kql
let detectionWindow = 1d;
let minEncryptedFiles = 20;
let suspiciousExtensions = dynamic([
    "lockbit", "locked", "crypt", "crypto", "enc", "encrypted", "ryuk", "conti", "hive", "clop", "blackcat", "akira"
]);
let recoveryInhibitCommands = dynamic([
    "vssadmin delete shadows",
    "vssadmin.exe delete shadows",
    "wmic shadowcopy delete",
    "wbadmin delete catalog",
    "wbadmin delete systemstatebackup",
    "get-wmiobject win32_shadowcopy",
    "get-ciminstance win32_shadowcopy",
    "bcdedit /set",
    "recoveryenabled no",
    "bootstatuspolicy ignoreallfailures",
    "schtasks.exe /change /tn \"\\microsoft\\windows\\systemrestore\\sr\" /disable",
    "reg add \"hklm\\software\\policies\\microsoft\\windows nt\\systemrestore\"",
    "disablesr",
    "wevtutil cl",
    "fsutil usn deletejournal",
    "cipher /w"
]);
let RansomPrepCommands =
    DeviceProcessEvents
    | where TimeGenerated >= ago(detectionWindow)
    | extend CommandLine = tolower(tostring(ProcessCommandLine))
    | where CommandLine has_any (recoveryInhibitCommands)
    | summarize
        RansomPrepCommandCount = dcount(CommandLine),
        RansomPrepCommandSamples = make_set(ProcessCommandLine, 20),
        PrepFirstSeen = min(TimeGenerated),
        PrepLastSeen = max(TimeGenerated),
        PrepAccounts = make_set(AccountName, 10)
        by DeviceId, DeviceName;
let MassEncryptionBehavior =
    DeviceFileEvents
    | where TimeGenerated >= ago(detectionWindow)
    | where ActionType in~ ("FileCreated", "FileRenamed", "FileModified")
    | extend FileExtension = tolower(tostring(split(FileName, ".")[-1]))
    | where FileExtension in (suspiciousExtensions)
    | summarize
        EncryptedFileCount = count(),
        SampleEncryptedFiles = make_set(FileName, 20),
        AffectedPaths = make_set(FolderPath, 20),
        EncryptionFirstSeen = min(TimeGenerated),
        EncryptionLastSeen = max(TimeGenerated),
        AccountsSeen = make_set(InitiatingProcessAccountName, 10)
        by DeviceId, DeviceName
    | where EncryptedFileCount >= minEncryptedFiles;
MassEncryptionBehavior
| join kind=fullouter RansomPrepCommands on DeviceId, DeviceName
| extend HasMassEncryption = coalesce(EncryptedFileCount, 0) >= minEncryptedFiles,
         HasRansomPrep = coalesce(RansomPrepCommandCount, 0) > 0
| extend ConfidenceScore = iif(HasMassEncryption, iif(EncryptedFileCount >= 50, 2, 1), 0) + iif(HasRansomPrep, 2, 0)
| where ConfidenceScore >= 2
| project
    TimeGenerated = coalesce(EncryptionLastSeen, PrepLastSeen),
    DeviceId,
    DeviceName,
    HasMassEncryption,
    EncryptedFileCount,
    SampleEncryptedFiles,
    AffectedPaths,
    AccountsSeen,
    HasRansomPrep,
    RansomPrepCommandCount,
    RansomPrepCommandSamples,
    PrepAccounts,
    EncryptionFirstSeen,
    EncryptionLastSeen,
    PrepFirstSeen,
    PrepLastSeen,
    ConfidenceScore
| extend timestamp = TimeGenerated,
         HostCustomEntity = DeviceName
| order by ConfidenceScore desc, TimeGenerated desc
```
```kql
GambitPoliciesIssues
| where State == "Active" and Severity == "High"
```

## Escalation Criteria
- Inhibit System Recovery activity observed on a privileged account or critical system.
- Activity follows or precedes other suspicious behaviour in the same investigation.
- Automated triage cannot confidently rule out malicious intent.

## False Positive Considerations
- Authorised backup software cleaning up old shadow copies — Backup agent process is signed, runs on a predictable schedule
- IT admin manual disk cleanup including shadows — Admin ran the command manually, confirmed via Live Response session

Generated by SOC Response Atlas by Basyrix.
