How to set the Local Administrator account to a Random Password

imageAs per my previous blog post Microsoft has release MS14-025 that blocks the ability to configure passwords using Group Policy Preferences. However as part of the guidance they have also published a PowerShell script that allows you to set a random password to the user local admin account. This blog post show you how you can use this script (bad word, I know) to manage the passwords of local accounts on the computers in your organisation.

TIP: Before starting remember that it is entirely practical to have an SOE with no local admin accounts enabled at all. If this ever gets you into tight water and you need to logon to the computer you can still follow my other blog post to logon to the computer (see How to enable a disabled Local Administrator account offline in Windows 7 (even when using BitLocker)

But, if you are using local admin accounts on your workstations then the following will give you an alternative to using the now disabled password feature in Group Policy Preferences. The PowerShell script that Microsoft provides generates a unique random password for each compute so it’s also a mitigation step against a Pass-the-Hash attacks. This is a nice side affect of setting a unique password as you cannot use the hash of one local admin account to access another computer.

Simply put, this PowerShell script contacts each computer over the network from a pre-defined list and then set the local account password to a random value.

Note: Because the computer’s need to be turned on for it to reset the passwords so you may have to perform this process on a regular basis to ensure that you cover all computers.

Next, it then saves this password to a file that can/should be encrypted with a “master password” of your choosing. This is of course necessary to give  added protection against anyone that “might” grab a copy of the password file as it means they would also have to know the encryption password to decrypt the password value.  Saving the password in a text file might not sound all that secure however it is a lot more secure than using Group Policy Preferences.

Recap: Group Policy Preferences saves the “cPassword” value in Active Directory System Volume in files that are readable by all users and with the same 32bit encryption password.

Warning: While this script is from Microsoft it clearly states that in no way shape or form is it actually support so the following is to be used at your own risk. (See MS14-025 for further disclaimers).

Pre-Requisites

As I said before this PowerShell script actually makes a connection to each computer you first need to enable WinRM on all the computers that you are changing the password on. To do this take a look at my previous post How to enable WinRM via Group Policy and ensure that it is applied to your computers.

Running the Password Change Script

Go to MS14-025 and take a copy the script the entire change password script into a text file on the computer you are going to be running the process from. Next you need to open a PowerShell Windows running as Administrator permission and then paste the contents of the script into the Windows. You will then need to press “Enter” twice to ensure that the entire script has run. (see below)

image

You have now created the require functions in that current PowerShell window to perform the password change process. You will need to do this each time you open a new PowerShell Window as the command are not persistent. This might be a little convoluted but doing it this way also removes the need to enable unrestricted or bypass of script signature checking. The next step is to generate an up to date list of computer names in a text file for the script to process though with the password change.

Below is my extremely complicated example file: image

Once you have you file created you now need to run the Invoke-PasswordRoll that will go though and set the password on each computer name in the list:

Invoke-PasswordRoll -ComputerName (Get-Content ComputerList.txt) -LocalAccounts Administrator -EncryptionKey "[email protected]" -PasswordLength 22 -TsvFileName "LocalAdminPasswords.tsv"

Of course you should use your own unique Encryption Key to encrypt  the password value. Tip: Don’t forget the password!!!!! image

As you can see the script will also warn you when there is other local accounts on the computer that is not affected by this script. The script will append to the “LocalAdminPasswords.tsv” file is as follow, this means that the last password value on the list for that computer name is the valid encrypted password. image

Now when you want to retrieve the password for that account you need to copy the corresponding “EncryptePassword” value and then run it through the “ConvertTo-CleartextPassword” command.

Tip: Be sure that the encryption key matched the value you used in the “Invoke-PasswordRoll“ command above.

ConvertTo-ClearTextPassword -encryptionkey "[email protected]" –EncryptedPassword (WAY TO LONG TO PUT HERE)

As highlighted below is the unique 22 character random password for the local admin account on the corresponding computer that you can now use to logon to the computer. image

Now that you have an alternative to the passwords in Group Policy Preferences be sure that the file is save in secure location and that you also periodically run the script. While this is no where near as easy as using Group Policy Preferences this is definitely are far more secure way to mange the local admin passwords on your computers. But as I mentioned above, if you can, its far better to disable all the local admin accounts entirely on your computers as this will be more secure and easier to manage.

25 Comments

  1. I have done this before, after I found, that users could read the password set via GPO in clear text in the registry or with Group Policy tools. Encrypted the password, put the encrypted key into a text file and used a PowerShell script to assign the password during computer shutdown.
    For increased security you can put the text file containing the encrypted password on a network share with regulated access (read access only necessary for Domain computers and the administrative person, who needs to change that file for a new one from time to time).
    Best greetings from Germany
    Olaf

  2. Detailed Error

    Invoke-PasswordRoll -ComputerName (Get-Content C:\ComputerList.txt) -LocalAccounts Manageitinfy -EncryptionKey “[email protected]” -PasswordLength 22 -TsvFileName “LocalAdminPasswords.tsv”
    Error creating TSV encryption key
    At line:289 char:20
    + Invoke-PasswordRoll <<<< -ComputerName (Get-Content C:\ComputerList.txt) -LocalAccounts Manageitinfy -EncryptionKey "[email protected]" -PasswordLength 22 -TsvFileName "LocalAdminPasswords.tsv"
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Invoke-PasswordRoll

  3. @Noah:
    1. Encrypt the wanted password:
    For this you can use a 2 line PowerShell script:
    $key = (1,2,3,4,5,1,2,3,4,5,11,12,13,14,15,20,30,40,50,5,4,3,2,1)
    read-host -assecurestring | convertfrom-securestring -key $key | out-file c:\script\file.txt

    This script will ask you to enter a password, encrypt it with the key from line one and put the encrypted password as secure string into file.txt

    2. Copy this file to a shared folder, which each machine can read (and each user cannot for improved security).

    3. Establish another PowerShell script for Computer Startup or Shutdown (I prefer shutdown) in Group Policies for the OU containing the computers, which basically contains:
    $key = (1,2,3,4,5,1,2,3,4,5,11,12,13,14,15,20,30,40,50,5,4,3,2,1)
    $pwd = get-content \\server\share\file.txt | convertto-securestring -key $key
    $user = “Administrator”
    $cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $user,$pwd
    $pw = $cred.GetNetworkCredential().Password
    $cmd = “net user $user $pw”
    Invoke-Expression $cmd

    Best greetings from Germany
    Olaf

    • Hahahahah. Very bad. The combination of the file and the key a hacker can find out the password. That is security..;-) hahahah

  4. I got the following error and the default Administrator Password did get reset though. However , you don’t see the encrypted password in the output .tsv file.

    PS C:\Infra\LocalAdminReset> Invoke-PasswordRoll -ComputerName (Get-Content ComputerList.txt) -LocalAccounts Administra
    or -EncryptionKey “[email protected]” -PasswordLength 22 -TsvFileName “LocalAdminPasswords.tsv”
    Connecting to server ‘ENV1-W2012VAS’ to roll specified local admin passwords
    WARNING: Server: ‘ENV1-W2012VAS’ has a local account ‘Guest’ whos password is NOT being changed by this script
    WARNING: Server: ‘ENV1-W2012VAS’ has a local account ‘VASuser’ whos password is NOT being changed by this script
    ConvertFrom-SecureString : Exception has been thrown by the target of an invocation.
    At line:178 char:99
    + $Record | Add-Member -MemberType NoteProperty -Name Encrypte …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [ConvertFrom-SecureString], TargetInvocationException
    + FullyQualifiedErrorId : System.Reflection.TargetInvocationException,Microsoft.PowerShell.Commands.ConvertFromSec
    ureStringCommand

    Could anyone give me a hint here?

    Thank you,

    Wade

  5. Hi all. I appreciate all the help from Alan and the other commenters. Here is a slightly tweaked version of MS’ script. It just changes the output order so the target computer is listed before the password, so it is easier to combine output from later runs and sort alphabetically, and it also adds a command at the end to mark extensionAttribute3 on the computer accounts in the tsv file (pwds successfully reset) so you can skip those computers on subsequent runs.

    [CmdletBinding(DefaultParameterSetName=”Encryption”)]
    Param(
    [Parameter(Mandatory=$true)]
    [String[]]
    $ComputerName,
    [Parameter(Mandatory=$true)]
    [String[]]
    $LocalAccounts,
    [Parameter(Mandatory=$true)]
    [String]
    $TsvFileName,
    [Parameter(ParameterSetName=”Encryption”, Mandatory=$true)]
    [String]
    $EncryptionKey,
    [Parameter()]
    [ValidateRange(20,120)]
    [Int]
    $PasswordLength = 20,
    [Parameter(ParameterSetName=”NoEncryption”, Mandatory=$true)]
    [Switch]
    $NoEncryption
    )
    #Load any needed .net classes
    Add-Type -AssemblyName “System.Web” -ErrorAction Stop
    #This is the scriptblock that will be executed on every computer specified in ComputerName
    $RemoteRollScript = {
    Param(
    [Parameter(Mandatory=$true, Position=1)]
    [String[]]
    $Passwords,
    [Parameter(Mandatory=$true, Position=2)]
    [String[]]
    $LocalAccounts,
    #This is here so I can record what the server name that the script connected to was, sometimes the DNS records get messed up, it can be nice to have this.
    [Parameter(Mandatory=$true, Position=3)]
    [String]
    $TargettedServerName
    )
    $LocalUsers = Get-WmiObject Win32_UserAccount -Filter “LocalAccount=true” | Foreach {$_.Name}
    #Check if the computer has any local user accounts whose passwords are not going to be rolled by this script
    foreach ($User in $LocalUsers)
    {
    if ($LocalAccounts -inotcontains $User)
    {
    Write-Warning “Server: ‘$($TargettedServerName)’ has a local account ‘$($User)’ whos password is NOT being changed by this script”
    }
    }
    #For every local account specified that exists on this server, change the password
    $PasswordIndex = 0
    foreach ($LocalAdmin in $LocalAccounts)
    {
    $Password = $Passwords[$PasswordIndex]
    if ($LocalUsers -icontains $LocalAdmin)
    {
    try
    {
    $objUser = [ADSI]”WinNT://localhost/$($LocalAdmin), user”
    $objUser.psbase.Invoke(“SetPassword”, $Password)
    $Properties = @{
    TargettedServerName = $TargettedServerName
    Username = $LocalAdmin
    Password = $Password
    RealServerName = $env:computername
    }
    $ReturnData = New-Object PSObject -Property $Properties
    Write-Output $ReturnData
    }
    catch
    {
    Write-Error “Error changing password for user:$($LocalAdmin) on server:$($TargettedServerName)”
    }
    }
    $PasswordIndex++
    }
    }
    #Generate the password on the client running this script, not on the remote machine. System.Web.Security isn’t available in the .NET Client profile. Making this call
    # on the client running the script ensures only 1 computer needs the full .NET runtime installed (as opposed to every system having the password rolled).
    function Create-RandomPassword
    {
    Param(
    [Parameter(Mandatory=$true)]
    [ValidateRange(20,120)]
    [Int]
    $PasswordLength
    )
    $Password = [System.Web.Security.Membership]::GeneratePassword($PasswordLength, $PasswordLength / 4)
    #This should never fail, but I’m putting a sanity check here anyways
    if ($Password.Length -ne $PasswordLength)
    {
    throw new Exception(“Password returned by GeneratePassword is not the same length as required. Required length: $($PasswordLength). Generated length: $($Password.Length)”)
    }
    return $Password
    }
    #Main functionality – Generate a password and remote in to machines to change the password of local accounts specified
    if ($PsCmdlet.ParameterSetName -ieq “Encryption”)
    {
    try
    {
    $Sha256 = new-object System.Security.Cryptography.SHA256CryptoServiceProvider
    $SecureStringKey = $Sha256.ComputeHash([System.Text.UnicodeEncoding]::Unicode.GetBytes($EncryptionKey))
    }
    catch
    {
    Write-Error “Error creating TSV encryption key” -ErrorAction Stop
    }
    }
    foreach ($Computer in $ComputerName)
    {
    #Need to generate 1 password for each account that could be changed
    $Passwords = @()
    for ($i = 0; $i -lt $LocalAccounts.Length; $i++)
    {
    $Passwords += Create-RandomPassword -PasswordLength $PasswordLength
    }
    Write-Output “Connecting to server ‘$($Computer)’ to roll specified local admin passwords”
    $Result = Invoke-Command -ScriptBlock $RemoteRollScript -ArgumentList @($Passwords, $LocalAccounts, $Computer) -ComputerName $Computer
    #If encryption is being used, encrypt the password with the user supplied key prior to writing to disk
    if ($Result -ne $null)
    {
    if ($PsCmdlet.ParameterSetName -ieq “NoEncryption”)
    {
    $Result | Select-Object Username,Password,TargettedServerName,RealServerName | Export-Csv -Append -Path $TsvFileName -NoTypeInformation
    #$tempwksname = $Result | Select-Object RealServerName
    #echo $tempwksname>>c:\ps\testlisting.txt
    }
    else
    {
    #Filters out $null entries returned
    $Result = $Result | Select-Object Username,Password,TargettedServerName,RealServerName
    foreach ($Record in $Result)
    {
    $PasswordSecureString = ConvertTo-SecureString -AsPlainText -Force -String ($Record.Password)
    $Record | Add-Member -MemberType NoteProperty -Name EncryptedPassword -Value (ConvertFrom-SecureString -Key $SecureStringKey -SecureString $PasswordSecureString)
    $Record.PSObject.Properties.Remove(“Password”)
    $Record | Select-Object Username,EncryptedPassword,TargettedServerName,RealServerName | Export-Csv -Append -Path $TsvFileName -NoTypeInformation
    }
    }
    }
    $list=import-csv $tsvFileName
    foreach ($target in $list) {set-adcomputer -Identity $target.RealServerName -Replace @{extensionAttribute3=”AdminReset”} -server }

    }

    • Here is the batch I am running in case it is helpful to anyone else. It needs work but has gotten us moving.

      @prompt $S
      @color 0d
      @mode con:cols=150 lines=3000
      @cls
      @set LocalAdmin= where you are running scripts from
      @echo Path is %LocalAdmin%
      @echo Available Targets are x,y,z…
      @set /P Target=Enter Target:
      @echo Target is %Target%
      @echo.
      @echo Please update script if any Core DCs have changed
      @rem Core DCs are important because they are needed for setting the attribute on the AD computer account later
      @pause
      @echo Set Current Time
      For /F “Tokens=1-7 Delims=/:. ” %%d In (“%Date% %Time%”) Do Set Now=%%g%%e%%f_%%h%%i%%j
      @echo.
      @echo Identify computer DNs in target OUs that have not yet had their local admin passwords reset
      @rem These OU lists are statically defined. We want to run on workstations only
      for /f %%a in (%LocalAdmin%\oulist_%Target%.txt) do @dsquery * %%a -limit 2000 -filter “(&(objectClass=computer)(!(extensionAttribute3=AdminReset)))”>>%LocalAdmin%\log\ComputerDNs_%Target%_%Now%.txt
      @echo.
      @echo Convert DNs to RDNs
      @pause
      @rem First used dsquery but it was slow: for /f %%i in (%LocalAdmin%\log\ComputerDNs_%Target%_%Now%.txt) do @dsquery computer -o rdn %%i >>%LocalAdmin%\log\ComputerList_%Target%_%Now%_unsorted.txt
      for /f “delims==, tokens=2” %%i in (%LocalAdmin%\log\ComputerDNs_%Target%_%Now%.txt) do @echo %%i>>%LocalAdmin%\log\ComputerList_%Target%_%Now%_unsorted.txt
      @echo.
      @echo Sort DNs and remove quotation marks
      @pause
      for /f %%j in (‘sort %LocalAdmin%\Log\ComputerList_%Target%_%Now%_unsorted.txt’) do @echo %%~j>>%LocalAdmin%\Log\ComputerList_%Target%_%Now%.txt
      @echo.
      @echo Delete temporary DN and unsorted RDN lists
      @pause
      del %LocalAdmin%\log\ComputerDNs_%Target%_%Now%.txt & del %LocalAdmin%\log\ComputerList_%Target%_%Now%_unsorted.txt
      @echo.
      @echo Attempt to roll passwords, outputing passwords to TSV. Script is domain specific so we can specify DC to use for marking extensionAttribute3 to AdminReset.
      @pause
      powershell “& “%LocalAdmin%\ps\InvokeOnly_%Target%.ps1 -computername (Get-Content %LocalAdmin%\Log\ComputerList_%Target%_%Now%.txt) -localaccounts administrator -NoEncryption -PasswordLength 20 -TsvFileName %LocalAdmin%\LocalAdminPasswords_%Target%_%Now%.tsv”
      @echo.
      @echo Append Results of this run to LocaladminPasswords_%Target%_Combined.tsv
      @pause
      for /f “skip=1” %%w in (%LocalAdmin%\LocalAdminPasswords_%Target%_%Now%.tsv) do echo %%w>>%LocalAdmin%\LocalAdminPasswords_%Target%_Combined.tsv
      @echo.
      @echo Sort Combined file so all entries are alphabetical
      @pause
      ren %LocalAdmin%\LocalAdminPasswords_%Target%_Combined.tsv LocalAdminPasswords_%Target%_Combined_unsorted.tsv
      echo “RealServerName”,”TargettedServerName”,”Username”,”Password”>%LocalAdmin%\LocalAdminPasswords_%Target%_Combined.tsv
      sort %LocalAdmin%\LocalAdminPasswords_%Target%_Combined_unsorted.tsv>>%LocalAdmin%\LocalAdminPasswords_%Target%_Combined.tsv
      del %LocalAdmin%\LocalAdminPasswords_%Target%_Combined_unsorted.tsv
      @echo.
      @echo.
      @echo Ready to Exit
      pause

Leave a Reply

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