検証用のサイトを公開し、2週間後にAWS WAFのログを確認してみた

2021.02.02

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

適当なサイトを立ち上げAWS WAFを設定し、WAFのログを確認してみました。ログ関連の運用のイメージを掴む一助になれば幸いです。

検証用のサイト

ALBの固定レスポンスで、Hello Worldを表示するサイトを作成しました。DNSドメインを取得し、私物のドメインで接続できるようにしました。

AWS WAFには以下のマネージドルールをブロックモードで設定しました。

  • AWS-AWSManagedRulesAdminProtectionRuleSet(管理ページへの外部アクセスをブロック)
  • AWS-AWSManagedRulesCommonRuleSet(コアルールセット)
  • AWS-AWSManagedRulesKnownBadInputsRuleSet(脆弱性の悪用または発見に関連するリクエストパターンをブロック)
  • AWS-AWSManagedRulesLinuxRuleSet(Linux固有の脆弱性の悪用に関連するリクエストパターンをブロック)

マネージドルールをEditすると、ルールを確認できます。コアルールセットには以下のようなルールがあります。例えば、NoUserAgent_HEADERはUser-Agentヘッダーのないリクエストをブロックします。Override rules actionを有効にすると、コアルールセット全体ではブロックで、NoUserAgent_HEADERルールはカウントするといった動きにできます。

ログの確認

Athenaでテーブルを作成します。LOCATIONは適宜、変更します。

CREATE EXTERNAL TABLE `waf_logs`(
  `timestamp` bigint,
  `formatversion` int,
  `webaclid` string,
  `terminatingruleid` string,
  `terminatingruletype` string,
  `action` string,
  `terminatingrulematchdetails` array<
                                  struct<
                                    conditiontype:string,
                                    location:string,
                                    matcheddata:array<string>
                                        >
                                     >,
  `httpsourcename` string,
  `httpsourceid` string,
  `rulegrouplist` array<string>,
  `ratebasedrulelist` array<
                        struct<
                          ratebasedruleid:string,
                          limitkey:string,
                          maxrateallowed:int
                              >
                           >,
  `nonterminatingmatchingrules` array<
                                  struct<
                                    ruleid:string,
                                    action:string
                                        >
                                     >,
  `httprequest` struct<
                      clientip:string,
                      country:string,
                      headers:array<
                                struct<
                                  name:string,
                                  value:string
                                      >
                                   >,
                      uri:string,
                      args:string,
                      httpversion:string,
                      httpmethod:string,
                      requestid:string
                      > 
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
 'paths'='action,formatVersion,httpRequest,httpSourceId,httpSourceName,nonTerminatingMatchingRules,rateBasedRuleList,ruleGroupList,terminatingRuleId,terminatingRuleMatchDetails,terminatingRuleType,timestamp,webaclId')
STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://awswaf-bucketname/2021/01/'

ブロックしたリクエストの一覧を表示します。

SELECT *
FROM waf_logs
WHERE action='BLOCK'
ORDER BY timestamp

結果をCSVファイルでダウンロードします。

ALBとWAFは2週間起動していたのですが、785件のブロックが発生していました。786行のうち、1行目は項目名です。

$ wc -l awswaflog.csv
     786 awswaflog.csv
$

ログを見ると、ホストヘッダーにIPアドレスが入っているケースがあります。おそらく、AWSのIPレンジに対して、スキャンや攻撃をしていると思われます。本来であれば、私が設定したDNS名が入っているはずです。

headers=[{name=host, value=203.0.113.1}]

CSVファイルからホストヘッダーがIPアドレスのものを除外すると、333件残りました。

$ sed -E "/name=Host, value=(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9;5D])/d" awswaflog.csv > awswaflog_exclude_hostheader-ipaddress.csv
$ wc -l awswaflog_exclude_hostheader-ipaddress.csv
     333 awswaflog_exclude_hostheader-ipaddress.csv
$

ホストヘッダーには以下のようなものや、実在するであろう検証には使っていないドメイン名のものがありました。検証用のサイトは特に告知などはしていないため、私が設定したドメインからアクセスした人は私一人だと思います。

name=Host, value=ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com

仮にexample.comでサイトを公開している場合、Hostヘッダーには基本的にexample.comがセットされます。正規のユーザーの通信をブロックしていないかを確認するには、「name=Host, value=example.com」のログを抽出すると良いでしょう。

