Sunday, May 24, 2009

Unique passwords on local user accounts using VBS and Group Policy

The purpose of the script (SetLocalPassword.v2.txt - just rename to "SetLocalPassword.vbs") is, to ensure assignment of unique and complex password to a specific local user account (typically the local administrator account) on a Windows client in an Active Directory (AD) domain environment.

The script can be used, if you (for one reason or another) want a specified local user account (e.g. administrator) to be active, but you still want to ensure, that the password used is unique for each computer, that the password is changed regularly (a given period of time) and that you are able to logon using the password at any time. Usually I would recommend customers to just deactivate the local administrator account, or set the password using Group Policy Preferences (preferably different passwords on different security areas), but if these solutions aren’t usable in the environment, “ChangeLocalPassword.vbs” could be the right solution.

The intention is to execute the script as a "Startup Script” within a Group Policy Object (GPO), which is aimed at the relevant computer accounts in AD (as you probably know GPO’s can be filtered by AD security groups, WMI filters, Organizational Units (OU), domain and/or site). This way we ensure that the script is executed in ”SYSTEM" context, in which we can pretty much do anything on the local computer(s). Furthermore, SYSTEM can access network resources on behalf of the computer, as long as the resource in question (a file share in this case) allows “Domain Computers”, the specific AD computer account og “Authenticated Users” to gain access.

It is crucial that the group ”Authenticated Users” is NOT given access to the network share – in that case all users within the domain will be able to read which passwords are used on all computers hit by the GPO. Share permissions (could be a hidden share$) can of course be set to Everyone Full Control, but NTFS must be set to allow only members of the group "Domain Computers" to read and write - domain administrators, and other relevant groups (e.g. helpdesk, supporters, backup account etc.) should also have read access. If you have a Distributed File System (DFS) up and running it could be used as the network share.

This illustrates the scripts cycle:

1. The SYSTEM account is used by the computer during the boot process
2. DNS and AD is contacted, and Group Policies are processed (machine policies)
3. The GPO with the Startup Script is loaded
4. The VBS script is executed (also in SYSTEM context)
5. All activity is logged to a local log file (strLocalLog)
6. Some preliminary checks are performed, this includes last modification of strLocalStamp and network access (strNetShare)
7. A password (strNewPassword) is generated from 4 different criteras (intPasswordLength, intWantNumber, intWantLcase and intWantUcase)
8. The username and password (clear text) is logged in a central log file (strnetFile)
9. The chosen local user account (strLocalUser) is assigned the newly generated password (only if 8 was completed without any errors)
10. A local timestamp file is created or modified if 9 was successfully completed

Some important notes...

First and foremost one must ensure, that the script file the GPO is pointing to cannot be modified by others than the relevant administrators. If a user gets write access to that file, he or she can do anything (locally) on all machines executing the code. This is of course true for any GPO Startup Script used.

Another important thing to note is, that if your users have local admin rights (I hope not), they will be able to “hack” the solution in a couple of ways. First of all they will of course be able to reset passwords for all local user accounts, but if they are a bit clever, they will also be able to take over the SYSTEM account (hint: AT command or PSEXEC) and access the network share we are using – and thus read or modify the log files with all the clear text passwords. But who in the world would allow users to be local administrators in the fist place, right?

A Startup Script will time out if the script takes too long to execute, but we should not have such a problem with this script (normally executed in less than a second). Startup Scripts react differently depending on whether the “Always wait for the network at computer startup and logo” setting is set or not - the script should work in both cases though.

Let’s take a look at the customizable variables.

intDays = 60
- default: 60 days between password change

strNetShare = "\\SERVER\SHARE\"
- define as a share with the correct NTFS permissions set
- is could be a hidden share, perhaps on a DFS
- remember a trailing backslash (\) or the script will fail!

strLocalLog = "C:\admpwd.log"
- placement of the local log file of all activity (except for the password itself)

strLocalStamp = "C:\admpwd.stp"
- placement of the file used as a timestamp

strLocalUser = "test-user"
- name the user account to control (e.g. "administrator")

intPasswordLength = 12
- the number of characters the password should have (exactly)
- must be at least the same as the domains minimum password length

intWantNumbers = 1
- set whether or not the password should contain numbers (complexity requirement)

intWantLcase = 1
- set whether or not the password should contain lowercase letters (complexity requirement)

intWantUcase = 1
- set whether or not the password should contain UPPERCASE letters (complexity requirement)

An example of the strLocalLog (default "c:\admpwd.log") local log file:

