Decrypting View State Messages

speckx1 pts0 comments

Zeroed Tech

Zeroed.Tech

Decrypting View State Messages<br>Published: 23/01/2026

I recently had someone reach out to me with an interesting problem. They had found a 1316 event in their Windows application logs that contained a likely malicious view state. There was just one catch, it was encrypted. To make matters worse, all they had access to was a disk image of the host. After extracting the web.config file for the affected site, they found the compromised site had been configured with automatically generated keys. They were able to dump the autogen keys from the Windows registry, however they didn’t know how to use these to decrypt their view state.<br>The complexity of decrypting a view state ranges from trivial for legacy setups (simply drop the validation hash from the end of the buffer, then decrypt using the right key and symmetric algorithm) to complex for modern setups, which have historically involved using reflection to “trick” IIS into decrypting values for us. I usually rely on CyberChef to decrypt legacy view states and Blacklist3r for modern view states. Personally, I find both tools a little painful to use for decrypting view states, so I’ll share a new tool with you at the end of this post.<br>Coming back to our original problem, regardless of whether our encrypted view state is legacy or modern, having the autogen keys won’t do us much good; we’ll need the final machine keys if we want to decrypt it.<br>But how do you go from the autogen key blob stored in the registry or an LSA secret to a decryption key we can actually use?<br>This post will be a continuation of View State, The unpatchable IIS forever day being actively exploited, which I highly recommend reading before continuing with this post. In my previous post, I covered the key generation process at a high level and mentioned some of the modifiers that can be passed into the key generation process to ensure key uniqueness across applications; however, that post was heavily focused on the legacy (but still dominant) crypto configuration and barely mentioned the modern configuration that apps should be moving towards.<br>In this post, I’ll be covering:<br>How the autogen keys are generated<br>How the master machine keys are derived from the autogen keys<br>How the final machine keys are derived from the master machine keys<br>How the final keys can be used to decrypt view state messages<br>And this time, I’ll be covering all this for both the legacy and the modern crypto configurations.<br>What is an autogen key, and how is it generated?<br>“Autogen keys” feels like a poor name choice given they aren’t really keys, but that’s what Microsoft refers to them as in both their code and the registry, so we’ll stick with that here. An autogen key is a 1024-byte blob of data that contains the master IIS validation and decryption machine keys at specific offsets. This is the value that is stored in the registry or an (encrypted) LSA secret and is read at runtime.<br>We can see how this “key” is generated in System.Web.HttpRuntime::SetAutogenKeys():<br>Every Namespace.Class::Function combo mentioned in this post can be found in the System.Web assembly in the .NET Framework.

internal static byte[] s_autogenKeys = new byte[1024];

private static void SetAutogenKeys()<br>byte[] array = new byte[HttpRuntime.s_autogenKeys.Length];<br>byte[] array2 = new byte[HttpRuntime.s_autogenKeys.Length];<br>bool flag = false;<br>RNGCryptoServiceProvider rngcryptoServiceProvider = new RNGCryptoServiceProvider();<br>rngcryptoServiceProvider.GetBytes(array);<br>if (!flag)<br>flag = UnsafeNativeMethods.EcbCallISAPI(IntPtr.Zero, UnsafeNativeMethods.CallISAPIFunc.GetAutogenKeys, array, array.Length, array2, array2.Length) == 1;<br>if (flag)<br>Buffer.BlockCopy(array2, 0, HttpRuntime.s_autogenKeys, 0, HttpRuntime.s_autogenKeys.Length);<br>return;<br>Buffer.BlockCopy(array, 0, HttpRuntime.s_autogenKeys, 0, HttpRuntime.s_autogenKeys.Length);<br>} The variable names generated by dbSpy aren’t very nice, so let’s clean things up a bit before diving in:<br>internal static byte[] s_autogenKeys = new byte[1024];

private static void SetAutogenKeys()<br>byte[] randBytes = new byte[HttpRuntime.s_autogenKeys.Length];<br>byte[] autogenKey = new byte[HttpRuntime.s_autogenKeys.Length];<br>bool existingKeyLoaded = false;<br>RNGCryptoServiceProvider rngcryptoServiceProvider = new RNGCryptoServiceProvider();<br>rngcryptoServiceProvider.GetBytes(randBytes);<br>if (!existingKeyLoaded)<br>existingKeyLoaded = UnsafeNativeMethods.EcbCallISAPI(IntPtr.Zero, UnsafeNativeMethods.CallISAPIFunc.GetAutogenKeys, randBytes, randBytes.Length, autogenKey, autogenKey.Length) == 1;<br>if (existingKeyLoaded)<br>Buffer.BlockCopy(autogenKey, 0, HttpRuntime.s_autogenKeys, 0, HttpRuntime.s_autogenKeys.Length);<br>return;<br>Buffer.BlockCopy(randBytes, 0, HttpRuntime.s_autogenKeys, 0, HttpRuntime.s_autogenKeys.Length);<br>} Much better.<br>When SetAutogenKeys is called (which happens as part of the HttpRuntime initalising), two 1024-byte arrays are defined. The first array, randBytes, is filled with random data whilst...

byte httpruntime s_autogenkeys keys length view

Related Articles