
log4j 2025/2026년 「설정 무음 실패」 CVE 7건 자가 점검: CVE×모듈 기준으로 4개 모듈을 판별하고, log4j2 설정을 구조적으로 파싱하여 applicability 노이즈를 줄이며, 한 번에 끝나도록 업그레이드할 버전을 계산(6건은 2.25.4를 지시하지만 1건은 2.25.5가 필요하며, 이를 Dependabot이 감지하지 못함) CVE-2026-49844 / CVE-2026-34477
log4j 2025/2026년 이번 "설정 무음 실패" 공지에 대한 자가 점검 도구. 런타임 의존성 제로, 단일 jar, 네트워크 연결 없음.
⚠️ 먼저 기준을 명확히 하자: 이번 것은 전부 medium이고, RCE는 하나도 없으며, Log4Shell 같은 것이 아니다. 이들의 공통점은 설정이 아무런 오류 없이 실패했다는 것이다 —— 속성이 무음으로 무시되고, 속성이 무음으로 이름이 바뀌고, 로그가 무음으로 버려진다. 따라서 버전 번호만 봐서는 당신이 영향을 받았는지 알 수 없고, 설정을 읽어야 한다. 만약 CVE-2021-44228(Log4Shell)을 찾고 있다면, 이 도구는 도움이 되지 않는다.
java -jar log4j-check.jar app.jar # fat jar 里带配置,一步到位
java -jar log4j-check.jar ./target ./src # 构建产物 + 源码
java -jar log4j-check.jar ~/.m2/repository --no-config
Dependabot은 "log4j에 N개의 취약점이 있습니다"라고 알려줍니다. 이 도구는 그것이 답하지 않는 세 가지를 답합니다:
log4j2.xml에 적혀 있고, 읽을 수 있고, 정확히 읽을 수 있다.이 7개 중 6개의 수정 버전이 모두 ≤ 2.25.4다. 그래서 대다수는 「2.25.4로 올린다」는 답에 도달한다.
하지만 CVE-2026-49844는 2.25.5를 요구한다(2.26 라인은 2.26.1). 그 advisory 원문에는 자신이
CVE-2026-34481의 불완전한 수정이라고 적혀 있다:
버전
2.25.4에서 릴리스된 수정 사항은 영향을 받는 모든 코드 경로를 다루지 못했습니다. 나머지 문제에는 CVE-2026-49844가 할당되었습니다 …
📌 정정(2026-08-16)
이 절은 원래 「그리고 이 항목이 바로 Dependabot이 구조적으로 보고할 수 없는 그 항목이다」라고 적혀 있었다 —— 그 문장은 이제 절반만 맞다.
GHSA-qv9r-c865-cp47은 2026-08-13에 GitHub에 의해reviewed로 전환되고 패키지 정보가 보완되었으며, 보완된 두 구간은>= 2.13.1, < 2.25.5와>= 2.26.0, < 2.26.1이다:
- ✅ 2.x를 쓰는 사람은 이제 Dependabot 경고를 받을 수 있다.
- 🔴 하지만
3.x프리뷰 라인(3.0.0-alpha1~3.0.0-beta2)은 아직 해당 구간이 없어, 그 라인은 여전히 보고되지 않는다.업그레이드 결론은 전혀 변하지 않았다 ——
log4j-api는 여전히 2.25.5(2.26 라인은 2.26.1)가 필요하다. 변한 것은 오직 「기계가 그것을 볼 수 있는지」뿐이다. 이 도구의 판정표는 버전 라인별로 하나씩 갱신되었고, 보고서에 찍히는 숫자는 표에서 계산되며 더 이상 하드코딩되지 않는다.🔑 이 일 자체가 원래 그 예시보다 기록할 가치가 있다: advisory의 형태는 시간이 지나면서 업스트림에 의해 보완된다, 그리고 당신이 지난번 스캔한 결론은 스스로 갱신되지 않으며, 누구도 알려주지 않는다. 「그때 스캔했다」와 「지금은 안전하다」는 두 가지 일이다.
그것의 GitHub advisory(GHSA-qv9r-c865-cp47)는 2026-08-13 이전에는 unreviewed였고,
vulnerabilities 배열이 비어 있었다 —— 패키지 이름도, 버전 구간도, 수정 버전도 없었으므로,
그 기간 동안 Dependabot은 당신의 의존성 트리와 비교할 수 있는 데이터를 전혀 얻지 못했다.
OSV.dev에서도 당시 마찬가지였다: 나머지 6개는 모두 GHSA alias를 통해 Maven 좌표를 얻을 수 있었지만, 그것만은 얻을 수 없었다
(OSV에서는 GHSA alias조차 없었다). 이는 세 번째 소스에서의 독립적 방증이며,
tools/recheck_before_publish.py는 매번 재실행할 때마다 다시 확인한다
(이 스크립트는 이제 「이미 보완되었다」는 사실 자체도 보고한다).
🔴 이 항목을 「Dependabot이 쓸모없다」로 읽지 말라. 그것은 「이 데이터가 당시 존재하지 않았다」이지, 「이 도구에 버그가 있다」가 아니다.
unreviewed는 GitHub의 정상적인 프로세스 상태이며, 08-13의 보완은 바로 그 프로세스가 정상적으로 완료된 것이다.
또 다른 체인은 CVE-2025-68161(2.25.3)→ CVE-2026-34477(2.25.4)이다:
verifyHostName 속성은 2.12.0에서 도입되었으나, 2.25.3까지 줄곧 조용히 무시되어,
설정해도 설정하지 않은 것과 같았다. 68161에 따라 2.25.3으로 올리고 속성(system property가 아닌)으로 호스트명 검증을 설정한 사람은 여전히 당한다.
| 모듈 | 해당 항목 | 올려야 할 버전 |
|---|---|---|
log4j-core | 34477 / 34478 / 34480 / 68161 | 2.25.4 |
log4j-api | 49844 | 2.25.5(2.26 라인: 2.26.1) |
log4j-1.2-api | 34479 | 2.25.4 |
log4j-layout-template-json | 34481 | 2.25.4 |
목표 버전은 같은 숫자가 아니다. 그리고 대다수 프로젝트의 pom에는 log4j-core만 적혀 있다
—— 그 하나의 좌표로 역조회하면 4개만 찾을 수 있고, 나머지 3개는 그들에게 존재하지도 않는다.
이 도구는 네 모듈의 버전을 각각 스캔하고 각각 판정한다. 네 모듈의 버전이 어긋날 때(BOM 오버라이드, 수동 제외, 오래된 WAR 안에 두 세대의 jar가 들어 있는 경우) 그것을 명확히 보고한다.
| CVE | 트리거 조건(공식 원문에서 발췌) |
|---|---|
CVE-2026-34477 | <Ssl>의 verifyHostName 속성 + Socket / Syslog / SMTP appender |
CVE-2025-68161 | SocketAppender + TLS |
CVE-2026-34478 | 직접 Rfc5424Layout을 구성하는 경우(SyslogAppender를 사용하는 경우는 영향 없음) |
CVE-2026-34480 | log4j-core의 XmlLayout |
CVE-2026-34479 | 브리지의 Log4j1XmlLayout, 또는 log4j 1 호환 계층 + org.apache.log4j.xml.XMLLayout |
CVE-2026-34481 | JsonTemplateLayout + MapMessage/ObjectMessage 내의 부동 소수점 값 |
CVE-2026-49844 | JsonTemplateLayout의 message resolver, 또는 MapMessage.asJson() |
실측(실제 아티팩트, 아래 참조): 동일한 세트에 영향을 받는 버전의 네 모듈을 설치했을 때,
Console + RollingFile + PatternLayout)인 경우 → 버전 계층에서 7건 보고, 실제 적중 0건Socket+<Ssl verifyHostName>+Rfc5424Layout+XmlLayout이 있는 경우 → 7건 보고, 실제 적중 4건두 가지 부정 판정 기준은 공식 원문에 명시된 것으로, 별도 항목으로 분리했다:
SyslogAppender를 사용하는 경우 34478에 해당하지 않음; HTTP appender의 verifyHostname만 사용하는 경우 34477에 해당하지 않음.
이 항목은 「찾지 못했다」와 신뢰도가 전혀 다르다 — 전자는 원문의 뒷받침이 있고, 후자는 단지 우리가 보지 못했을 뿐이다.
🔴
verifyHostName(대문자 N,<Ssl>, 해당됨)과verifyHostname(소문자 n, HTTP appender, 공식 문서에 영향 없음으로 명시)은 한 글자 대소문자만 다를 뿐 결론은 완전히 반대다. 마찬가지로XmlLayout(34480, log4j-core)은Log4j1XmlLayout(34479, 브리지)의 부분 문자열이다. 이름만 grep하면 사용자에게 해당하지 않는 CVE까지 추가로 떠안게 된다. 그래서 설정 계층에서는 텍스트 매칭이 아닌 구조적 파싱을 한다.
| 格式 | 怎么读 | 能不能判准属性归属 |
|---|---|---|
log4j2*.xml(含 strict 模式 <Appender type="…">) | DOM 解析,XXE 已关 | ✅ |
log4j2*.properties | 按 xxx.type = Foo 建前缀→插件映射 | ✅ |
log4j2*.yaml / .json | 文本匹配(JDK 无 YAML 解析器,而本工具坚持零依赖) | ❌ 标为「文本依据」 |
.java | 文本匹配(已剥注释,认字符串) | ❌ |
설정은 아카이브 내부에서도 스캔한다(BOOT-INF/classes/, WEB-INF/classes/),
따라서 Spring Boot fat jar 하나만 넘겨도 노이즈 감소가 가능하다 — 소스 트리가 필요 없다.
log4j-spring.xml / log4j2-test.xml / log4j.xml(1 호환 계층) 모두 인식한다.
「트리거 조건을 찾지 못함」은 안전과 같지 않다, 최소 네 가지 상황에서 거짓 안심이 될 수 있다:
ConfigurationBuilder / Configurator.initialize);log4j2.configurationFile이 다른 곳을 가리키거나, 컨테이너에 마운트됨);「트리거 조건이 모두 성립함」도 감염 확인과 같지 않다: 대부분의 항목은 추가로 「공격자가 로그에 기록되는 그 값을 제어할 수 있음」을 요구하는데, 이 점은 도구가 판정할 수 없다. 따라서 결론은 우선순위 정렬에만 충분하고, 사고 선언에는 부족하다.
이 도구는 log4j 1.x(log4j:log4j)를 커버하지 않는다. 스캔되면 경고하지만 판정하지는 않는다 — 그것은 별개의 코드베이스다.
python tools/gen_rules.py # 从两个一手源重建 CveTable.java + Triggers.java
logging.apache.org/cyclonedx/vdr.xml)——
전체 항목 + 모듈별 정확한 구간 + 설명 원문.
🔴
/repos/apache/logging-log4j2/security-advisories실측 결과 0건 반환 —— Apache는 GitHub Security Advisories를 사용하지 않습니다. 그 엔드포인트만 조회하면 「log4j는 매우 평온하다」는 결과를 얻고, 게다가 오류도 나지 않습니다.
17개 단언, 어느 하나라도 충족되지 않으면 중단하고 파일을 쓰지 않는다. 그중 몇 개는 핵심을 떠받치는 것들이다:
| 단언 | 고정하는 사항 |
|---|---|
| ASSERT2 | 이중 소스 사각지대: 공식 7건 vs 단일 좌표 4건 vs 네 좌표 6건, 세 숫자를 모두 측정해야 함 |
| ASSERT13 | 그 1건의 구조적 사각지대는 검증 가능한 근거를 제시해야 하며, 문구에 하드코딩해서는 안 됨 |
| ASSERT7 | 각 수정 버전을 Central에서 실측 + 음성 대조(탐지기가 정말 「가져올 수 없음」이라고 말하는지 증명) |
| ASSERT8 | 수정 버전은 구간 상한에서 도출됨 → 반드시 공식 recommendation 원문에 그대로 나타나야 함 |
| ASSERT9 | 두 패치 공백 체인의 원문 표현 + 뒤쪽 고리의 수정 버전이 실제로 더 높아야 함 |
| ASSERT10 | 가장 흔한 수정 버전으로 업그레이드해도 여전히 누락이 있어야 함 —— 그렇지 않으면 「교집합 구하기」는 가치가 없음 |
| ASSERT14 | XmlLayout≠Log4j1XmlLayout, verifyHostName≠verifyHostname, 정·반 샘플을 각각 한 번씩 테스트 |
| ASSERT17 | 설명 원문은 완전한 문장이어야 하며, asciidoc의 강제 줄바꿈으로 끊긴 잔여 문장이 아니어야 함 |
tools/recheck_before_publish.py):
생성 스크립트의 출력을 전혀 읽지 않고, 세 가지 소스(OSV.dev / 공식 보안 웹페이지 / Maven Central)로 다시 계산실제 아티팩트 재검증으로 단위 테스트로는 잡을 수 없는 문제 3건을 발견했고, 모두 수정 + 회귀 테스트를 추가했다: