FlawOpen

flawopen.com/Incidents/log4shell-jndi-injection-december-2021

● Apache Software Foundation · CVE-2021-44228 · December 2021 · CVSS 10.0 · 심각
보안 연구 · FlawOpen

Log4Shell (CVE-2021-44228): 원인과 해결

CVE-2021-44228은 로그 데이터가 Log4j 2 조회와 JNDI 클래스 로딩을 유발하게 했습니다. Log4j를 업데이트하고 포함된 사본도 찾으세요.[1], [2], [4]

💡 알기 쉬운 설명 (ELI5)

방문객이 한 말을 받아 적는 것만이 일인 직원이 있는 사무실을 떠올려 보세요. 누군가, 방문객이 특정 문구를 말하면 직원이 받아 적기를 멈추고 문구에 나온 주소로 가서 낯선 사람에게서 봉인된 봉투를 받아 그 안의 지시를 따른다는 것을 알아냅니다. 직원은 받아 적기만 하면 되는 사람이었는데 말이죠.[1]

핵심 개념 및 용어

메시지 룩업
로그를 쓸 때 메시지 안의 ${...} 식을 값으로 바꿔 주던 Log4j 2 기능입니다. 2.15.0 이전에는 설정된 패턴뿐 아니라 메시지 안의 데이터에도 적용되었습니다.[1]
JNDI
Java Naming and Directory Interface. LDAP 같은 디렉터리 서비스에서 객체를 찾는 API입니다. 원격 서버로의 JNDI 룩업은 JVM이 불러올 클래스를 돌려줄 수 있었습니다.[1]
식 인젝션(CWE-917)
신뢰할 수 없는 데이터가 식 평가기에 도달해, 데이터로 넘긴 텍스트가 명령으로 실행되는 것입니다.[1]
전이 의존성
다른 라이브러리가 필요로 한다는 이유만으로 애플리케이션에 들어온 라이브러리입니다. Log4Shell 노출의 상당수는 아무도 목록에 올리지 않은 Log4j 사본에서 비롯되었습니다.[4]
소프트웨어 자재 명세서(SBOM)
빌드에 들어간 모든 구성 요소와 버전을 기계가 읽을 수 있게 정리한 목록입니다. "Log4j 2.14는 어디서 돌아가는가?"에 빠르게 답할 수 있게 해 줍니다.[4], [3]

프레임워크 매핑

사고 타임라인

2021-11-24

Alibaba Cloud Security의 천자오쥔이 Apache에 결함을 비공개로 제보합니다.[1]

2021-12-09

결함이 공개되고 Log4j 2.15.0이 배포되며, 몇 시간 만에 대규모 스캔이 시작됩니다.[1], [4]

2021-12-17

CISA가 긴급 지시 22-02를 발령해 미국 연방 기관에 Log4j를 찾아 완화하도록 요구합니다.[3]

2021-12-28

Log4j 2.17.1이 CVE-2021-44832를 수정합니다. 2.15.0 이후 나온 세 CVE(CVE-2021-45046, CVE-2021-45105 포함) 중 마지막입니다.[1]

단계별 공격 실행 흐름

단계 1

공격자의 텍스트가 로그 호출에 닿는다

애플리케이션이 기록하는 사용자 이름, 헤더, 검색어 같은 어떤 값이든 공격자가 고른 룩업 식을 담을 수 있습니다.[1]

단계 2

Log4j가 식을 평가한다

취약한 버전은 기록하는 데이터 안의 룩업을 처리하므로, 식이 텍스트로 쓰이지 않고 해석됩니다.[1], [2]

단계 3

JNDI가 외부로 연결한다

JNDI 룩업이 서버를 식에 지정된 디렉터리 서비스에 연결하게 만들며, 그곳은 흔히 공격자의 인프라입니다.[1], [4]

단계 4

원격 코드가 실행된다

원격 디렉터리는 JVM이 불러올 클래스를 알려 줄 수 있고, 그 클래스는 애플리케이션 권한으로 실행됩니다. 외부 연결이 막혀 있던 곳에서는 이 단계가 실패했습니다.[1], [2]

소스 코드 비교: 취약한 구현 vs 보안 패치

설명용 Maven 설정입니다. log4j-bom을 가져오면 다른 라이브러리가 끌어온 사본까지 포함해 모든 Log4j 모듈이 하나의 최신 버전으로 해석됩니다. 이어서 enforcer 규칙이 2.17.1보다 오래된 log4j-core가 전이적으로라도 남아 있으면 빌드를 실패시키므로, 새 의존성이 취약한 코드를 몰래 다시 들여올 수 없습니다. 진짜 수정은 업그레이드이며, 애플리케이션 서버에서 외부로 나가는 LDAP와 RMI를 막은 것은 패치가 늦은 곳에서도 악용을 끊어 낸 심층 방어였습니다.

취약한 구현
<!-- pom.xml: a direct dependency pinned to a vulnerable release -->
<dependencies>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.14.1</version>
  </dependency>
</dependencies>
<!-- Nothing stops another library from pulling in an older log4j-core transitively. -->
보안 강화 패치
<!-- pom.xml: one version for every Log4j module, and a build that fails on old ones -->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-bom</artifactId>
      <version>2.24.3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-enforcer-plugin</artifactId>
      <version>3.5.0</version>
      <executions>
        <execution>
          <id>ban-vulnerable-log4j</id>
          <goals><goal>enforce</goal></goals>
          <configuration>
            <rules>
              <bannedDependencies>
                <searchTransitive>true</searchTransitive>
                <excludes>
                  <exclude>org.apache.logging.log4j:log4j-core:[2.0-beta9,2.17.1)</exclude>
                </excludes>
              </bannedDependencies>
            </rules>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

엔지니어링 및 시스템 보안 강화 체크리스트

출처