Get BitLocker Recovery From Active Directory with Powershell

At the company I currently work for we’ve been using BitLocker since Vista (although Vista install base was rather small). Having moved from another “Pretty Good” Disk encryption technology, one thing we struggled with was Recovery information. While the previous solution had its own server to store all recovery keys, BitLocker links these to the Computer Object. When that computer object is deleted, so is the key. This was a battle for us at first since we have automation in place to remove stale AD Accounts, but–at the time–did not have any method to backup that information. We immediately added a table to a database and as part of our cleanup would backup these Keys.

First: Why not MBAM?

I loosely touched on this in another post regarding TPM Backup information in AD and i’ll give the quick summary again. By the time MBAM cam around, we already had our backup system in place. This was added into a global utility that allowed our technicians to perform AD tasks without AD permissions (Create computer, reset, move ou, etc…). MBAM would have just been “another thing” for us to maintain and manage. With our custom tool and SCCM already in place, MBAM did not provide us any additional benefits. For those of you using MBAM, This may not be for you, however this still provides a great way to quickly grab the information for those ad-hoc requests.

As we move to upgrade our various automation from the tried-and-true VBScript to the ever-so-loved Powershell, Obtaining the BitLocker Recovery Information was an odd one. If you use the RSAT tools and pull up details of a computer object there is a “BitLocker Recovery tab”.

RecoveryB

Each BitLocker Recovery password is an object of the computer account object. The odd thing is that by doing a Get-ADComputer, Recovery information is not returned as a “property” like other items–and where I expected it. As usual, first step was to use a search engine and see what others had done. I found a few results that gave me what i wanted (notably this post) but didn’t like the way it was scripted. The snippet that does the work is here:

$objADObject = get-adobject -Filter * | Where-Object {$_.DistinguishedName -match $objComputer.Name -and $_.ObjectClass -eq "msFVE-RecoveryInformation"}

While this gets the job done it is slow and by slow i mean the combination of “-Filter *” and piping to “Where-Object” is looking at everything before seeing if its the item (droid) that I’m looking for. Recovery Key look-up for my system took approximately 15 seconds for the first result, and didnt finish processing until a few seconds after that.

Let’s step back and look at a BitLocker Recovery object (msFVE-RecoveryInformation); really lets justify the filter “$_.DistinguishedName -match $objComputer.Name”.

Each BitLocker Recovery object is of the Object Class: msFVE-RecoveryInformation. If you look at the DistinguishedName of an object we can see it stems from the Distinguished name of the computer object it is attached to:

DistinguishedName : CN=2014-10-20T13:10:38-06:00{E59D69FF-6A3B-42A6-89C0-57A0DA0E302A},CN=WIN7X86PC01,OU=swComputers,DC=swansonglab,DC=com

Unfortunately this is the best way (I’ve found) to tie a recovery object back to the computer account. For those curious, here is what’s contained in a msFVE-RecoveryInformation object:

CanonicalName : swansonglab.com/swComputers/WIN7X86PC01/2014-10-20T13:10:38-06:00{E59D69FF-6A3B-42A6-89C0-57A0DA0E302A}
CN : 2014-10-20T13:10:38-06:00{E59D69FF-6A3B-42A6-89C0-57A0DA0E302A}
Created : 10/20/2014 1:10:55 PM
createTimeStamp : 10/20/2014 1:10:55 PM
Deleted :
Description :
DisplayName :
DistinguishedName : CN=2014-10-20T13:10:38-06:00{E59D69FF-6A3B-42A6-89C0-57A0DA0E302A},CN=WIN7X86PC01,OU=swComputers,DC=swansonglab,DC=com
dSCorePropagationData : {12/31/1600 6:00:00 PM}
instanceType : 4
isDeleted :
LastKnownParent :
Modified : 10/20/2014 1:10:55 PM
modifyTimeStamp : 10/20/2014 1:10:55 PM
msFVE-KeyPackage : {136, 1, 0, 0...}
msFVE-RecoveryGuid : {255, 105, 157, 229...}
msFVE-RecoveryPassword : 465762-121880-049797-598411-533643-549890-128436-549736
msFVE-VolumeGuid : {146, 36, 120, 28...}
Name : 2014-10-20T13:10:38-06:00{E59D69FF-6A3B-42A6-89C0-57A0DA0E302A}
nTSecurityDescriptor : System.DirectoryServices.ActiveDirectorySecurity
ObjectCategory : CN=ms-FVE-RecoveryInformation,CN=Schema,CN=Configuration,DC=swansonglab,DC=com
ObjectClass : msFVE-RecoveryInformation
ObjectGUID : d0a15cc8-5f86-42ed-8942-633cec25b6b1
ProtectedFromAccidentalDeletion : False
sDRightsEffective : 15
showInAdvancedViewOnly : True
uSNChanged : 99936
uSNCreated : 99936
whenChanged : 10/20/2014 1:10:55 PM
whenCreated : 10/20/2014 1:10:55 PM

