Public/Invoke-TakeControlChaos.ps1

function Invoke-TakeControlChaos {
    <#
    .SYNOPSIS
        A "Chaos Monkey" script to break the Take Control agent for testing repair logic.
     
    .DESCRIPTION
        Deliberately induces failure states in the N-able Take Control installation to validate
        recovery scripts.
     
        AVAILABLE SCENARIOS (-Scenario):
        -------------------------------------------------------------------------
        * ZombieConfig : Removes MSPID from BASupSrvc.ini (Simulates Orphaned Agent).
        * Quarantine : Deletes binary but leaves service (Simulates AV Quarantine).
                          [!] High Risk - Requires -AllowDestruction.
        * BreakServices : Stops and Disables the Take Control services.
        * LockFile : Spawns a hidden process locking the install folder.
                          [!] High Risk - Requires -AllowDestruction.
        * RestoreBinary : Restores the binary from .bak (Undo Quarantine).
        -------------------------------------------------------------------------
         
        WARNING: THIS IS DESTRUCTIVE.
        High-Risk scenarios mimic malware behavior and may trigger EDRs.
     
    .PARAMETER Scenario
        Selects the failure state to induce. See DESCRIPTION for list.
     
    .PARAMETER AllowDestruction
        Required ONLY for 'LockFile' and 'Quarantine' scenarios.
     
    .EXAMPLE
        Invoke-TakeControlChaos -Scenario ZombieConfig
        (Prompts for confirmation)
     
    .EXAMPLE
        Invoke-TakeControlChaos -Scenario LockFile -AllowDestruction
        (Prompts for confirmation AND checks for switch)
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateSet('ZombieConfig', 'Quarantine', 'BreakServices', 'LockFile', 'RestoreBinary')]
        [string]$Scenario,
    
        [Parameter(Mandatory = $false)]
        [switch]$AllowDestruction
    )
    
    # Configuration mapping (Matches Repair Script)
    $Config = Get-TakeControlConfig
    
    # --- Safety Checks ---
    if (-not (Test-TakeControlIsAdmin)) {
        Write-Error "Must run as Administrator."
        return
    }
    
    Write-TakeControlLog -Message "Starting Chaos Monkey - Scenario: $Scenario" -LogPath $Config.LogPath
    
    # --- Execution ---
    switch ($Scenario) {
        'ZombieConfig' {
            if ($PSCmdlet.ShouldProcess("BASupSrvc.ini", "Corrupt MSPID (Simulate Zombie)")) {
                if (Test-Path $Config.IniPath) {
                    Write-TakeControlLog -Message "Reading INI file..." -LogPath $Config.LogPath
                    $content = Get-Content $Config.IniPath -Raw
                    
                    if ($content -match "MSPID=") {
                        Write-TakeControlLog -Message "Removing MSPID to simulate Zombie state..." -LogPath $Config.LogPath
                        $newContent = $content -replace "MSPID=.*", "MSPID="
                        Set-Content -Path $Config.IniPath -Value $newContent -Force
                        Write-TakeControlLog -Message "Corruption applied. Agent is now orphaned." -Level Warning -LogPath $Config.LogPath
                    }
                    else {
                        Write-TakeControlLog -Message "MSPID not found or already empty." -Level Warning -LogPath $Config.LogPath
                    }
                }
                else {
                    Write-Error "INI file not found at $($Config.IniPath)"
                }
            }
        }
    
        'Quarantine' {
            # High Risk Check
            if (-not $AllowDestruction) {
                Write-Warning "Scenario 'Quarantine' manipulates trusted binaries and may trigger EDR heuristics."
                Write-Error "You must specify -AllowDestruction to proceed with this scenario."
                return
            }
    
            if ($PSCmdlet.ShouldProcess("BASupSrvc.exe", "Delete Binary (Simulate Quarantine)")) {
                $binPath = Join-Path $Config.AgentInstallPath $Config.Binary
                
                $Config.Services | ForEach-Object { Stop-Service $_ -Force -ErrorAction SilentlyContinue }
                
                if (Test-Path $binPath) {
                    Write-TakeControlLog -Message "Renaming binary to .bak to simulate AV quarantine..." -LogPath $Config.LogPath
                    Rename-Item -Path $binPath -NewName "$($Config.Binary).bak" -Force
                    Write-TakeControlLog -Message "Binary removed. Services are still registered." -Level Warning -LogPath $Config.LogPath
                }
                else {
                    Write-TakeControlLog -Message "Binary already missing." -LogPath $Config.LogPath
                }
            }
        }
    
        'BreakServices' {
            if ($PSCmdlet.ShouldProcess("Services", "Disable and Stop")) {
                foreach ($svc in $Config.Services) {
                    if (Get-Service $svc -ErrorAction SilentlyContinue) {
                        Write-TakeControlLog -Message "Breaking service: $svc" -LogPath $Config.LogPath
                        Stop-Service $svc -Force -ErrorAction SilentlyContinue
                        Set-Service $svc -StartupType Disabled
                    }
                }
                Write-TakeControlLog -Message "Services disabled and stopped." -Level Warning -LogPath $Config.LogPath
            }
        }
    
        'LockFile' {
            # High Risk Check
            if (-not $AllowDestruction) {
                Write-Warning "Scenario 'LockFile' uses encoded commands and background processes. This WILL trigger EDR heuristics."
                Write-Error "You must specify -AllowDestruction to proceed with this scenario."
                return
            }
    
            if ($PSCmdlet.ShouldProcess("File Lock", "Start Background PowerShell Process (HEURISTIC TRIGGER)")) {
                Write-Warning "Spawning hidden PowerShell process with -EncodedCommand."
                Write-Warning "This is a known IoC (Indicator of Compromise) for EDRs."
                
                $lockScript = @"
                `$path = '$($Config.AgentInstallPath)'
                `$file = Join-Path `$path 'locktest.dat'
                if (-not (Test-Path `$path)) { New-Item -ItemType Directory -Path `$path -Force }
                `$fs = [System.IO.File]::Open(`$file, 'OpenOrCreate', 'ReadWrite', 'None')
                Write-Host 'Locking ' `$file
                while (`$true) { Start-Sleep -Seconds 1 }
"@

                $encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($lockScript))
                Start-Process PowerShell.exe -ArgumentList "-NoProfile -EncodedCommand $encoded" -WindowStyle Minimized
                
                Write-TakeControlLog -Message "Launched background process to lock folder: $($Config.AgentInstallPath)" -Level Warning -LogPath $Config.LogPath
                Write-TakeControlLog -Message "Run repair script to see if it kills the locker." -LogPath $Config.LogPath
            }
        }
    
        'RestoreBinary' {
            if ($PSCmdlet.ShouldProcess("BASupSrvc.exe", "Restore from .bak")) {
                $bak = Join-Path $Config.AgentInstallPath "$($Config.Binary).bak"
                if (Test-Path $bak) {
                    Rename-Item $bak -NewName $Config.Binary -Force
                    Write-TakeControlLog -Message "Binary restored." -Level Success -LogPath $Config.LogPath
                }
                else {
                    Write-TakeControlLog -Message "Backup file not found." -LogPath $Config.LogPath
                }
            }
        }
    }
}