WAFのログには、IPアドレスから推測される国名が含まれます。country=JPといった形です。利用者に日本のユーザーが多い場合、日本のログを見ておくと良さそうです。今回は4件ありました。

$ cat awswaflog_exclude_hostheader-ipaddress.csv | grep "country=JP" > awswaflog_exclude_hostheader-ipaddress-JP.csv
$ wc -l awswaflog_exclude_hostheader-ipaddress-JP.csv
       4 awswaflog_exclude_hostheader-ipaddress-JP.csv
$

通常のユーザーであれば、ChromeやEdgeなどのユーザーエージェントヘッダーがセットされるはずということでNoUserAgent_HEADERルールに一致した通信を除外します。

$ cat awswaflog_exclude_hostheader-ipaddress-JP.csv | grep -v "NoUserAgent_HEADER" > awswaflog_exclude_hostheader-ipaddress-JP_excludeNoUserAgent.csv

おさらいです。まず、ホストヘッダーにIPアドレスがセットされている場合は、除外しました。次に、IPアドレスから日本と推定されたログにしぼり、更にNoUserAgent_HEADERルールに一致した通信を除外しました。私の環境では、1件のログが残りました。

$ wc -l awswaflog_exclude_hostheader-ipaddress-JP_excludeNoUserAgent.csv
       1 awswaflog_exclude_hostheader-ipaddress-JP_excludeNoUserAgent.csv
$

csvファイルをスプレッドシートにインポートします。J列がrulegrouplist。M列がhttprequestです。

rulegrouplistはリクエストで動作したルールグループのリストです。今回は以下のようになりました。

[{"nonterminatingmatchingrules":[],"rulegroupid":"AWS#AWSManagedRulesAdminProtectionRuleSet","terminatingrule":"null","excludedrules":"null"}, {"nonterminatingmatchingrules":[],"rulegroupid":"AWS#AWSManagedRulesCommonRuleSet","terminatingrule":"null","excludedrules":"null"}, {"nonterminatingmatchingrules":[],"rulegroupid":"AWS#AWSManagedRulesKnownBadInputsRuleSet","terminatingrule":"null","excludedrules":"null"}, {"nonterminatingmatchingrules":[{"rulematchdetails":[],"action":"COUNT","ruleid":"LFI_URIPATH_COUNT"}],"rulegroupid":"AWS#AWSManagedRulesLinuxRuleSet","terminatingrule":{"rulematchdetails":"null","action":"BLOCK","ruleid":"LFI_URIPATH"},"excludedrules":"null"}]

注目すべきは以下です。AWSManagedRulesLinuxRuleSet中のLFI_URIPATHでブロックしたことがわかります。

["rulegroupid":"AWS#AWSManagedRulesLinuxRuleSet","terminatingrule":{"rulematchdetails":"null","action":"BLOCK","ruleid":"LFI_URIPATH"}

httprequestのuriを見ると、/etc/passwdとあることからURIでLFI攻撃を行おうとしたということがわかります。ちなみに、IPアドレスに心あたりがあり、このリクエストは私が自分でやったものであることを思い出しました。

uri=/etc/passwd, args=, httpversion=HTTP/1.1, httpmethod=GET

仮にこの通信を通過させたい場合は、AWS-AWSManagedRulesLinuxRuleSet>LFI_URIPATHのOverride rules actionを有効にします。

AWS WAFマネージドルールを利用する場合、Countモードで導入し、ログを確認しOverride rules actionを一通り設定してから、Blockに変更すると良いかと思います。また、Blockにしてからも定期的にログを確認し、Override rules actionを設定すると良いかと思います。

Override rules actionを設定するかの判断に使える例を紹介します。まず、心当たりのあるIPアドレスの場合は、関係者がシステムに攻撃をすることはないという前提でOverride rules actionに登録すると良いでしょう。次にuriが存在しないしない場合は攻撃と考えて良いかと思います。今回の検証では存在しない以下への通信がブロックされていました。

uri=/jenkins/login
uri=/manager/html
uri=/setup.cgi

その他、コアルールセットのSizeRestrictions_BODYはリクエスト本文のサイズをチェックするルールですが、アップロードなどを行う場合はOverride rules actionの対象になると思います。

おわりに

AWS WAFを設定し、WAFのログを確認する方法を紹介しました。検証用のサイトを2週間起動しただけで、785件の検知がありました。ログを除外した上で、rulegrouplist、httprequest、clientipに注目し、Override rules actionを有効するか検討すると良いかと思います。

参考