Remember in most cases getting recovery is searched by the GUID. This can be filtered on using Powershell with no problems or computer object linking. The example I’m looking at is getting all recovery objects attached to a computer account.

So my issues with the snippet above (using “-Filter *” and “Where-Object”), again, was time consuming. Since the Recovery Information objects are attached to a computer object we can simply use the computer account’s Distinguished Name as the search base for the command:

# Get Computer Object
> $computer = Get-ADComputer -Filter {Name -eq 'WIN7X86PC01'}

# Get all BitLocker Recovery Keys for that Computer. Note the 'SearchBase' parameter
> $BitLockerObjects = Get-ADObject -Filter {objectclass -eq 'msFVE-RecoveryInformation'} -SearchBase $computer.DistinguishedName -Properties 'msFVE-RecoveryPassword'

# Output the results!
> $BitLockerObjects
DistinguishedName : CN=2014-10-20T13:10:38-06:00{E59D69FF-6A3B-42A6-89C0-57A0DA0E302A},CN=WIN7X86PC01,OU=swCompute
rs,DC=swansong,DC=com
msFVE-RecoveryPassword : 465762-121880-049797-598411-533643-549890-128436-549736
Name : 2014-10-20T13:10:38-06:00{E59D69FF-6A3B-42A6-89C0-57A0DA0E302A}
ObjectClass : msFVE-RecoveryInformation
ObjectGUID : d0a15cc8-5f86-42ed-8942-633cec25b6b1

DistinguishedName : CN=2014-10-20T13:11:29-06:00{450547C6-675C-4A61-B276-17CC620D3885},CN=WIN7X86PC01,OU=swCompute
rs,DC=swansong,DC=com
msFVE-RecoveryPassword : 632126-201135-053504-485045-151657-139986-094820-137687
Name : 2014-10-20T13:11:29-06:00{450547C6-675C-4A61-B276-17CC620D3885}
ObjectClass : msFVE-RecoveryInformation
ObjectGUID : 8c3963ea-89ec-4b41-934b-ee6023d9d1e9

DistinguishedName : CN=2014-10-20T13:12:03-06:00{A29D2D47-89D6-4459-B106-40B1F62A04EF},CN=WIN7X86PC01,OU=swCompute
rs,DC=swansong,DC=com
msFVE-RecoveryPassword : 497178-478654-023111-302291-606034-162855-504163-720698
Name : 2014-10-20T13:12:03-06:00{A29D2D47-89D6-4459-B106-40B1F62A04EF}
ObjectClass : msFVE-RecoveryInformation
ObjectGUID : 4a72004e-e76e-4cb3-a828-152011b8b541

Hopefully this has provided some additional information on BitLocker Recovery keys and how to obtain them should you ever need to!

Enjoi!

Advertisements

TPM Not Found After OSD

Today I experienced something frustrating. After applying OSD on some new hardware I attempted to enable BitLocker (TPM+PIN Configuration). To my surprise I received an error that a valid TPM could not be found. Here is my experience and methodology for troubleshooting a missing TPM.

(1) Check WMI
Using Powershell:

Get-WmiObject Win32_TPM -Namespace root/cimv2/security/MicrosoftTPM | Select IsActivated_InitialValue, IsEnabled_InitialValue, IsOwned_InitialValue | Format-List

Usually you’d see something like this:

IsActivated_InitialValue : True
IsEnabled_InitialValue   : True
IsOwned_InitialValue     : True

