Exception HMAC does not match

Hi @stefannagy,

These scans have been appearing in my log file for quite some time now.

Initially, I did set up a fail2ban rule/jail:

# /etc/fail2ban/jail.d/apache-php-scanner.local
[apache-php-scanner]
enabled  = true
filter   = apache-php-scanner

logpath  = /var/log/apache2/access.log

maxretry = 1
bantime  = 86400

# Which ports to block (http/https)
port     = http,https
protocol = tcp

# Use automatic backend detection
backend  = auto

# Never ban local/admin ranges — adjust to your environment.
ignoreip = 127.0.0.1/8 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12

# No explicit action -> use Fail2ban default action

with this filter:

# /etc/fail2ban/filter.d/apache-php-scanner.conf
[Definition]
# typical files from bad boy scans

failregex = ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /1\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /404\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /96i\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /\.well-known/.*\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /\.well-knownold/ HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /Admin/uploads/ HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /ALFA_DATA/ HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /CLA\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /Sanskrit\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /a\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /aa\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /aaa\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /aaaa\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /abcd\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /about\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /adminfuns\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /admin/function\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /admin/uploads/ HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /ahax\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /ar\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /asd67\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /autoload_classmap\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /axx\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /bless\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /bolt\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /bv3\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /cfile\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /class9\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /co\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /cool\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /dejavu\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /dex\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /ffile\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /file\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /file17\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /file2\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /file4\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /file5\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /file7\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /file8\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /gfile\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /gmo\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /gold\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /golden\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /great\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /lc\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /lock360\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /lo\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /moon\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /nc4\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /new\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /num\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /ol\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /ot\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /pass\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /past\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /pp\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /re\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /ss\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /(upload|uploads)/.* HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /userfuns\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /we\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /witmm\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /(wp-admin|wp-includes|wp-content)/.* HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /wp-.*\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /wp\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /wsr2\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /xpas2\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) /z\.php HTTP/[12]\.[01].*
            ^<HOST> - - \[.*\] "(?:GET|HEAD|POST) [^"]*/cache\.php\?pass=[0-9a-fA-F]{32} HTTP/[12]\.[01].*

I created the rules with the utmost care. They precisely match the pattern of the recurring scans. Since these are exclusively non-existent targets on my server, I use ‘maxretry = 1’, meaning these IPs are banned for 24 hours on the first attempt.

And then I started to analyze the IPs more closely, and I discovered that they all originated from Microsoft Azure Cloud Infrastructure, like the IP in your example:

whois 68.218.114.244

#
# ARIN WHOIS data and services are subject to the Terms of Use
# available at: https://www.arin.net/resources/registry/whois/tou/
#
# If you see inaccuracies in the results, please report at
# https://www.arin.net/resources/registry/whois/inaccuracy_reporting/
#
# Copyright 1997-2025, American Registry for Internet Numbers, Ltd.
#



# start

NetRange:       68.218.0.0 - 68.221.255.255
CIDR:           68.218.0.0/15, 68.220.0.0/15
NetName:        MSFT
NetHandle:      NET-68-218-0-0-2
Parent:         NET68 (NET-68-0-0-0-0)
NetType:        Direct Allocation
OriginAS:
Organization:   Microsoft Corporation (MSFT)

/ ... etc. /

Microsoft has been scanning web servers worldwide on a large scale for years, but particularly intensively since 2023–2025, after Microsoft began using the entire Azure cloud and edge infrastructure for:

  • “Security Baseline Observability”
  • “Threat Intelligence Feed Collection”
  • “Compromise Detection Telemetry”

And my Fail2ban output shows exactly the known Azure scanner ranges:

########################################
# Azure / MSFT scanner ranges
########################################

# NetRange: 4.176.0.0 - 4.191.255.255
# CIDR:     4.176.0.0/12

# NetRange: 4.192.0.0 - 4.207.255.255
# CIDR:     4.192.0.0/12

