LoginSignup
16
0

More than 1 year has passed since last update.

ELK ではじめる Cisco Secure Firewall のログ分析

Last updated at Posted at 2022-12-14

はじめに

この記事は Cisco Systems Japan の有志による Cisco Systems Japan Advent Calendar 2022 として投稿しています。

やったこと

Cisco Secure Firewall のログを簡単に分析できる環境を作るために以下の2つを実施しました。

  • docker-elk をたてる
  • Logstash の grok パターンでパースする

モチベーション

大量のログから必要な情報だけを抜き出したりそれを使って分析したいとき、コマンドラインのツールだけでは(自分の知識では)限界があると感じたため GUI で操作ができるものを調査したところ、ELK が便利そうかつ無料でしたので構築してみました。

今回はたまたま Cisco Secure Firewall のログを題材にパースしましたが、異なる構造を持ったログにも応用が効くプリミティブな内容になっています。

構成図

image.png

Cisco Secure Firewall とは

Cisco 社の次世代ファイアウォール&IPS です。Sourcefire 社から買収した製品をベースに作られており旧称 Firepower です。詳しくはこちら

ELK とは

ELK とは、Elasticsearch、Logstash、Kibana という3つのオープンソースプロジェクトの頭文字です。Elasticsearch は検索・分析エンジン、Logstash はサーバサイドのデータ処理エンジン、Kibana はユーザインターフェイスです。

最近では Elastic Agent や Beats など新機能を使うことでファイルの取り込みが容易になっているらしい1 ですが今回は昔ながらの ELK を使います。

ELK を使うメリット

簡単かつ柔軟に全文検索、可視化、分析ができる環境が手に入ることです。

実は ELK を構築しなくても Cisco Secure Firewall を管理する FMC (Cisco Firepower Management Center )で軽快にログ検索ができます。
特に version 7.0 から追加された新機能 Unified Event Viewer はコリレーション情報も含めたイベント情報を直感的に一行で表示してくれて便利です。

ELK 環境の構築

git clone  https://github.com/deviantony/docker-elk.git

構築には Docker、Docker Compose、Git が必要です。
Docker、Docker Compose のインストール方法は公式ガイドもしくはこちらをご参照ください。

インストール後、デフォルトパスワードを変更してサービス再起動します。この記事では割愛します。
参照:https://qiita.com/Toru_Kubota/items/5a9462ef1e625167f257

ログファイルの取り込み設定

今回扱うログは Connection Event のログとし、 docker-elk 配下の mylog ディレクトリに置きました。Logstash は syslog をリアルタイムに受け取ることもできますが、今回は静的にファイルを参照する方式とします。

設定箇所は以下2箇所です。
1. docker-compose.yml におけるボリュームマウントの指定

/docker-elk/docker-compose.yml
(中略)
  logstash:
    build:
      context: logstash/
      args:
        ELASTIC_VERSION: ${ELASTIC_VERSION}
    volumes:
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro,Z
      - ./logstash/pipeline:/usr/share/logstash/pipeline:ro,Z
      - ./mylog/:/usr/share/logstash/mylog:ro   # <= これを追記
(中略)

2. logstash.conf における input の指定

/docker-elk/logstash/pipeline/logstash.conf
input {
  file {
    path=>"/usr/share/logstash/mylog/*/conn/*.log"   # <= ログファイルのパスを指定
    start_position=>"beginning"
    sincedb_path => "/dev/null"
    tags => "conn"
  }
}

正規表現が使えます。

パースの設定

grok と呼ばれるフィルタを使ってログを構造や意味ごとに分割(パース)していくことができます。中身は正規表現です。

grok フィルタを利用しない場合の見え方(参考)

Cisco Secure Firewall の Connection Event ログは例えば以下のような構造になっています。ご覧の通り通信の内容によって若干フィールドが変わります。(ちなみに Intrusion Event や File Event のログも全然違います😇エグいです😇)

#ブロックされた http 通信
Nov 15 02:59:24 firepower-1   %FTD-6-430003: EventPriority: High, DeviceUUID: 5b924422-3881-11e1-a2bd-9dcb2d79f6cd, InstanceID: 4, FirstPacketSecond: 2022-11-15T02:59:24Z, ConnectionID: 5157, AccessControlRuleAction: Block, AccessControlRuleReason: Intrusion Block, SrcIP: 10.1.12.12, DstIP: 10.2.9.8, SrcPort: 53396, DstPort: 80, Protocol: tcp, IngressInterface: outside, EgressInterface: inside, IngressZone: outzone-tp, EgressZone: inzone-tp, ACPolicy: ACP-1, AccessControlRuleName: CATCH-ALL, Prefilter Policy: Default Prefilter Policy, ConnectionDuration: 0, IPSCount: 2, InitiatorPackets: 2, ResponderPackets: 1, InitiatorBytes: 140, ResponderBytes: 74, NAPPolicy: Balanced Security and Connectivity

#許可された http 通信
Nov 15 04:40:41 firepower-2   %FTD-6-430003: EventPriority: Low, DeviceUUID: dbe055ce-3881-11e1-8dfb-f19c7899208c, InstanceID: 6, FirstPacketSecond: 2022-11-15T04:40:35Z, ConnectionID: 17012, AccessControlRuleAction: Allow, SrcIP: 10.1.90.17, DstIP: 10.2.5.8, SrcPort: 34791, DstPort: 80, Protocol: tcp, IngressInterface: outside, EgressInterface: inside, IngressZone: outzone-tp, EgressZone: inzone-tp, ACPolicy: ACP-2, AccessControlRuleName: CATCH-ALL, Prefilter Policy: Default Prefilter Policy, UserAgent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393, Client: Edge, ClientVersion: 14.14393, ApplicationProtocol: HTTP, WebApplication: Web Browsing, ConnectionDuration: 6, InitiatorPackets: 4, ResponderPackets: 39, InitiatorBytes: 463, ResponderBytes: 48747, NAPPolicy: Balanced Security and Connectivity, HTTPResponse: 404, ReferencedHost: 10.2.5.8, URL: http://10.2.5.8/old

#許可された dns 通信
Nov 15 07:44:00 firepower-2   %FTD-6-430003: EventPriority: Low, DeviceUUID: dbe055ce-3881-11e1-8dfb-f19c7899208c, InstanceID: 8, FirstPacketSecond: 2022-11-15T07:44:00Z, ConnectionID: 29241, AccessControlRuleAction: Allow, SrcIP: 10.2.5.1, DstIP: 128.8.10.90, SrcPort: 24887, DstPort: 53, Protocol: udp, IngressInterface: inside, EgressInterface: outside, IngressZone: inzone-tp, EgressZone: outzone-tp, ACPolicy: ACP-2, AccessControlRuleName: CATCH-ALL, Prefilter Policy: Default Prefilter Policy, Client: DNS, ApplicationProtocol: DNS, ConnectionDuration: 0, InitiatorPackets: 1, ResponderPackets: 0, InitiatorBytes: 70, ResponderBytes: 0, NAPPolicy: Balanced Security and Connectivity

#許可された http 通信
Nov 15 08:43:54 firepower-2   %FTD-6-430003: EventPriority: Low, DeviceUUID: dbe055ce-3881-11e1-8dfb-f19c7899208c, InstanceID: 5, FirstPacketSecond: 2022-11-15T08:43:54Z, ConnectionID: 39454, AccessControlRuleAction: Allow, SrcIP: 10.1.250.32, DstIP: 10.2.5.10, SrcPort: 48241, DstPort: 443, Protocol: tcp, IngressInterface: outside, EgressInterface: inside, IngressZone: outzone-tp, EgressZone: inzone-tp, ACPolicy: ACP-2, AccessControlRuleName: CATCH-ALL, Prefilter Policy: Default Prefilter Policy, ConnectionDuration: 0, InitiatorPackets: 1, ResponderPackets: 1, InitiatorBytes: 0, ResponderBytes: 0, NAPPolicy: Balanced Security and Connectivity

この情報をパースせずに投入した場合の Kibana の Discover(検索結果)画面の様子がこちらです。message というフィールドに投入したログの情報がまるっと入ります。構造化されていないですが、この状態でもログに含まれる情報は検索することができ便利です。一方、フィールドを抜き出して分析にしたりグラフにする場合は扱いづらいと言えます。
image.png

grok フィルタの作成

パースの設定は Logstash の設定ファイルにおいて filter の grok フィルターで指定します。Pattern と呼ばれる別ファイルで指定するか、grok の中に直接記述するか選べますが、今回は直接書きます。
どんなフィルタ条件であればパースが成功するのか、Kibana の Dev Tools にある Grok Debugger で事前にシミュレーションすることができます。

Grok Debugger の URL:
http://{docker-elk の IP}:5601/app/dev_tools#/grokdebugger

実際に試してみましょう。例えば FTD のログの日時表示は Syslog 形式で始まりこれは Grok のパターンとしてすでに定義済みの SYSLOGTIMESTAMP が使えます。同じく次の文字列は HOSTNAME が使えます。残りその他をすべてマッチさせる表現が GREEDYDATA となります。コロンで区切った右側がそのフィールドの任意の名前です。
参照:https://qiita.com/tuneyukkie/items/75cbb4d44f901fec2188

定義済みの正規表現のパターンはこちらで参照できます。(公式の github.com のリンクが死んでいます)

途中経過がこちら。
image.png
time や device といった新しいフィールドが Key: Value 形式で取り出せました。

上記の Grok の パターンはこのような内容です。
%{SYSLOGTIMESTAMP:time}%{SPACE}%{HOSTNAME:device}%{SPACE}%{WORD:messageid}%{GREEDYDATA:msg1}

さらに grok フィルタの作成

分析するために欲しい情報としては通信が許可されたかどうか、送信元や宛先 IP や Port などですので、そこを中心にコツコツパターンを書いていくとこんな形になります。

image.png

{
  "msg3": "NAPPolicy: Balanced Security and Connectivity, HTTPResponse: 404, ReferencedHost: 10.2.5.8, URL: http://10.2.5.8/old",
  "srcip": "10.1.90.17",
  "ptorocol": "tcp",
  "msg2": "IngressZone: outzone-tp, EgressZone: inzone-tp, ACPolicy: ACP-2, AccessControlRuleName: CATCH-ALL, Prefilter Policy: Default Prefilter Policy, UserAgent: Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393, Client: Edge, ClientVersion: 14.14393, ApplicationProtocol: HTTP, WebApplication: Web Browsing, ConnectionDuration: 6, InitiatorPackets: 4, ResponderPackets: 39",
  "msg1": "%FTD-6-430003: EventPriority: Low, DeviceUUID: dbe055ce-3881-11e1-8dfb-f19c7899208c, InstanceID: 6, FirstPacketSecond: 2022-11-15T04:40:35Z, ConnectionID: 17012",
  "dstport": "80",
  "respBytes": "48747",
  "ingressIF": "outside",
  "egressIF": "inside",
  "initBytes": "463",
  "action": "Allow",
  "srcport": "34791",
  "time": "Nov 15 04:40:41",
  "dstip": "10.2.5.8",
  "device": "firepower-2"
}

このパターンにより以下のような情報を抽出することができそうです。

  • イベントの時間
  • 送信元 IP
  • 宛先 IP
  • プロトコル
  • 送信元ポート
  • 宛先ポート
  • 送信バイト
  • 受信バイト
  • 受信インターフェイス
  • 送信インターフェイス

上記の Grok パターンはこのような内容です。
%{SYSLOGTIMESTAMP:time}%{SPACE}%{HOSTNAME:device}%{SPACE}%{GREEDYDATA:msg1},%{SPACE}AccessControlRuleAction: %{WORD:action},%{SPACE}SrcIP: %{IP:srcip},%{SPACE}DstIP: %{IP:dstip},%{SPACE}SrcPort: %{INT:srcport},%{SPACE}DstPort: %{INT:dstport},%{SPACE}Protocol: %{WORD:ptorocol},%{SPACE}IngressInterface: %{WORD:ingressIF},%{SPACE}EgressInterface: %{WORD:egressIF},%{SPACE}%{GREEDYDATA:msg2},%{SPACE}InitiatorBytes: %{NUMBER:initBytes},%{SPACE}ResponderBytes: %{NUMBER:respBytes},%{SPACE}%{GREEDYDATA:msg3}

