소프트웨어 개발보안 가이드 분석(2021) : 신뢰할 수 없는 데이터의 역직렬화

신뢰할 수 없는 데이터의 역직렬화란?

신뢰할 수 없는 데이터의 역직렬화 취약점은 애플리케이션에서 신뢰할 수 없는 출처로부터 받은 데이터를 역직렬화할 때 발생하는 보안 취약점입니다. 역직렬화는 직렬화된 데이터를 다시 객체나 데이터 구조로 변환하는 과정을 말하며, 이 과정에서 신뢰할 수 없는 데이터를 역직렬화하면 데이터 손상, 애플리케이션 충돌, 서비스 거부(DoS) 공격 등에 취약할 수 있습니다.

 

공격 메커니즘

신뢰할 수 없는 데이터의 역직렬화 취약점은 공격자가 신뢰할 수 없는 데이터를 애플리케이션에 역직렬화하도록 유도하는 과정에서 발생합니다. 이 과정에서 취약한 시스템 내의 버그를 작동시켜 데이터 손상, 애플리케이션 충돌 또는 원격 코드 실행 등과 같은 보안 위협이 발생할 수 있습니다.

  1. 원격 코드 실행(RCE): 공격자가 악의적인 코드를 원격으로 실행하여 시스템을 제어하거나 민감한 정보에 접근합니다.
  2. 서비스 거부(DoS) 공격: 역직렬화 과정에서 과도한 리소스를 소모하게 하여 서비스를 중단시킵니다.

 

실제로 역직렬화 취약점을 이용한 사례가 존재하는데요, 가장 주목할 만한 사례 중 하나는 마이크로소프트의 Exchange Server에 발견된 취약점입니다.

 

마이크로소프트 Exchange Server 취약점 (CVE-2021-26857)

CVE-2021-26857은 마이크로소프트 Exchange Server에서 발견된 역직렬화 취약점으로, Exchange Unified Messaging Service에서 발생합니다. 공격자는 Exchange Unified Messaging Service 내의 역직렬화 취약점을 이용하여 Windows 작업 스케줄러에 악의적인 PowerShell 코드를 삽입하고, 관리자 권한으로 시스템을 조작할 수 있습니다. 이 취약점이 발견된 이후 마이크로소프트는 CVE-2021-26857을 포함한 취약점에 대한 긴급 패치를 발표하였습니다. 마이크로소프트 Exchange Server 취약점 사례는 역직렬화 취약점이 얼마나 심각한 결과를 초래할 수 있는지를 보여줍니다.

 

취약한 웹 애플리케이션의 예

다음은 Java에서의 데이터 검증 없이 역직렬화하는 예시 코드입니다.

import java.io.*;

public class VulnerableDeserialization {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        String serializedData = args[0];
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(serializedData)));
        Object obj = ois.readObject();
        ois.close();
        System.out.println(obj);
    }
}

ObjectInputStream을 사용하여 Base64로 인코딩된 직렬화 데이터를 디코딩하고, 이를 역직렬화하여 객체로 변환합니다. 외부로부터 받은 직렬화된 데이터를 검증 없이 역직렬화하면 악의적인 객체가 시스템 내에서 실행될 수 있으며 원격 코드 실행(RCE) 공격으로 이어질 수 있습니다.

 

시큐어코딩 적용 방법

신뢰할 수 없는 데이터의 역직렬화 취약점은 다음과 같은 방법을 통하여 취약점을 보안할 수 있습니다.

  1. 입력 데이터 검증: 역직렬화 과정에서 신뢰할 수 없는 데이터를 사용하는 경우, 입력 데이터를 검증하여 알려진 안전한 객체만 역직렬화하도록 합니다.
  2. ObjectInputStream 상속 및 오버라이드를 통한 역직렬화 객체 타입 제한: ObjectInputStream 클래스를 상속 또는 readObject 메서드를 오버라이드하여 역직렬화되는 객체의 타입을 제한하여 허용된 클래스의 객체만 역직렬화 할 수 있도록 합니다.

역직렬화 과정에서 특정 클래스의 객체만을 허용하도록 제한하는 방법은 잠재적인 보안 취약점을 줄이는 데 도움이 됩니다.

다음 코드는 ObjectInputStream 클래스를 상속받아 resolveClass 메서드를 오버라이드하여 역직렬화 시 특정 클래스의 객체만 허용하도록 제한하도록 구현한 예시 코드입니다.

import java.io.*;

public class SecureDeserialization {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        String serializedData = args[0];
        SafeObjectInputStream ois = new SafeObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(serializedData)));
        Object obj = ois.readObject();
        ois.close();
        System.out.println(obj);
    }

    private static class SafeObjectInputStream extends ObjectInputStream {
        public SafeObjectInputStream(InputStream in) throws IOException {
            super(in);
        }

        @Override
        protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
            if (!"안전한 클래스 이름".equals(desc.getName())) {
                throw new InvalidClassException("Unauthorized deserialization attempt");
            }
            return super.resolveClass(desc);
        }
    }
}

이 예제에서는 SafeObjectInputStream 클래스를 통해 역직렬화 과정에서 특정 클래스의 객체만 허용하도록 역직렬 시에 발생 할 수 있는 취약점을 보안하였습니다.

  • 이전 소프트웨어 개발보안 가이드 분석(2021) : 초기화되지 않은 변수 사용
  • 다음 소프트웨어 개발보안 가이드 분석(2021) : 잘못된 세션에 의한 데이터 정보 노출