Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
log4j-jndi-be-gone — CVE-2021-44228, 즉 log4j 2.x "JNDI LDAP" 취약점에 대한 Byte Buddy Java 에이전트 기반 수정입니다. | Kitploit
도구/GitHubGitHub/nccgroup/log4j-jndi-be-gone
Defensive ToolsVulnerability AnalysisCode AnalysisExploitationSupply Chain Security
GitHubnccgroup/log4j-jndi-be-gone

log4j-jndi-be-gone

CVE-2021-44228, 즉 log4j 2.x "JNDI LDAP" 취약점에 대한 Byte Buddy Java 에이전트 기반 수정입니다.

저장소 보기
721654년 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
웹사이트

log4j-jndi-be-gone

Byte Buddy 기반 Java 에이전트로 CVE-2021-44228, 즉 log4j 2.x의 "JNDI LDAP" 취약점을 수정합니다.

다음 세 가지 작업을 수행합니다.

  • jndi: 형식 문자열("lookups")에 대한 내부 메서드 핸들러를 비활성화합니다.
  • log4j JNDI 시도가 있었음을 나타내는 메시지를 System.err(즉, stderr)에 기록합니다. (시도된 형식 문자열을 포함하며, 전이 주입을 방지하기 위해 ${} 문자는 삭제됩니다.)
  • 전이 주입을 방지하기 위해 로그 메시지의 형식 문자열을 "(log4j jndi disabled)"로 처리합니다.

사용법

java 명령에 -javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar를 추가하세요.

참고: 클래스패스에 Byte Buddy가 이미 있다면 log4j-jndi-be-gone-1.0.0.jar를 사용해 보세요.

root@kitploit:~
$ java -javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar -jar path/to/some.jar

버전 1.1.0부터 log4j-jndi-be-gone은 애플리케이션의 의존성 버전과 다른 의존성이 포함한 동일 의존성 버전 간의 충돌을 방지하기 위해 JAR 내부에 대체 패키지 이름으로 포함될 수 있는 리패키징(일명 "shaded")된 log4j 버전도 기본적으로 처리하려고 시도합니다. 단, log4j는 정적 클래스 이름 및/또는 내장 구성 파일의 클래스 이름과 함께 리플렉션을 사용하기 때문에 대체 패키지 이름/접두사로 쉽게 리패키징되지 않는 것으로 보입니다.

이 동작은 -javaagent: 인자에서 에이전트 JAR 경로 뒤에 =structureMatch=0을 넣어 비활성화할 수 있습니다. 예:

root@kitploit:~
-javaagent:path/to/log4j-jndi-be-gone-1.0.0-standalone.jar=structureMatch=0

그러면 1.0.0과 동일한 매칭 동작, 즉 클래스 이름에 대한 단순한 정확한 문자열 비교가 수행됩니다.

log4j-jndi-be-gone 얻기

./gradlew로 JAR를 빌드하거나(build/libs/log4j-jndi-be-gone-1.0.0(-standalone).jar) 릴리스 페이지에서 받을 수 있습니다.

호환성

log4j-jndi-be-gone 에이전트 JAR는 Java 6-17+를 지원합니다.

클래스 매칭

구현은 org.apache.logging.log4j.core.lookup.JndiLookup의 가장 안쪽 하위 패키지 및 클래스 이름과 일치하는 접미사를 가진 클래스(즉, lookup.JndiLookup)에 대한 매칭부터 시작합니다. org.apache.logging.log4j.core는 패키지 이름을 보존하지 않는 리패키징 규칙에 의해 변경되었을 가능성이 높기 때문입니다. 또한, 다른 예상되는 log4j 유형에 대해 단순히 유사한 검사를 수행하는 대신 해당 유형이 동일한 기본 패키지 아래에 존재하는지도 확인합니다.

그런 다음 구현은 식별된 잠재적 log4j lookup.JndiLookup 클래스의 구조를 살펴보며 다음 항목을 검증합니다:

  • 클래스 자체의 수정자
  • 클래스의 부모 클래스 및/또는 구현된 인터페이스(이들은 log4j 버전에 따라 다름)
  • 모든 2.x 버전에 걸쳐 예상되는 org.apache.logging.log4j.core.config.plugins.Plugin 어노테이션(어노테이션 매개변수 및 해당 값 포함)
  • lookup() 메서드의 수정자 및 타입 시그니처 일치(2.0의 1-인자 버전은 무시)
  • convertJndiName() 메서드의 수정자 및 타입 시그니처 일치
  • CONTAINER_JNDI_RESOURCE_PATH_PREFIX 필드의 수정자 일치

