Log4Shell - Log4jの脆弱性(CVE-2021-44228)を検出する(続報)

Security Marcus LaFerrera

者/寄稿者:Splunkのセキュリティはいつでもファミリービジネスです。この記事は、Ryan Kovar、Shannon Davis、Johan Bjerke、James Brodsky、Dave Herrald、John Stoner、Drew Church、Mick Baccio、Jay Holladay、Lily Lee、Audra Streetman、Tamara Chaconの協力のもと執筆されました。

最新情報:Log4j RCE

Splunk SURGeチームは先日、世界中のセキュリティ防御チームに徹夜の対応を迫ったLog4jの脆弱性「Log4Shell」について、Splunk製品での対策をまとめた速報ブログセキュリティアドバイザリーを公開しています。

このブログでは、組織への攻撃の検出方法に関する追加の最新情報をお伝えします。攻撃の兆候を検出するために必要なログを収集していなくても、まだ間に合います。自社のホストが標的になっているかどうかを調査する方法がほかにも見つかりました。

Log4Shellの動作を検出する

スイスCERTは、この攻撃の各段階の概略を示す図を含む有用なブログを公開しました。この図には、検索の鍵となる情報も示されています。

Splunkでの検出方法は第1~2段階に集中しています。この段階では、脆弱性のあるサーバーに、攻撃の足掛かりとなるJNDIリクエストが送られます。

このログを記録していない場合でも、第3段階で検出する良い方法があります。そこで重要になるデータソースが、ネットワークトラフィックログとDNSクエリーログです。以下では、この2つのログを使用して、組織の環境内で侵害されたホストを検出する方法をご説明します。

SplunkでLog4Shell (Log4j 2 RCE)攻撃を検出する

侵入検知アラート

前提としてIDSの導入は欠かせません。IDSのルールを最新の状態に更新し、Splunkでアラートをインデックスするように設定していることを確認してください。ここではSuricataを使用しますが、この脆弱性のシグネチャに対応したIDSであれば製品は問いません。攻撃を検出するにはまず、次のインデックスをサーチします。

index=suricata ("2021-44228" OR "Log4j" OR "Log4Shell") 
| table _time, dest_ip, alert.signature, alert.signature_id

組織のネットワークから外部へのLDAPアクセスを検出する

組織と外部のネットワーク境界にあるファイアウォールで外部へのLDAPトラフィックを許可することはまずないでしょう。このトラフィックが検出された場合は、Log4Shellの初期アクセス活動が行われている可能性があります。SplunkのベストプラクティスとNetwork Trafficデータモデルを使用したtstatsによるサーチがこちらです。このサーチを使えば、プライベート(RFC1918)アドレス範囲外のIPアドレスに対するLDAP接続を検出できます。

| tstats earliest(_time) as earliest_time latest(_time) as latest_time values(All_Traffic.dest_ip) from datamodel=Network_Traffic.All_Traffic where All_Traffic.dest_port = 1389 OR All_Traffic.dest_port = 389 OR All_Traffic.dest_port = 636 AND NOT (All_Traffic.dest_ip = 10.0.0.0/8 OR All_Traffic.dest_ip=192.168.0.0/16 OR All_Traffic.dest_ip = 172.16.0.0/12) by All_Traffic.src_ip
| convert ctime(earliest_time) ctime(latest_time)

JNDIプローブとDNSクエリーを相関付ける

Log4jの脆弱性悪用の兆候を示すJNDI文字列の検出方法はすでに特定しています。では、その結果とプローブ(攻撃前の探査)の成功をどのように相関付ければよいでしょうか。ここで役立つのがDNSです。

最初のサーチでは、まず、正規表現を使ってJNDI文字列からドメインを抽出します。次に、抽出したドメインを追加してルックアップテーブルを更新します。このルックアップテーブルは次のサーチで使用します。このクエリーでは非構造化データを処理するため、通常のCPUサイクルの数倍、サーチするデータ量によってはある程度の時間がかかることに注意してください。このサーチを初めて実行するときは、まずルックアップファイルを作成するために、クエリー内のlookup行をコメントアウトしてください。

index=*  jndi
| rex field=_raw max_match=0 "[jJnNdDiI]{4}(\:|\%3A|\/|\%2F)(?\w+)(\:\/\/|\%3A\%2F\%2F)(\$\{.*?\}(\.)?)?(?[a-zA-Z0-9\.\-\_\$\{\:]+)"
| mvexpand rce_dest
| rex field=rce_dest "(?\d+\.\d+\.\d+\.\d+)"
| eval rce_domain = case(isnull(rce_ip),rce_dest)
| rex field=rce_domain "(?[0-9a-zA-A\-]+\.[0-9a-zA-A\-]+$)"
| dedup top_level_domain
| eval top_level_domain = "*.".top_level_domain
| where top_level_domain!=""
| lookup log4j_scanning_domain.csv query as top_level_domain OUTPUT query AS old_query
| where isnull(old_query)
| rename top_level_domain as query
| table query
| outputlookup append=t log4j_scanning_domain.csv

