[CVE] CVE 분석 · 작성: 2026-07-20 03:10:34 · 수정: 2026-07-21 23:19:31 · 조회 22
class.module.classLoader라는, 기존 블랙리스트(class.classLoader)가 놓친 새 경로로 클래스로더에 접근을 허용해서, Tomcat의 AccessLogValve 설정을 조작해 웹 루트에 JSP 웹쉘을 심을 수 있다.패치는 (1) 블랙리스트를 문자열 패턴이 아니라 위험한 프로퍼티 경로 전체를 포괄하도록 강화, (2) WebDataBinder가 허용된 프로퍼티만 바인딩하도록 기본 정책을 전환하는 두 지점으로 요약된다.
CachedIntrospectionResults — 블랙리스트가 새 경로를 놓친 지점Before
// 2010년 CVE-2010-1622 패치 당시 추가된 블랙리스트
private static final String[] DISALLOWED_PREFIXES = {
"class.classLoader" // 문자열 패턴 하나만 차단
};
boolean isDisallowed(String propertyPath) {
for (String prefix : DISALLOWED_PREFIXES) {
if (propertyPath.startsWith(prefix)) return true;
}
return false;
}
Java 9의 모듈 시스템(JPMS)이 Class.getModule().getClassLoader()라는 새 경로를 추가하면서, class.module.classLoader라는 문자열은 옛 블랙리스트 패턴(class.classLoader)과 정확히 일치하지 않아 그대로 통과했다.
BeanWrapperImpl.setPropertyValue() — 중첩 경로를 그대로 리플렉션 호출Before
public void setPropertyValue(String propertyPath, Object value) {
// 점(.)으로 구분된 경로를 그대로 따라가며 getter/setter를 리플렉션으로 호출
// "class", "module", "classLoader" 각 세그먼트마다 별도 화이트리스트 검사 없음
Object target = resolveNestedProperty(propertyPath);
invokeSetter(target, finalSegment, value);
}
After (5.3.18 / 5.2.20)
private static final Set<String> DISALLOWED_PREFIXES = Set.of(
"class.", "Class." // "class"로 시작하는 프로퍼티 경로 전체를 원천 차단
);
public void setPropertyValue(String propertyPath, Object value) {
if (isDisallowedProperty(propertyPath)) { // 문자열 패턴이 아니라 세그먼트 단위로 검사
throw new InvalidPropertyException(...);
}
...
}
class로 시작하는 프로퍼티 경로 전체를 차단 대상으로 넓히고, 이후 버전에서는 허용된 프로퍼티만 화이트리스트로 바인딩되도록 WebDataBinder의 기본 정책 자체를 강화했다.
class.module.classLoader.resources.context.parent.pipeline.first.pattern 등 파라미터로 JSP 웹쉘 코드가 담긴 로그 포맷 문자열 전송BeanWrapperImpl이 이 경로를 그대로 타고 들어가 Tomcat AccessLogValve 인스턴스에 도달, pattern/suffix/directory/prefix 프로퍼티를 재설정.jsp)로 새 로그 파일이 생성되고 그 안에 웹쉘 코드가 기록됨.jsp 경로로 접속 → JSP 엔진이 정상 JSP로 파싱·컴파일·실행 → 원격 코드 실행AccessLogValve 같은 "쓰기 가능한 파일 경로 프로퍼티"가 없는 환경(Jetty, 실행 가능 JAR 배포)에서는 클래스로더 접근이 되어도 곧바로 RCE로 격상되지 않는 경우도 있었다 — 1차 취약점 뒤에 어떤 "쓰기 가능한 기능"이 대기하는지가 최종 피해를 결정한다