各EC2インスタンスに配置したSnortのアラートをELK (ElasticSearch, Logstash, Kibana) で監視する

EC2でネットワークのパケット監視をしたい場合、どうするのがいいんでしょうか。商用だとDeep SecurityとかAlert Logicとかありそうですが、値段もそれなりにします。なんとかOSSでできないかということで、ネットワーク型IDS/IPSであるSnortと、ELK (ElasticSearch, Logstash, Kibana)の組み合わせを試してみました。

f:id:ks888:20160607131820p:plain

構成

Snortを使う場合、監視用のサーバにSnortを入れて、そのサーバにネットワークを流れるパケットを監視させる形がふつうなようです。ただEC2でこの形を実現するのは大変そうなので、今回は各EC2インスタンスSnortを入れる形にしてみます。各インスタンスは自分のところに来たパケットだけを監視します。

また、これだけだとSnortの出すアラートが各インスタンスに散らばってしまいます。そこで、アラートをlogstashで集めて、Amazon Elasticsearch Serviceに送るようにします。Snortに対応したネットワーク監視WebアプリとしてSnorbyというのもあるようですが、こういうログもKibanaから見たいなーと思ってElasticsearch Serviceにしてみました。

Snortの設定

Snortの設定はまじめにやると面倒そうです。こちらの記事等に手順はまとまってますが、かなり長いです。

今回はお試しということで、こちらのシェルスクリプトを実行すると、aptでのインストール+必要な設定がされるようにしました。OSはUbuntu 14.04です。スクリプト内では、以下の設定をしています。

  • 監視範囲のネットワークを絞る(自ホストだけ監視するようにする)
  • アラートのフォーマットをlogstashから扱いやすいものにする
  • checksumモードの変更。デフォルトのモードだと、Snortルールのuricontentという記法がうまく機能しなかったため。
  • logstashから読めるように、ファイルとディレクトリのパーミッションを変更。

Amazon Elasticsearch Serviceの設定

AWSのコンソールからぽちぽちやって、Elasticsearch+Kibanaを構築しました。ほぼデフォルトのままですが、Access Policyは、Snortを入れたEC2インスタンスのパブリックIPアドレスと、Kibanaでログを見るマシンのIPアドレスからアクセスできるように設定しました。Elasticsearchのエンドポイントは後で使うのでメモしておいて下さい。

構築できたら、次のようなリクエストを出して、snort-alertというインデックスを作成します。stringタイプのフィールドの値をindex化するとき、値を勝手に変更しないように設定しています。これをしないと、a-bという値が、abに分割されたりします。また、SrcGeo.locationというプロパティの型も指定しています。これは後で位置情報を使ったグラフを作るときに役立ちます。

curl -XPUT [Elasticsearchのエンドポイント]/snort-alert -d '{
  "mappings": {
    "logs": {
      "dynamic_templates": [
        {
          "notanalyzed": {
            "match": "*",
            "match_mapping_type": "string",
            "mapping": {
              "type": "string",
              "index": "not_analyzed"
            }
          }
        }
      ],
      "properties": {
        "SrcGeo": {
          "properties": {
            "location": {
              "type": "geo_point"
            }
          }
        }
      }
    }
  }
}'

Logstashの設定

Logstashは、こちらのシェルスクリプトを実行すると、elastic社のレポジトリからlogstashをインストール+必要な設定をするようにしました。スクリプト内では、Snort/var/log/snort/alertというファイルに出したアラートを、Elasticsearchに投げる設定をしています。ENDPOINT変数は、Elasticsearchのエンドポイントを入れて下さい。

擬似的な攻撃をしてみる

では、Snortを入れたサーバに攻撃してみて、Kibanaからログを確認できるか試してみます。と言いたいところですが、EC2インスタンスに攻撃する場合は、事前申請が必要です。

申請して許可を待つのも時間がかかるので、今回は擬似的な攻撃と、擬似的な攻撃に反応してアラートを出すSnortルールを作成します。擬似的な攻撃として、pseudo_web_attackあるいはpseudo_dos_attackという文字列をHTTPリクエストのURIに含める攻撃がある、とします。この攻撃に反応するSnortルールとして、次のようなルールを作成します。

alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"Pseudo WEB-ATTACKS"; flow:to_server,established; uricontent:"pseudo_web_attack"; nocase; classtype:web-application-attack; sid:1000001; rev:1;)
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS (msg:"Pseudo DoS attack"; flow:to_server,established; uricontent:"pseudo_dos_attack"; nocase; classtype:attempted-dos; sid:1000002; rev:1;)

パッと見はややこしそうですが、実際は、HTTPリクエストのURIに特定の文字列が含まれていないか見ているだけです。このルールは、Snortを入れたサーバの/etc/snort/rules/local.rulesに追記して下さい。追記したらSnortを再起動します。

準備ができたら、http://[Snortを入れたサーバのIPアドレス]/pseudo_dos_attack、あるいはhttp://[Snortを入れたサーバのIPアドレス]/pseudo_web_attackにアクセスします。

成功していれば、KibanaのDiscover画面から、Snortのアラートログが確認できると思います。例えば次のような画面です。

f:id:ks888:20160531184953p:plain

攻撃の様子をグラフで表示する

せっかくKibanaにログを集めたので、色々なグラフを出してみます。まずは直近の攻撃回数の折れ線グラフです。攻撃の種類ごとに線の色を分けてみました。

f:id:ks888:20160531182035p:plain

Web Application Attack(緑色の線)とAttempted Denial of Service(濃い青色の線)は、擬似的な攻撃によるものです。残りは何もしていないのに出ているアラートです。False Positiveなアラートだと思うので、Snortルールのチューニングが必要そうです。

次も直近の攻撃回数の折れ線グラフですが、ホストごとに線の色を分けています。よく攻撃されているホストがわかります。

f:id:ks888:20160607131754p:plain

最後は攻撃元の位置情報を世界地図上にマップした図です。円の色の濃さで攻撃回数の大小がわかります。

f:id:ks888:20160531182322p:plain

まとめて、ダッシュボード化してみました。

f:id:ks888:20160607131820p:plain

まとめ

Snort+ELKでネットワークのパケット監視をやってみました。今回はSnortの設定をかなり省略しましたが、実際はルールの更新とかルールのチューニングが必要そうです。また、Snortを各サーバに入れる形だと、Snortがサーバ本来の処理を阻害しないか不安です。そもそも実際の環境だと、全サーバにSnortを入れるより、入り口付近のサーバにだけ入れれば十分な気がします。このあたりはまた調べたいなーと思います。