# NetRange: 4.208.0.0 - 4.223.255.255
# CIDR:     4.208.0.0/12

# NetRange: 4.224.0.0 - 4.239.255.255
# CIDR:     4.224.0.0/12

# NetRange: 4.240.0.0 - 4.255.255.255
# CIDR:     4.240.0.0/12

# NetRange: 13.64.0.0 - 13.107.255.255
# CIDR:     13.64.0.0/11, 13.104.0.0/14, 13.96.0.0/13

# NetRange: 20.0.0.0 - 20.31.255.255
# CIDR:     20.0.0.0/11

# NetRange: 20.33.0.0 - 20.128.255.255
# CIDR:     20.33.0.0/16, 20.34.0.0/15, 20.36.0.0/14, 20.40.0.0/13, 20.48.0.0/12, 20.64.0.0/10, 

# NetRange: 20.180.0.0 - 20.191.255.255
# CIDR:     20.184.0.0/13, 20.180.0.0/14

# NetRange: 20.192.0.0 - 20.255.255.255
# CIDR:     20.192.0.0/10

# NetRange: 40.74.0.0 - 40.125.127.255
# CIDR:     40.76.0.0/14, 40.74.0.0/15, 40.80.0.0/12, 40.120.0.0/14, 40.125.0.0/17, 

# NetRange: 52.145.0.0 - 52.191.255.255
# CIDR:     52.145.0.0/16, 52.146.0.0/15, 52.148.0.0/14, 52.152.0.0/13, 52.160.0.0/11

# NetRange: 52.224.0.0 - 52.255.255.255
# CIDR:     52.224.0.0/11

# NetRange: 65.52.0.0 - 65.55.255.255
# CIDR:     65.52.0.0/14

# NetRange: 68.218.0.0 - 68.221.255.255
# CIDR:     68.218.0.0/15, 68.220.0.0/15

# NetRange: 104.208.0.0 - 104.215.255.255
# CIDR:     104.208.0.0/13

These bots clearly belong to Azure Public Cloud (Microsoft).

The following run automatically from there:

  • Web shell scanner
  • WP scanner
  • Directory brute forcer
  • Ransomware reconnaissance
  • CVE signature checks
  • Backdoor search routines

These bots are not “good crawlers” like Bingbot or BingPreview—they are telemetry scanners from Microsoft security products that:

  • scan completely indiscriminately,
  • take no account of server load,
  • often send faulty or crude exploit payloads.

Microsoft itself claims in its advisories:

“Microsoft continuously scans public IPs for security exposure and threat intelligence enrichment.”

The reality:

These bots behave like script kiddie scanners and trigger every fail2ban filter worldwide.

And yes: it’s Microsoft itself—not spoofing!

Then I switched to blocking all these IP ranges.

Because they:

  • don’t generate legitimate access
  • generate a lot of log noise
  • test thousands of PHP files (WordPress, backdoors, shells)
  • try .well-known PHP files
  • sometimes run at 10-30 requests per second
  • don’t come randomly—they occur daily, systematically

It’s completely safe to block these ranges as long as you:

  • don’t need to offer a service to Azure customers
  • don’t operate a publicly accessible API for Microsoft services
  • don’t have a Microsoft webhook integration

This is 100% safe for Nextcloud.

I then created a mechanism that allows me to easily block IP blacklists using fail2ban with nftables as the action backend, from individual IPs to entire ranges in CIDR notation:

So this is my blacklis at present:
“/etc/fail2ban/ip-blacklist”

