Basic Java Deserialization with ObjectInputStream readObject
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
์ด POST์์๋ java.io.Serializable์ ์ฌ์ฉํ๋ ์์ ๋ฅผ ์ค๋ช
ํ ๊ฒ์ด๋ฉฐ, incoming stream์ด ๊ณต๊ฒฉ์๊ฐ ์ ์ดํ๋ ๊ฒฝ์ฐ readObject()๋ฅผ ์ฌ์ ์ํ๋ ๊ฒ์ด ์ผ๋ง๋ ์ํํ ์ ์๋์ง์ ๋ํด ์ค๋ช
ํ ๊ฒ์
๋๋ค.
Serializable
Java Serializable ์ธํฐํ์ด์ค(java.io.Serializable)๋ ํด๋์ค๊ฐ ์ง๋ ฌํ ๋ฐ ์ญ์ง๋ ฌํ๋๊ธฐ ์ํด ๊ตฌํํด์ผ ํ๋ ๋ง์ปค ์ธํฐํ์ด์ค์
๋๋ค. Java ๊ฐ์ฒด ์ง๋ ฌํ(์ฐ๊ธฐ)๋ ObjectOutputStream์ผ๋ก ์ํ๋๋ฉฐ, ์ญ์ง๋ ฌํ(์ฝ๊ธฐ)๋ ObjectInputStream์ผ๋ก ์ํ๋ฉ๋๋ค.
Reminder: Which methods are implicitly invoked during deserialization?
readObject()โ ํด๋์ค๋ณ ์ฝ๊ธฐ ๋ก์ง(๊ตฌํ๋ ๊ฒฝ์ฐ ๋ฐ private).readResolve()โ ์ญ์ง๋ ฌํ๋ ๊ฐ์ฒด๋ฅผ ๋ค๋ฅธ ๊ฐ์ฒด๋ก ๋์ฒดํ ์ ์์ต๋๋ค.validateObject()โObjectInputValidation์ฝ๋ฐฑ์ ํตํด.readExternal()โExternalizable์ ๊ตฌํํ๋ ํด๋์ค์ ๊ฒฝ์ฐ.- ์์ฑ์๋ ์คํ๋์ง ์์ต๋๋ค โ ๋ฐ๋ผ์ ๊ฐ์ ฏ ์ฒด์ธ์ ์ด์ ์ฝ๋ฐฑ์๋ง ์์กดํฉ๋๋ค.
๊ทธ ์ฒด์ธ์์ ๊ณต๊ฒฉ์๊ฐ ์ ์ดํ๋ ๋ฐ์ดํฐ๋ฅผ ํธ์ถํ๋ ๋ฉ์๋๋(๋ช ๋ น ์คํ, JNDI ์กฐํ, ๋ฆฌํ๋ ์ ๋ฑ) ์ญ์ง๋ ฌํ ๋ฃจํด์ RCE ๊ฐ์ ฏ์ผ๋ก ์ ํํฉ๋๋ค.
์ง๋ ฌํ ๊ฐ๋ฅํ Person ํด๋์ค๋ฅผ ์๋ก ๋ค์ด ๋ณด๊ฒ ์ต๋๋ค. ์ด ํด๋์ค๋ readObject ํจ์๋ฅผ ์ฌ์ ์ํ๋ฏ๋ก, ์ด ํด๋์ค์ ์ด๋ค ๊ฐ์ฒด๊ฐ ์ญ์ง๋ ฌํ๋ ๋ ์ด ํจ์๊ฐ ์คํ๋ฉ๋๋ค.
์์ ์์, Person ํด๋์ค์ readObject ํจ์๋ ๊ทธ์ ์ ์๋๋ฌผ์ eat() ํจ์๋ฅผ ํธ์ถํ๊ณ , Dog์ eat() ํจ์๋(์ด๋ค ์ด์ ๋ก) calc.exe๋ฅผ ํธ์ถํฉ๋๋ค. ์ด ๊ณ์ฐ๊ธฐ๋ฅผ ์คํํ๊ธฐ ์ํด Person ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํํ๊ณ ์ญ์ง๋ ฌํํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค:
๋ค์ ์์ ๋ 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");
}
}
๊ฒฐ๋ก (๊ณ ์ ์๋๋ฆฌ์ค)
์ด ๋งค์ฐ ๊ธฐ๋ณธ์ ์ธ ์์์ ๋ณผ ์ ์๋ฏ์ด, ์ฌ๊ธฐ์์ โ์ทจ์ฝ์ โ์ readObject() ๋ฉ์๋๊ฐ ๊ณต๊ฒฉ์๊ฐ ์ ์ดํ๋ ๋ค๋ฅธ ์ฝ๋๋ฅผ ํธ์ถํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํฉ๋๋ค. ์ค์ ์ธ๊ณ์ ๊ฐ์ ฏ ์ฒด์ธ์์๋ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(Commons-Collections, Spring, Groovy, Rome, SnakeYAML ๋ฑ)์ ํฌํจ๋ ์์ฒ ๊ฐ์ ํด๋์ค๊ฐ ์ ์ฉ๋ ์ ์์ผ๋ฉฐ, ๊ณต๊ฒฉ์๋ ์ฝ๋ ์คํ์ ์ป๊ธฐ ์ํด ํ๋์ ์ ๊ทผ ๊ฐ๋ฅํ ๊ฐ์ ฏ๋ง ํ์ํฉ๋๋ค.
2023-2025: Java ์ญ์ง๋ ฌํ ๊ณต๊ฒฉ์ ์๋ก์ด ๋ด์ฉ์ ๋ฌด์์ธ๊ฐ?
- 2023 โ CVE-2023-34040:
checkDeserExWhen*ํ๋๊ทธ๊ฐ ํ์ฑํ๋ ์ํ์์ Spring-Kafka์ ์ค๋ฅ ๋ ์ฝ๋ ํค๋ ์ญ์ง๋ ฌํ๋ก ์ธํด ๊ณต๊ฒฉ์๊ฐ ๊ฒ์ํ ์ฃผ์ ๋ก๋ถํฐ ์์์ ๊ฐ์ ฏ ๊ตฌ์ฑ์ด ๊ฐ๋ฅํด์ก์ต๋๋ค. 3.0.10 / 2.9.11์์ ์์ ๋จ. ยน - 2023 โ CVE-2023-36480: Aerospike Java ํด๋ผ์ด์ธํธ์ ์ ๋ขฐํ ์ ์๋ ์๋ฒ ๊ฐ์ ์ด ๊นจ์ง โ ์ ์์ ์ธ ์๋ฒ ์๋ต์ ํด๋ผ์ด์ธํธ์ ์ํด ์ญ์ง๋ ฌํ๋ ์ง๋ ฌํ๋ ํ์ด๋ก๋๊ฐ ํฌํจ๋จ โ RCE. ยฒ
- 2023 โ CVE-2023-25581:
pac4j-core์ฌ์ฉ์ ํ๋กํ ์์ฑ ํ์ฑ์ด{#sb64}์ ๋์ฌ๊ฐ ๋ถ์ Base64 ๋ธ๋กญ์ ์์ฉํ๊ณRestrictedObjectInputStream์๋ ๋ถ๊ตฌํ๊ณ ์ด๋ฅผ ์ญ์ง๋ ฌํํ์ต๋๋ค. 4.0.0 ์ด์์ผ๋ก ์ ๊ทธ๋ ์ด๋ํ์ญ์์ค. - 2023 โ CVE-2023-4528: JSCAPE MFT Manager Service (ํฌํธ 10880)๊ฐ XML ์ธ์ฝ๋ฉ๋ Java ๊ฐ์ฒด๋ฅผ ์์ฉํ์ฌ root/SYSTEM์ผ๋ก RCE๋ฅผ ์ด๋ํ์ต๋๋ค.
- 2024 โ Hibernate5, TomcatEmbed ๋ฐ SnakeYAML 2.x ํด๋์ค๋ฅผ ํฌํจํ ์ฌ๋ฌ ์๋ก์ด ๊ฐ์ ฏ ์ฒด์ธ์ด ysoserial-plus(mod)์ ์ถ๊ฐ๋์ด ์ผ๋ถ ์ค๋๋ ํํฐ๋ฅผ ์ฐํํฉ๋๋ค.
๋ฐฐํฌํด์ผ ํ ํ๋์ ์ํ์ฑ
- JEP 290 / ์ง๋ ฌํ ํํฐ๋ง (Java 9+) ํด๋์ค์ ํ์ฉ ๋ชฉ๋ก ๋๋ ๊ฑฐ๋ถ ๋ชฉ๋ก ์ถ๊ฐ:
# ๋น์ ์ DTO์ java.base๋ง ์์ฉํ๊ณ ๋๋จธ์ง๋ ๊ฑฐ๋ถ
-Djdk.serialFilter="com.example.dto.*;java.base/*;!*"
ํ๋ก๊ทธ๋๋ฐ ์:
var filter = ObjectInputFilter.Config.createFilter("com.example.dto.*;java.base/*;!*" );
ObjectInputFilter.Config.setSerialFilter(filter);
- JEP 415 (Java 17+) ์ปจํ
์คํธ๋ณ ํํฐ ํฉํ ๋ฆฌ โ
BinaryOperator<ObjectInputFilter>๋ฅผ ์ฌ์ฉํ์ฌ ์คํ ์ปจํ ์คํธ(์: RMI ํธ์ถ๋น, ๋ฉ์์ง ํ ์๋น์๋น)๋ง๋ค ๋ค๋ฅธ ํํฐ๋ฅผ ์ ์ฉํฉ๋๋ค. - ์์
ObjectInputStream์ ๋คํธ์ํฌ์ ๋ ธ์ถํ์ง ๋ง์ญ์์ค โ ์ฝ๋ ์คํ ์๋ฏธ๊ฐ ์๋ JSON/์ด์ง ์ธ์ฝ๋ฉ์ ์ ํธํ์ญ์์ค (Jackson์์DefaultTyping๋นํ์ฑํ ํ, Protobuf, Avro ๋ฑ). - ์ฌ์ธต ๋ฐฉ์ด ํ๊ณ โ ์ต๋ ๋ฐฐ์ด ๊ธธ์ด, ๊น์ด, ์ฐธ์กฐ ์ค์ :
-Djdk.serialFilter="maxbytes=16384;maxdepth=5;maxrefs=1000"
- ์ง์์ ์ธ ๊ฐ์ ฏ ์ค์บ๋ โ
gadget-inspector๋๋serialpwn-cli์ ๊ฐ์ ๋๊ตฌ๋ฅผ CI์์ ์คํํ์ฌ ์ํํ ๊ฐ์ ฏ์ด ์ ๊ทผ ๊ฐ๋ฅํด์ง๋ฉด ๋น๋๋ฅผ ์คํจํ๊ฒ ํฉ๋๋ค.
์ ๋ฐ์ดํธ๋ ๋๊ตฌ ์นํธ ์ํธ (2024)
ysoserial-plus.jarโ > 130๊ฐ์ ๊ฐ์ ฏ ์ฒด์ธ์ ๊ฐ์ง ์ปค๋ฎค๋ํฐ ํฌํฌ:
java -jar ysoserial-plus.jar CommonsCollections6 'calc' | base64 -w0
marshalsecโ JNDI ๊ฐ์ ฏ ์์ฑ์ ์ํ ์ฌ์ ํ ์ฐธ์กฐ๋๋ ๋๊ตฌ (LDAP/RMI).gadget-probeโ ๋คํธ์ํฌ ์๋น์ค์ ๋ํ ๋น ๋ฅธ ๋ธ๋๋ฐ์ค ๊ฐ์ ฏ ๋ฐ๊ฒฌ.SerialSnifferโObjectInputStream์ ์ํด ์ฝํ ๋ชจ๋ ํด๋์ค๋ฅผ ์ถ๋ ฅํ๋ JVMTI ์์ด์ ํธ (ํํฐ๋ฅผ ์์ฑํ๋ ๋ฐ ์ ์ฉ).- ํ์ง ํ โ ํํฐ ๊ฒฐ์ ๋ฐ ๊ฑฐ๋ถ๋ ํด๋์ค๋ฅผ ๊ธฐ๋กํ๊ธฐ ์ํด
-Djdk.serialDebug=true(JDK 22+)๋ฅผ ํ์ฑํํ์ญ์์ค.
์์ ํ readObject() ๊ตฌํ์ ์ํ ๋น ๋ฅธ ์ฒดํฌ๋ฆฌ์คํธ
- ๋ฉ์๋๋ฅผ
private๋ก ๋ง๋ค๊ณ@Serial์ฃผ์์ ์ถ๊ฐํ์ญ์์ค (์ ์ ๋ถ์์ ๋์์ด ๋ฉ๋๋ค). - ์ฌ์ฉ์ ์ ๊ณต ๋ฉ์๋๋ฅผ ํธ์ถํ๊ฑฐ๋ ๋ฉ์๋ ๋ด์์ I/O๋ฅผ ์ํํ์ง ๋ง์ญ์์ค โ ํ๋๋ง ์ฝ์ผ์ญ์์ค.
- ๊ฒ์ฆ์ด ํ์ํ ๊ฒฝ์ฐ, ์ญ์ง๋ ฌํ ํ์
readObject()์ธ๋ถ์์ ์ํํ์ญ์์ค. Externalizable์ ๊ตฌํํ๊ณ ๊ธฐ๋ณธ ์ง๋ ฌํ ๋์ ๋ช ์์ ํ๋ ์ฝ๊ธฐ๋ฅผ ์ ํธํ์ญ์์ค.- ๋ด๋ถ ์๋น์ค์ ๋ํด์๋ ๊ฐํ๋
ObjectInputFilter๋ฅผ ๋ฑ๋กํ์ญ์์ค (ํํ ์ ํญ ์ค๊ณ).
์ฐธ๊ณ ๋ฌธํ
- Spring Security Advisory โ CVE-2023-34040 Java Deserialization in Spring-Kafka (2023๋ 8์)
- GitHub Security Lab โ GHSL-2023-044: Unsafe Deserialization in Aerospike Java Client (2023๋ 7์)
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


