Skip to content
View in the app

A better way to browse. Learn more.

Learn more

A client VPN portal taking 50,000 password-spray attempts a day, and how we shut it down

Featured Replies

This started as a routine look at a client's remote access portal logs. We found around 50,000 password-spray sessions a day coming from just five networks, with zero successful logins. Nobody had noticed, because spraying is quiet by design. Instead of hammering one account with thousands of passwords, the attacker tries a few common passwords against thousands of usernames, which stays under per-account lockout thresholds. Anything with a public login page gets this as constant background noise. Here's how we spotted it, measured it and shut it off at the edge.

Access you'll want

  • The portal or VPN authentication logs, whether that's local logs, a syslog server or a SIEM
  • A Linux shell with the usual tools (grep, awk, sort, uniq)
  • Admin access to the edge firewall or load balancer in front of the portal

What it looks like in the logs

Once you've seen it, the pattern is easy to recognize:

  • Thousands to tens of thousands of authentication sessions per day, far more than your user count could explain
  • Lots of usernames, often ones that don't exist in your directory, like admin, test, scanner, or first.last guesses from a scraped staff list
  • A handful of source /24 networks, often hosting providers, rotating through addresses within each block
  • No successes at all. If any attempts succeed, stop reading and start incident response.

Counting failures by network

Every product logs differently, so you'll need to adapt the field extraction. Here's a generic log line:

2026-10-01T03:12:44Z portal auth=fail user=jsmith src=203.0.113.45 reason=bad_password

Count failed attempts per source /24:

grep 'auth=fail' portal.log \
  | grep -oE 'src=[0-9.]+' | cut -d= -f2 \
  | awk -F. '{print $1"."$2"."$3".0/24"}' \
  | sort | uniq -c | sort -rn | head -20

Then check how many distinct usernames each network tried. A real user behind a NAT fails with one or two names. A sprayer tries hundreds.

grep 'auth=fail' portal.log \
  | awk '{for(i=1;i<=NF;i++){if($i~/^user=/)u=substr($i,6); if($i~/^src=/)s=substr($i,5)}
         split(s,o,"."); print o[1]"."o[2]"."o[3]".0/24", u}' \
  | sort -u | awk '{print $1}' | uniq -c | sort -rn | head -20

And confirm there were no successes from those networks:

grep 'auth=success' portal.log | grep -E 'src=(203\.0\.113|198\.51\.100)\.' | wc -l

For this client, five networks accounted for essentially all of the failures, each had tried hundreds of usernames, and the success count was zero.

Block it before it reaches login and MFA

Where you block matters. If the portal forwards each attempt to your MFA provider or directory, the spray costs you more than log space. It loads your domain controllers, can lock out real staff whose usernames happen to match, and can spam people with push prompts if a password ever does match. Drop the traffic before it gets to the login logic.

Most platforms have a named list you can block against. It might be an address list or address group on a firewall, a data group referenced by a load balancer rule, or an external dynamic list. Keep the list in one place so adding a network is a one-line change. On a Linux edge, ipset does the same job:

ipset create spray-block hash:net
ipset add spray-block 203.0.113.0/24
ipset add spray-block 198.51.100.0/24
iptables -I INPUT -p tcp --dport 443 -m set --match-set spray-block src -j DROP

Make the rule persistent using your distribution's mechanism, and put a comment or ticket reference on each entry. Whoever looks at that list next will want to know why it's there.

Geo-blocking, if your users are all in one country

Blocking five networks handles today's sprayer, and the next one will come from somewhere else. If every legitimate user is in one country, allowing only that country to reach the portal removes most of the noise with one rule. Most commercial firewalls have built-in geo objects. Keep an exception process for staff who travel. Geo-IP data is approximate and VPN providers can appear to be anywhere, so I treat this as noise reduction, not a security control on its own.

MFA and lockout still do the real work

Edge blocking cuts load and noise, but it doesn't protect accounts. Attackers rotate sources, and spraying is slow on purpose. What actually protects your users:

  • MFA on every remote access path, preferably phishing-resistant or number-matching, so a guessed password alone isn't enough
  • A lockout or smart-lockout policy that slows guessing without making it trivial to lock out the whole staff
  • Banned-password lists so seasonal passwords like Fall2026! get rejected
  • Alerts on any successful login from an unusual country or network, and on MFA prompts users report they didn't trigger

Next-day checks

  • Re-run the per-network count on the next day's log. The blocked networks should be gone.
  • The firewall rule's hit counter should be climbing.
  • Real users can still log in, including from home ISPs and mobile carriers.
  • The MFA provider logs no longer show a stream of authentication requests for unknown users.

Be careful with these

  • Before blocking a network, check that it isn't a mobile carrier or a large ISP your users share. Hosting provider ranges are usually safe to block. Residential ranges are not.
  • If a load balancer sits in front of the portal, make sure the real client IP is being logged. Otherwise every attempt looks like it came from one internal address.
  • A block rule placed below a broad allow rule never matches. Check the hit counts after you add it.
  • The list is easy to forget about. Review the blocked networks every few months and remove stale entries.

We re-run the per-network count on that portal every so often. New sprayers keep turning up from new places, and adding another /24 to the list takes about a minute.

Create an account or sign in to comment

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.