4.176.0.0/12      [2026-01-01 14:30:00]
4.192.0.0/12      [2026-01-01 14:30:00]
4.208.0.0/12      [2026-01-01 14:30:00]
4.224.0.0/12      [2026-01-01 14:30:00]
4.240.0.0/12      [2026-01-01 14:30:00]
13.64.0.0/11      [2026-01-01 14:30:00]
13.96.0.0/13      [2026-01-01 14:30:00]
13.104.0.0/14     [2026-01-01 14:30:00]
20.0.0.0/11       [2026-01-01 14:30:00]
20.33.0.0/16      [2026-01-01 14:30:00]
20.34.0.0/15      [2026-01-01 14:30:00]
20.36.0.0/14      [2026-01-01 14:30:00]
20.40.0.0/13      [2026-01-01 14:30:00]
20.48.0.0/12      [2026-01-01 14:30:00]
20.64.0.0/10      [2026-01-01 14:30:00]
20.128.0.0/16     [2026-01-01 14:30:00]
20.180.0.0/14     [2026-01-01 14:30:00]
20.184.0.0/13     [2026-01-01 14:30:00]
20.192.0.0/10     [2026-01-01 14:30:00]
40.74.0.0/15      [2026-01-01 14:30:00]
40.76.0.0/14      [2026-01-01 14:30:00]
40.80.0.0/12      [2026-01-01 14:30:00]
40.96.0.0/12      [2026-01-01 14:30:00]
40.112.0.0/13     [2026-01-01 14:30:00]
40.120.0.0/14     [2026-01-01 14:30:00]
40.124.0.0/16     [2026-01-01 14:30:00]
40.125.0.0/17     [2026-01-01 14:30:00]
52.132.0.0/14     [2026-01-01 14:30:00]
52.136.0.0/13     [2026-01-01 14:30:00]
52.145.0.0/16     [2026-01-01 14:30:00]
52.146.0.0/15     [2026-01-01 14:30:00]
52.148.0.0/14     [2026-01-01 14:30:00]
52.152.0.0/13     [2026-01-01 14:30:00]
52.160.0.0/11     [2026-01-01 14:30:00]
52.224.0.0/11     [2026-01-01 14:30:00]
65.52.0.0/14      [2026-01-01 14:30:00]
68.218.0.0/15     [2026-01-01 14:30:00]
68.220.0.0/15     [2026-01-01 14:30:00]
74.176.0.0/14     [2026-01-01 14:30:00]
104.208.0.0/13    [2026-01-01 14:30:00]

This is the jail file:

# /etc/fail2ban/jail.d/ip-blacklist.local

[ip-blacklist]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist
filter    = ip-blacklist
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_8]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=8]
filter    = ip-blacklist_8
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_9]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=9]
filter    = ip-blacklist_9
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_10]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=10]
filter    = ip-blacklist_10
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_11]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=11]
filter    = ip-blacklist_11
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_12]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=12]
filter    = ip-blacklist_12
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_13]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=13]
filter    = ip-blacklist_13
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_14]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=14]
filter    = ip-blacklist_14
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_15]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=15]
filter    = ip-blacklist_15
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_16]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=16]
filter    = ip-blacklist_16
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_17]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=17]
filter    = ip-blacklist_17
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_18]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=18]
filter    = ip-blacklist_18
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_19]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=19]
filter    = ip-blacklist_19
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_20]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=20]
filter    = ip-blacklist_20
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_21]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=21]
filter    = ip-blacklist_21
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_22]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=22]
filter    = ip-blacklist_22
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_23]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=23]
filter    = ip-blacklist_23
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

[ip-blacklist_24]
enabled   = true
port      = anyport
action    = nftables-ip-blacklist[mask=24]
filter    = ip-blacklist_24
logpath   = /etc/fail2ban/ip-blacklist
maxretry  = 0
findtime  = 15552000
bantime   = -1

This is the filter file for single IP’s:

# "/etc/fail2ban/filter.d/ip-blacklist.conf"

[Definition]
failregex = ^<HOST>(/32.*|[^/]*)?$
ignoreregex =

and then you need for every one of the possible CIDR Notations one filter file. This is just an example for ip/16:

# "/etc/fail2ban/filter.d/ip-blacklist_16.conf"

[Definition]
failregex = ^<HOST>/16.*$
ignoreregex =

this for /11:

# "/etc/fail2ban/filter.d/ip-blacklist_11.conf"

