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
- 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.
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?
readObject()
– lógica de leitura específica da classe (se implementada e privada).readResolve()
– pode substituir o objeto desserializado por outro.validateObject()
– via callbacks deObjectInputValidation
.readExternal()
– para classes que implementamExternalizable
.- 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
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 umRestrictedObjectInputStream
. 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
- JEP 290 / Filtragem de Serialização (Java 9+) Adicione uma lista de permissão ou lista de negação de classes:
# Aceitar apenas seus DTOs e java.base, rejeitar tudo o mais
-Djdk.serialFilter="com.example.dto.*;java.base/*;!*"
Exemplo programático:
var filter = ObjectInputFilter.Config.createFilter("com.example.dto.*;java.base/*;!*" );
ObjectInputFilter.Config.setSerialFilter(filter);
- 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). - 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 desabilitarDefaultTyping
, Protobuf, Avro, etc.). - Limites de Defesa em Profundidade – Defina comprimento máximo de array, profundidade, referências:
-Djdk.serialFilter="maxbytes=16384;maxdepth=5;maxrefs=1000"
- Escaneamento contínuo de gadgets – execute ferramentas como
gadget-inspector
ouserialpwn-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:
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 porObjectInputStream
(ú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()
- Torne o método
private
e adicione a anotação@Serial
(ajuda na análise estática). - Nunca chame métodos fornecidos pelo usuário ou realize I/O no método – apenas leia campos.
- Se a validação for necessária, realize-a após a deserialização, fora de
readObject()
. - Prefira implementar
Externalizable
e faça leituras de campos explícitas em vez de serialização padrão. - Registre um
ObjectInputFilter
endurecido mesmo para serviços internos (design resiliente a compromissos).
Referências
- Aviso de Segurança do Spring – CVE-2023-34040 Deserialização Java no Spring-Kafka (Ago 2023)
- 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
- 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.