Log4j 레트로스펙티브(Retrospective) Part 1: 취약점 배경
타임라인
2021년 11월 24일에 Apache Foundation은 Alibaba의 클라우드 보안 팀으로부터 다음과 같은 내용을 비공개로 통보받았습니다. Java 기반 로깅 라이브러리로원격 코드 실행(RCE)은 물론 개인 정보가 유출될 수 있는 주요 취약점이 포함되어 있다는 사실이었습니다. 이 취약점은 2013년부터 존재했습니다.
다음 날 Apache Foundation은 CVE-2021-44228에 대한 해결 방법을 연구하기 시작했습니다. 향후 12일 동안 문제를 해결하기 위해 소스 코드에 몇 가지 변경 사항이 도입되었으며 2021년 12월 9일에 이 취약점이 대중에게 공개되었습니다.
그 결과, 그 이후로 놀라운 속도로 증가하는 악용 시도가 쇄도했습니다.
Log4j란?
취약점을 제대로 이해하려면 Log4j가 무엇인지 알아야 합니다. Log4j는 Java 커뮤니티에서 개발자들 사이에 널리 사용되는 라이브러리입니다. 오류 메시지, 진단 정보 등을 기록할 수 있는 간단하면서도 강력한 프레임워크를 제공합니다.
인상적인 기능 중 하나는 콘솔, 파일, TCP, Syslog, NT 이벤트 로그 및 이메일을 사용하는 원격 서버를 포함하여(이에 국한되지 않음) 여러 대상에 기록하는 기능입니다. 또한 로그 메시지, 로그 수준, 사용자 지정 레이아웃 등을 계층적으로 필터링할 수 있습니다.
간단히 말해, 이 라이브러리는 개발자들이 활용할 수 있는 매우 매력적인 라이브러리로서, 웹 애플리케이션부터 임베디드 장치에 이르기까지 모든 분야에서 보편적으로 활용되고 있습니다.
룩업(Lookups) 및 네스팅(nesting)
Llog4j가 지원하는 매우 강력한 기능 중 하나는 룩업(lookups)입니다. 개발자는 룩업을 통해 출력하기 전에 Log4j 에 의해 자동으로 평가되는 텍스트에 변수 또는 식을 포함시킬 수 있습니다. 예를 들어, 개발자는 다음 텍스트를 기록하는 코드를 작성할 수 있습니다.
“${date:MM-dd-yyyy} All Systems Good”
Log4j는 ${date:MM-dd-yyyy} 패턴을 날짜 룩업으로 인식하고, 해당 식을 오늘 날짜로 완전히 바꿉니다. 예를 들어 날짜가 2021년 12월 20일인 경우, 텍스트를 대상으로 출력하기 전에 다음과 같이 수정합니다.
“12-20-2021 All Systems Good”
이 기능은 개발자에게 매우 유용합니다. 이 기능이 없으면 날짜를 조회하고, 문자열에 형식을 지정하고, 로그 행에 붙여 넣은 다음 출력하는 코드를 수동으로 작성해야 합니다. 위의 코드가 특별히 어렵거나 쓰기 어려운 것은 아니지만, 소프트웨어의 핵심 비즈니스 로직과 관련이 없으며 한 프로젝트에서 다음 프로젝트로 반복됩니다.
개발자는 Log4j 라이브러리 내에서 이미 코딩된 기능을 활용하여 프로젝트에 중요한 차별화 요소에 집중할 수 있으며, Log4j를 통해 모든 로깅 관련 작업을 처리할 수 있습니다.
Log4j이 지원하는 다양한 유형의 룩업(lookup) 식이 있습니다. 이 문서에서 주요하게 다룰 두 가지를 더 살펴보겠습니다. env와 lower. env를 사용하면 호스트 시스템의 환경 변수를 로그 행에 포함시킬 수 있습니다. 예를 들어, 개발자가 다음곽 같이 텍스트를 로깅합니다.
“The current user is ${env:USER}”
소프트웨어가 사용자 Administrator로 실행 중인 경우 다음과 같은 출력이 생성됩니다.
“The current user is Administrator”
텍스트에 새 데이터를 삽입하는 env 및 날짜와 달리, lower는 이미 존재하는 데이터를 조작하는 데 사용할 수 있습니다. Log4j는 단순히 식 내에 나타나는 것을 소문자로 표시합니다. 예:
“The lower case text is ${lower:ABCDEFG}”
출력:
“The lower case text is abcdefg”
이 예시 자체로는 그리 흥미롭지 않습니다. 문자열을 직접 소문자로 표시하지 않는 이유는 무엇일까요? Log4j가 룩업(lookup) 식의 네스팅(nesting)을 허용하는 점을 고려할 때 이 기능의 힘이 더욱 명확하게 드러납니다.
이전의 두 식을 다음과 같이 조합할 수 있습니다.
“The lower case current user is ${lower:${env:USER}}”
이를 통해 Log4j는 먼저 ${env:USER} 식을 Administrator로 평가합니다.그런 다음 administrator를 생성하는 lower로 피드하여 궁극적으로는다음 행이 생성됩니다.
“The lower case current user is administrator”
JNDI
날짜 ,env ,그리고 lower는 흥미롭고 매우 유용하지만, 이 취약점은 JNDI 룩업(lookup)이 없다면불가능할 것입니다. JNDI(Java Naming and Directory Interface)는 Java 개발 및 런타임 환경에 기본적으로 내장된 메커니즘으로, 공통 인터페이스를 사용하여 다양한 디렉터리 서비스에 대한 정보를 간단하게 쿼리할 수 있습니다.
이 경우 여러가지 유형의 디렉터리 서비스가 지원됩니다. 예를 들어, JNDI는 DNS 서버를 쿼리하여 호스트의 IP 주소를 검색하고 AD 및 LDAP에서 디렉터리 항목을 쿼리하도록 지원합니다. 또한 실행 중인 Java 환경 자체의 환경 항목(Environmental Entries) 쿼리도 지원합니다.이는 현재 실행 중인 소프트웨어에 대한 특수 구성 옵션으로 생각할 수 있습니다.
Log4j의 JNDI 룩업(lookup) 식을 사용하면 개발자가 기록된 텍스트에 포함된 식을 통해 매우 강력한 하위 시스템에 직접 접속할 수 있습니다. 예를 들어 개발자가 다음 문자열을 로그하려고 할 경우를 들어보겠습니다.
“The current mail host is ${jndi:java:comp/env/mailhost}”
Log4j는 ${jndi:java:comp/env/mailhost} 식을 JNDI 룩업(lookup)으로 인식하고 java:comp/env/mailhost의 유사 URL을 JNDI 하위 시스템으로 전달합니다. JNDI는 이 특정 URL 유형을 현재 실행 중인 구성 요소에 대해 mailhost라는 구성 옵션을 조회하는 쿼리로 인식합니다.
이것이 mymailserver.example.com으로 구성되어 있다고 가정해 보겠습니다. JNDI는 이 정보를 Log4j에 다시 전달하며, 이 경우 룩업(lookup) 식이 mymailserver.example.com으로 대체되어 다음과 같은 아웃풋이 발생합니다.
“The current mail host is mymailserver.example.com”
취약점이 존재하는 방식 이해하기
간단히 말해, Apache의 Log4j 취약점은 특히 라이브러리의 광범위한 인기와 룩업(lookup), 네스팅(nesting) 및 JNDI 기능 때문에 공격자에게 큰 기회를 제공했습니다. 이 기능은 개발자에게는 강력하지만, 데이터를 유출시키거나 RCE를 유발할 수 있는 요청을 전달할 기회를 만듭니다. 이러한 지식을 바탕으로 이제 취약점과 공격자가 시스템을 악용하는 방법을 더욱 잘 이해할 수 있습니다.
다음 단계
이 시리즈의 Part 2에서는 공격자가 데이터 유출 및 RCE를 위해 이 취약점을 악용하기 시작한 방법에 대해 살펴보겠습니다.