Decrypting ASP.NET 4.5

The title mentions ASP.NET 4.5.x, but the encryption algorithm is exactly the same in ASP.NET 4.6.x. It won’t work however in earlier versions of ASP.NET.

Some time ago I published a post entitled “Decrypting ASP.NET identity cookies”. In that post we wrote a Python script to decrypt ASP.NET Identity cookies. You could have also learnt how the derived keys, used to encrypt those cookies, are calculated. If you are interested in details, please have a look at that article. But to summarize, the following steps are performed by ASP.NET:

  1. Extract the encryption and the validation key from the web.config file
  2. Calculate the derived keys using the SP800-108 specification, with the context and the label taken from an adequate Purpose class instance
  3. Validate and decrypt the cipher

The above procedure applies not only to the cookies decryption, but also to many other cryptographic operations, such as ViewState encryption, Forms Authentication, Anti-Forgery tokens creation etc. However, there is still a missing gap in the presented flow. What if the encryption and the validation keys are not explicitly set in the web.config file? Today, we will answer this question.

Machine Key

When no keys are specified in either the project web.config or the global web.config/machine.config, both the encryption and validation keys are set to a value: Autogenerated,IsolateApps. The encryption algorithm is then AES-256, and the validation algorithm HMACSHA256. The Autogenerated setting makes ASP.NET produce the keys when they are requested for the first time. To accomplish that ASP.NET generates a random 1024 bytes long key material (HttpRuntime.SetAutogenKeys), from which it extracts the first 32 bytes for the symmetric encryption key, and the next 32 bytes for the HMAC validation (MachineKeyMasterKeyProvider.GenerateCryptographics). The key might be later “customized” per application by providing IsolateApps (default) or IsolateByAppId setting. The personalization is just a process of creating a derived key (using, you guessed, SP800-108) with a context set to “MachineKeyDerivation”, and a label set to the “IsolateApps: /” combined with the Application Name (or consequently “IsolateByAppId: /” combined with the Application Id). If you feel lost, don’t worry – in a moment we will have a demo of the decryption process. But first, let’s examine where those 1024 bytes are stored.

After generating the key, ASP.NET first checks the result of the OpenLsaPolicy function. If it succeeds (which by default applies to the Administrator and SYSTEM account), the key will be stored as the LSA Private Data with the secret “L$ASP.NETAutoGenKeysV44.0.30319.0” (using the LsaStorePrivateData method), and you may find it in the registry under the HKEY_LOCAL_MACHINE\SECURITY\Policy\Secrets\L$ASP.NETAutoGenKeysV44.0.30319.0 key. To decrypt the value you may use the CQSecretsDumper, written by Michał Grzegorzewski and provided by Paula Januszkiewicz in one of the CQURE Academy posts. This tool is able to decrypt the private data either in the online or offline mode. In the online scenario, you may simply run: CQSecretsDumper --secret=L$ASP.NETAutoGenKeysV44.0.30319.0, and the decrypted key material will appear on the console.

If the OpenLsaPolicy call fails, ASP.NET checks if it can find the HKEY_CURRENT_USER\Software\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeyV4 key. If it is not there, it tries to create it. For an account with a profile (such as a normal user account, but also NETWORK SERVICE or LOCAL SERVICE) this should succeed. The value stored under this key is not encrypted.

The above approach won’t work if the w3wp process owner is a virtual account (all the accounts starting with IIS AppPool\ are virtual accounts). In such a case the ASP.NET code tries to create the HKU\.DEFAULT\Software\Microsoft\ASP.NET key, which fails with an ACCESS DENIED error. The ASP.NET then tries its luck with the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\{SID}\AutoGenKeyV4 key, where SID is the SID of the virtual account. The key material stored under this key will be unencrypted. Interestingly, I observed that on Windows 7 those virtual accounts actually have their own HKCU hives, and in consequence the ASP.NET AutoGenerated keys are stored in them.

If, for any reason, the key could not be saved, it will exist only in the process memory and will be lost when process exits. All the encrypted data will be unrecoverable too, forcing all users to sign in again. The key in the memory can be extracted by examining the instance of the System.Web.Security.Cryptography.MachineKeyMasterKeyProvider class (should be one per web application), example:

0:000> !mdt 0dad1870
0dad1870 (System.Web.Security.Cryptography.MachineKeyMasterKeyProvider)
    _applicationId:NULL (System.String)
    _applicationName:07ac6844 (System.String) Length=1, String="/"
    _autogenKeys:0dad2168 (System.Web.Security.Cryptography.CryptographicKey)
    _encryptionKey:0dad257c (System.Web.Security.Cryptography.CryptographicKey)
    _keyDerivationFunction:0dad2174 (System.Web.Security.Cryptography.KeyDerivationFunction)
    _machineKeySection:0dad1424 (System.Web.Configuration.MachineKeySection)
    _validationKey:0dad25f0 (System.Web.Security.Cryptography.CryptographicKey)

