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