Overview

This is Part 3 of a multi-part series on managing local admin passwords. In this part I will discuss how to securely connect to Active Directory and update an Active Directory attribute using PowerShell.  In case you missed it;

Here is Part 1 - Overview

Here is Part 2 - Random Password Generation

If you want to skip straight to the script you can copy it from this post directly, or you can download the script which is attached to this post as a text file.

The Problem


One of the challenges associated with providing a means for authorized administrative users to view the local administrator account's password is where to store it. In this series, the password will ultimately be stored in Active Directory which leads to the next challenge; how to securely transmit the password to Active Directory after it has been generated on the local workstation while preventing unauthorized users from viewing the password (man in the middle) as it transits the network. In this post, I will demonstrate how to use PowerShell to make an LDAP signed and secured connection to Active Directory and how to use that connection to securely write to an Active Directory Attribute. When I first started researching this problem I found plenty of examples of how to securely connect to Active Directory using VBScript, what I did not readily find were PowerShell examples to do the same thing.

The Solution

The solution lies within the LDAP Connection class which is documented on MSDN. Within the LDAP Connection class there are session options which provide a means to sign and encrypt the LDAP connection using Kerberos encryption and signing. The following script is a function that will securely bind to Active Directory and write the value contained in the variable $NewValue to the Active Directory attribute contained in the variable $Attribute.

#==================================================================================
# Kerberos Secured LDAP Update
#==================================================================================
function fnSecureAttributeUpdate{
param([string]$Attribute,[string]$NewValue)
    try{
        #Store Domain
        $sDomain = (gwmi Win32_ComputerSystem).Domain

     #LDAP Search Filter
     $filter = "(&(objectCategory=computer)(objectClass=computer)(cn=$env:COMPUTERNAME))"

        #Store AD DN Of localhost
        $sDN = ([adsisearcher]$filter).FindOne().Properties.distinguishedname

        #Search Base
        $sSearchRoot="DC=" + $sDomain.replace(".",",DC=")
       
        #Format Attribute
        $Attribute = $Attribute.ToLower()

     #Add Assembly
     Add-Type -AssemblyName System.DirectoryServices.Protocols

     #Create Connection
     $connection=New-Object System.DirectoryServices.Protocols.LDAPConnection($sDomain)

     #Enable Kerberos Encryption
     $connection.SessionOptions.Sealing=$true 
     $connection.SessionOptions.Signing=$true

        try{
            #Update Attribute Field if Field is Currently Null
         $req = New-Object System.DirectoryServices.Protocols.ModifyRequest($sDN,"Add",$Attribute,$NewValue)
            $rsp = $connection.SendRequest($req)}
        catch{
            if($rsp.ResultCode -ne "Success"){
            try{
                #Update Attribute Field if Field Currently Contains A Value
                $req = New-Object System.DirectoryServices.Protocols.ModifyRequest($sDN,"Replace",$Attribute,$NewValue)
                $rsp = $connection.SendRequest($req)}
            catch{}
            }
        }

        if($rsp.ResultCode -eq "Success")
        {
            #Log Output
            #fnLog -LogPath $LogDir -LogFileName $LogFileName -Data "INFO: $($rsp.ResultCode) writing to AD Attribute: $Attribute"
            Return "SUCCESS"
        }
        else
        {
            #Log Output
            #fnLog -LogPath $LogDir -LogFileName $LogFileName -Data "ERROR: $($rsp.ResultCode) failed to Update AD Attribute: $Attribute. Account used may have insufficient permissions to update the attribute or the domain controller may be a RODC"
            Return "ERROR"
        }
    }
    catch{
        #Log Output
        #fnLog -LogPath $LogDir -LogFileName $LogFileName -Data "ERROR: Failed to connect to $sDomain using provided credentials"

        #Return Code
        Return "ERROR"
    }
}

Below is a screenshot of what happens when the preceding function is called. For simplicity, in the following example I am telling the secure attribute update function to update the description attribute of the computer object with the value mySecur3P@SSword. Needless to say, this is strictly an example only and should not be where you actually store confidential information including passwords.

Below is a screenshot of the computer object that was updated in Active Directory. I ran the script from a computer with a hostname of EX01.

There is the old saying, trust but verify, and when it comes to handling something as sensitive as local administrator passwords, I decided it was  good idea to verify that the passwords were indeed being transmitted to the domain controller in a secure fashion. To do so, I started up Microsoft Message Analyzer (NetMon's replacement) and took a network trace while running the secure attribute update function. The following screenshot shows the relevant portion of the trace. An inspection of the following entries proved to me that the update was indeed Kerberos signed and encrypted.

The keen observer may have noticed that I commented out the fnLog function in the example script above. The function above is a part of a larger local administrator password management solution which I will continue to reveal in the next part of this series. What you have so far is a configurable random password generator which can be integrated into pretty much any random password script, a cryptographically random character generator which can also be used for many different purposes, and now you have a means to securely transmit the password to Active Directory using Kerberos to secure the data in transit.


Limitations

If you have an Active Directory environment that includes Read Only Domain Controllers (RODCs) additional logic will need to be developed to ensure this script does not attempt to update the attribute using one of the RODCs.

Still to Come

The upcoming parts in this series will explain how to do the following:

  • Set the local administrator password based on fnGeneratePassword
  • Write a log file that logs the success and failure of each function
  • Create a confidential attribute to store the local admin password
  • Create fnMain to control the order in which all of the functions are called
  • Create a XAML based secure password viewer to retrieve the local admin password

Each portion of the solution is modularized using functions which allows the IT administrator to make use of all or just parts of the solution and allows the IT administrator to easily integrate any portion they wish into a larger script or even a different solution entirely. So stay tuned as Part 4 discusses how to update the local administrator account with a new password using PowerShell.