[Definition]
failregex = ^<HOST>/11.*$
ignoreregex =

I don’t need to repeat them all here; the principle should be clear.

And finaly this is the action file, the core of everything:

# "/etc/fail2ban/action.d/nftables-ip-blacklist.conf"

[Definition]
# executed once at start
actionstart = nft add table inet f2b-table
              nft -- add chain inet f2b-table f2b-chain \{ type filter hook input priority -1 \; \}
              nft add set inet f2b-table <addr_set> \{ type <addr_type> \; flags interval \; \}
              nft add rule inet f2b-table f2b-chain ip saddr @<addr_set> reject

# executed once at stop
actionstop  = { nft -a list chain inet f2b-table f2b-chain | grep -oP '@<addr_set>\s+.*\s+\Khandle\s+(\d+)$' || true; } | while read -r hdl; do
                  nft delete rule inet f2b-table f2b-chain $hdl || true
              done
              nft delete set inet f2b-table <addr_set>
              { nft list table inet f2b-table | grep -qP '^\s+set\s+'; } || { nft delete table inet f2b-table; }

# executed before each ban
actioncheck = nft list chain inet f2b-table f2b-chain | grep -q '@<addr_set>[ \t]'

# ban = add element to set, **with CIDR mask from jail parameter**
actionban   = nft add element inet f2b-table <addr_set> { <ip>/<mask> }

# unban = remove element (same CIDR)
actionunban = nft delete element inet f2b-table <addr_set> { <ip>/<mask> }

[Init]
# name of the nftables set / logical action
name           = ip-blacklist

protocol       = all
chain          = input
table          = f2b-table
table_family   = inet
chain_type     = filter
chain_hook     = input
chain_priority = -1
blocktype      = reject
nftables       = nft

addr_set       = addr-set-<name>
addr_type      = ipv4_addr
addr_family    = ip

# default mask if none is passed from jail (overridden by [mask=10] etc.)
mask           = 32

The only thing fail2ban can’t do is display the blocked IP ranges. So, to check if the ranges are actually blocked, you have to look at it directly with nftables:

nft list ruleset
table inet f2b-table {
        set addr-set-ip-blacklist {
                type ipv4_addr
                flags interval
        }

        set addr-set-ip-blacklist_10 {
                type ipv4_addr
                flags interval
                elements = { 20.64.0.0/10, 20.192.0.0/10 }
        }

        set addr-set-ip-blacklist_11 {
                type ipv4_addr
                flags interval
                elements = { 13.64.0.0/11, 20.0.0.0/11,
                             52.160.0.0/11, 52.224.0.0/11 }
        }

        set addr-set-ip-blacklist_12 {
                type ipv4_addr
                flags interval
                elements = { 4.176.0.0/12, 4.192.0.0/12,
                             4.208.0.0/12, 4.224.0.0/12,
                             4.240.0.0/12, 20.48.0.0/12,
                             40.80.0.0/12, 40.96.0.0/12 }
        }

        set addr-set-ip-blacklist_13 {
                type ipv4_addr
                flags interval
                elements = { 13.96.0.0/13, 20.40.0.0/13,
                             20.184.0.0/13, 40.112.0.0/13,
                             52.136.0.0/13, 52.152.0.0/13,
                             104.208.0.0/13 }
        }

        set addr-set-ip-blacklist_14 {
                type ipv4_addr
                flags interval
                elements = { 13.104.0.0/14, 20.36.0.0/14,
                             20.180.0.0/14, 40.76.0.0/14,
                             40.120.0.0/14, 52.132.0.0/14,
                             52.148.0.0/14, 65.52.0.0/14,
                             74.176.0.0/14 }
        }

        set addr-set-ip-blacklist_15 {
                type ipv4_addr
                flags interval
                elements = { 20.34.0.0/15, 40.74.0.0/15,
                             52.146.0.0/15, 68.218.0.0/15,
                             68.220.0.0/15 }
        }

        set addr-set-ip-blacklist_16 {
                type ipv4_addr
                flags interval
                elements = { 20.33.0.0/16, 20.128.0.0/16,
                             40.124.0.0/16, 52.145.0.0/16 }
        }

        set addr-set-ip-blacklist_17 {
                type ipv4_addr
                flags interval
                elements = { 40.125.0.0/17 }
        }

        set addr-set-apache-php-scanner {
                type ipv4_addr
                elements = { 135.235.161.115 }
        }

        set addr-set-apache-selfref {
                type ipv4_addr
                elements = { 145.239.10.137 }
        }

        chain f2b-chain {
                type filter hook input priority filter - 1; policy accept;
                ip saddr @addr-set-ip-blacklist reject with icmp port-unreachable
                ip saddr @addr-set-ip-blacklist_10 reject with icmp port-unreachable
                ip saddr @addr-set-ip-blacklist_11 reject with icmp port-unreachable
                ip saddr @addr-set-ip-blacklist_12 reject with icmp port-unreachable
                ip saddr @addr-set-ip-blacklist_13 reject with icmp port-unreachable
                ip saddr @addr-set-ip-blacklist_14 reject with icmp port-unreachable
                tcp dport { 80, 443 } ip saddr @addr-set-apache-modsecurity reject with icmp port-unreachable
                ip saddr @addr-set-ip-blacklist_15 reject with icmp port-unreachable
                ip saddr @addr-set-ip-blacklist_16 reject with icmp port-unreachable
                ip saddr @addr-set-ip-blacklist_17 reject with icmp port-unreachable
                tcp dport { 80, 443 } ip saddr @addr-set-apache-php-scanner reject with icmp port-unreachable
                tcp dport { 80, 443 } ip saddr @addr-set-apache-selfref reject with icmp port-unreachable
        }
}

