Every computer on the network that's running Windows NT 4.0 or later—with the exception of Active Directory (AD) domain controllers (DCs)—has a local SAM database that contains local user accounts and passwords. I wrote a script, Set-LocalPass.js, to help maintain effective security by making sure that the passwords for these local accounts, particularly accounts that are a member of the Administrators group, are changed regularly.
SetLocalPass.js lets you change a specific account's password on one or more computers to a specified password or a random password. You can specify a single computer name or a text file that contains a list of computer names. The script's command-line syntax is as follows:
\[cscript\] SetLocalPass.js \[@\]
/pwd: | /random: \[/hidepwd\]
SetLocalPass.js requires the CScript host, so the cscript keyword at the beginning of the command line is required only if CScript isn't your default script host.
By default, SetLocalPass.js displays the new password as a part of its output.If you want to suppress this display,add the /hidepwd option to the command line when you run the script. Figure 1 shows some sample output for the script when the / hidepwd option isn't included.
Because SetLocalPass.js returns all information to the command window, it requires the CScript host. You can use output redirection to write the output to a text file for later review. For example, you can schedule a task to run the script and review its output to ensure that the password changes were successful. For example, you could use the following command line in a scheduled task:
cmd /c cscript C:\Scripts\SetLocalPass.js @C:\Scripts\Computers.txt Administrator /random:14 > C:\Scripts\SetLocalPass.log
This command attempts to set the password for the Administrator account for each computer in the file C:\Scripts\Computers.txt to a random 14-character password. The script's output will be saved in the file C:\Scripts\ SetLocalPass.log. In high-security environments, you can use the /hidepwd option to prevent the new password from appearing in the output. (Note that the command line starts with cmd /c because output redirection via the > character is a cmd.exe feature.)
Of course, you must execute SetLocalPass.js under the context of a user account that's a member of the local Administrators group on every remote computer that you want to manage.
You can download SetLocalPass.js from the Windows Scripting Solutions Web site. Go to http://www.windowsitpro.com/windowsscripting, enter 49356 in the InstantDoc ID text box, then click the 49356.zip hotlink. The script first declares two global variables: SCRIPT_NAME, which is used by the usage function, and CHARSET, which is the list of characters that the randompwd function uses when constructing a random password. I'll discuss how the randompwd function works in a moment. The script then calls the main function.
The main function. The main function, which Listing 1 shows, first declares its local variables, then proceeds to parse the script's commandline options. It uses the args variable to simplify access to the WScript. Arguments object. If there are fewer than two unnamed arguments (i.e., arguments that don't begin with the / character), if there are no named arguments (i.e., arguments that begin with /), or if the /? argument is present, the main function calls the usage function (as callout A in Listing 1 shows), which displays a short usage message and ends the script.
Next, the main function calls the scripthost function to determine the name of the script host executable that's running the script. If the script isn't being executed by cscript.exe, the main function displays an error message and ends the script with a nonzero exit code.
The main function then uses the charAt method to examine the first character of the first unnamed command-line argument. If the argument's first character is @, the function checks whether the first unnamed argument is only one character long (i.e., the argument consists of only the @ character). If this is the case, the function displays an error message and ends the script with a non-zero exit code. Otherwise, the main function calls the filetoarray function to read the contents of the file into the computers variable, as shown in callout B. I'll discuss how the filetoarray function works shortly.
If the first unnamed argument on the command line doesn't start with the @ character, the main function uses the argument to create an array with a single member, as shown in callout C. In this way, the main function can simply iterate the array of computer names, whether the array contains one member or multiple members.
The main function next verifies that the computers array has at least one member. If the array's length property is zero (i.e., it contains no members), the function displays an error message and ends the script with a non-zero exit code.
The main function then reads the account name from the command line. The account name is the second unnamed command-line argument. At this point, the computers variable contains an array of computer names, and the account variable contains the account name.
The main function's next task is to determine whether the /pwd command-line option is present, as callout D shows. If the /pwd option exists but doesn't contain an argument, the main function will display an error message and end the script with a non-zero exit code.
If the /pwd option isn't present, the main function checks whether the /random option is present instead. The main function uses the parseInt function to convert the /random option's argument to a whole number. If the result of this conversion is less than 1 or isn't a number (as determined by the isNaN function), the main function displays an error message and ends the script with a nonzero exit code. If the /random option's argument is valid, the main function calls the randompwd function to generate a random password containing the number of characters specified as the argument to the /random option.
At this point, the main function should contain a value in the pwd variable. If the variable still hasn't been assigned a value (i.e., it still contains null), then the /pwd or /random argument wasn't present on the command line, and the main function displays an error message and ends the script with a non-zero exit code.
Next, the main function displays the user account name followed by a return. After this, it determines whether the /hidepwd option is present on the command line; if the / hidepwd option isn't present, the main function displays the new password, followed by a return.
The main function's final task is to iterate the array of computer names and attempt to set the password for the named account for each computer in the array. The for loop that callout E shows uses the GetObject function to retrieve a reference to the user account on the named computer. GetObject uses the ADSI WinNT provider and calls the SetPassword method to set the account's password. The try... catch block ensures that runtime errors will be trapped and dealt with gracefully rather than ending the script. When the SetPassword method succeeds, the main function displays a success message; if it fails, the script displays an error message containing the hexadecimal-representation of the error number.
To decipher an error number, use the last four hex digits in the error message, convert them to decimal, and use the result with the Net Helpmsg command. For example, for the error 0x80070035, the hex number 35 is the decimal number 53, and the command
Net Helpmsg 53
provides the description The network path was not found.
The filetoarray function. SetLocal-Pass.js uses the filetoarray function to read each nonblank line of a text file into an array. The function first creates an instance of the Scripting.FileSystemObject object. When the text file exists, the filetoarray function uses FileSystemObject's OpenTextFile method to return a TextStream object that contains the contents of the file. The function then uses the ReadLine method of the TextStream object to read each line in the text file. If a line isn't empty, the function adds the line as the next item in the array.
The randompwd function. The randompwd function uses the Math object's random method to choose a random character from the CHARSET variable that's defined at the beginning of SetLocalPass.js. The random method returns a pseudorandom number greater than or equal to 0 but less than 1. The script multiplies this number by the number of characters in the CHARSET variable and uses the Math object's floor method to round it down to a whole number. The net effect is a whole number between 0 and the number of characters in the CHARSET variable, minus 1. The for loop repeats this process for the requested number of random characters and returns the result. To increase the complexity of passwords returned by the randompwd function, add the desired characters to the CHARSET variable at the beginning of the script.
Controlling Local Passwords
Local accounts need not be a potential security hole on your network, no matter what its size. SetLocalPass.js provides a simple way to make sure that privileged local account passwords are changed uniformly.