주의사항

  • log4j 라이브러리가 난독화되었거나 기본적인 리패키징(즉, "shading") 외에 클래스 패키지/이름이 수정된 경우 log4j-jndi-be-gone은 작동하지 않습니다.

    • 참고로, log4j 2.x는 리패키징에 대해 상당히 융통성이 없어서 그러한 관행이 얼마나 흔한지는 불분명합니다.
  • log4j-jndi-be-gone-1.0.0-standalone.jar에는 Byte Buddy가 번들로 포함되어 있습니다. 이미 Byte Buddy를 사용 중이라면 문제가 발생할 수 있습니다. 대신 log4j-jndi-be-gone-1.0.0.jar를 사용해 보세요. 단, log4j-jndi-be-gone은 Byte Buddy 1.12.x를 기대합니다. 버전 1.1.0부터 log4j-jndi-be-gone 독립형 JAR는 자체 패키지 접두사 아래에 리패키징된 Byte Buddy를 번들로 포함합니다. 이로 인해 충돌이 방지됩니다.

  • 허니팟을 구성하거나 lookup() 호출을 기록하는 구현으로 JndiLookup 클래스를 교체한 경우, log4j-jndi-be-gone이 해당 lookup 메서드를 비활성화하여 작동하지 않게 만들 수 있습니다.

예제

tests/jnditest 디렉터리에는 log4j 로깅 호출이 JNDI LDAP 형식 문자열을 전달하는 간단한 테스트 사례가 있습니다. 또한 자체 포트 리스너를 설정하여 log4j가 연결 시도를 했는지 확인하고, 연결이 수신되면 테스트를 실패시킵니다.

root@kitploit:~
$ ./tests/jnditest/test-uninstrumented.sh

BUILD SUCCESSFUL in 1s
6 actionable tasks: 5 executed, 1 up-to-date

BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 up-to-date
JUnit version 4.12
.16:08:49.547 [main] ERROR trust.nccgroup.jnditest.test.JndiTest - Hello, _${jndi:ldap://127.0.0.1:8899/evil}_!
E
Time: 0.929
There was 1 failure:
1) logging(trust.nccgroup.jnditest.test.JndiTest)
java.lang.AssertionError: jndi ldap connection received
	at org.junit.Assert.fail(Assert.java:88)
	at trust.nccgroup.jnditest.test.JndiTest.logging(JndiTest.java:55)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runners.Suite.runChild(Suite.java:128)
	at org.junit.runners.Suite.runChild(Suite.java:27)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runners.Suite.runChild(Suite.java:128)
	at org.junit.runners.Suite.runChild(Suite.java:27)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
	at org.junit.runner.JUnitCore.runMain(JUnitCore.java:77)
	at org.junit.runner.JUnitCore.main(JUnitCore.java:36)
	at trust.nccgroup.jnditest.Main.main(Main.java:24)

FAILURES!!!
Tests run: 1,  Failures: 1

$ ./tests/jnditest/test-instrumented.sh

BUILD SUCCESSFUL in 1s
6 actionable tasks: 5 executed, 1 up-to-date

BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 up-to-date
JUnit version 4.12
.log4j jndi lookup attempted: (sanitized) ldap://127.0.0.1:8899/evil
16:09:06.064 [main] ERROR trust.nccgroup.jnditest.test.JndiTest - Hello, _(log4j jndi disabled)_!

Time: 1.362

OK (1 test)

라이선스

Apache 2 라이선스에 따라 사용이 허가됩니다.

호환성

테스트된 Java 버전

log4j-jndi-be-gone은 OpenJDK 6, 8, 11, 17 및 HotSpot, OpenJ9 JVM에서 테스트되었습니다.

테스트된 Log4j 버전

  • 2.0
  • 2.0.1
  • 2.0.2
  • 2.1
  • 2.2
  • 2.3
  • 2.4
  • 2.4.1
  • 2.5
  • 2.6
  • 2.6.1
  • 2.6.2
  • 2.7
  • 2.8
  • 2.8.1
  • 2.8.2
  • 2.9.0
  • 2.9.1
  • 2.10.0
  • 2.11.0
  • 2.11.1
  • 2.11.2
  • 2.12.0
  • 2.12.1
  • 2.12.2
  • 2.13.0
  • 2.13.1
  • 2.13.2
  • 2.13.3
  • 2.14.0
  • 2.14.1
  • 2.15.0
  • 2.16.0
  • 2.17.0
도구 다운로드