Spring Framework のリモートコード実行の脆弱性(CVE-2018-1270)について
久しぶりにブログ書きます。 最近あまりネタがなかったのでちょっとさぼり気味でした。
今月に公開されたSpring Frameworkの脆弱性(CVE-2018-1270)についてです。
CVE-2018-1270: Remote Code Execution with spring-messaging | Security | Pivotal
Apache Struts同様JAVAのアプリケーションフレームワークです。
概要
STOMP (Streaming Text Oriented Messaging Protocol) というメッセージプロトコルをWebSocketで利用している場合に、攻撃者から細工されたメッセージを受け取ることで、任意のコード実行が行われる脆弱性。
同様に細工されたURLにアクセスすることでディレクトリトラバーサルが行われる脆弱性も報告されています(CVE-2018-1271)。
影響を受けるバージョン
- Spring Framework 5.0 から 5.0.4
- Spring Framework 4.3 から 4.3.15
また、すでにサポートが終了している過去のバージョンにおいても本脆弱性の影響を受けるとのことです。
修正バージョン
- Spring Framework 5.0.5
- Spring Framework 4.3.16
Spring Bootを利用している場合は 2.0.1.RELEASE で修正されています。
検証
PoCがすでに公開されており、検証用のアプリケーションもあったので、検証してみました。 ベースに「openjdk:8-jdk-alpine」のDockerコンテナを用い、Spring Boot 2.0.0.RELEASE で検証用のアプリケーションを動かしました。
8080ポートを開けてコンテナを動かします。
docker run -p 8080:8080 -it openjdk:8-jdk-alpine /bin/sh
このコンテナの中に、Spring Bootの脆弱性のあるバージョンを持ってきます(このへんから)。
解凍したSpring Bootのソースの中に検証用のアプリケーションのソースとpom.xmlを入れて、Mavenでビルドし、アプリケーションを動かします。
./mvnw spring-boot:run
Spring Bootが色々と必要なものを落としてきて、アプリケーションを動かしてくれます。 終わったら、localhostの8080番にアクセスします。
検証用のサンプルなので、すでにPoCが入力されており、あとはSendするだけという親切設計になっています。
で、Sendします。
アプリケーションを止めて、/tmp 配下を確認すると、確かに実行した通り、/etc/passwd の中身がコピーされていました。
ついでに修正されたバージョンでもやってみました。 詳しくは上と同じなので書きませんが、Spring Boot 2.0.1.RELEASE を用意し、pom.xml の中身のバージョンを書き換えます。
<spring.boot.version>2.0.1.RELEASE</spring.boot.version>
これやらないと、せっかく修正バージョンを用意しても Spring Boot が気を利かせて、脆弱なバージョンを持ってきてビルドするみたいですね。
で、同じようにやってみましたが、コード実行は失敗、それ以外には変化はありませんでした。
攻撃時のパケット
パケットキャプチャをとってみました。
GETリクエスト ⇒ 101(Switching Protocols) ⇒ WebSocketでやり取り(STOMP)
という感じですかね。 攻撃コードがバイナリになってどこが何をやっているのかがちょっとわからないです……。
どうやらWebSocketがデータ圧縮を行っているため、攻撃コードが可読でなくなってるみたいです。 「Sec-WebSocket-Extensions: permessage-deflate」というヘッダのせいみたいですね。
試しにBurpを間に挟んで攻撃コードを実行してみました。
例のヘッダがなくなり、一部平文で見えるようになりました。ただ、それでも攻撃コードはマスクされています。 (BurpってWebSocketも対応してるんですね)
ただ、WireSharkで見ると、マスクを解除してくれるので、攻撃コードがちゃんと見えています。
普通のHTTP通信ならレスポンスが圧縮されることはあってもリクエストは平文だったのですが、WebSocketはリクエストが圧縮されたりマスクされたりするので、攻撃のポイントが見えにくくなり、検知がしにくくなります。 WebSocket経由の攻撃はこれまで見たことなかったのですが、割と面倒くさいなというのが今回の印象です。
追記(2018/04/17)
WebSocketのマスクついて調べてみました。
RFC 6455 - The WebSocket Protocol
RFC 6455 - The WebSocket Protocol (日本語訳)
ポイントとしては
- クライアントからサーバへ送信される すべてのフレームはマスクされる
- マスクに利用されるキーは32bitのランダムな値
- ペイロードは「マスク用キー + マスクされたペイロード」という形で流れる
- 元のペイロード = マスク用キー XOR マスクされたペイロード
ということらしいです。
攻撃者が任意のトラフィックを通信路に流せると、キャッシュ汚染の恐れがあるといった理由でこうなっているみたいです。
これをIDSなどで検知しようとすると、攻撃コードがWebSocketのマスクされた値の中にあるので、それを解除したうえで、検知を行う必要があり、かなり難しいと思われます。 WebSocket経由での攻撃というのは初めて聞いたのですが、このようなWebSocketのリクエストに対応した検知システムとかって、何かあるんでしょうかね……。
まとめ
Spring Framework は Apache Struts と同様JAVAのWebアプリケーションフレームワークなので、Strutsの脆弱性に懲りてSpring Framework に乗り換えようという人もいるかもしれませんが、こまめにアップデートする必要はいずれにせよありそうですね。 今回Spring Frameworkは初めて触ったのですが、割と開発の使い勝手は良さそうな印象でした。
今回の攻撃は影響は大きそうですが、まだ攻撃が行われたという話は聞かないです。 ただ、先に述べたように一般的なWebの攻撃に比べて検知が難しいので、気づかないうちに攻撃が行われている、なんてこともあるかもしれません。自分の管理するサーバに異変がないか、確認してみるのがいいかと思います。
参考
Spring Frameworkに含まれるリモートコード実行に関する脆弱性(CVE-2018-1270)についての検証レポート | NTTデータ先端技術株式会社