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

Advertisements

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!

Easily Identify Windows To Go In Your Scripts

Updated Powershell and vbscript Functions 8/16/2015

When Windows 8 was released into the wild this new supported feature “Windows To Go” was released with it. Now that Windows 8.1 is here the “To Go” OS was upgraded and released just the same.

From a scripting perspective, being able to differentiate between an internal OS drive and an external “To Go” configuration may be desired. Luckily with Windows 8 and up, Microsoft added a property to the Win32_OperatingSystem WMI Class to make this easier.

vbScript: (Updated 8/16/2015)

Function IsPortableOS(ComputerName)
   Dim SWBemlocator, objWMIProc
   Dim objOS, colOS, strOSVer, bIsPortableOS

   ' Create connection objects
   Set SWBemlocator = CreateObject("WbemScripting.SWbemLocator")
   Set objWMIProc = SWBemlocator.ConnectServer(ComputerName, "root\CIMV2")
   Set colOS = objWMIProc.ExecQuery("Select * from Win32_OperatingSystem")

   For Each objOS In colOS
      strOSVer = Left(objOS.Version,3)
      

      If ((strOSVer > "6.1") OR (LEFT(strOSVer,2) = "10"))Then
         ' Only return a value if Win8 or above.
         IsPortableOS = objOS.PortableOperatingSystem
      Else
      	' Property doesn't exist in Win7 and before
      	IsPortableOS = False
      End If
   Next
End Function

Powershell: (Updated 8/16/2015)

function Get-OperatingSystemInformation()
{
   [cmdletbinding()]
   param(
      [Parameter(Mandatory=$true)]
      [string] $ComputerName
   )

   try
   {
      $wmiObjOperatingSystem = Get-WmiObject Win32_OperatingSystem -ComputerName $ComputerName
   }
   catch
   {
      Write-Verbose -Message "Unable to get Operating System from WMI for computer $computer"
      return [HashTable]::new($null)
   }
	
   # Cast the version property to type System.Version
   [System.Version]$vOsVersion = $wmiObjOperatingSystem.Version

   # We do a try here because with Windows 7 This property does not exist.
   try
   {
      if ($wmiOsObject.PortableOperatingSystem)
      {
         $boolIsPortableOs = $true
      }
      else
      {
         $boolIsPortableOs = $false
      }
   }
   catch
   {
      $boolIsPortableOs = $false
   }

   return $htOs = @{
      'caption'=$wmiObjOperatingSystem.Caption;
      'fullversion'=$wmiObjOperatingSystem.Version;
      'osarchitecture'=$wmiObjOperatingSystem.OSArchitecture;
      'versionMajor'=$vOsVersion.Major;
      'versionMinor'=$vOsVersion.Minor;
      'versionBuild'=$vOsVersion.Build;
      'isPortableOS'=$boolIsPortableOs
   }
}

One important detail to note is that we need to do an OS version evaluation before checking the PortableOperatingSystem property. Until Windows 8 this property didn’t exist. You could put “$wmiOperatingSystem.PortableOperatingSystem” in a try/catch if you wanted to as well.

Now its not that often that you’ll have to detect or identify a Windows To Go configuration but if you’re like me and you manage a TPM BitLocker configuration for standard laptops those policies and the To Go sticks will not play nicely. You can simply create a Group Policy WMI filter for portable operating systems:

Select * from Win32_OperatingSystem where PortableOperatingSystem = "True"

Enjoy!
-NDS