AWS提供のマネージドルールでDevelopersIOへの攻撃を検出してみた

ブログ環境に設置した AWS WAFログを Athena、QuickSightで解析してみました。
2020.09.11

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

AWSチームのすずきです。

Developers.IO の コンテンツ配信環境に対する攻撃の緩和対策としての有効性を確認するため、 AWS提供のマネージドルールを IDS(不正侵入検知システム)相当とした AWS WAFを設定、 そのフルログを Athen と QuickSight を利用して解析する機会がありましたので、紹介させていただきます。

環境について

構成図

WordPress 宛の リクエストを AWS WAF の対象としました。

環境の詳細は以下記事をご覧ください。

経緯

2016年頃

過去の Developers.IO、大量アクセスによるサーバダウンが起きやすい環境であったため、 AWS WAFのレートルールや、Kinesis、Norikra を 利用した 異常アクセスの遮断を実施していました。

2020年春

2020年春のリニューアル後、WAFによる保護を必要とする機会は減少しましたが、 大量のクロールや 脆弱性のスキャン については AWS WAFのカスタムルールによる対処が必要となる場合がありました。

2020年夏

AWS WAFの 実費のみで 利用できる AWS 提供の マネージドルール について、その有効性を評価するため、 Windows OS用 以外のルール、攻撃判定したリクエストを 全て「カウント」とする設定にした AWS WAF を用意し、ALBを通過するリクエストの判定を実施していました。

正規アクセスに対してはごく少数しか発生しない AWS WAFのカウント判定ログ、 効率的な処理を実現するため、判定結果に応じたログを事前に処理する簡易ETLを用意しました。

Athena

テーブル作成

AWS WAFのログ を解析する Athena の設定は、AWS ドキュメント記載の定義を利用しました。

S3の指定は「カウント」判定済みのものを対象としたため、パーティション設定などは省略しています。 大容量の WAFログ(フルログ)を処理する場合には、Athena の費用に注意してご利用ください。

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://athenawaflogs/WebACL/'

テーブル変換

カウントと判定されたログレコードは、UNNEST、CROSS JOIN を実施して解析がしやすい状態に変換しました。

変換結果は「CTASクエリ」を利用してテーブルに出力しています。

CREATE TABLE waf_logs_count AS
SELECT
  "date_format"("from_unixtime"(("timestamp" / 1000)), '%Y-%m-%dT%h:%i:%s') "timestamp_utc",
  "t"."nonterminatingmatchingrule"."ruleid" as ruleid,
  "httpRequest"."clientip" as clientip,
  "httpRequest"."country" as country,
  "useragent"."value" as "UserAgent",
  "httpRequest"."uri" as uri,
  split_part("httpRequest"."uri", '/', 2) as path_1,
  split_part("httpRequest"."uri", '/', cardinality(split("httpRequest"."uri", '/'))) as path_last
FROM
  (waf_logs CROSS JOIN UNNEST("nonterminatingmatchingrules") t(nonterminatingmatchingrule)),
  UNNEST("httpRequest"."headers") u(useragent)
WHERE
  (((
        (
          "nonterminatingmatchingrule"."action" = 'COUNT'
        )
      AND (
          "useragent"."name" = 'User-Agent'
        )
      )))
  • 変換前サンプル
{
    "timestamp": 1598361187218,
    "formatVersion": 1,
    "webaclId": "arn:aws:wafv2:ap-northeast-1:000000000000:regional/webacl/wafv2-webacl/0000-0000-0000-0000",
    "terminatingRuleId": "Default_Action",
    "terminatingRuleType": "REGULAR",
    "action": "ALLOW",
    "terminatingRuleMatchDetails": [],
    "nonTerminatingMatchingRules": [
        {
            "action": "COUNT",
            "ruleId": "AWS-AWSManagedRulesCommonRuleSet"
        },
        {
            "action": "COUNT",
            "ruleId": "AWS-AWSManagedRulesAdminProtectionRuleSet"
        }
    ],
    "httpRequest": {
        "clientIp": "0.0.0.0",
        "country": "RO",
        "headers": [
            {
                "name": "Host",
                "value": "dev.classmethod.jp"
            },
            {
                "name": "User-Agent",
                "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36"
            }
        ],
        "uri": "/articles/introduction-of-devcleaner/",
        "args": "",
        "httpVersion": "HTTP/1.0",
        "httpMethod": "GET",
        "requestId": null
    }
}
  • 変換後サンプル
{
    "timestamp": "2020-08-25T13:13:07",
    "ruleId": "AWS-AWSManagedRulesCommonRuleSet"
    "clientIp": "0.0.0.0",
    "country": "RO",
    "name": "User-Agent",
    "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36"
    "uri": "/articles/introduction-of-devcleaner/",
}
{
    "timestamp": "2020-08-25T13:13:07",,
    "ruleId": "AWS-AWSManagedRulesAdminProtectionRuleSet"
    "clientIp": "0.0.0.0",
    "country": "RO",
    "name": "User-Agent",
    "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36"
    "uri": "/articles/introduction-of-devcleaner/",
}

件数確認

今回、約2週間分のWAFログを対象に確認を試みました。

SELECT max(timestamp_utc), min(timestamp_utc), count(1)
FROM waf_logs_count
  • 開始日: 2020-08-26T11:59:04
  • 終了日: 2020-09-10T12:59:58
  • カウント件数: 566276

QuickSight

Athena の「CTASクエリ」で作成した解析用のテーブル、Amazon QuickSight の SPiCE (無料枠)にインポートして利用しました。

結果

ルール別判定数

コアルールセット と 管理者保護 のルール がカウント判定の殆どを占める事が確認できました。

SELECT count(1), ruleid FROM waf_logs_count group by ruleid
カウント数 ルール名 ManagedRuleGroupName WCU
330335 コアルールセット AWSManagedRulesCommonRuleSet 700
10870 管理者保護 AWSManagedRulesAdminProtectionRuleSet 100
725 PHPアプリケーション AWSManagedRulesPHPRuleSet 100
179 SQL データベース AWSManagedRulesSQLiRuleSet 200
105 Linux オペレーティングシステム AWSManagedRulesLinuxRuleSet 200
98 Amazon IP 評価リスト AWSManagedRulesAmazonIpReputationList 25
68 匿名 IP リスト AWSManagedRulesAnonymousIpList 50
68 POSIXオペレーティングシステム AWSManagedRulesUnixRuleSet 100
68 WordPress アプリケーション AWSManagedRulesWordPressRuleSet 100
29 既知の不正な入力 AWSManagedRulesKnownBadInputsRuleSet 200

国別解析

「JP」の判定はベスト10圏外でした。

SELECT count(1), ruleid FROM waf_logs_count group by ruleid order by count(1) desc limit 10
順位 件数 country
1 178006 US
2 45916 DE
3 25178 CA
4 18093 AU
5 13010 IL
6 21556 CA
7 9633 RU
8 9094 SG
9 7005 FR
10 6199 HK

UserAgent

Botを名乗る「User-Agent」に限定して、カウントされた件数を数えてみました。

SELECT count(1), useragent FROM waf_logs_count 
where useragent like '%bot%' group by useragent order by count(1) desc limit 10
順位 件数 useragent
1 39649 Mozilla/5.0 (compatible; MJ12bot/v1.4.8; http://mj12bot.com/)
2 22596 Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
3 17342 rogerbot/1.0 (http://www.moz.com/dp/rogerbot, rogerbot-crawler@moz.com)
4 6065 Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.92 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
5 3664 Mozilla/5.0 (compatible; AhrefsBot/6.1; +http://ahrefs.com/robot/)
6 1280 Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)
7 472 rogerbot/1.0 (http://moz.com/help/pro/what-is-rogerbot-, rogerbot-crawler+shiny@moz.com)
8 445 Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)
9 114 ITnews-bot
10 105 Mozilla/5.0 (compatible; SemrushBot/6~bl; +http://www.semrush.com/bot.html)

詐称の可能性もありますが有益なBotが含まれている可能性があるため、継続調査予定です。

URI

uri のパスの第一階層とファイル名を抽出して、集計を試みました。

SELECT count(1),path_1, path_last FROM waf_logs_count 
group by path_1, path_last order by count(1) desc limit 10
順位 件数 パス(1) ファイル名
1 243791   xmlrpc.php
2 17309 category -
3 15264 articles -
4 11965 cloud -
5 5303 tag -
6 4434 etc -
7 3532 phpmyadmin -
8 2696 clerical -
9 2432 smartphone -
10 1449 server-side -

「/xmlrpc.php」「/phpmyadmin/」宛のリクエストがカウントされている事が確認できました。

xmlrpc.php

「xmlrpc.php」のリクエストに限定して、接続元IP、国別の確認を試みてみました。

  • IPアドレス

  • 国別

  • 「xmlrpc.php」を除くファイル名

PHPアプリの脆弱性スキャンと推測されるリクエストが多く検出されました。

まとめ

AWS が提供するマネージドルール、多数のリクエストが検知されました。 ブロックモードで利用する事で、ウェブ攻撃の緩和が期待できる事が確認できました。

検出数の大半を占めた WordPress の脆弱性(xmlrpc.php) や、ブログ環境として必要としない事が明確なパス宛のリクエストについては、 AWS WAFの カスタムルールでの 遮断を実施する予定です。

直近、システム障害に至る規模のウェブ攻撃は発生していないため、 引き続き AWS WAFのマネージドルールは IDS 相当で利用し、カウント判定のログ回収を継続する予定です。

  • PerformanceInsight(DBLoad)

誤判定が無い利用ができる目処がついたマネージドルールから順次ブロックを有効とし AWS WAF を IPS (不正侵入防止システム)として活用する方法についても、おって紹介させていただきたいと思います。