If I get a hit from an IP range that isn’t yet banned, I locate the range (no problem using whois) and add it to the file “/etc/fail2ban/ip-blacklist”. I then set the date next to each IP range to the current date and save the file. Fail2ban will recognize this immediately because it treats the blacklist as a log file and will ban the new range as well.

So far, I’ve only encountered one case, an app-source from Microsoft for the Visual Studio code Package:
packages.microsoft.com:443 used 13.107.246.45 which is in the banned range.
I simply disable fail2ban when I want to upgrade the code package

I hardly ever get any scans now.

Microsoft offers the IP ranges on their download page. While one could ban them ALL at once, I prefer to only ban them after they’ve been flagged.

Microsoft has not officially published a statement saying, “We scan external servers.”

Microsoft does not confirm anywhere that they do this.

BUT: What you are seeing definitely originates from publicly documented Azure IP ranges, and these are used for security-related services, bots, telemetry, health checks, monitoring, API crawlers, antivirus systems, compliance scanners, etc.

Microsoft operates one of the world’s largest security networks.
They collect global signals to:

  • Detect botnet activity
  • Identify new attack patterns
  • Monitor vulnerabilities
  • Catalog exploit attempts
  • Determine the global reputation of IP addresses
  • Analyze malware behavior
  • Detect DDoS patterns

This is officially called the “Microsoft Intelligent Security Graph” by Microsoft.

This data feeds into:

  • Microsoft Defender (all versions)
  • Azure Sentinel
  • Defender for Cloud
  • Outlook spam/phishing detection
  • Windows SmartScreen Reputation

Microsoft uses it for global threat detection.

Similar activity regularly comes from:

  • Google Cloud
  • AWS
  • Cloudflare
  • Akamai
  • Oracle Cloud
  • Hetzner Probes
  • GitHub Actions
  • Palo Alto Threat Labs
  • Fortinet Threat Intelligence
  • Rapid7 Sonar
  • Shadowserver
  • Censys
  • Shodan (official, publicly documented)

In reality, you are triggered daily by dozens of official scanners – Microsoft is just the largest.

h.t.h.


ernolf

4 Likes