(2) Check TPM MMC Console
If and when WMI is blank I move on to the TPM MMC console snap-in (tpm.msc).

(3) Check BIOS
At this point I’ve determined the TPM isn’t visible to the Operating System; It happens! On most BIOS you’ll have settings whether or not the Operating System can see and/or manage the TPM Device. Boot into the BIOS, look for a security section and check the TPM Status. In my instance this looked good! TPM was listed as Enabled and Activated. I rebooted back to the OS, repeated steps 1 and 2…both still with the same result.

(4) Vendor Software
When it comes to using vendor-provided installers/software/executables to install drivers, I typically have one rule: I DON’T! In my experience (Dell, Lenovo, HP, Samsung, MS Surface) Plug-n-Play will identify and capture 99% of the hardware without the necessity to install the vendor’s software. In this instance this was an Infineon TPM device so I grabbed the driver CD, extracted and ran. Unless absolutely necessary, I’ll usually use the software to install only the driver where possible. A lot of driver installations will also provide an application that tromps over the built in Windows functions with their own; Bloatware, Crapware…call it what you want, I find it extremely unnecessary. The most infuriating being the old “HP Wireless Assistant” which was a clunky “remix” of Windows’ normal Wireless Connection Menu and tray icon–I found this to be slow, clunky, and down right unnecessary!

Back to the TPM…I launched the “Infineon TPM Professional Package”. I select custom install hoping to see “Driver”. Instead I see a bunch of extra stuff I don’t need and when I went to tell the installer to “not install” a component, I saw this wasn’t an option…It would appear that in order to get an Infineon TPM device seen to the OS, I have to use their crappy application to “manage and control” it. Not gonna happen!

As I contemplated what to do next I eventually did what I probably should have done at step (1)…

Check Device Manager
Usually, device manger would show a TPM device under the “security” category as seen:

TPM_devmgmt_cropped

 

 

Unfortunately, not found! I then started digging in Device Manager and eventually stumbled across “Infineon Trusted Platform Module” under “System Devices”. That explains why the TPM MMC couldn’t find it!

TPM_inSYS

