Skip to content

Latest commit

 

History

History

BitLocker with JEA

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

BitLocker management with JEA

About the lab

In this lab you will learn about benefits of JEA for day-to-day administration - not only for Securing the environment, but also for double-hop mitigation. To create Win10 image, use CreateParentDisk script located in tools folder.

LabConfig

$LabConfig=@{ DomainAdminName='LabAdmin'; AdminPassword='LS1setup!'; Prefix = 'WSLab-'; SwitchName = 'LabSwitch'; DCEdition='4'; AdditionalNetworksConfig=@(); Internet=$true ; VMs=@()}

$LabConfig.VMs += @{ VMName = 'BitLocker1' ; Configuration = 'Simple' ; ParentVHD = 'Win1019H1_G2.vhdx'  ; MemoryStartupBytes= 1GB ; MemoryMinimumBytes=1GB ; AddToolsVHD=$True ; DisableWCF=$True ; EnableWinRM=$True ; vTPM=$True}
$LabConfig.VMs += @{ VMName = 'BitLocker2' ; Configuration = 'Simple' ; ParentVHD = 'Win1019H1_G2.vhdx'  ; MemoryStartupBytes= 1GB ; MemoryMinimumBytes=1GB ; AddToolsVHD=$True ; DisableWCF=$True ; EnableWinRM=$True ; vTPM=$True}
$LabConfig.VMs += @{ VMName = 'Management' ; Configuration = 'Simple' ; ParentVHD = 'Win1019H1_G2.vhdx'  ; MemoryStartupBytes= 1GB ; MemoryMinimumBytes=1GB ; AddToolsVHD=$True ; DisableWCF=$True ; EnableWinRM=$True ; vTPM=$True}
 

Let's play with BitLocker a bit

All tasks will be done from Management (Windows 10) machine. Note the labconfig - WinRM is enabled on all machines, therefore PowerShell remoting will work from very beginning.

First make sure that RSAT is installed

    $Capabilities="Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0"
    foreach ($Capability in $Capabilities){
        Add-WindowsCapability -Name $Capability -Online
    }
 

Run following command to enable BitLocker on management machine

    Enable-BitLocker -MountPoint c: -SkipHardwareTest -UsedSpaceOnly -RecoveryPasswordProtector
 

That was smooth

Let's try the same on remote computers. Since Enable-BitLocker does not have -ComputerName or -Cimsession parameter, we will have to use Invoke-Command

Invoke-Command -ComputerName "BitLocker1","BitLocker2" -ScriptBlock {
    Enable-BitLocker -MountPoint c: -SkipHardwareTest -UsedSpaceOnly -RecoveryPasswordProtector
}
 

That's not nice!

It's because TPM gets initialized after user logon, see? (note TpmReady column)

Invoke-Command -ComputerName "Management","BitLocker1","BitLocker2" -ScriptBlock {Get-TPM} | ft
 

Let's initialize it first and then enable BitLocker

Invoke-Command -ComputerName "BitLocker1","BitLocker2" -ScriptBlock {
    Initialize-Tpm
    Enable-BitLocker -MountPoint c: -SkipHardwareTest -UsedSpaceOnly -RecoveryPasswordProtector
}
 

That's better

OK, now we need to backup recovery information to AD. Let's do it first on Management machine.

$KeyProtectorID=((Get-BitLockerVolume -MountPoint c:).KeyProtector | where KeyProtectorType -eq RecoveryPassword).KeyProtectorID
Backup-BitLockerKeyProtector -MountPoint c: -KeyProtectorId $KeyProtectorID
 

huh, nothing in AD!

and here is some PowerShell to read that info

$ComputerObject = Get-ADComputer -Filter {cn -eq "Management"} -Property msTPM-OwnerInformation, msTPM-TpmInformationForComputer
Get-ADObject -Filter {objectclass -eq 'msFVE-RecoveryInformation'} -SearchBase $ComputerObject.DistinguishedName -Properties 'msFVE-RecoveryPassword'
 

Let's try again, but now with some registries added

New-Item -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Force
New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name OSActiveDirectoryBackup -Value 1 -PropertyType DWORD -Force
New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name OSRecovery -Value 1 -PropertyType DWORD -Force
$KeyProtectorID=((Get-BitLockerVolume -MountPoint c:).KeyProtector | where KeyProtectorType -eq RecoveryPassword).KeyProtectorID
Backup-BitLockerKeyProtector -MountPoint c: -KeyProtectorId $KeyProtectorID
$ComputerObject = Get-ADComputer -Filter {cn -eq "Management"} -Property msTPM-OwnerInformation, msTPM-TpmInformationForComputer
Get-ADObject -Filter {objectclass -eq 'msFVE-RecoveryInformation'} -SearchBase $ComputerObject.DistinguishedName -Properties 'msFVE-RecoveryPassword'
 

And let's try it on BitLocker1 and 2

Invoke-Command -ComputerName "BitLocker1","BitLocker2" -ScriptBlock {
    New-Item -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Force
    New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name OSActiveDirectoryBackup -Value 1 -PropertyType DWORD -Force
    New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name OSRecovery -Value 1 -PropertyType DWORD -Force
    $KeyProtectorID=((Get-BitLockerVolume -MountPoint c:).KeyProtector | where KeyProtectorType -eq RecoveryPassword).KeyProtectorID
    Backup-BitLockerKeyProtector -MountPoint c: -KeyProtectorId $KeyProtectorID
}
 

Not good! We just hit double-hop issue!

JEA

So let's try some JEA. Following example is based on this blog

First let's create some users and groups.

#Create group for JEA-BitLocker Admins and Viewers
New-ADGroup -Name JEA-BitLockerAdmins  -Path "ou=workshop,dc=corp,dc=contoso,dc=com" -GroupScope Global
New-ADGroup -Name JEA-BitLockerViewers -Path "ou=workshop,dc=corp,dc=contoso,dc=com" -GroupScope Global

#Create users with password LS1setup!
New-ADUser -Name JohnDoe -AccountPassword  (ConvertTo-SecureString "LS1setup!" -AsPlainText -Force) -Enabled $True -Path  "ou=workshop,dc=corp,dc=contoso,dc=com"
New-ADUser -Name JaneDoe -AccountPassword  (ConvertTo-SecureString "LS1setup!" -AsPlainText -Force) -Enabled $True -Path  "ou=workshop,dc=corp,dc=contoso,dc=com"

#add users to groups
Add-ADGroupMember -Identity "JEA-BitLockerAdmins"  -Members JohnDoe
Add-ADGroupMember -Identity "JEA-BitLockerViewers" -Members JaneDoe
 

And let's configure JEA on computers BitLocker1 and BitLocker2. Note how many commands are needed. It's because commandlets in BitLocker module are using it (I did not test all, therefore the list might be bigger). The only extra command is wmoami just to demonstrate whoami output.

$computers="BitLocker1","BitLocker2"

Invoke-Command -ComputerName $computers -ScriptBlock {
    $Modules="BitLocker","TrustedPlatformModule"
    $AdminVisibleCmdLets=@()
    $AdminVisibleCmdLets +="BitLocker\*","TrustedPlatformModule\*","Get-CimInstance","Out-String","Where-Object" #All commands from BitLocker module + all others since Enable-BitLocker and Backup-BitLockerKeyProtector needs it.
    $AdminVisibleCmdLets += @{
        Name="New-Item";
        Parameters = @{Name='Path' ; ValidatePattern="^HKLM:\\SOFTWARE\\Policies\\Microsoft\\FVE.*"}
    }
    $AdminVisibleCmdLets += @{
        Name="New-ItemProperty";
        Parameters = @{Name='Force'},
                     @{Name='Path' ; ValidatePattern="^HKLM:\\SOFTWARE\\Policies\\Microsoft\\FVE.*"},
                     @{Name='Name'},
                     @{Name='Value'},
                     @{Name='PropertyType'}
    }
    $AdminVisibleExternalCommands = "C:\Windows\System32\whoami.exe" #just to demonstrate who is running command
    $AdminVisibleProviders= "registry","Variable"
    $ViewerVisibleCmdLets="BitLocker\Get-*","TrustedPlatformModule\Get-*","Get-CimInstance" #All commands from BitLocker module that start with Get-
    $AdminRoleName= "BitLockerAdmin"
    $ViewerRoleName="BitLockerViewer"
    $ConfigurationName="JEA-BitLocker"
    $AdminGroup= "JEA-BitLockerAdmins"
    $ViewerGroup="JEA-BitLockerViewers"

    #Create folders for JEA
    $Folders="$env:ProgramFiles\WindowsPowerShell\Modules\JEARoles\","$env:ProgramFiles\WindowsPowerShell\Modules\JEARoles\RoleCapabilities","$env:ProgramData\JEAConfiguration"
    foreach ($Folder in $Folders){
        New-Item -Path $Folder -ItemType Directory -force
    }
    #Create JEA Manifest
    New-ModuleManifest -Path "$env:ProgramFiles\WindowsPowerShell\Modules\JEARoles\JEARoles.psd1" -Description "Contains custom JEA Role Capabilities"

    #Create RoleCapabilityFile and SessionConfigurationFile
    New-PSRoleCapabilityFile -Path "$env:ProgramFiles\WindowsPowerShell\Modules\JEARoles\RoleCapabilities\$AdminRoleName.psrc" -ModulesToImport $Modules -VisibleCmdlets $AdminVisibleCmdLets -VisibleExternalCommands $AdminVisibleExternalCommands -VisibleProviders $AdminVisibleProviders
    New-PSRoleCapabilityFile -Path "$env:ProgramFiles\WindowsPowerShell\Modules\JEARoles\RoleCapabilities\$ViewerRoleName.psrc" -ModulesToImport $Modules -VisibleCmdlets $ViewerVisibleCmdLets
    New-PSSessionConfigurationFile -Path $env:ProgramData\JEAConfiguration\$ConfigurationName.pssc -SessionType RestrictedRemoteServer -LanguageMode ConstrainedLanguage -RunAsVirtualAccount -RoleDefinitions @{"$env:USERDOMAIN\$AdminGroup" = @{ RoleCapabilities = "$AdminRoleName" };"$env:USERDOMAIN\$ViewerGroup" = @{ RoleCapabilities = "$ViewerRoleName" }}

    #RegisterPSsessionConfiguration
    Register-PSSessionConfiguration -Path $env:ProgramData\JEAConfiguration\$ConfigurationName.pssc -Name $ConfigurationName -Force
}
 

Note: error in the end is expected Now Let's try if JEA was applied successfully. Note: type password "LS1setup!" for user JohnDoe

Invoke-Command -ComputerName "BitLocker1","BitLocker2" -Credential JohnDoe -ConfigurationName JEA-BitLocker -ScriptBlock {Get-Command}
 

That's great! JohnDoe can do just BitLocker administration!

Let's backup BitLocker key, but now with JEA!

Invoke-Command -ComputerName "BitLocker1","BitLocker2" -Credential JohnDoe -ConfigurationName JEA-BitLocker -ScriptBlock {
    New-Item -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -ErrorAction SilentlyContinue
    New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name OSActiveDirectoryBackup -Value 1 -PropertyType DWORD -Force
    New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name OSRecovery -Value 1 -PropertyType DWORD -Force
    $KeyProtectorID=((Get-BitLockerVolume -MountPoint c:).KeyProtector | where-object KeyProtectorType -eq RecoveryPassword).KeyProtectorID
    Backup-BitLockerKeyProtector -MountPoint c: -KeyProtectorId $KeyProtectorID
}
 

And let's check the keys in AD

$ComputerObjects = Get-ADComputer -Filter {cn -like "BitLocker*"} -Property msTPM-OwnerInformation, msTPM-TpmInformationForComputer
foreach ($ComputerObject in $ComputerObjects){
    Get-ADObject -Filter {objectclass -eq 'msFVE-RecoveryInformation'} -SearchBase $ComputerObject.DistinguishedName -Properties 'msFVE-RecoveryPassword'
}
 

Success!

So how was this possible?

It's easy, because you are using virtual account. It also means your identity is remote computer itself

Enter-PSSession -ComputerName BitLocker1 -Credential JohnDoe -ConfigurationName JEA-BitLocker
whoami
exit
 

So let's encrypt also D drives with autounlock and also backup to AD with JEA

Invoke-Command -ComputerName "BitLocker1","BitLocker2" -Credential JohnDoe -ConfigurationName JEA-BitLocker -ScriptBlock {
    Enable-BitLocker -MountPoint d: -UsedSpaceOnly -RecoveryPasswordProtector
    Enable-BitLockerAutoUnlock -MountPoint d:
    New-Item -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Force
    New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name FDVActiveDirectoryBackup -Value 1 -PropertyType DWORD -Force
    New-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\FVE -Name FDVRecovery -Value 1 -PropertyType DWORD -Force
    $KeyProtectorID=((Get-BitLockerVolume -MountPoint d:).KeyProtector | where-object KeyProtectorType -eq RecoveryPassword).KeyProtectorID
    Backup-BitLockerKeyProtector -MountPoint d: -KeyProtectorId $KeyProtectorID
}
 

Note: there is some error complaining about "A parameter cannot be found that matches parameter name 'First'." (I need to investigate what it requires to run, but all seems to be OK)

And now let's ask Jane to check if all is ok on PC's BitLocker1 and BitLocker2

Invoke-Command -ComputerName "BitLocker1","BitLocker2" -Credential JaneDoe -ConfigurationName JEA-BitLocker -ScriptBlock {
    Get-BitLockerVolume
}
 

Seems OK!

BitLocker event log

Now let's see if we can see event about BitLocker key successfully backed up into AD. Note: I borrowed following code from this blog.

#Grab events from remote computers
$Computers="BitLocker1","BitLocker2"
$allevents=Invoke-Command -ComputerName $Computers -ScriptBlock {
    $events=Get-WinEvent -FilterHashtable @{"ProviderName"="Microsoft-Windows-BitLocker-API";Id=784}
    ForEach ($Event in $Events) {
        # Convert the event to XML
        $eventXML = [xml]$Event.ToXml()
        # create custom object for all values
        for ($i=0; $i -lt $eventXML.Event.EventData.Data.Count; $i++) {
            # Append these as object properties
            Add-Member -InputObject $Event -MemberType NoteProperty -Force -Name  $eventXML.Event.EventData.Data[$i].name -Value $eventXML.Event.EventData.Data[$i].'#text'
        }
    }
    return $events
}
$allevents | select * | ogv
 

Events displayed in Out-GridView

PowerShell Logging

In this part we will enable PowerShell logging into event log as documented in this article

First let's enable scriptblocklogging with following registries.

#Enable ScriptBlock logging
$Computers="BitLocker1","BitLocker2"
Invoke-Command -Computername $Computers -ScriptBlock {
    New-Item -Path HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging -Force
    New-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging -Name EnableScriptBlockLogging           -Value 1 -PropertyType DWORD -Force
    New-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging -Name EnableScriptBlockInvocationLogging -Value 1 -PropertyType DWORD -Force
}
 

And let's run get-bitlockervolume command in normal and then in JEA session

Invoke-Command -computername "BitLocker1", "BitLocker2" -ScriptBlock {
    Get-BitlockerVolume
}

Invoke-Command -computername "BitLocker1", "BitLocker2" -Credential JohnDoe -ConfigurationName JEA-BitLocker -ScriptBlock {
    Get-BitlockerVolume
}
 

Let's check what's in EventLog under eventID 4104

$Computers="BitLocker1","BitLocker2"
$allevents=Invoke-Command -ComputerName $Computers -ScriptBlock {
    $events=Get-WinEvent -FilterHashtable @{"ProviderName"="Microsoft-Windows-PowerShell";Id=4104}
    ForEach ($Event in $Events) {
        # Convert the event to XML
        $eventXML = [xml]$Event.ToXml()
        # create custom object for all values
        for ($i=0; $i -lt $eventXML.Event.EventData.Data.Count; $i++) {
            # Append these as object properties
            Add-Member -InputObject $Event -MemberType NoteProperty -Force -Name  $eventXML.Event.EventData.Data[$i].name -Value $eventXML.Event.EventData.Data[$i].'#text'
        }
    }
    return $events
}
$allevents | select * | ogv
 

OK, that is a bit mess. But as you can see, BitLocker is not well recorded, while script for grabbing events is nicely recorded. Let's try another transcripting.

JEA transcription

(optional) first let's create transcript directory and secure it as described in this article

$Computers="BitLocker1","BitLocker2"
Invoke-Command -ComputerName $Computers -ScriptBlock {
    md c:\Transcripts
    ## Kill all inherited permissions
    $acl = Get-Acl c:\Transcripts
    $acl.SetAccessRuleProtection($true, $false)
    ## Grant Administrators full control
    $administrators = [System.Security.Principal.NTAccount] "Administrators"
    $permission = $administrators,"FullControl","ObjectInherit,ContainerInherit","None","Allow"
    $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
    $acl.AddAccessRule($accessRule)
    ## Grant everyone else Write and ReadAttributes. This prevents users from listing
    ## transcripts from other machines on the domain.
    $everyone = [System.Security.Principal.NTAccount] "Everyone"
    $permission = $everyone,"Write,ReadAttributes","ObjectInherit,ContainerInherit","None","Allow"
    $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
    $acl.AddAccessRule($accessRule)
    ## Deny "Creator Owner" everything. This prevents users from
    ## viewing the content of previously written files.
    $creatorOwner = [System.Security.Principal.NTAccount] "Creator Owner"
    $permission = $creatorOwner,"FullControl","ObjectInherit,ContainerInherit","InheritOnly","Deny"
    $accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
    $acl.AddAccessRule($accessRule)
    ## Set the ACL
    $acl | Set-Acl c:\Transcripts\
}
 

Let's configure transcription now using psrc file (does not require restart). It's the same as we used before, just with one more parameter (transcriptdirectory)

$Computers="BitLocker1","BitLocker2"
Invoke-Command -ComputerName $Computers -ScriptBlock {
    $Modules="BitLocker"
    $AdminVisibleCmdLets ="BitLocker\*","Get-CimInstance","New-ItemProperty","New-Item","Out-String","Where-Object","Select-Object"    #All commands from BitLocker module + all others since Enable-BitLocker and Backup-BitLockerKeyProtector needs it.
    $AdminVisibleExternalCommands = "C:\Windows\System32\where.exe","C:\Windows\System32\whoami.exe"
    $AdminVisibleProviders= "registry","Variable"
    $ViewerVisibleCmdLets="BitLocker\Get-*","Get-CimInstance" #All commands from BitLocker module that start with Get-
    $AdminRoleName= "BitLockerAdmin"
    $ViewerRoleName="BitLockerViewer"
    $ConfigurationName="JEA-BitLocker"
    $AdminGroup= "JEA-BitLockerAdmins"
    $ViewerGroup="JEA-BitLockerViewers"
    $TranscriptDirectory="C:\Transcripts"

    #Create folders for JEA
    $Folders="$env:ProgramFiles\WindowsPowerShell\Modules\JEARoles\","$env:ProgramFiles\WindowsPowerShell\Modules\JEARoles\RoleCapabilities","$env:ProgramData\JEAConfiguration"
    foreach ($Folder in $Folders){
        New-Item -Path $Folder -ItemType Directory -force
    }
    #Create JEA Manifest
    New-ModuleManifest -Path "$env:ProgramFiles\WindowsPowerShell\Modules\JEARoles\JEARoles.psd1" -Description "Contains custom JEA Role Capabilities"

    #Create RoleCapabilityFile and SessionConfigurationFile
    New-PSRoleCapabilityFile -Path "$env:ProgramFiles\WindowsPowerShell\Modules\JEARoles\RoleCapabilities\$AdminRoleName.psrc" -ModulesToImport $Modules -VisibleCmdLets $AdminVisibleCmdLets -VisibleExternalCommands $AdminVisibleExternalCommands -VisibleProviders $AdminVisibleProviders
    New-PSRoleCapabilityFile -Path "$env:ProgramFiles\WindowsPowerShell\Modules\JEARoles\RoleCapabilities\$ViewerRoleName.psrc" -ModulesToImport $Modules -VisibleCmdLets $ViewerVisibleCmdLets
    New-PSSessionConfigurationFile -Path $env:ProgramData\JEAConfiguration\$ConfigurationName.pssc -SessionType RestrictedRemoteServer -LanguageMode FullLanguage -RunAsVirtualAccount -RoleDefinitions @{"$env:USERDOMAIN\$AdminGroup" = @{ RoleCapabilities = "$AdminRoleName" };"$env:USERDOMAIN\$ViewerGroup" = @{ RoleCapabilities = "$ViewerRoleName" }} -TranscriptDirectory $TranscriptDirectory

    #RegisterPSsessionConfiguration
    Register-PSSessionConfiguration -Path $env:ProgramData\JEAConfiguration\$ConfigurationName.pssc -Name $ConfigurationName -Force
}
 

Let's shoot some more PowerShell just to see what's recorded in text files.

$Computers="BitLocker1","BitLocker2"
Invoke-Command -computername $Computers -ScriptBlock {
    Get-BitlockerVolume
}

Invoke-Command -computername $Computers -Credential JohnDoe -ConfigurationName JEA-BitLocker -ScriptBlock {
    Get-BitlockerVolume
}
 

And let's check the transcript. Notice just JEA sessions are recorded

$Computers="BitLocker1","BitLocker2"
Invoke-Command -computername $Computers -ScriptBlock {
    $logfiles=Get-ChildItem -Path "C:\Transcripts" -recurse | where name -like *.txt
    Foreach ($logfile in $logfiles){
         $logfile.FullName
         Get-Content -Path $logfile.FullName
    }
}