Windows 10

Running a Script in Safe Mode Remotely



Microsoft Windows Logo Curious Blue Background

INTRO

Recently we had some antivirus agents on some endpoints go wonky and needing to be reinstalled. In this particular case the product was SentinelOne. At a previous place of employment, we used ESET. Both of these products have their own removal tools. In the case of SentinelOne you will need to obtain SentinelCleaner from a SentinelOne provider as it’s not publicly available.

The challenge with these tools is that they need to be run in Safe Mode. From a remote administration perspective, this is not ideal. When Windows boots into Safe Mode many services are not started and can’t be started, many device drivers aren’t loaded, and even specific things like programs running from the Startup folder are ignored.

Due to these variables, our RMM tools don’t run and even if they did, it’s possible certain services and network drivers aren’t running meaning we can’t remote to the device anyway.

So, we need a way to use all the capabilities in Windows available to us to automate the following in one-shot while disconnected from the endpoint.

  • Create a local Administrative user account called Support.
    • Add Support to the local Administrators group.
    • Set the password for Support.
  • Set Windows to boot into Safe Mode with Minimal.
  • Suspend BitLocker Drive Encryption
  • Configure RunOnce registry key pointing to the removal script.
  • Configure temporary Auto-Logon for the Support account.
  • Restart the endpoint.
  • Auto-login with the Support account.
  • Run SentinelCleaner, or other AV removal tools, and commands.
  • Remove the Safe Mode flag so Windows boots normally on the next boot.
  • Disable the Support account.
  • Restart the endpoint.

I spent some hours working on this and I feel I got it ironed out. In this case I will be using Datto RMM to deploy to endpoints.

STEP 1: CREATE PRE-DEPLOYMENT SCRIPT

Here is my batch script component in Datto RMM:

SentinelCleaner [WIN] – Safe Mode with Networking – Silent Run – v22.1.2.217 – x64

BAT (Batchfile)
bcdedit /set {current} safeboot minimal

net user Support /add
net localgroup Administrators Support /add
net user Support Password01!

md "C:\TEMP"
copy "SentinelCleaner.cmd" "C:\TEMP" /y
copy "SentinelCleaner_22_1GA_64.exe" "C:\TEMP" /y
reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce" /v "*SentinelCleaner" /t REG_SZ /d "C:\TEMP\SentinelCleaner.cmd" /f

reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v "AutoAdminLogon" /t REG_SZ /d 1 /f
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v "DefaultUsername" /t REG_SZ /d Support /f
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v "DefaultPassword" /t REG_SZ /d Password01! /f

manage-bde -protectors -disable C: -RebootCount 3

shutdown /r /f /t 10

I have two files attached to the component:

  • SentinelCleaner_22_1GA_64.exe
  • SentinelCleaner.cmd

CONTEXT:

Datto RMM will run the batch code from the same directory where the Datto agent downloads the attached files on the endpoint. The actual path contains a unique identifier.

When a job runs a script that is running against an endpoint, the attempt is shown in the Security event log. The event log shows Datto copies the script down to the endpoint and runs it from the following location (example):

  • C:\ProgramData\CentraStage\Packages\d0b49e6e-9eb6-48bf-8e28-99271b329f93#\%script%

So it’s not possible to manually point to that, but to solve this with batch we can use the following to change the current directory in the script to the location of the batch file where any programs you’re pulling down can be referenced in the script.

BAT (Batchfile)
@cd /d "%~dp0"

However, for PowerShell you can use the following variable and code to do so when using directory paths:

PowerShell
$ExecutingScriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent

If you would like to attach a PowerShell script to the component and run it from batch you can do so with the following example code:

BAT (Batchfile)
PowerShell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File "./example.ps1"

RUNONCE REGISTRY KEYS SECRET

What really allows us to automate anything in Safe Mode is the following secret sauce regarding RunOnce registry keys:

By default, these keys are ignored when the computer is started in Safe Mode. The value name of RunOnce keys can be prefixed with an asterisk (*) to force the program to run even in Safe mode.

Normally, RunOnce keys are ignored in Safe Mode.  Using an * at the very beginning of the key string name, the key will NOT be ignored when the local Support (or other) user account logs in automatically or manually. Once run, the key is automatically deleted so it won’t run again. You can read more about Run and RunOnce Registry Keys, here.

Before I learned about this secret, I tried the following alternatives which did not work for me:

  • Placing a script in:
    • C:\Users\%USERNAME%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
      • This path is ignored in Safe Mode.
  • Creating a Service that runs a script:
BAT (Batchfile)
sc create SentinelCleaner binpath= "C:\Windows\System32\cmd.exe /C C:\TEMP\SentinelCleaner.cmd"
  • I tried using Microsoft’s built-in Sc commands and a third-party tool called NSSM. The Service gets created but errors out and does not run the script. If I use NSSM the script runs, but once in Safe Mode the service is blocked from running.

STEP 2: RUNONCE FINISHING SCRIPT

And lastly here is the code contained within SentinelCleaner.cmd:

BAT (Batchfile)
timeout 120

bcdedit /deletevalue {current} safeboot

cd "C:\TEMP"
SentinelCleaner_22_1GA_64.exe -d 0