Right-Click -> Uninstall (making sure to check “Delete the driver software for this device”.

uninstallTPM

Richt-Click –> “Scan for Hardware Changes”

Just like magic, the TPM was detected and placed in the “Security Devices” category. TPM.msc detected it. BitLocker was enabled and there was much rejoicing. I then removed the “tpm driver” from the Driver Package, updated distribution points and all was well!

 

My hope is that this will save some of you from additional headaches if this comes up in your OSD world.

 

Enjoy!

Change BitLocker PIN with Complexity

BitLocker Enabled for OS Drives? Check!
Using PIN authentication? Check!
Enforcing PIN Complexity? …

True, we get a lot of options with BitLocker in Windows. One thing that we struggled with is the inability to require any kind of complexity with the PIN. Through Group Policy we can set a minimum length which helps but what if I need to enforce alternate complexity? Say, require One Upper Case, One Lower Case, and One Number?

Well, I’ve done that work for you so sharpen your copy/paste skills and lets have at it!

Before this will work completely, make sure that you’ve enabled the Group Policy “Allow Enhanced PINs for Startup”

We’ve used this code since our first step into Windows 7 and vbscript was the winner. To add a GUI, we dropped the vbscript into an HTA and had a functional utility. Then the loveable storm I call Powershell came along, and naturally a rewrite was inevitable–Integrating a Windows Form was an obvious “plus” as well. These were tested on Windows 7 – Windows 8.1 (including Windows To-Go–Yeah, it’s a bit different for Portable OS’s).

Both utilities break apart the logic in some easy to call/use functions/subs. I’ve also added some nice messages that the user will see to help maintain their complexity in the full scripts (linked at the bottom). Hopefully if you don’t need the functionality of the utilities you can use the code snippets for other projects.

Check Length:

Powershell:

function CheckStringLength([int]$lowerInt, [int]$upperInt, [string]$stringValue)
{
    If ($stringValue.Length -lt $lowerInt)
    { return -1 }
    ElseIf ($stringValue.Length -gt $upperInt)
    { return 1 }
    else
    { return 0 }
}

vbScript:

Function CheckStringLength(lowerInt, upperInt, stringValue)
    If Len(stringValue) < lowerInt Then CheckStringLength = -1 Exit Function ElseIf Len(stringValue) > upperInt Then
        CheckStringLength = 1
        Exit Function
    Else
        CheckStringLength = 0
    End If

End Function

Require Uppercase:

Powershell:

If (-Not ($stringValue.CompareTo($stringValue.ToLower())))
{ return $false }

vbScript:

Function ContainsUpperCase(stringValue)
    If stringValue <> LCase(stringValue) Then
        ' Upper Case passed
        ContainsUpperCase = True
        Exit Function
    End If

    ContainsUpperCase = False

End Function

Require Lowercase:

Powershell:

If (-Not ($stringValue.CompareTo($stringValue.ToUpper())))
    { return $false }

vbScript:

Function ContainsLowerCase(stringValue)
    If stringValue <> UCase(stringValue) Then
        ' Lower case Passed
        ContainsLowerCase = True
        Exit Function
    End If	

    ContainsLowerCase = False

End Function

 

Require Number:

Powershell:

function ContainsNumber([string]$stringValue)
{
    foreach ($c in $stringValue.ToCharArray())
    {
        If ($c -match "[0-9]")
        {
            return $true
        }
    }

    return $false
}

vbScript:

Function ContainsNumber(stringValue)

    Dim iNum

    For iNum = 1 To Len(stringValue)
        If IsNumeric(Mid(stringValue, iNum, 1)) Then
            ContainsNumber = True
            Exit Function
        End If
    Next

    ContainsNumber = False

End Function

 

Prevent Special Characters:

Powershell:

function ContainsSpecialCharacter([string]$stringValue)
{
    $charArray = [int[]][char[]]$stringValue

    foreach ($c in $charArray)
    {
        if (($c -lt 32) -or ($c -gt 128))
        {
            return $true
        }
    }

    return $false
}

vbScript:

Function ContainsSpecialCharacter(stringValue)

    Dim iChar, cChar

    For iChar = 1 To Len(stringValue)
        cChar = Mid(stringValue, iChar, 1)

        ' Check if Character is in range
        If Asc(cChar) < 32 Or Asc(cChar) > 128 Then
            ContainsSpecialCharacter = True
            Exit Function
        End If
    Next

    ContainsSpecialCharacter = False

End Function

 

Complete scripts:

vbScript/HTA: https://skydrive.live.com/redir?resid=3D90B836AD7F51CF%21786
Powershell: https://skydrive.live.com/redir?resid=3D90B836AD7F51CF%21785

Using TPM Backups with Powershell

Backing up TPM information may not be a complete necessity for all, but certainly has its advantages. Understanding why we do this and what we can do with it.

Why not MBAM? : I’ll be brief about this…by the time MBAM came around, we had already built out all that it had to offer. We had key backup and retention and a web portal to access Recovery information for our support staff to use (Along with auditing of who accessed what). Anything else was just extra “stuff”. We didn’t need it. If you have MBAM and are happy with it: Great!

AD Schema Extensions/Updates

I won’t go into too much detail here other than pointing out a few things:

Group Policy Settings

In order to have your TPM information backed up to Active Directory there’s one Policy to set. As read in the description, this policy will require ADDS connectivity to backup this information whenever a TPM owner password is set or changed. With this setting enabled we’ll see TPM information backup to Active Directory.

This is the policy that is required for AD backup to work:

Location: Computer Configuration> Administrative Templates> System> Trusted Platform Module Services

Policy: Turn on TPM Backup to Active Directory Domain Services

Setting: Enabled

Some other notable settings that correspond with this and may benefit you (depending on your BitLocker configuration). You can set the TPM Lockout duration and Lockout threshold. For more information on those policies: http://technet.microsoft.com/en-us/library/jj679889.aspx.

What the Backup Looks Like

Depending on if you’re running Windows 7 or Windows 8/8.1 the TPM information will backup differently.

Windows 7

With Windows 7, the TPM information is added to the “msTPM-OwnerInformation” attribute of the AD Computer object.

Windows 8+

With Windows 8+ things get a bit more complex. The 2012 Schema update mentioned above adds a new container at the root of your domain “TPM Device”. Inside of this is (you guessed it!) TPM objects along with their TPM Recovery passwords. The AD Computer Object is linked to the TPM object through the Computer’s Attribute “msTPM-TpmInformationForComputer”.

For example, my Computer object will have the msTPM-TpmInformationForComputer attribute who’s value will reference the TPM object in the “TPM Devices” container. The TPM Object contains the recovery information in the “msTPM-OwnerInformation” attribute. This will be much clearer further on.

Back Together

In both instances the TPM Recovery information is a 20-byte binary value encoded to a 28-byte base64 null-terminated string. Basically, it’ll be a string of gibberish/rubbish/balderdash that you can’t read, but will find is very useful!

Using TPM Recovery Information

Organizations who utilize a TPM + PIN configuration for BitLocker will benefit the greatest from this information. Having a PIN for users means one more password for a user, which means one more password for someone to forget, which means they’ll mash and mash the wrong password convincing themselves they typed it correctly. In doing this, the TPM will identify this as an intrusion, and will eventually go into a lockout status. We are then forced to boot with our recovery password (hopefully backed up to Active Directory) and can at least get into the system. Just because we used BitLocker Recovery, doesn’t mean that our TPM lockout issue was resolved.

Now let’s say I reboot my system before the TPM lockout has reset itself. Recovery again but now what? There’s not exactly a countdown saying “your TPM will reset in XX minutes” otherwise you’d be telling whoever’s trying to break into your system “Wait 5 minutes to try again”. To reset the TPM lockout, we have an option to “Reset TPM Lockout” which requires the TPM Owner information that we’ve backed up to Active Directory. We pass in the TPM password to the system and the lockout state is reset and we can use our PIN like normal again.
Script it, Automate it, Any Way You Want It

So let’s put this information and scenario to good use. The above link for “Preparing Active Directory” includes some sample vbscripts that can help pull down this information. However, we have Powershell and we want to exercise its greatness.

We can grab the Information from Active Directory (Powershell or vbscript) and then pass that recovery into the ResetTPMLockout Method of the Win32_TPM class. With Powershell we’ll need the Active Directory modules (included with the RSAT tools).

Powershell:

Import-Module ActiveDirectory

# you can also setup the computer name as an argument to do this remotely 
$computerName = $env:COMPUTERNAME
$tpmPassword = ""

# Gets the AD Object of the computer
# We'll grab the Property for Windows 7 and Windows 8+
$computerAdObject = Get-ADComputer -Filter 'Name -eq $computerName' -Properties Name, OperatingSystem, 'msTPM-TpmInformationForComputer', 'msTPM-OwnerInformation'

# Evaluating the OS
if (($computerAdObject.OperatingSystem) -like "Windows 7" )
{
     $tpmPassword = $computerAdObject.'msTPM-OwnerInformation'
}

if (($computerAdObject.OperatingSystem) -like "Windows 8")
{
     # Gets the TPM object
     $tpmAdObject = Get-ADObject -Filter 'distinguishedName -eq $($computerAdObject.'msTPM-TpmInformationForComputer')' -Properties CN, Name, msTPM-OwnerInformation -Credential $authCreds

     # Stores the TPM password
     $tpmPassword = $tpmAdObject.'msTPM-OwnerInformation'
}

# Reset TPM Lockout
if (-Not ($tpmPassword = ""))
{
     # Get local TPM object
     $tpmObject = Get-WmiObject Win32_TPM -Namespace root/cimv2/security/MicrosoftTpm -ComputerName $computerName

     $return = $tpmObject.ResetAuthLockout($tpmPassword)
}

One Step Further…

Let’s say we want to change the TPM Owners password. We’ll still need to do the above to get the backed up password from Active Directory. Once we have that we can use the Win32_TPM WMI class to do this. However, we can’t just pass a string of text, we need to convert to that “gibberish” format we saw above. Just a couple quick lines in Powershell and we’ve successfully changed the TPM Owner Password:

Powershell:

$newEncodedPassword = ($tpmObject.ConvertToOwnerAuth("NewPassword")).OwnerAuth
$tpmObject.ChangeOwnerAuth($tpmPassword,$newEncodedPassword)

Hopefully this gives you a better understanding of how to use the TPM Passwords that you’ve been backing up to Active Directory.

Thanks for reading!