2009-05-22 13:20:26 [STARTED]
2009-05-22 13:20:26 [VARIABLES - A]
2009-05-22 13:20:26 - intDays : 1
2009-05-22 13:20:26 - strNetShare : '\\SERVER\SHARE\'
2009-05-22 13:20:26 - strLocalLog : 'C:\admpwd.log'
2009-05-22 13:20:26 - strLocalStamp : 'C:\admpwd.stp'
2009-05-22 13:20:26 - strLocalUser : 'test-user'
2009-05-22 13:20:26 - strComputer : 'COMPUTER1'
2009-05-22 13:20:26 - strNetFile : '\\SERVER\SHARE\COMPUTER1.log'
2009-05-22 13:20:26 STATUS - No local stamp file, probably first run
2009-05-22 13:20:26 SUCCESS - ALIVE:\\SERVER\SHARE\
2009-05-22 13:20:26 [VARIABLES - B]
2009-05-22 13:20:26 - intPasswordLength: 12
2009-05-22 13:20:26 - intWantNumbers : 1
2009-05-22 13:20:26 - intWantLcase : 1
2009-05-22 13:20:26 - intWantUcase : 1
2009-05-22 13:20:26 SUCCESS - PWD SET for: 'test-user'
2009-05-22 13:20:26 SUCCESS - PWD written to: '\\SERVER\SHARE\COMPUTER1.log'
2009-05-22 13:20:26 SUCCESS - TIME written to: 'C:\admpwd.stp'
2009-05-22 13:20:26 [COMPLETED]

2009-05-22 13:27:45 [STARTED]
2009-05-22 13:27:45 [VARIABLES - A]
2009-05-22 13:27:45 - intDays : 1
2009-05-22 13:27:45 - strNetShare : '\\SERVER\SHARE\'
2009-05-22 13:27:45 - strLocalLog : 'C:\admpwd.log'
2009-05-22 13:27:45 - strLocalStamp : 'C:\admpwd.stp'
2009-05-22 13:27:45 - strLocalUser : 'test-user'
2009-05-22 13:27:45 - strComputer : 'COMPUTER1'
2009-05-22 13:27:45 - strNetFile : '\\SERVER\SHARE\COMPUTER1.log'
2009-05-22 13:27:45 STATUS - STAMP last modified: 22-05-2009 13:20:26
2009-05-22 13:27:45 STATUS - STAMP younger than: 1 days!
2009-05-22 13:27:45 [COMPLETED]

An example of the strNetFile (named [computername].log) network log file:

2009-05-20 13:20:26 test-user : 'W57Ja6c5Xcus'
2009-05-22 08:10:39 test-user : 'sdEc7s9Gbba8'

Final note:

The code could most definitely be more optimized (and prettier), but it works like a charm (and pretty fast too) on Windows 2000, Windows XP, Windows Vista, Windows Server 2003, Windows Server 2008 and Windows 7.

I hope it will turn out to be useful to someone out there - enjoy!



Derek said...

This is a great theory for a script, thanks. Although I'm having a bit of an issue with it. I have made my modifications to the script regarding the days (changed to 90), the path for local and the path for network. The admpwd.log file gets created, but it never creates the stamp file.

I'm running on Vista, and at first, it gave me an access denied error when trying to create the log files in the root of C. I created a subdirectory in C and got past that.

Here is the error from the admpwd.log -

2009-06-04 08:31:25 [STARTED]
2009-06-04 08:31:25 [VARIABLES - A]
2009-06-04 08:31:25 - intDays : 90
2009-06-04 08:31:25 - strNetShare : '\\server\share\'
2009-06-04 08:31:25 - strLocalLog : 'C:\admin\admpwd.log'
2009-06-04 08:31:25 - strLocalStamp : 'C:\admin\admpwd.stp'
2009-06-04 08:31:25 - strLocalUser : 'zzadmin'
2009-06-04 08:31:25 - strComputer : 'computername'
2009-06-04 08:31:25 - strNetFile : '\\server\shares\computername.log'
2009-06-04 08:31:25 STATUS - No local stamp file, probably first run
2009-06-04 08:31:25 SUCCESS - ALIVE:\\server\share\
2009-06-04 08:31:25 [VARIABLES - B]
2009-06-04 08:31:25 - intPasswordLength: 8
2009-06-04 08:31:25 - intWantNumbers : 1
2009-06-04 08:31:25 - intWantLcase : 1
2009-06-04 08:31:25 - intWantUcase : 1
2009-06-04 08:31:25 SUCCESS - PWD written to: '\\server\share\computername.log'
2009-06-04 08:32:00 FAILURE - PWD NOT SET for 'zzadmin'
2009-06-04 08:32:00 [ABORTED]

Jakob H. Heidelberg said...

Hi Derek,

I believe it's UAC making trouble - both with the C-root write issue and the PWD set issue...

Please look at this if you have Vista UAC enabled in the environment:

Please let me know whether that fixes the problem or not.


Jakob H. Heidelberg said...

BTW - from my testing, the script works perfectly on Windows 7 with UAC enabled - Vista UAC suxx :)

Best regards

jnelson35 said...

Jakob, I really like your approach. I've done something similar that scrambles the password on every reboot, but logging to a central log file file is a nice touch.

Davey said...

If you set the permissions on the file share slightly differently then you can reduce the risk of having someone run as local system and see all the machine passwords.

Set Domain Computers to have the following permissions. (Apply onto This folder only)
Traverse Folder, List folder, Read Attributes, Read Extended Attributes, Create Files, Read Permissions

Set Creator Owner full control Files Only.

Then the computer can create its own file but not read the log from any other machine.

Also if you enable ABE (Access based enumeration) on the share then each machine will only be able to see its own log files.

Helps to increase the security a bit.

Jakob H. Heidelberg said...

Great tip Davey ;-)