Decryption demo

Let’s assume we have the following cookie to decrypt:

.AspNet.ApplicationCookie=vITJ34FczpstfZ2_7IpSDUNPs_rIFmEXHUfIq0t7-7idXiX0s9rMiJSitnmi05f3jctR6ih2CYYWzXlml6gFJeNVo4xl2KBL8TY71LD6IebRi_YQvOKPS-QR-tybV4CwwQnkZ5YVyRl_GkAavioDOA6epId85_YQvDCfd8wGcC9NVEjCUmLxHmZExr-XS99_o0eRb1-06eh0CuDGExNuULqvQLf8eCUKRHKulWzYPokoVv65m-y6PNN3wSEhxh8gYQtWN8Qr5kzEQNlw7TKtiVHx12CPR93eWNnbpQA9A6SNLQ5kzUTsL4DrHmei1mIypJ8jNNN6FIYBwnPtfdiqwkjddZn3Q1cGab1_2iqvIl7UQyhJ6UdtOgzmED_bmBCkh0hkNEpG4PRmMRyEQy3s8mIPDLAw3p71Evu--dedpvdB5_vvtGcfNYEaSaMFngpJkkYze1deGrzbylHtSG10hpvfy0AuJoEt6pWSj8kqrzk

Our ASP.NET application is run by the “ASP.NET V4.0 Integrated” application pool, thus we are using a virtual account, and we can extract the machine key material from the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\{SID}\ AutoGenKeyV4. We can find the SID by running psgetsid, example:

PS temp> psgetsid "IIS AppPool\ASP.NET V4.0 Integrated"
PsGetSid v1.45 - Translates SIDs to names and vice versa
Copyright (C) 1999-2016 Mark Russinovich
Sysinternals - www.sysinternals.com
SID for IIS AppPool\ASP.NET V4.0 Integrated:
S-1-5-82-1473323969-2192115299-762856444-358384564-2710455378

As the key for the virtual account is unencrypted we may save the first 32-bytes as the encryption key, and the next 32-bytes as the validation key. In our case, the encryption key would be:

8F 12 48 32 E2 20 40 18 C6 CE 3E 5D E8 AB BD D2 
44 BB 83 F9 BB F5 63 C8 11 72 FB 0F D3 1B 51 77

And the validation key:

17 26 E7 44 C1 FF 4A 6E 84 A1 B5 11 CD DA DD 10 
A1 AB 08 20 44 23 8A 10 53 3F 8B BB 87 20 19 26

We are using the default ASP.NET settings, thus our master keys are application-specific (Autogenerated,IsolatedApps). We then need to create derived keys. I prepared a special tool for this purpose, named AspNetKeyDerive, available in my github repository. My test application name is simply “/”. The derived keys will be then:

PS Debug> .\AspNetDerive.exe -k 8F124832E2204018C6CE3E5DE8ABBDD244BB83F9BBF563C81172FB0FD31B5177 `
-c MachineKeyDerivation -l "IsolateApps: /"
0000: 15 45 f6 86 b6 ae 6d c2 98 fa fc 7a ae 95 96 54  .Eö...mÂ.úüz...T
0010: 48 97 31 c8 b3 37 fa 16 68 cb 56 7f 48 12 bd c6  H.1E.7ú.hËV.H..A
PS Debug> .\AspNetDerive.exe -k 1726E744C1FF4A6E84A1B511CDDADD10A1AB082044238A10533F8BBB87201926 `
-c "MachineKeyDerivation" -l "IsolateApps: /"
0000: f2 e0 94 2f 79 0a d1 bb 01 eb 90 50 5c 8b b8 c0  oa./y.N».ë.P\..A
0010: f5 28 41 9b bc fb 6a e2 42 cc cc 7b 51 52 53 8c  o(A..ujâBII{QRS. 

Finally, we may use another tool from the same repository, named AspNetCrypter (you may as well use the Python script from the past :)):

PS Debug> .\AspNetCrypter.exe --dk=1545f686b6ae6dc298fafc7aae959654489731c8b337fa1668cb567f4812bdc6 `
--vk=f2e0942f790ad1bb01eb90505c8bb8c0f528419bbcfb6ae242cccc7b5152538c `
--purpose=owin.cookie --base64 vITJ34FczpstfZ2_7IpSDUNPs_rIFmEXHUfIq0t7-7idXiX0s9rMiJSitnmi05f3jctR6ih2CYYWzXlml6gFJeNVo4xl2KBL8TY71LD6IebRi_YQvOKPS-QR-tybV4CwwQnkZ5YVyRl_GkAavioDOA6epId85_YQvDCfd8wGcC9NVEjCUmLxHmZExr-XS99_o0eRb1-06eh0CuDGExNuULqvQLf8eCUKRHKulWzYPokoVv65m-y6PNN3wSEhxh8gYQtWN8Qr5kzEQNlw7TKtiVHx12CPR93eWNnbpQA9A6SNLQ5kzUTsL4DrHmei1mIypJ8jNNN6FIYBwnPtfdiqwkjddZn3Q1cGab1_2iqvIl7UQyhJ6UdtOgzmED_bmBCkh0hkNEpG4PRmMRyEQy3s8mIPDLAw3p71Evu--dedpvdB5_vvtGcfNYEaSaMFngpJkkYze1deGrzbylHtSG10hpvfy0AuJoEt6pWSj8kqrzk

0000: 03 00 00 00 11 41 70 70 6c 69 63 61 74 69 6f 6e  .....Application
0010: 43 6f 6f 6b 69 65 01 00 01 00 04 00 00 00 44 68  Cookie........Dh
0020: 74 74 70 3a 2f 2f 73 63 68 65 6d 61 73 2e 78 6d  ttp://schemas.xm
0030: 6c 73 6f 61 70 2e 6f 72 67 2f 77 73 2f 32 30 30  lsoap.org/ws/200
0040: 35 2f 30 35 2f 69 64 65 6e 74 69 74 79 2f 63 6c  5/05/identity/cl
0050: 61 69 6d 73 2f 6e 61 6d 65 69 64 65 6e 74 69 66  aims/nameidentif
0060: 69 65 72 24 31 64 35 31 62 32 34 63 2d 66 35 65  ier.1d51b24c-f5e
0070: 61 2d 34 61 33 62 2d 39 39 39 65 2d 63 35 37 31  a-4a3b-999e-c571
0080: 61 39 34 31 30 63 63 64 01 00 01 00 01 00 01 00  a9410ccd........
0090: 0d 74 65 73 74 40 74 65 73 74 2e 63 6f 6d 01 00  .test@test.com..
00a0: 01 00 01 00 51 68 74 74 70 3a 2f 2f 73 63 68 65  ....Qhttp://sche
00b0: 6d 61 73 2e 6d 69 63 72 6f 73 6f 66 74 2e 63 6f  mas.microsoft.co
00c0: 6d 2f 61 63 63 65 73 73 63 6f 6e 74 72 6f 6c 73  m/accesscontrols
00d0: 65 72 76 69 63 65 2f 32 30 31 30 2f 30 37 2f 63  ervice/2010/07/c
00e0: 6c 61 69 6d 73 2f 69 64 65 6e 74 69 74 79 70 72  laims/identitypr
00f0: 6f 76 69 64 65 72 10 41 53 50 2e 4e 45 54 20 49  ovider.ASP.NET.I
0100: 64 65 6e 74 69 74 79 01 00 01 00 01 00 1d 41 73  dentity.......As
0110: 70 4e 65 74 2e 49 64 65 6e 74 69 74 79 2e 53 65  pNet.Identity.Se
0120: 63 75 72 69 74 79 53 74 61 6d 70 24 36 33 62 61  curityStamp.63ba
0130: 39 65 62 33 2d 33 66 64 38 2d 34 31 36 35 2d 39  9eb3-3fd8-4165-9
0140: 32 34 33 2d 38 37 33 62 64 33 66 62 64 34 35 39  243-873bd3fbd459
0150: 01 00 01 00 01 00 00 00 00 00 01 00 00 00 02 00  ................
0160: 00 00 07 2e 69 73 73 75 65 64 1d 54 75 65 2c 20  ....issued.Tue,.
0170: 32 39 20 4e 6f 76 20 32 30 31 36 20 31 37 3a 34  29.Nov.2016.17:4
0180: 36 3a 34 32 20 47 4d 54 08 2e 65 78 70 69 72 65  6:42.GMT..expire
0190: 73 1d 54 75 65 2c 20 31 33 20 44 65 63 20 32 30  s.Tue,.13.Dec.20
01a0: 31 36 20 31 37 3a 34 36 3a 34 32 20 47 4d 54     16.17:46:42.GMT

2 thoughts on “Decrypting ASP.NET 4.5

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.