Basiese Java Deserialisering met ObjectInputStream readObject
Tip
Leer & oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).
Ondersteun HackTricks
- Kyk na die intekenplanne!
- Sluit aan by die 💬 Discord-groep, die telegram-groep, volg @hacktricks_live op X/Twitter, of kyk na die LinkedIn-bladsy en YouTube-kanaal.
- Deel hacking tricks deur PRs in te stuur na die HackTricks en HackTricks Cloud github repos.
In hierdie POST word ’n voorbeeld verduidelik wat java.io.Serializable gebruik en waarom om readObject() te oorleer uiters gevaarlik kan wees as die inkomende stroom deur ’n aanvaller beheer word.
Serializable
Die Java Serializable interface (java.io.Serializable) is ’n marker-interface wat jou klasse moet implementeer as hulle geserialiseer en gedeserialiseer moet word. Java object serialization (skryf) word gedoen met die ObjectOutputStream en deserialisering (lees) word gedoen met die ObjectInputStream.
Herinnering: Watter metodes word implisiet aangeroep tydens deserialisering?
readObject()– klas-spesifieke leeslogika (indien geïmplementeer en private).readResolve()– kan die gedeserialiseerde objek vervang met ’n ander een.validateObject()– viaObjectInputValidationcallbacks.readExternal()– vir klasse watExternalizableimplementeer.- Constructors word nie uitgevoer nie – daarom vertrou gadget-kettings uitsluitlik op die vorige callbacks.
Enige metode in daardie ketting wat eindig met die aanroeping van deur ’n aanvaller beheerde data (command execution, JNDI lookups, reflection, ens.) verander die deserialiseringsroetine in ’n RCE-gadget.
Kom ons kyk na ’n voorbeeld met ’n class Person wat serializable is. Hierdie klas oorlees die readObject() funksie, so wanneer enige objekt van hierdie klas gedeserialiseer word hierdie funksie gaan uitgevoer word.\
In die voorbeeld roep die readObject() funksie van die klas Person die funksie eat() van sy troeteldier aan en die eat() funksie van ’n Dog (om ’n of ander rede) roep ’n calc.exe aan. Ons gaan sien hoe om ’n Person object te serialize en te deserialize om hierdie calculator uit te voer:
Die volgende voorbeeld is van 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");
}
}
Gevolgtrekking (klassieke scenario)
Soos jy in hierdie baie basiese voorbeeld kan sien, verskyn die “vulnerability” hier omdat die readObject() metode ander aanvallers-beheerde kode aanroep. In werklike gadget-kettings kan duisende klasse in eksterne biblioteke (Commons-Collections, Spring, Groovy, Rome, SnakeYAML, ens.) misbruik word – die aanvaller het net een bereikbare gadget nodig om kode-uitvoering te kry.
2023-2025: Wat het in die werklike wêreld verander in Java deserialisasie-bugs?
Onlangs gevalle herinner ons daaraan dat ObjectInputStream-bugs nie meer net “laai ’n .ser-lêer op na ’n ou HTTP-endpoint” is nie:
- Broker / queue verbruikers: Spring-Kafka (
CVE-2023-34040) het getoon dat die deserialisering van exception headers uit aanvaller-beheerde topics genoeg is as die verbruiker die ongewonecheckDeserExWhen*flags aanskakel. - Client-side vertroue in remote servers: die Aerospike Java client (
CVE-2023-36480) het objects wat vanaf die server ontvang is, gedeserialiseer. Die vendor se reaksie was opvallend: nuwer clients het Java runtime serialization/deserialization ondersteuning verwyder eerder as om te probeer om dit agter ’n swakke filter te behou. - “Restricted” streams is dikwels steeds te breed:
pac4j-core(CVE-2023-25581) het probeer om deserialisering te beskerm metRestrictedObjectInputStream, maar die aanvaarbare klasse-lys was steeds groot genoeg om gadget-misbruik moontlik te maak.
Die offensiewe les is dat die gevaarlike vertrouensgrens dikwels nie “gebruikers laai ’n blob op nie”, maar “’n komponent wat die ontwikkelaar as vertrou beskou het, kan bytes in ’n stroom insit wat uiteindelik by readObject() uitkom”.
As jy lae-ruis bereikbaarheidstoetse nodig het voordat jy tyd spandeer op volledige gadget-navorsing, gebruik die toegewyde Java-bladsye vir:
Java DNS Deserialization, GadgetProbe and Java Deserialization Scanner
readObject() anti-patrone wat steeds gadget-toegangspunte skep
Selfs as jou klas self nie ’n voor die hand liggende RCE-gadget is nie, is die volgende patrone genoeg om dit uitbuitbaar te maak wanneer aanvaller-beheerde objects in die graf ingebed is:
- Aanroep van oor-skryfbare metodes of interface-metodes vanaf
readObject()(pet.eat()in die PoC hierbo is die klassieke voorbeeld). - Uitvoeren van lookups, reflection, class loading, expression evaluation, of JNDI-operasies tydens deserialisering.
- Itereer oor aanvaller-beheerde kolleksies of maps, wat moontlik
hashCode(),equals(), comparators, of transformers as newe-effekte kan veroorsaak. - Registrasie van
ObjectInputValidationcallbacks wat gevaarlike naverwerking uitvoer. - Aanname dat “private
readObject()” genoeg beskerming is. Dit beheer slegs dispatch-semantiek; dit maak deserialisering nie veilig nie.
Moderne mitigasies wat jy moet implementeer
- JEP 290 / Serialization Filtering (Java 9+) Gebruik ’n allow-list en eksplisiete graf-beperkings:
-Djdk.serialFilter="com.example.dto.*;java.base/*;maxdepth=5;maxrefs=1000;maxbytes=16384;!*"
- Pas ’n filter toe op elke onbetroubare stroom, nie net globaal nie:
try (var ois = new ObjectInputStream(input)) {
var filter = ObjectInputFilter.Config.createFilter(
"com.example.dto.*;java.base/*;maxdepth=5;maxrefs=1000;!*"
);
ois.setObjectInputFilter(filter);
return (Message) ois.readObject();
}
- JEP 415 (Java 17+) Context-Specific Filter Factories Voorkeur hier as dieselfde JVM verskeie deserialisering-kontekste het (RMI, cache replicatie, message consumers, admin-only imports) en elkeen ’n ander allow-list nodig het.
- Hou
readObject()eentonig Roep slegsdefaultReadObject()/ eksplisiete veldleeswerk aan, en voer dan streng invariantkontroles uit. Doen geen I/O, logging wat aanvaller-beheerde objects dereference nie, geen dinamiese lookups, en geen metode-aanroepe op gedeserialiseerde sub-objects nie. - As dit moontlik is, verwyder Java native serialization uit die ontwerp
Die Aerospike-fix is ’n goeie model: as die funksie nie essensieel is nie, is dit dikwels veiliger om
readObject()/writeObject()gebruik te verwyder as om te probeer om perfekte filters vir ewig te onderhou.
Opsporing en navorsings-werkvloei
ysoserialbly die basislyn vir gadget-validasie en vinnige RCE/URLDNS probes.marshalsecis steeds nuttig wanneer die sink na JNDI/LDAP/RMI terrein skuif.GadgetInspectoris nuttig wanneer jy die teiken jars het en aplikasie-spesifieke gadget-kettings moet soek.- Java 17 het die
jdk.DeserializationFlight Recorder event bygevoeg, wat nuttig is om te sien waarObjectInputStreamwerklik gebruik word en of filters toegepas word.
Vinnige kontrolelys vir veilige readObject()-implementerings
- Maak die metode
privateen annoteer serialization hooks met@Serialsodat kompileerders verkeerd-deklareerde handtekeninge kan opspoor. - Roep
defaultReadObject()eerste aan tensy jy ’n sterk rede het om die volle objekgraf handmatig te lees. - Behandel elke geneste object as aanvaller-beheerd totdat dit gevalideer is.
- Roep nooit metodes aan op gedeserialiseerde medewerkers van binne
readObject()nie. - Pareer die kodereview met ’n
ObjectInputFilter-review; “veilig-likendereadObject()kode” is nie genoeg as die stroom steeds arbitrêre klasse aanvaar nie.
References
- OpenJDK JEP 415: Context-Specific Deserialization Filters
- GitHub Security Lab: GHSL-2022-085 / CVE-2023-25581 (
pac4j-coredeserialization leading to RCE)
Tip
Leer & oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer & oefen Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Blaai deur die volledige HackTricks Training-katalogus vir die assesseringsroetes (ARTA/GRTA/AzRTA) en Linux Hacking Expert (LHE).
Ondersteun HackTricks
- Kyk na die intekenplanne!
- Sluit aan by die 💬 Discord-groep, die telegram-groep, volg @hacktricks_live op X/Twitter, of kyk na die LinkedIn-bladsy en YouTube-kanaal.
- Deel hacking tricks deur PRs in te stuur na die HackTricks en HackTricks Cloud github repos.


