TT Lab
はじめる
学ぶ 学習パス コース

cron が午前三時に curl を叩いた

ルールは文章ではなくデータ構造だ

TT Labで続きを見る

一言でいうと

Sigmaは、「このログから、こういうものを探せ」をツールに依存しないYAMLで書く形式です。必須項目はtitle・logsource・detectionの3つだけで、検知の本体は、detectionの中の名前付きのセレクション(selection)と、それを組み立てるconditionの1行です。

なぜ必要なのか

検知ルールは、ツールごとに文法が違います。同じ「スケジュールされたジョブが外部にリクエストした」を、検索エンジンではクエリ文で、ログパイプラインではフィルターで、EDRでは自分の画面のフォームで書きます。会社を移ると、ルールを最初から書き直します。さらに悪いのは、ルールを共有できないという点です。あるチームが新しい手口を捕まえるルールを作っても、ほかのチームは自分のツールの言葉に書き直す必要があり、書き直す途中で、条件が1つ静かに抜け落ちます。

Sigmaは、その中間に形式を1つ置きます。ルールはYAMLで書き、バックエンドがそれを各ツールのクエリ文に変換します。そのため、ルールは、人が読んでレビューしてgitに載せるコードになり、ツールは交換できる裏方になります。

どう動くのか

ルール1本の骨組みは、次のとおりです。

title: 예약 작업이 사외로 요청했다
id: 8b2f5f4a-1d3c-4c22-9f0e-6a7b1c2d3e4f
status: test
description: cron 밑에서 curl·wget 이 실행되고 목적지가 사내가 아닌 경우
logsource:
  product: linux
  category: process_creation
detection:
  selection:
    ParentImage|endswith: '/cron'
    CommandLine|contains:
      - 'curl'
      - 'wget'
  filter_internal:
    CommandLine|contains: 'repo.corp.internal'
  condition: selection and not filter_internal
level: high

このコードブロックの韓国語は、titleが「スケジュールされたジョブが社外にリクエストした」、descriptionが「cronの下でcurlやwgetが実行され、宛先が社内ではない場合」という意味です。

ルールのドキュメントが定めている決まりは3つです。title・logsource・detectionは必須で、statusはstable・test・experimental・deprecated・unsupportedのいずれか、levelはcritical・high・medium・low・informationalのいずれかです。logsourceは、category(Webサーバー・ファイアウォールのような分類)・product(windows・linuxのような製品)・service(その製品の中のサービス)で、どのログを対象にするかを絞り込みます(ログソースのドキュメント)。

いちばん紛らわしいのは、マッピングとリストは意味が違うという点です。セレクションの中でフィールドを複数並べるとAND、1つのフィールドに値を複数与えるとORです。上の例では、ParentImageとCommandLineは両方が合う必要がありますが、curlとwgetは、どちらか一方が合えばよいです。

conditionは、それらのセレクションを組み立てる小さな言語です(条件のドキュメント)。and・or・notと括弧を使い、名前が複数あるときは、1 of selection_*(1つでも)とall of selection_*(すべて)でまとめます。1 of them・all of themもありますが、ドキュメントは、共有用のルールでこの2つを勧めていません。あとでセレクションを1つ足したときに、ルールの意味が気づかないうちに変わってしまうからです。

値を比較する方法は、フィールド名のあとのモディファイアが決めます(モディファイアのドキュメント)。|contains・|startswith・|endswith・|reがよく使われ、|contains|allのようにつなげると、「並べた値がすべて含まれている必要がある」になります。ここで、初心者が必ず一度は痛い目に遭うのが、|containsの単語境界です。ncを探そうとしてCommandLine|contains: 'nc'と書くと、rsyncが引っかかります。文字列の中にnのあとにcがあるからです。

現場での姿

現場でルールが死ぬ理由は、たいてい2つのうち1つです。1つは誤検知が多くて、誰も見なくなることです。最初は、アラートが鳴れば人が確認しますが、10回のうち9回が正常なバックアップ作業なら、11回目からは、誰も開かなくなります。そのため、ルールを広く書いておいて、「とりあえずすべて捕まえて、あとで絞ろう」という計画は、ほとんどいつも失敗します。

もう1つは、アドレスをルールに埋め込むことです。今回のインシデントの宛先がcdn.updates-cache.netだったからとその文字列をルールに入れると、翌週にドメインが変わった瞬間、ルールは何も捕まえられなくなります。長く生きるルールは、アドレスではなく振る舞いを書きます。取得してきたものをそのままシェルにパイプで渡す、あるいは、シェルを付けて外部に接続する、といったことです。

そして、メタデータを飾りだと考えないことが大切です。falsepositivesに「正常なバックアップ作業」と1行書いてあれば、午前3時にページされた人が、3分で判断します。その行がなければ、30分を使います。referencesとtagsも、同じ価値があります。

次のラボですること

ラボでは、合成したプロセス実行記録24件を受け取って、ルールを6本書きます。最も小さなルールから始めて、だんだん絞り込み、正常なバックアップ作業をフィルターで取り除き、アドレスの代わりに振る舞いで捕まえるルールを書き、2つの系統を1 ofでまとめます。ルールはそのたびに、実際にインシデント記録に対して実行してみて、何件当てて何件見逃したかを、数字で確認します。このラボはファイルだけを扱うので、ラボのPodで動きます。