Last active
August 10, 2023 09:50
-
-
Save Bill-Stewart/9379a8df293de418ed96ee6ea82c4459 to your computer and use it in GitHub Desktop.
FixUnquotedServicePaths.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// FixUnquotedServicePaths.js | |
// WSH script written by Bill Stewart (bstewart AT iname.com) | |
// | |
// Corrects each service on the local machine that has an "unquoted service | |
// path" and logs its actions to the application event log. | |
// | |
// The idea here is to run this as a computer startup script, so that unquoted | |
// service paths will get identified and corrected automatically at every | |
// reboot. | |
// | |
// (In 99.9% of real-world cases, this is not an actual security vulnerability, | |
// because by default this "vulnerability" can only be exploited by an account | |
// that's already a member of the local Administrators group. However, this | |
// script will remediate this condition to prevent vulnerability scanning tools | |
// from erroneously identifying this condition as a vulnerability and causing | |
// unnecessary wasted time to "fix" it.) | |
// Version history: | |
// | |
// 1.0.0 (2022-04-19) | |
// * Initial version. | |
// | |
// 1.0.1 (2022-04-28) | |
// * Minor adjustment to regex to account for leading spaces. | |
// | |
// 1.0.2 (2022-05-04) | |
// * Minor bug fix (log event if WMI connection failed). | |
// | |
// 1.0.3 (2022-11-01) | |
// * Minor regex tweak to account for potential mismatched quotes in command. | |
// Script logs to the Application event log (source WSH) | |
var EventLogText = "Script: " + WScript.ScriptName + "\r\n" + | |
"This script fixes any services on this computer with an 'unquoted service path'."; | |
// Global SWbemServices object (after ConnectWMI function succeeds) | |
var SWbemServices = null; | |
// Test if using /test parameter | |
var TestMode = false; | |
// Returns n as a hex string. | |
function hex(n) { | |
return n < 0 ? (n + Math.pow(2,32)).toString(0x10).toUpperCase() : | |
n.toString(0x10).toUpperCase(); | |
} | |
// Gets the computer's SWbemServices object. | |
function ConnectWMI() { | |
var result = 0; | |
try { | |
var sWBemLocator = new ActiveXObject("WbemScripting.SWbemLocator"); | |
SWbemServices = sWBemLocator.ConnectServer(".","root\\CIMV2"); | |
} | |
catch(err) { | |
result = err.number; | |
EventLogText += "\r\n\r\nError 0x" + hex(result) + " connecting to WMI"; | |
} | |
return result; | |
} | |
// Returns a properly quoted service command string. | |
function quoteServiceCommandString(commandString) { | |
var result = commandString; | |
var re = /^\s*(?:"?(.+\.exe)"?)(\s+(?:.*))?/i; | |
var matches = re.exec(commandString); | |
if ( matches != null ) { | |
if ( (matches[1].charAt(0) != '"') && (matches[1].indexOf(" ") != -1) ) { | |
result = '"' + matches[1] + '"'; | |
if ( matches[2] != "" ) { | |
result += matches[2]; | |
} | |
} | |
} | |
return result; | |
} | |
// Updates a service's command (i.e., the ImagePath value in the registry). | |
function updateServiceCommand(service,serviceCommand) { | |
var oldCommand = service.PathName; | |
var result = 0; | |
if ( ! TestMode ) { | |
result = service.Change(null, // DisplayName | |
serviceCommand, // PathName | |
null, // ServiceType | |
null, // ErrorControl | |
null, // StartMode | |
null, // DesktopInteract | |
null, // StartName | |
null, // StartPassword | |
null, // LoadOrderGroup | |
null, // LoadOrderGroupDependencies | |
null); // ServiceDependencies | |
} | |
EventLogText += "\r\n\r\nService: " + service.Name + "\r\n"; | |
switch ( result ) { | |
case 0: { | |
EventLogText += "Old ImagePath: " + oldCommand + "\r\n" + | |
"Updated ImagePath: " + serviceCommand; | |
break; | |
} | |
case 1: { | |
EventLogText += "ERROR: Not Supported"; | |
break; | |
} | |
case 2: { | |
EventLogText += "ERROR: Access Denied"; | |
break; | |
} | |
case 3: { | |
EventLogText += "ERROR: Dependent Services Running"; | |
break; | |
} | |
case 4: { | |
EventLogText += "ERROR: Invalid Service Control"; | |
break; | |
} | |
case 5: { | |
EventLogText += "ERROR: Service Cannot Accept Control"; | |
break; | |
} | |
case 6: { | |
EventLogText += "ERROR: Service Not Active"; | |
break; | |
} | |
case 7: { | |
EventLogText += "ERROR: Service Request Timeout"; | |
break; | |
} | |
case 8: { | |
EventLogText += "ERROR: Unknown Failure"; | |
break; | |
} | |
case 9: { | |
EventLogText += "ERROR: Path Not Found"; | |
break; | |
} | |
case 10: { | |
EventLogText += "ERROR: Service Already Running"; | |
break; | |
} | |
case 11: { | |
EventLogText += "ERROR: Service Database Locked"; | |
break; | |
} | |
case 12: { | |
EventLogText += "ERROR: Service Dependency Deleted"; | |
break; | |
} | |
case 13: { | |
EventLogText += "ERROR: Service Dependency Failure"; | |
break; | |
} | |
case 14: { | |
EventLogText += "ERROR: Service Disabled"; | |
break; | |
} | |
case 15: { | |
EventLogText += "ERROR: Service Logon Failed"; | |
break; | |
} | |
case 16: { | |
EventLogText += "ERROR: Service Marked For Deletion"; | |
break; | |
} | |
case 17: { | |
EventLogText += "ERROR: Service No Thread"; | |
break; | |
} | |
case 18: { | |
EventLogText += "ERROR: Status Circular Dependency"; | |
break; | |
} | |
case 19: { | |
EventLogText += "ERROR: Status Duplicate Name"; | |
break; | |
} | |
case 20: { | |
EventLogText += "ERROR: Status Invalid Name"; | |
break; | |
} | |
case 21: { | |
EventLogText += "ERROR: Status Invalid Parameter"; | |
break; | |
} | |
case 22: { | |
EventLogText += "ERROR: Status Invalid Service Account"; | |
break; | |
} | |
case 23: { | |
EventLogText += "ERROR: Status Service Exists"; | |
break; | |
} | |
case 24: { | |
EventLogText += "ERROR: Service Already Paused"; | |
break; | |
} | |
case 25: { | |
EventLogText += "ERROR: Other"; | |
break; | |
} | |
} | |
return result; | |
} | |
function main() { | |
TestMode = WScript.Arguments.Named.Exists("test"); | |
var result = ConnectWMI(); | |
var logEvent = result != 0; | |
if ( result == 0 ) { | |
var services = new Enumerator(SWbemServices.ExecQuery("SELECT Name,PathName FROM Win32_Service")); | |
for ( ; ! services.atEnd(); services.moveNext() ) { | |
var service = services.item(); | |
var properlyQuotedCommandString = quoteServiceCommandString(service.PathName); | |
if ( service.PathName != properlyQuotedCommandString ) { | |
logEvent = true; | |
result = updateServiceCommand(service,properlyQuotedCommandString); | |
if ( result != 0 ) { | |
break; | |
} | |
} | |
} | |
} | |
if ( logEvent ) { | |
var wshShell = new ActiveXObject("WScript.Shell"); | |
// LogEvent method 1st parameter is event type (0 = success, 1 = error) | |
wshShell.LogEvent((result == 0 ? 0 : 1),EventLogText); | |
} | |
return result; | |
} | |
WScript.Quit(main()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment