2024年セキュリティの現状:競争が激化するAIの活用
先進的なサイバーセキュリティ対策を推進する組織がどのように課題を克服し、AIを活用してイノベーションを推進し、デジタルレジリエンスを強化しているかをご確認ください。
著者/寄稿者: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の協力のもと執筆されました。
Splunk SURGeチームは先日、世界中のセキュリティ防御チームに徹夜の対応を迫ったLog4jの脆弱性「Log4Shell」について、Splunk製品での対策をまとめた速報ブログとセキュリティアドバイザリーを公開しています。
このブログでは、組織への攻撃の検出方法に関する追加の最新情報をお伝えします。攻撃の兆候を検出するために必要なログを収集していなくても、まだ間に合います。自社のホストが標的になっているかどうかを調査する方法がほかにも見つかりました。
スイスCERTは、この攻撃の各段階の概略を示す図を含む有用なブログを公開しました。この図には、検索の鍵となる情報も示されています。
Splunkでの検出方法は第1~2段階に集中しています。この段階では、脆弱性のあるサーバーに、攻撃の足掛かりとなるJNDIリクエストが送られます。
このログを記録していない場合でも、第3段階で検出する良い方法があります。そこで重要になるデータソースが、ネットワークトラフィックログとDNSクエリーログです。以下では、この2つのログを使用して、組織の環境内で侵害されたホストを検出する方法をご説明します。
前提としてIDSの導入は欠かせません。IDSのルールを最新の状態に更新し、Splunkでアラートをインデックスするように設定していることを確認してください。ここではSuricataを使用しますが、この脆弱性のシグネチャに対応したIDSであれば製品は問いません。攻撃を検出するにはまず、次のインデックスをサーチします。
index=suricata ("2021-44228" OR "Log4j" OR "Log4Shell") | table _time, dest_ip, alert.signature, alert.signature_id
組織と外部のネットワーク境界にあるファイアウォールで外部への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)
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
このサーチはいくつかの方法で変更できます。
| tstats summariesonly=false allow_old_summaries=true earliest(_time) as earliest latest(_time) as latest values(All_Traffic.action) as action values(All_Traffic.app) as app values(All_Traffic.dest_ip) as dest_ip values(All_Traffic.dest_port) as dest_port values(sourcetype) as sourcetype count from datamodel=Network_Traffic where (NOT (All_Traffic.dest_category="internal" OR All_Traffic.dest_ip=10.0.0.0/8 OR All_Traffic.dest_ip=172.16.0.0/12 OR All_Traffic.dest_ip=192.168.0.0/16 OR All_Traffic.dest_ip=100.64.0.0/10)) by All_Traffic.src_ip All_Traffic.dest_ip | rename "All_Traffic.*" as * | 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
これは、前述のtstatsを使ったサーチの応用です。ルックアップに格納した過去のアクティビティをベースラインとして使用します。初回は期間を長く設定してサーチを実行することでベースラインを作成し、2回目からはクエリーの実行頻度を増やして(1時間ごとなど)、ベースラインを継続的に最新の状態にします。この方法を使えば、環境内の最新のアクティビティがベースラインに常に反映されます。
注:このサーチを初めて実行するときは、ルックアップ「egress_src_dest_tracker.csv」が設定されていないとエラーになります。エラーを避けるには、この名前の空のルックアップを作成するか、初回はこの行をコメントアウトしてサーチを実行してください。
| lookup egress_src_dest_tracker.csv dest_ip src_ip OUTPUT earliest AS previous_earliest latest AS previous_latest | tstats summariesonly=false allow_old_summaries=true earliest(_time) as earliest latest(_time) as latest values(All_Traffic.action) as action values(All_Traffic.app) as app values(All_Traffic.dest_ip) as dest_ip values(All_Traffic.dest_port) as dest_port values(sourcetype) as sourcetype count from datamodel=Network_Traffic where (NOT (All_Traffic.dest_category="internal" OR All_Traffic.dest_ip=10.0.0.0/8 OR All_Traffic.dest_ip=172.16.0.0/12 OR All_Traffic.dest_ip=192.168.0.0/16 OR All_Traffic.dest_ip=100.64.0.0/10)) by All_Traffic.src_ip All_Traffic.dest_ip | rename "All_Traffic.*" as * | lookup egress_src_dest_tracker.csv dest_ip src_ip OUTPUT earliest AS previous_earliest latest AS previous_latest | eval earliest=min(earliest, previous_earliest), latest=max(latest, previous_latest) | fields - previous_* | appendpipe [ | fields src_ip dest_ip latest earliest | stats min(earliest) as earliest max(latest) as latest by src_ip, dest_ip | inputlookup append=t egress_src_dest_tracker.csv | stats min(earliest) as earliest max(latest) as latest by src_ip, dest_ip | outputlookup egress_src_dest_tracker.csv | where a=b ] | eventstats max(latest) as maxlatest | eval comparisonTime="-1h@h" | 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
ベースラインに基づいて新しい国からの受信トラフィックを検出する
このサーチの考え方は前述のサーチと同じです。違いは、内部IPにアクセスする外部IPを検出し、その外部IPの位置情報を調べる点です。ルックアップは、送信元、宛先、送信元の国で構成されます。前述のサーチと同様に、大量の結果が返され、誤検知が含まれる場合があります。その緩和策として、調査したい特定のアプリケーションサーバーのみに検索範囲を限定すること、そして特に、デスクトップシステムを除外することをお勧めします。このサーチは、有意義な結果が返るまで検索クエリーを手動で実行するのが最も効果的かもしれません。
初回はタイムフレームを長く設定してサーチを実行することでベースラインを作成し、2回目からはクエリーの実行頻度を増やして(1時間ごとなど)、ベースラインを継続的に最新の状態にします。この方法を使えば、環境内の最新のアクティビティがベースラインに常に反映されます。
注:このサーチを初めて実行するときは、ルックアップ「ingess_src_dest_country_tracker.csv」が設定されていないとエラーになります。エラーを避けるには、この名前の空のルックアップを作成するか、初回はこの行をコメントアウトしてサーチを実行してください。
| lookup ingess_src_dest_country_tracker.csv dest_ip src_ip Country OUTPUT earliest AS previous_earliest latest AS previous_latest | tstats summariesonly=false allow_old_summaries=true earliest(_time) as earliest latest(_time) as latest values(All_Traffic.action) as action values(All_Traffic.app) as app values(All_Traffic.dest_ip) as dest_ip values(All_Traffic.dest_port) as dest_port values(sourcetype) as sourcetype count from datamodel=Network_Traffic where (All_Traffic.dest_category="internal" OR All_Traffic.dest_ip=10.0.0.0/8 OR All_Traffic.dest_ip=172.16.0.0/12 OR All_Traffic.dest_ip=192.168.0.0/16 OR All_Traffic.dest_ip=100.64.0.0/10) AND (All_Traffic.src_category="external" OR (All_Traffic.src_ip!=10.0.0.0/8 AND All_Traffic.src_ip!=172.16.0.0/12 AND All_Traffic.src_ip!=192.168.0.0/16 AND All_Traffic.src_ip!=100.64.0.0/10)) by All_Traffic.src_ip All_Traffic.dest_ip | rename "All_Traffic.*" as * | iplocation src_ip | lookup ingess_src_dest_country_tracker.csv dest_ip src_ip Country OUTPUT earliest AS previous_earliest latest AS previous_latest | eval earliest=min(earliest, previous_earliest), latest=max(latest, previous_latest) | fields - previous_* | appendpipe [ | fields src_ip dest_ip Country latest earliest | stats min(earliest) as earliest max(latest) as latest by src_ip, dest_ip, Country | inputlookup append=t ingess_src_dest_country_tracker.csv | stats min(earliest) as earliest max(latest) as latest by src_ip, dest_ip, Country | outputlookup ingess_src_dest_country_tracker.csv | where a=b ] | eventstats max(latest) as maxlatest | eval comparisonTime="-1h@h" | 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
この脆弱性に対応するにはやはりパッチの適応が最善策です。パッチを適用できない場合、次善の策は、攻撃範囲を最小限にとどめるための緩和策を実行することです。SURGeは引き続きこの脆弱性の動向を監視し、最新情報を適宜公開します。また、Splunkの脅威調査チームも、この脅威を検出するためのESCUと自動対応のためのSOARプレイブックの作成に全力で取り組んでおり、できる限り早急にリリースする予定です。
Splunk SURGeは、お客様が新たな脅威や未知の脅威を迅速に検出、調査、対応できるよう高度な分析とインサイトを提供する専門のセキュリティ調査チームです。SURGeアラートではセキュリティ調査と技法に関するガイダンスを受け取ることができます。ぜひご登録ください。
このブログはこちらの英語ブログの翻訳、藤盛 秀憲によるレビューです。
Splunkプラットフォームは、データを行動へとつなげる際に立ちはだかる障壁を取り除いて、オブザーバビリティチーム、IT運用チーム、セキュリティチームの能力を引き出し、組織のセキュリティ、レジリエンス(回復力)、イノベーションを強化します。
Splunkは、2003年に設立され、世界の21の地域で事業を展開し、7,500人以上の従業員が働くグローバル企業です。取得した特許数は1,020を超え、あらゆる環境間でデータを共有できるオープンで拡張性の高いプラットフォームを提供しています。Splunkプラットフォームを使用すれば、組織内のすべてのサービス間通信やビジネスプロセスをエンドツーエンドで可視化し、コンテキストに基づいて状況を把握できます。Splunkなら、強力なデータ基盤の構築が可能です。