October 28, 2026
A Hidden Form Field That Deserialized the Entire Server: ASP.NET ViewState Deserialization
ASP.NETβs ViewState mechanism is one of those pieces of framework machinery most developers interact with constantly without ever reallyβ¦

By T4nv1
4 min read
ASP.NET's ViewState mechanism is one of those pieces of framework machinery most developers interact with constantly without ever really understanding what it's doing underneath. It exists to preserve a web form's state across postbacks, packed into a hidden field and sent back and forth with every request. For most of its history, that mechanism has carried a serious risk hiding in plain sight: unless explicitly configured otherwise, the data inside that hidden field is a serialized .NET object graph, and the framework's own protections against tampering depend entirely on a machine key being correctly configured and kept secret.
The target, which I'll call Oakmere Systems here per their program's disclosure terms, is an enterprise scheduling platform built on a legacy ASP.NET Web Forms application, carrying forward years of architecture decisions from well before modern .NET security defaults existed.
What ViewState Actually Is
Every ASP.NET Web Forms page can embed a __VIEWSTATE hidden field, holding a base64-encoded, serialized representation of the page's control state. Properly configured, this field is both encrypted and integrity-protected using a server-side machine key, meaning a client can't read its contents and can't tamper with it without invalidating a cryptographic signature the server checks on every postback. The entire security model depends on that machine key being a genuine secret, generated per-application, and never exposed.
The First Signal
I decoded a __VIEWSTATE value from an ordinary page load, out of habit more than specific suspicion, and the base64-decoded bytes didn't look encrypted at all β readable fragments of what appeared to be .NET type names and string data were visible directly in the decoded output, rather than the uniform, structureless appearance genuinely encrypted data produces. That's a strong early signal that ViewState encryption wasn't actually enabled for this application, even though the signature-validation half of the protection might still be active independently.
Confirming Validation Was the Real Gap
Encryption being disabled is a real information-disclosure concern on its own β anyone could read the serialized control state β but the considerably more serious question was whether the integrity signature was being validated at all, because if it wasn't, the application would accept a modified ViewState payload without complaint, and modified, unencrypted .NET serialized data handed back to the server for deserialization is a direct and well-documented path toward remote code execution.
I took a legitimate ViewState value, decoded it, made a trivial, harmless modification to a string field within it, re-encoded it, and submitted it as a postback in place of the real value. If signature validation was active, this should have been rejected outright with a cryptographic validation error. It wasn't β the modified postback was accepted and processed normally, confirming that whatever machine key configuration the application was running under either wasn't validating signatures at all, or was validating against a key I could determine.
Finding the Key
Legacy ASP.NET applications, particularly ones deployed across a server farm without centralized key management, sometimes rely on a small, well-documented set of default or widely reused machine key values, especially in applications scaffolded from older project templates that shipped with a fixed example key developers were supposed to β but didn't always β replace before deployment. I checked the modified ViewState's behavior against a short list of known, publicly documented default and commonly-reused machine key values from legacy ASP.NET tooling, generating a correctly-signed malicious ViewState payload offline using each candidate key and testing whether the server accepted it as valid.
One candidate worked. The server accepted a ViewState payload I had generated entirely offline, signed with a publicly known default key value, confirming Oakmere's application was running with that same default key still in place rather than a unique, properly generated one.
From a Known Key to Remote Code Execution
With a working, correctly-signing key in hand, the rest of the chain followed a well-documented pattern for this exact vulnerability class: construct a malicious serialized .NET object graph using a known gadget chain β a sequence of classes whose deserialization behavior, when chained together, ultimately invokes a dangerous operation β sign it with the recovered key, and submit it as the ViewState value on any ordinary postback.
I used a minimal, publicly documented gadget chain for this exact scenario, configured to execute a single, inert, clearly observable command rather than anything destructive, and confirmed execution via an out-of-band network callback triggered the moment the server deserialized my submitted ViewState payload and executed the embedded command as part of reconstructing the object graph it contained.
Keeping the Proof of Concept Minimal
I used only the inert callback-triggering command, confirmed execution once, and immediately stopped β making no attempt to read files, explore the server's internal network, or establish any form of persistence through the access this chain demonstrated.
The Report
Title: ASP.NET ViewState deserialization via known default machine key allows remote code execution
Root cause: The application has ViewState encryption disabled and is signing ViewState payloads using a publicly known, default machine key value rather than a unique, properly generated secret. This allows an attacker to construct, sign, and submit an arbitrary serialized .NET object graph that the server will deserialize and execute as part of normal postback processing.
Reproduction: The unencrypted ViewState observation, the signature-bypass confirmation using a known default key, and command execution confirmation via an out-of-band callback triggered during deserialization, using only a single inert command.
Impact: Full remote code execution on the application server, achievable by any party able to submit a postback request, requiring no authentication and no prior access.
Fix recommendations:
- Generate a unique, cryptographically random machine key for this application specifically, never reused across environments or inherited from a project template's default
- Enable both encryption and signature validation for ViewState explicitly in the application's configuration, rather than relying on framework defaults that may vary across hosting environments and .NET versions
- Migrate away from ViewState-dependent legacy Web Forms architecture where feasible, since this vulnerability class recurs across the ecosystem specifically because of how easy it is for a default or template-inherited key to go unnoticed for years
What Happened After
The platform rotated to a unique, properly generated machine key and enabled full ViewState encryption and validation as an emergency fix, given the direct and complete remote code execution path the misconfiguration represented.
Why Legacy Framework Internals Still Deserve Attention
ViewState deserialization is a vulnerability class that feels dated, tied to a framework era many newer developers have never worked with directly β which is exactly why it persists in production longer than it should, on systems built years ago and never fully modernized. Any ASP.NET Web Forms target is worth a direct check of whether ViewState is genuinely encrypted and validated, since a template-inherited default key is a mistake that, once made, tends to survive untouched for the application's entire lifetime.