上記のサーチが完了すると、ドメインを格納したルックアップテーブルが作成されるので、Network Resolutionデータモデルを使ってtstatsサーチを実行し、JNDIプローブのドメインと一致するDNSクエリーを探します。

| tstats summariesonly=true allow_old_summaries=true
      values(host) as host, values(DNS.query_type) as DNS.query_type, values(DNS.reply_code) as DNS.reply_code, values(DNS.transport) as DNS.transport
      count from datamodel=Network_Resolution.DNS
      where [| inputlookup log4j_scanning_domain.csv | rename query as DNS.query | format] 
      by "DNS.src",sourcetype, DNS.query index _time span=1s
| stats earliest(_time) as first_seen, latest(_time) as last_seen sum(count) as count, values(DNS.reply_code) as DNS.reply_code, values(index) as index, values(DNS.src) as DNS.src, values(DNS.query_type) as DNS.query_type, values(DNS.transport) as DNS.transport by host DNS.query sourcetype
| convert timeformat="%m/%d/%Y %H:%M:%S" ctime(first_seen), ctime(last_seen)

新しいアウトバウンドトラフィックを検出する

2021年12月9日以前に送信トラフィックを生成していない内部サーバー(Egress)からのアウトバウンドトラフィックを検索する方法もあります。そのためには、期間をこの日(2021-12-09)の24時間以上前に設定して、標準的なトラフィックと比較できるようにします。このように挙動サーチの範囲を拡大すると、検索時間は長くなりますが、検索網を最大限に広げて侵害の兆候を捉えることができるメリットがあります。基本となるSPLがこちらです。

index=* src_ip=* dest_ip=* 
(NOT (dest_category="internal" OR dest_ip=10.0.0.0/8 OR dest_ip=172.16.0.0/12 OR dest_ip=192.168.0.0/16 OR dest_ip=100.64.0.0/10))
| stats
earliest(_time) as earliest 
latest(_time) as latest 
values(action) as action 
values(app) as app 
values(dest_port) as dest_port 
values(sourcetype) as sourcetype count 
by src_ip dest_ip
| eventstats max(latest) as maxlatest
```This is 2021-12-09 00:00:00```
| eval comparisonTime="1639008000"
```| eval comparisonTime="-1d@d" ```
| eval isOutlier=if(earliest >= relative_time(maxlatest, comparisonTime), 1, 0)
| convert timeformat="%Y-%m-%dT%H:%M:%S" ctime(earliest),ctime(latest) ,ctime(maxlatest)
| where isOutlier=1

このサーチはいくつかの方法で変更できます。

このブログはこちらの英語ブログの翻訳、藤盛 秀憲によるレビューです。

関連記事

SOCを最新化してレジリエンスを向上させるための基盤を構築する
セキュリティ
9 分程度

SOCを最新化してレジリエンスを向上させるための基盤を構築する

Splunkのセキュリティソリューションは、セキュリティ態勢を包括的に可視化して、ビジネスリスクを低減することにより、デジタルレジリエンスジャーニーの推進をサポートします。リスクベースの脅威検出、調査、対応を実現してSOCの最新化を支援し、活発なコミュニティを通じてセキュリティイノベーションを後押します。
暗号技術におけるハッシュ化の解説:仕組みやアルゴリズムから実際の活用例まで
セキュリティ
11 分程度

暗号技術におけるハッシュ化の解説:仕組みやアルゴリズムから実際の活用例まで

暗号技術におけるハッシュ化の定義や仕組みをはじめ、SHA-256などの主要なアルゴリズム、実際の活用例、そしてデータの安全を守るためのベストプラクティスについて解説します。
ゼロトラスト・セキュリティとその屋台骨を担うSplunk
セキュリティ
8 分程度

ゼロトラスト・セキュリティとその屋台骨を担うSplunk

ゼロトラストとは、セキュリティ境界線に関わるものを信頼せず、すべてのアクセスを検証するネットワークセキュリティの概念と脅威モデルを指します。「データ収集と分析」の5つの領域とそれらを監視する2つの対策が存在します。この記事では、それらとゼロトラストにおけるSplunkのSIEMとしての役割を紹介します。