ossec-hidsでPostfix関連のログを監視してactive-responseする件

状況説明

サーバ管理者をやっていると、ログ(の中身)やファイルインテグリティの監視が必要になる。

ログ監視からのコマンド実行っていうと昔から swatch が有名だし、(特に /etc/* なんかの) ファイルを監視して変化があれば通報するっていうと tripwire あたりが default choice なのかなと思う。 あるいは、AIDE なんかは少し新しい世代のようで多機能統合型になっているようだ。

そういうわけだけれども、何故か僕がずっと使っているのがこの OSSEC-HIDS である。(キチンと比較したりはもちろんしていない :-)

まあ、トレンドマイクロさんの回し者ではないのだが、一応、

OSSEC is an Open Source Host-based Intrusion Detection System. It performs log analysis, integrity checking, Windows registry monitoring, rootkit detection, time-based alerting and active response.

ってことで、必要な機能を網羅しているのが使う理由である。

環境

例によって、環境は FreeBSD 10.2-RELEASE-p6 amd64 で、ossec-hids のインストールは pkg/ports から secirity/ossec-hids-local を。

-local とあるのは謂わば stand alone な ossec-hids で、沢山のノードをまとめて監視するなら -server と -client を使えばできるが、それはまた別の物語である。

インストールは

$ sudo pkg install ossec-hids-local

とか

$ cd /usr/ports/security/ossec-hids-local
$ sudo make install

とかで /usr/local/ossec-hids/ 以下にインストールされる。 /etc/rc.conf に

ossechids_enable="YES"

と書けば

$ sudo service ossec-hids start

で起動できるだろう。(書かなくても onestart でいけるけど)

この後、ossec-hids 関連のファイルについて相対パスで書いた時は、 /usr/local/ossec-hids からの相対パス だと思ってほしい。

基本的な設定

まずは基本的な設定をする。 etc/ossec.conf を編集した diff が次の通り。

アラートをメールで通知してくれる機能の送り先を設定するのと、各種ログファ イル等のパスが default 設定では Linux 風でうまくないので適宜修正して、 /etc や /usr/local/etc の下のファイルについては diff 付きで変化を通知 するようにしているくらい。

*** etc/ossec.conf.sample    Wed Oct 28 09:17:35 2015
--- etc/ossec.conf   Mon Nov  9 20:30:17 2015
***************
*** 3,11 ****
  <ossec_config>
    <global>
      <email_notification>yes</email_notification>
!     <email_to>daniel.cid@xxx.com</email_to>
!     <smtp_server>smtp.xxx.com.</smtp_server>
!     <email_from>ossecm@ossec.xxx.com.</email_from>
    </global>

    <rules>
--- 3,11 ----
  <ossec_config>
    <global>
      <email_notification>yes</email_notification>
!     <email_to>moto@kawasaki3.org</email_to>
!     <smtp_server>127.0.0.1</smtp_server>
!     <email_from>ossecm@flyingdutchman.kawasaki3.org.</email_from>
    </global>

    <rules>
***************
*** 74,102 ****
   <frequency>17200</frequency>

   <!-- Directories to check  (perform all possible verifications) -->
!     <directories check_all="yes">/etc,/usr/bin,/usr/sbin</directories>
      <directories check_all="yes">/bin,/sbin</directories>

      <!-- Files/directories to ignore -->
!     <ignore>/etc/mtab</ignore>
!     <ignore>/etc/hosts.deny</ignore>
!     <ignore>/etc/mail/statistics</ignore>
!     <ignore>/etc/random-seed</ignore>
!     <ignore>/etc/adjtime</ignore>
!     <ignore>/etc/httpd/logs</ignore>
    </syscheck>

    <rootcheck>
!     <rootkit_files>/var/ossec/etc/shared/rootkit_files.txt</rootkit_files>
!     <rootkit_trojans>/var/ossec/etc/shared/rootkit_trojans.txt</rootkit_trojans>
    </rootcheck>

    <global>
      <white_list>127.0.0.1</white_list>
!     <white_list>192.168.2.1</white_list>
!     <white_list>192.168.2.190</white_list>
!     <white_list>192.168.2.32</white_list>
!     <white_list>192.168.2.10</white_list>
    </global>

    <alerts>
--- 74,102 ----
      <frequency>17200</frequency>

      <!-- Directories to check  (perform all possible verifications) -->
!     <directories check_all="yes" report_changes="yes">/etc,/usr/local/etc</directories>
!     <directories check_all="yes">/usr/local/bin,/usr/local/sbin</directories>
!     <directories check_all="yes">/usr/bin,/usr/sbin</directories>
      <directories check_all="yes">/bin,/sbin</directories>

      <!-- Files/directories to ignore -->
!     <!-- <ignore>/etc/mtab</ignore> -->
!     <!-- <ignore>/etc/hosts.deny</ignore> -->
!     <!-- <ignore>/etc/mail/statistics</ignore> -->
!     <!-- <ignore>/etc/random-seed</ignore> -->
!     <!-- <ignore>/etc/adjtime</ignore> -->
!     <!-- <ignore>/etc/httpd/logs</ignore> -->
    </syscheck>

    <rootcheck>
!     <rootkit_files>/usr/local/ossec-hids/etc/shared/rootkit_files.txt</rootkit_files>
!     <rootkit_trojans>/usr/local/ossec-hids/etc/shared/rootkit_trojans.txt</rootkit_trojans>
    </rootcheck>

    <global>
      <white_list>127.0.0.1</white_list>
!     <white_list>192.168.227.254</white_list>
!     <white_list>124.109.182.21</white_list>
    </global>

    <alerts>
***************
*** 159,170 ****

    <localfile>
      <log_format>syslog</log_format>
!     <location>/var/log/authlog</location>
    </localfile>

    <localfile>
      <log_format>syslog</log_format>
!     <location>/var/log/secure</location>
    </localfile>

    <localfile>
--- 183,194 ----

    <localfile>
      <log_format>syslog</log_format>
!     <location>/var/log/auth.log</location>
    </localfile>

    <localfile>
      <log_format>syslog</log_format>
!     <location>/var/log/security</location>
    </localfile>

    <localfile>
***************
*** 179,189 ****

    <localfile>
      <log_format>apache</log_format>
!     <location>/var/www/logs/access_log</location>
    </localfile>

    <localfile>
      <log_format>apache</log_format>
!     <location>/var/www/logs/error_log</location>
    </localfile>
  </ossec_config>
--- 203,213 ----

    <localfile>
      <log_format>apache</log_format>
!     <location>/var/log/httpd-access.log</location>
    </localfile>

    <localfile>
      <log_format>apache</log_format>
!     <location>/var/log/httpd-error.log</location>
    </localfile>
  </ossec_config>

アクセス制御用コマンド

これでとりあえずは動くのだが、ちょっと気に入らないのでさらに設定を変更する。

まず、インストール時の設定だと active-response で動くコマンドが /etc/hosts.allow や iptables/pf などを使うものになっているので、これをやめるために active-response の host-deny や firewall-drop を disable する。

代わりに、ipfw を使う command を追加。さらに、command の ipfw を使うように active-response も追加。この ipfw.sh は ossec-hids についてきたもので /usr/local/ossec-hids/active-response/bin/ にある。(他の command も同様)

追記

このパッチでは、既存の active-response に <disabled>yes</disabled> を追加して無効化するようになっているが、これは思っていたようには動かないことが判明した。

この方法でそれぞれの active-response の有効/無効を切り替えられると思っていたが、いずれかの active-response がこの方法で disabled されていると、すべての active-response が無効化されてしまい、その副作用で ossec-execd が起動されなくなる。 その時に ossec-execd が logs/ossec.log に出すログはこれ。

2015/11/09 22:09:24 ossec-execd(1350): INFO: Active response disabled. Exiting.

というわけで、使わない active-response はコメントアウトするか削除するのが良いことになる。

*** etc/ossec.conf.sample    Wed Oct 28 09:17:35 2015
--- etc/ossec.conf   Mon Nov  9 20:30:17 2015
***************
*** 125,133 ****
--- 125,141 ----
      <timeout_allowed>yes</timeout_allowed>
    </command>

+   <!-- moto kawasaki: added -->
+   <command>
+     <name>ipfw</name>
+     <executable>ipfw.sh</executable>
+     <expect>srcip</expect>
+     <timeout_allowed>yes</timeout_allowed>
+   </command>

    <!-- Active Response Config -->
    <active-response>
+     <disabled>yes</disabled>
      <!-- This response is going to execute the host-deny
         - command for every event that fires a rule with
         - level (severity) >= 6.
***************
*** 140,145 ****
--- 148,154 ----
    </active-response>

    <active-response>
+     <disabled>yes</disabled>
      <!-- Firewall Drop response. Block the IP for
         - 600 seconds on the firewall (iptables,
         - ipfilter, etc).
***************
*** 147,155 ****
      <command>firewall-drop</command>
      <location>local</location>
      <level>6</level>
!     <timeout>600</timeout>
    </active-response>

    <!-- Files to monitor (localfiles) -->

    <localfile>
--- 156,179 ----
      <command>firewall-drop</command>
      <location>local</location>
      <level>6</level>
!     <timeout>600</timeout>
!   </active-response>
!
!   <active-response>
!     <!-- Firewall Drop response via ipfw.
!        - Block the IP for 3600 seconds on the firewall
!        - (ipfw table 1)
!        - See also /etc/ipfw.rules, blocking packets from
!        - srcip in table 1 is required.
!       -->
!     <command>ipfw</command>
!     <location>local</location>
!     <level>6</level>
!     <timeout>600</timeout>
!     <repeated_offenders>30,90,180</repeated_offenders>
    </active-response>

    <!-- Files to monitor (localfiles) -->

    <localfile>

3rd party relay 対策

さて、次は active-response の ipfw が特定条件下で fire されるようにする必要があるわけだが、既存のルールで 3rd party relay への対応が動く。

例えばこういうログがあったとして、

Nov  9 09:34:00 flyingdutchman postfix/smtpd[69302]: NOQUEUE: reject: RCPT from xxx-xx-x-xxx.dynamic.**net.net[xxx.xx.x.xxx]: 554 5.7.1 <gk***awn@ya***.com.tw>: Relay access denied; from=<z2***tw@**hoo.com.tw> to=<gk***awn@ya***.com.tw> proto=SMTP helo=<124.109.182.21>

このログは単体で rule_id=3301 に match する。 rules/postfix_rules.xml のこのルールで、まあ、SMTP 的 return code == 554 のログがログファイルに出現すれば何でも引っかかるわけだ。 ただし、id=554 で引っ掛けるためには decoder がログから id=554 を抽出していなければならない。これについては後で別の例で触れる。

<rule id="3301" level="6">
  <if_sid>3300</if_sid>
  <id>^554$</id>
  <description>Attempt to use mail server as relay </description>
  <description>(client host rejected).</description>
  <group>spam,</group>
</rule>

rule 3301 は level 6 なので、これが fire するとさっき追加した ipfw active-response が fire する。 なぜなら、active-response の定義の中に、level 6 以上で fire するように書いてあるから。

<active-response>
  <!-- Firewall Drop response via ipfw.
     - Block the IP for 3600 seconds on the firewall
     - (ipfw table 1)
     - See also /etc/ipfw.rules, blocking packets from
     - srcip in table 1 is required.
    -->
  <command>ipfw</command>
  <location>local</location>
  <level>6</level>
  <timeout>600</timeout>
  <repeated_offenders>30,90,180</repeated_offenders>
</active-response>

そうすると、active-response が起動するコマンドは ipfw だと書いてある (<command>ipfw</command>) ので、さっき追加した ipfw command が起動されることになる。

<command>
  <name>ipfw</name>
  <executable>ipfw.sh</executable>
  <expect>srcip</expect>
  <timeout_allowed>yes</timeout_allowed>
</command>

<command> の定義に <executable>ipfw.sh</executable> とあるので、active-response/bin/ipfw.sh に srcip その他のパラメータを渡してくれるわけだ。

ipfw.sh は渡された srcip を ipfw の table 1 に追加するので、ipfw 側で

ipfw add deny ip from table(1) to any

のような設定をしておけば、 3rd party relay を試みる輩からは 600 秒の間は繋がせないということができる。 さらに継続的にやって来るようなら、30/90/180分とだんだん長い時間にわたって繋がせないという設定をしている。 (が、ちゃんと試験まではやっていないので動かなかったらごめんなさい。 まあそれを言ったらいろいろなところで最後までは確認していないが :-)

さて、rule 3301 が fire して ipfw にスパマーの purge が動くと、それは ossec-hids のログに記録される。 logs/active-responses.log がそれだ。 (ちょっと前の設定の時のログなので、600 秒ではなくて 3600 秒の間ブロックしている)

Mon Nov  9 20:34:02 JST 2015 /usr/local/ossec-hids/active-response/bin/ipfw.sh add - xxx.xx.x.xxx 1447029267.9890 3301
Mon Nov  9 20:34:03 JST 2015 /usr/local/ossec-hids/active-response/bin/ipfw.sh delete - xxx.xx.x.xxx 1447029267.9890 3301

ハーベスターさん対策

この他にもハーベスターさんがいらっしゃるので更にルールを追加する。

まず、smtp/tcp に接続して AUTH コマンドを叩いてくる輩がいるので、こういうログが /var/log/maillog に残る。

Nov  4 23:56:21 flyingdutchman postfix/smtpd[12254]: lost connection after AUTH from unknown[***.***.***.***]

あるいは、smtp/tcp に接続して(こっちが広報していないのに) STARTTLS を叩いてくる奴もいて、こういうログが残る。

Nov  2 16:01:23 flyingdutchman postfix/submission/smtpd[34727]: lost connection after STARTTLS from p****-ipbffx02*****uchi.***.ocn.ne.jp[**.***.***.***]

ご丁寧に 2-3 分毎にやってきたりするので、こちらもちょっと工夫をして対策する。 すなわち、/usr/local/ossec-hids/rules/local_rules.xml に以下を追加する。

*** local_rules.xml.orig     Thu Jun 11 00:38:07 2015
--- local_rules.xml  Mon Nov  9 21:16:41 2015
***************
*** 50,57 ****
      <description>List of rules to be ignored.</description>
    </rule>
    -->
-
- </group> <!-- SYSLOG,LOCAL -->


  <!-- EOF -->
--- 50,74 ----
      <description>List of rules to be ignored.</description>
    </rule>
    -->

+ </group> <!-- SYSLOG,LOCAL -->
+
+ <!-- moto kawasaki -->
+ <group name="syslog,postfix,">
+   <rule id="3335" level="5">
+     <if_sid>3320</if_sid>
+     <match>lost connection after AUTH|lost connection after STARTTLS</match>
+     <description>Postfix authentication abandonment.</description>
+     <group>authentication_failed,</group>
+   </rule>
+   <!-- <rule id="3358" level="10" frequency="2" timeframe="1200" ignore="60"> -->
+   <rule id="3358" level="6" frequency="2" timeframe="600" ignore="60">
+     <if_matched_sid>3335</if_matched_sid>
+     <same_source_ip />
+     <description>Multiple AUTH failures.</description>
+     <group>authentication_failures,</group>
+   </rule>
+ </group> <!-- SYSLOG,POSTFIX, -->
+ <!-- moto kawasaki -->

  <!-- EOF -->

空き番号の rule 3335 にこれらのログに合致するパターンを書くが、level=5 なのでこれ単体が fire しても ipfw は呼ばれない。 代わりに、これも空き番号の rule 3358 に同一 SRCIP について timeframe=600 秒以内に frequency=2 なので 4 回の rule 3335 的 fire があると level=6 で fire するルールを書いている。

frequency=2 と書くのだから想定時間内に 2 回のイベントがあれば fire しそうなものだが、マニュアルにも「プラス2回で」 fire するとあるので仕方がない。 1回目に監視を始め、2回目3回目で frequency=2 が満たされ、その次の4回目に fire するのかなと勝手に想像している。(ほんまかいな)

実はちょっと落とし穴があって数日間に渡って ossec-hids を罵る日々を送ったわけだが、それは、このパターンのログを発見した時に ossec-hids が <expect>srcip</expect> を抽出して active-response ipfw に渡さなければ発動しない点である。 まー expect されてるんだからそうなんだけどね。エラーくらい出してくれてもよろしくてよ。ぶちぶち。

気を取り直して srcip の抽出だが、 etc/local_decoder.xml を作成して以下のように decoder を追加すればできる。

*** local_decoder.xml.orig   Mon Nov  9 21:21:12 2015
--- local_decoder.xml        Mon Nov  9 21:22:51 2015
***************
*** 0 ****
--- 1,6 ----
+ <decoder name="postfix-lostconn">
+   <parent>postfix</parent>
+   <prematch>^lost connection after </prematch>
+   <regex offset="after_prematch">[(\d+.\d+.\d+.\d+)]</regex>
+   <order>srcip</order>
+ </decoder>

Postfix のログを解析しているという文脈で ‘^lost connection after ‘ なログを発見したら、その後ろの IP アドレスが [] に入ったパターン ([(d+.d+.d+.d+)]) から srcip を抽出するわけだ。

これで、いきなり AUTH な輩やいきなり STARTTLS なヤツを補足して ipfw table 1 へ突っ込むことができる。

一応動作してから書いているので上記はかなり天下り的だが、実際にはルールを動かして ossec-hids の解析結果を確認する長い長い作業が必要であった。 それは /usr/local/ossec-hids/bin/ossec-logtest コマンドを使って行う。

$ sudo bin/ossec-logtest 2015/11/09 18:29:59 ossec-testrule: INFO: Reading local decoder file. 2015/11/09 18:29:59 ossec-testrule: INFO: Started (pid: 82036). ossec-testrule: Type one log per line.

Nov 9 09:34:00 flyingdutchman postfix/smtpd[69302]: NOQUEUE: reject: RCPT from *---*.*****.***.net[*..*.***]: 554 5.7.1 <g****@*****.com.tw>: Relay access denied; from=<z***tw@*****.com.tw> to=<g****@*****.com.tw> proto=SMTP helo=<124.109.182.21>

**Phase 1: Completed pre-decoding.
full event: ‘Nov 9 09:34:00 flyingdutchman postfix/smtpd[69302]: NOQUEUE: reject: RCPT from *---**.*****.*****.net[*..*.***]: 554 5.7.1 <g****@*****.com.tw>: Relay access denied; from=<z****@*****.com.tw> to=<g****@*****.com.tw> proto=SMTP helo=<124.109.182.21>’ hostname: ‘flyingdutchman’ program_name: ‘postfix/smtpd’ log: ‘NOQUEUE: reject: RCPT from *---**.*******.*****.net[*.*.*.***]: 554 5.7.1 <g****@*****.com.tw>: Relay access denied; from=<z*****@*****.com.tw> to=<g*****@*****.com.tw> proto=SMTP helo=<124.109.182.21>’
**Phase 2: Completed decoding.
decoder: ‘postfix’ srcip: ‘*..*.*** id: ‘554’
**Phase 3: Completed filtering (rules).
Rule id: ‘3301’ Level: ‘6’ Description: ‘Attempt to use mail server as relay (client host rejected).’

**Alert to be generated.

きっとわかってくれると思うが、Phase 2 に示された decode 結果に srcip が見えていることが重要である。

よしなしごと

良く出来ているソフトウェアだとは思うんだけれども、もうちょっと単純明快なほうが僕にはありがたいかなあ。 でもだからといって tripwire/swatch で頑張る気にもならないかなぁ。

ossec-hids には他にもrootcheck とかの機能もあるし、server/agent 構成も可能なので、使い込めばさらに味が出るような気はする。 そして、Windows にも対応しているので、「Windows なんぞ知らん」と言えない時にも役に立つ(はず)。

備考

  • 2015/Nov/09 ごろ書いた。