Monday morning started quietly. Of all the sites on my box, relocation-support.ch is the one I think about least: a small static site, plus a YOURLS link shortener on sc.relocation-support.ch that gets a handful of hits on a good day (and that would be a really good day!!).

The Polite Redirect

Between 08:52 and 08:59 it received about 9’000 requests.

“The MIRE was waiting, but the hits fell through the gaps.”

Seven minutes of curl mayhem

The traffic came from a single IP, 185.177.72.29, geolocated to France, using a single user agent: curl/8.7.1. It made no attempt to look like a browser and behaved like a teenager playing “All Out Attack” mode in FC 27…

At the peak, the shortener was taking about a thousand requests a minute. In total: 5’959 requests to sc.relocation-support.ch, which naively bounced 2’984 of them on to www.relocation-support.ch, the scanner sending around 50 probes of its own to www directly.

Field Report§01The scanCaddy vs The MIRE
8’993
Requests in Caddy’s logs
7 min
08:52–08:59 CEST
1
Source IP, curl/8.7.1
~2’980
Distinct probe paths
54
Handled by The MIRE

Both charts use the same scale. The MIRE’s busiest minute was 18 requests.

The modus operandi was familiar. There were about 2’980 distinct paths, and nearly all of them were looking for secrets someone had left lying around:

  • about 955 variations on .env: /.env.production, /xampp/.env, /zmusic-frontend/.env
  • about 200 phpinfo and xdebug pages
  • about 130 cloud credential paths, such as /.aws/credentials
  • about 110 .git paths
  • the usual backups, dumps, lockfiles and log files

Almost every dot was URL-encoded: /phpinfo%2ephp, /%2f%2eaws%2fcredentials. It costs the scanner nothing and gets past any filter that only looks for a literal .env.

Of course this IP has been seen before. It turns up in the logs in May, June, July, August and early September. Monday was just the biggest run so far.

The part that bothered me

The MIRE is supposed to detect and intercept such attacks, introducing delay and creating its own noise: someone asking for .env gets a convincing fake, someone asking for admin_login.php can try to log into something – and everything is logged.

So I checked what The MIRE saw of this scan: 54 requests. Needless to say, I expected more!

A scanner spent seven minutes asking my server for its most sensitive files, and the system built to watch for exactly that saw less than one percent of it.

Nothing had failed. No errors, no alerts, no dropped connections. Everything had worked exactly as configured, and that was the problem.

Following the hops

The access logs showed a pattern. Each probe didn’t produce one request; it produced three:

Field Report§02One probe, three requestsCaddy logs
caddy access log — 28 Sep, relocation-support.ch
# time      host                        uri                  status  →
08:52:52  sc.relocation-support.ch    /pageinfo%2ephp      302     http://sc.relocation-support.ch
08:52:52  sc.relocation-support.ch    /                    302     https://www.relocation-support.ch
08:52:52  www.relocation-support.ch   /                    200     homepage, 13’360 bytes
08:52:52  sc.relocation-support.ch    /info%2ephp%2eback   302     http://sc.relocation-support.ch
08:52:52  sc.relocation-support.ch    /                    302     https://www.relocation-support.ch
08:52:53  www.relocation-support.ch   /                    200     homepage, 13’360 bytes
# client 185.177.72.29 · curl/8.7.1 · follows every redirect · via Cloudflare edge

The scanner followed redirects, so every probe ended on the homepage. That’s why www showed about 3’000 hits on /, and why the load was split roughly two to one between the two hostnames.

The blind spot

To the web server, a redirect is a success. The MIRE only steps in on a 404, so every probe the shortener politely redirected was invisible to it.

My first guess was that the root redirect from the URL shortener to www was to blame. Wrong: that was just a later stop in this chain, and the fix was further up.

The real cause was YOURLS being too “nice”. When someone requests a short link that doesn’t exist, YOURLS does something friendly. Right at the end of yourls-loader.php:

Field Report§03The friendly fallbackYOURLS
yourls-loader.php
yourls_do_action( 'redirect_keyword_not_found', $keyword );
yourls_do_action( 'loader_failed', $request );
yourls_redirect( YOURLS_SITE, 302 );

A person mistyped a shortened link? Try not to be ugly and display a page. For a human that’s good UX. For a scanner, it neuters their attack (well done, YOURLS developers), but it’s not the response we want when protecting sites.

The configuration sending traffic to The MIRE only ever cared about 404 errors. The MIRE was waiting, but the hits fell through the gaps.

Same morning, different outcome

About twenty minutes after the scan ended, a second visitor appeared: 20.219.11.16, an Azure address, sending requests to www every fifteen to twenty seconds with no User-Agent at all. Seventy requests between 09:19 and 09:36. It had worked through the toce.ch host the same way two days earlier.

The MIRE caught all seventy.

The difference wasn’t the attacker’s skill or how noisy they were. Since the YOURLS configuration was not in the attack chain, The MIRE handled the inbound traffic without breaking a sweat.

The fix

A YOURLS keyword has a recognisable shape: one path segment of letters, digits and hyphens, optionally with a trailing + for the stats page. /abc123 could be a short link. /.aws/credentials never could.

So the rule is: if a path isn’t a real file and can’t be a short link, it’s a probe, and it must go to The MIRE and not to YOURLS.

Short links still resolve and mistyped keywords still get the friendly redirect. Anything shaped like an attack goes to The MIRE with the URI exactly as the scanner sent it. The configuration in front of The MIRE and YOURLS was adapted to ensure this attack capture was not going to fall through the gaps any more.

The Takeaway

Logs and statistics are valuable and need to be regularly assessed; seeing an odd peak on a quiet website set my expectations that The MIRE had a busy time. But no, it was the application that “handled” the attack.

Every friendly catch-all is a blind spot. While YOURLS has a good approach to unwanted and unexpected URIs, handling them as a 302 and trying to display something nice left them all in limbo – and sent the wrong signal to the attacker.

Good UX and good detection pull in opposite directions. The balance is to handle human errors in a humane way and automated probes in another way – in this case, sending them to The MIRE.

Like The Banana Skin in Cloudflare Tunnel, this is a type of blind spot where the traffic avoids The MIRE. These improvements are vital to the enhancements to The MIRE and its roadmap.

For now, the next time 185.177.72.29 comes back, I expect the two numbers to be in alignment.