개요
Java 에서 안전하지 않은 역직렬화 취약점 예제를 쉽게 설명한 블로그가 있었다. 요점을 정리해본다.
상세
다음과 같은 코드가 있다고 하자.
User코드
- User 클래스는 readObject 함수를 재정의했다. 이 함수는 name 값을 실행하는 코드가 왜인지(?) 들어가 있다! 취약하다.
import java.io.IOException;
import java.io.Serializable;
public class User implements Serializable{
private String name;
private String password;
private String email;
private int age;
public User(String name, String password, String email, int age){
this.name = name;
this.password = password;
this.email = email;
this.age = age;
}
public String toString(){
return "(" + name +", " + password + ", " + email + ", " + age + ")";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();
Runtime.getRuntime().exec(this.name);
}
}
User 역직렬화 코드
User를 역직렬화하는 코드다. User의 name에 calc라는 값을 저장한 후에 역직렬화를 시도하면 어떻게 될까?
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class UserDeserTest {
private static final String path = "user.ser"; //프로젝트 루트에 저장됨.
public static void serialize(List<User> users){
try{
FileOutputStream fos = new FileOutputStream(path);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream out = new ObjectOutputStream(bos);
for(User user: users) {
out.writeObject(user);
}
out.writeObject(users);
out.close();
System.out.println("직렬화 완료");
}catch(Exception e){
e.printStackTrace();
}
}
public static void deserialize(){
try {
FileInputStream fis = new FileInputStream(path);
BufferedInputStream bis = new BufferedInputStream(fis);
ObjectInputStream in = new ObjectInputStream(bis);
User u1 = (User) in.readObject();
User u2 = (User) in.readObject();
ArrayList list = (ArrayList) in.readObject();
System.out.println(u1.toString());
System.out.println(u2.toString());
System.out.println("count : " + list.size());
System.out.println(list.toString());
System.out.println("역직렬화완료");
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
User u1 = new User("calc", "1234", "ldd@naver.com", 30);
User u2 = new User("User2", "5678", "teser@gmail.com", 25);
ArrayList list =new ArrayList<>();
list.add(u1);
list.add(u2);
serialize(list);
deserialize();
}
}
실행결과
readObject가 역직렬화과정에서 자동으로 호출되어(매직메서드) 계산기가 실행된다. 아주 단순화한 것이지만 역직렬화과정에서 자동으로 실행되는 매직메서드를 공략하면 공격이 가능하다는 것을 느낄 수 있다.
참고
경로가 다른 클래스를 역직렬화시킬 수 있을까?
내용이 동일한 클래스라도 경로가 다르면 역직렬화할 수 없다. 역직렬화하려고 하면 다음과 같은 에러가 발생한다.
java.lang.ClassCastException: class evil.User cannot be cast to class User (evil.User and User are in unnamed module of loader 'app')
at UserDeserTest.serialize(UserDeserTest.java:23)
at UserDeserTest.main(UserDeserTest.java:63)
java.io.EOFException
at java.base/java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2926)
at java.base/java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:3421)
at java.base/java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:959)
at java.base/java.io.ObjectInputStream.<init>(ObjectInputStream.java:397)
at UserDeserTest.deserialize(UserDeserTest.java:39)
at UserDeserTest.main(UserDeserTest.java:64)
OAST 체크를 하고 싶은 경우
다음 코드를 삽입해서 Burp의 Collaborator서버와 통신시킬 수 있다.
String url = "https://2svr1jmg0fvhi8ei4e0glnvcy34uskg9.oastify.com";
var client = HttpClient.newHttpClient(); //java 11
var request = HttpRequest.newBuilder(URI.create(url))
.GET()
.build();
HttpResponse<String> res = client.send(request, HttpResponse.BodyHandlers.ofString());
// System.out.println(res);
}
참고
- https://medium.com/@dub-flow/deserialization-what-the-heck-actually-is-a-gadget-chain-1ea35e32df69