timeout 2

echo %errorlevel% > "C:\TEMP\sentinelcleaner_results.txt"

reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v "AutoAdminLogon" /f
reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v "DefaultUsername" /f
reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v "DefaultPassword" /f

net user Support /active:no

shutdown /r /f /t 10

del "C:\TEMP\SentinelCleaner_22_1GA_64.exe"
del "C:\TEMP\SentinelCleaner.cmd"

It’s important to note that if Support has never been logged in before Windows could take quite a while before the desktop is visible so I add a timeout of 120 seconds before SentinelCleaner runs as I want to see the process running on my deployment test clients. You may need to increase the timeout if the computer is very slow, and you want to see the process as logon times could exceed 120 seconds. 10-year-old processors with HDDs, I am looking at you!

Another thing is that Support’s password will be in plaintext in the registry, so I delete the registry keys we added.  I have a Datto job scheduled daily to cycle the Support password. I also have a post on that here.

KNOWN ISSUES

Although everything works, I did run into some issues for certain endpoints. For example, on an old computer with Windows 10 installed with Legacy MBR, the bcdedit command to enable Safe Mode with Networking fails with the following error:

An error has occurred setting the element data.

There is also another error that can occur for installs with UEFI GPT with Secure Boot enabled which says:

An error has occurred setting the element data. The value is protected by Secure Boot policy and cannot be modified or deleted.

I have not been able to figure this one out quite yet. I tested on a Generation 2 VM with UEFI and Secure Boot and no issues. So, it seems like setting Safe Mode with Networking via command-line is hit or miss right now. I did find that a workaround is to use msconfig.exe to set Safe Mode with Networking but that can’t be automated as far as I know without third-party options. Obviously, you could just remote on to the device and enable it, then run the Datto component but that’s not ideal.

I did find that if I remote on to the device manually and use msconfig.exe to enable Safe Mode with Networking then run the Datto component, the finishing script does remove the Safe Mode flag so when the device reboots it boots normally.

UPDATE: 3/14/2024

Using MSCONFIG on some Windows 11 endpoints was not working. I discovered using Minimal instead of Network had a greater percentage of success in Step 1 but some endpoints had to manually be done. I discovered I had to change “Startup selection” under the General tab to “Selective startup” then I could set minimal at least but still not Network. Some research online mentioned disabling “Fast startup” in Windows, so here a script to do that which might help.

PowerShell
# Disable Fast Startup
$regPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power"
$regName = "HiberbootEnabled"

# Check if the registry key exists before modifying it
if (Test-Path $regPath) {
    Set-ItemProperty -Path $regPath -Name $regName -Value 0
    Write-Host "Fast Startup is disabled. You may need to restart your computer for changes to take effect."
} else {
    Write-Host "Error: Unable to find the registry path. Make sure you have the necessary permissions and try running the script as administrator."
}

AV REMOVAL TOOL NOTES

SentinelCleaner:

As of writing the latest version of SentinelCleaner is 22.1.2.217 (22_1GA), it’s not publicly available. You must get it from your provider. Below I have posted the Readme document for the cleaners.

Run the Cleaning utility:

Option 1: When ‘Self-Protection’ is ON and Windows is in Normal mode:
Open CMD ‘as Administrator’
Run:
cd C:\SentinelCleaner_.exe -t “Site token for diagnostics” -k “PASS PHRASE FROM STEP TWO”

Option 2: 2When ‘Self-Protection’ is OFF or the machine is in Safe mode:
Open CMD ‘as Administrator’
Run:
cd C:\SentinelCleaner_.exe -t “Site token for diagnostics”

Follow the guidelines on the CMD window

To make sure that the cleaner was executed successfully, please run the following:

From CMD: echo %errorlevel%
From Powershell: $LastExitCode

If the error code is different than 0 (zero – successful execution), please make sure to send it over to Support
Save the CMD history (as it might be needed for further analysis)

Reboot the machine

Note: If no Diagnostics are needed please use:
-d 0

Sets if diagnostic information is collected. The default is 1 (enabled) and does not have to be added to the command.
To run the Cleaner tool without collecting diagnostic information, set d to 0.

ESET:

  • KB article on using ESET Uninstaller, here.
  • You can download the AV removal tool, for many third-party AVs out there, here.

Webroot:

Webroot has no removal tool so here is some batch code I created for running in Safe Mode to kill it. I recommend deleting the registry keys stored for it as well, but I don’t have that part yet.

BAT (Batchfile)
cd "C:\Program Files (x86)\Webroot"
WRSA.exe -uninstall

timeout 30

cd "C:\Program Files\Webroot"
WRSA.exe -uninstall

timeout 30

net stop WRCoreService
net stop WRSkyClient
sc delete WRCoreService
sc delete WRSkyClient

timeout 10

rd /s /q "%PROGRAMDATA%\WRData"
rd /s /q "%PROGRAMDATA%\WRCore"
rd /s /q "C:\Program Files\Webroot"
rd /s /q "C:\Program Files (x86)\Webroot"

CONCLUSION

Hopefully this helps you out. If this has helped you let me know. If you have any improvements or suggestions, be sure to let me know in the comments or send me a message via the Contact page. Happy troubleshooting!

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *