Closed jsdhasfedssad closed 1 year ago
Hi @jsdhasfedssad , the thing is that the output of the GoldenGMSA.exe compute
command is the password itself and not the data structure that is understood by the ConvertFrom-ADManagedPasswordBlob
cmdlet.
So just use PowerShell 101 to calculate the NT hash of the password:
$encodedPassword = 'ZSWQIz5mLbEcw6mm8J49DWCADxDT703UPFLbRftDTqE7WzAfmd7mQt75o1ZvIpTqGo5okZycD2rZgBdVu9wbTxIQL6H4hbAcODW43Yv0tXxo0IYajhCiyrJ7Vvchs4dWUtCED/0pOdStxG2H7dSriLgoN62YJushjRIkHGQvK4E5zPvu5dREepOCDuhAE6F1mo33kKtpo7fWvlNgQgYNIvCYqtv4PnYzNkRZYqbMZwhocjkMRFq9WzA5YzYZwj5wgeBmi7Fl2bcm7139AIAIZgUs1XaMEPXnjlFGizH8ry9b9iOsv6YaHWBtktm5ECSVcHGHpLISVk7Rzn2tRvljew=='
$binaryPassword = [System.Convert]::FromBase64String($encodedPassword)
$unicodePassword = [System.Text.Encoding]::Unicode.GetString($binaryPassword)
$encryptedPassword = ConvertTo-SecureString -String $unicodePassword -AsPlainText -Force
ConvertTo-NTHash -Password $encryptedPassword
The output should be 768b14d275314229334684ebc73833b9
. And if you want to generate Kerberos AES keys instead, use the ConvertTo-KerberosKey
cmdlet, which additionally requires the corresponding salt.
I tested converting a blob to a NT hash using DSInternals and your Powershell instructions. While this works the outputted NT hash is incorrect. If I instead use Python to convert the very same input the outputted NT hash is correct. The two NT hashes converted using the same input does not match. See below.
Enumeration of GMSA accounts, calculation of the password blob by requesting the KDS key and finally converting to a NT hash using Powershell and DSInternals:
Using the same values as above, converting to a NT hash using Python and verification of both NT hashes of which the one outputted by DSInternals is incorrect:
Even though I now have a way of getting the NT hash that works I am still interested in doing the same using Powershell and DSInternals if possible.
Hi @jsdhasfedssad , I digged deeper and the issue seems to be UTF-16 related.
Example:
When I use DSInternals to decode a sample managed password blob from AD, I get this BASE64(Unicode(cleartext)) value:
1TkRlDJIn28+Z5CsuUTNvt7krq1eMo5pDzbBWcMctnovsxLDMDUh8Nh344cOt+JxBue/7AnFoEbFposEBQiuMdob80eOTLjswaYUKRPyYbtfAuS41PNICPthtmioAZGUaOiRwspVZqi0/7RDDw39/0KcOH0V4uqfp9an07PN9dMNU5c7Zm/BgR0Kww8vfpusOSIA9Crho1PqA9Qo8CqPJsA6HpbFuoVpYIRnQD9KcdVlsDkLL610Ztwr4gAsXtKQkScIsktw6KBdqUJFlQT9/4nhPRUY+k22/f9iMZoBgijZs+NPSIr9//DklemB+2KLBE6VeFcg9MNskQVI0BbJ8g==
My code sample from above calculates the right NT hash, which is 2b242f534c181917f3f3c8a1afd98ec2
. But GoldenGMSA.exe
gives me a value that differs in just a few bytes (out of 256):
1TkRlDJIn28+Z5CsuUTNvt7krq1eMo5pDzbBWcMctnovsxLDMDUh8Nh344cOt+JxBue/7AnFoEbFposEBQiuMdob80eOTLjswaYUKRPyYbtfAuS41PNICPthtmioAZGUaOiRwspVZqi0/7RDDw3z2UKcOH0V4uqfp9an07PN9dMNU5c7Zm/BgR0Kww8vfpusOSIA9Crho1PqA9Qo8CqPJsA6HpbFuoVpYIRnQD9KcdVlsDkLL610Ztwr4gAsXtKQkScIsktw6KBdqUJFlQSF2onhPRUY+k2299piMZoBgijZs+NPSIrE3/DklemB+2KLBE6VeFcg9MNskQVI0BbJ8g==
The difference seems to be in Unicode normalization (DSInternals produces normalized strings, while GoldenGMSA does not), but calling the String.Normalize()
method does not seem to do the trick.
OK, @jsdhasfedssad , so here is a code snippet that gives the right results:
[string] $encodedPassword = '1TkRlDJIn28+Z5CsuUTNvt7krq1eMo5pDzbBWcMctnovsxLDMDUh8Nh344cOt+JxBue/7AnFoEbFposEBQiuMdob80eOTLjswaYUKRPyYbtfAuS41PNICPthtmioAZGUaOiRwspVZqi0/7RDDw3z2UKcOH0V4uqfp9an07PN9dMNU5c7Zm/BgR0Kww8vfpusOSIA9Crho1PqA9Qo8CqPJsA6HpbFuoVpYIRnQD9KcdVlsDkLL610Ztwr4gAsXtKQkScIsktw6KBdqUJFlQSF2onhPRUY+k2299piMZoBgijZs+NPSIrE3/DklemB+2KLBE6VeFcg9MNskQVI0BbJ8g=='
[byte[]] $binaryPassword = [System.Convert]::FromBase64String($encodedPassword)
[System.Runtime.InteropServices.GCHandle] $pinnedArray = [System.Runtime.InteropServices.GCHandle]::Alloc($binaryPassword, [System.Runtime.InteropServices.GCHandleType]::Pinned)
try
{
[string] $unicodePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($pinnedArray.AddrOfPinnedObject(), $binaryPassword.Count / 2)
}
finally
{
$pinnedArray.Free()
}
$encryptedPassword = ConvertTo-SecureString -String $unicodePassword -AsPlainText -Force
ConvertTo-NTHash -Password $encryptedPassword
Probably not the cleanest solution, but it at least works.
Hi,
Thank you for this tool!
I read DSInternals post Retrieving Cleartext GMSA Passwords from Active Directory and wanted to combine that with the cross-domain Golden GMSA attack described here.
During testing I can successfully retrieve the password as described in the post Retrieving Cleartext GMSA Passwords from Active Directory. However, this requires being logged in on a DC that has access to the GMSA account as an account that has read rights which is not possible during cross-domain attacks. Using the tool GoldenGMSA it is possible to retrieve the password blob remotely across domains which solved this problem.
GoldenGMSA's output is a base64 encoded version of the password blob which cannot be fed into the function
ConvertFrom-ADManagedPasswordBlob
even after decoding it. The error I get is "Input string was not in a correct format". I understand why this happens which is why I ask you if there is any way to make this function to work on a non-DC using a manually inputted base64 encoded or decoded password blob?The end goal is to be able to use the function
ConvertTo-NTHash
to get the NT hash of the GMSA account's password.Thanks!