それではこれを logstash.conf の filter に適用します。

/docker-elk/logstash/pipeline/logstash.conf
filter {
  grok {
    match=>{
      "message" => "%{SYSLOGTIMESTAMP:time}%{SPACE}%{HOSTNAME:device}%{SPACE}%{GREEDYDATA:msg1},%{SPACE}AccessControlRuleAction: %{WORD:action},%{SPACE}SrcIP: %{IP:srcip},%{SPACE}DstIP: %{IP:dstip},%{SPACE}SrcPort: %{INT:srcport},%{SPACE}DstPort: %{INT:dstport},%{SPACE}Protocol: %{WORD:ptorocol},%{SPACE}IngressInterface: %{WORD:ingressIF},%{SPACE}EgressInterface: %{WORD:egressIF},%{SPACE}%{GREEDYDATA:msg2},%{SPACE}InitiatorBytes: %{NUMBER:initBytes},%{SPACE}ResponderBytes: %{NUMBER:respBytes},%{SPACE}%{GREEDYDATA:msg3}"
    }
  }
}

ちなみに output の設定はデフォルトのままです。
この状態で docker-compose しなおす(docker-compose up -d --build)とこんな形で新しいフィールドが見えてきます。読み込みに数分かかります。
image.png

パースしたフィールドで検索した例がこちらです。
image.png
クエリで AND や OR、NOT 検索することでより効率的に検索ができます。
以上で IP や Port、通信の方向などを構造化された情報として扱うことができるようになり、高度な分析をするための準備が整いました!

最後に

長くなってしまいましたが最後までお読みいただきありがとうございました。
本記事では Cisco Secure Firewall の Connection Event ログを ELK で取り込み grok でパースしてフィールドごとに分析できる状態にしました。

今後の展望として、この環境を使った脅威ハンティングの調査例の紹介、Cisco Secure Firewall が生成する各種ログの完璧な grok パターンの生成、Kibana で便利なグラフの作成、別のセキュリティ製品のログの統合、Elastic Agent や Beats の活用などがありそうです。
本記事についてのアドバイス、ご意見、ご質問などがありましたらお待ちしております!

よい年末をお過ごしください~ :christmas_tree:

Appendix

実際に試される方向けにメモなどを残しておきます↓

動作確認で使ったコマンド

docker ps
docker logs {container id}
curl -u elastic:{password} -XGET http://localhost:9200/_cat/indices?v
curl -u elastic:{password} -XGET http://localhost:9200/{index name}/_mappings?pretty

はまった場所

  • Grok Debugger の存在に気付かず、grok パターンを logstash.conf で直接試行錯誤していて時間を浪費しました。

  • 何度も docker-compose down => docker-compose up -d を繰り返していて docker system や overlay のボリュームが溢れてしまい Elasticsearch が正常に起動しなくなりました。 これは docker system prune -a docker system prune --volumes で一度イメージやボリュームをすべて削除することで解決しました。

  • grok パターン WORD でスペースを含む文字列が抜き出せなかったり、HOSTNAME でコンマを含む文字列がうまく抜き出せなかったりするのは grok のバグだと思ってますがよくわかりませんでした(正確には、Grok Debugger ではうまくいくが logstash.conf では異なる挙動となる)。対処方法として、スペースを含む文字列は GREEDYDATA としました。コンマを含むホスト名は事前にログの中身を簡易な文字列に置換しました。

参照

https://github.com/deviantony/docker-elk
https://qiita.com/ohhara_shiojiri/items/0b45fd000103b7345073
https://qiita.com/subretu/items/5857628534b53f29f5a3
https://qiita.com/mug-cup/items/ba5dd0a14838e83e69ac

  1. 詳しくはこちらこちらで私も勉強します。

16
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
0