mirror of
https://github.com/pldubouilh/blockfast.git
synced 2026-09-05 00:27:20 -04:00
109 lines
7.5 KiB
JSON
109 lines
7.5 KiB
JSON
{
|
|
"comment": "blockfast probelist - TEMPLATE. This file documents the format and how to generate a real one from server logs. blockfast loads it with --probelist=<file>; it then REPLACES the built-in probe list entirely, so a generated file must include generic scanner rules too, not only setup-specific ones. All `comment` fields (and this whole header) are ignored by the loader; entries containing only a `comment` are section separators. Everything else must follow the format below.",
|
|
|
|
"format": [
|
|
"each probe entry: { path, match?, status?, allowance?, comment? }",
|
|
"path (required): the string to match against the request URI, matched case-insensitively",
|
|
"match (optional, default 'contains'): 'contains' matches anywhere in the full URI including the query string; 'prefix' and 'exact' match against the query-stripped path only",
|
|
"status (optional): restricts the probe to these response statuses, as a comma-separated string with trailing-x ranges, e.g. '401' or '403,5xx' or '40x'. Without it the probe matches ANY status - important, since SPAs with catch-all routes answer 200 to probe paths",
|
|
"allowance (optional, 1-255): overrides blockfast's global --allowance for this probe, i.e. how many offences an IP may accumulate (within the jailtime window, shared across all probes) before being banned. Use a HIGHER value for endpoints legitimate users occasionally trip, a LOW value is implicit for pure probe paths"
|
|
],
|
|
|
|
"howto_generate": [
|
|
"You are generating a ban-rule file for blockfast, a fail2ban-like daemon: it tails web server logs (Common Log Format or Caddy JSON), counts offences per client IP, and firewalls IPs that exceed their allowance. A wrong rule bans legitimate users, so precision beats recall.",
|
|
"1. Group the log lines by client IP. Classify each IP: legitimate clients use real app routes and mostly get 2xx/3xx; scanners enumerate paths (/.env, /phpinfo.php, /wp-*, credential/config filenames) that the app never serves.",
|
|
"2. Paths requested ONLY by scanners and NEVER by legitimate clients become unconditional probes (no status filter). Do not rely on the response status for these: a catch-all route may answer 200.",
|
|
"3. Endpoints that legitimate users DO touch, but that indicate abuse in volume (login endpoints, session checks answering 401, method-probing answering 405), become narrow rules: match 'prefix' or 'exact' + a status filter + a generous allowance. Never add such an endpoint without a status filter.",
|
|
"4. NEVER add: paths served by the app (assets, API routes), /favicon.ico, /robots.txt, /.well-known (ACME!), or anything a browser or well-behaved bot requests on its own. When unsure, leave it out.",
|
|
"5. Prefer 'exact' for short or generic names ('/env', '/i.php') - as substrings they would match legitimate paths ('/i.php' is inside '/api.php'). Use 'contains' for names that are unambiguous anywhere in a URI ('/.env', 'phpinfo').",
|
|
"6. Keep the generic scanner families in the output (see sections below), then append the setup-specific rules derived from the logs, each with a comment explaining the evidence.",
|
|
"7. Sanity-check the finished list by replaying the logs against it: no IP classified as a legitimate client may match any rule."
|
|
],
|
|
|
|
"probes": [
|
|
{ "comment": "--- setup-specific rules, derived from the logs (EXAMPLES - replace with real findings) ---" },
|
|
{ "path": "/api/auth", "match": "prefix", "status": "401", "allowance": 10, "comment": "example: the SPA answers 401 on session checks when logged out - normal a few times, credential stuffing in bulk. prefix + status + generous allowance" },
|
|
{ "path": "/", "match": "exact", "status": "405", "comment": "example: scanners POST to the root fishing for handlers; browsers never trigger 405 there" },
|
|
{ "path": "/backup.tar.gz", "match": "exact", "comment": "example: artifact hunted by scanners in these logs, never linked by the app" },
|
|
|
|
{ "comment": "--- generic: secrets & dotfiles ---" },
|
|
{ "path": "/.env", "comment": "also matches /.env.bak, /backend/.env, ..." },
|
|
{ "path": "%2eenv", "comment": "url-encoded .env" },
|
|
{ "path": "/.git", "comment": "/.git/config, /.git/HEAD, /.gitconfig" },
|
|
{ "path": "/.svn" },
|
|
{ "path": "/.hg/" },
|
|
{ "path": "/.aws" },
|
|
{ "path": "/.ssh" },
|
|
{ "path": "/.docker" },
|
|
|
|
{ "comment": "--- generic: cloud credentials & config dumps ---" },
|
|
{ "path": "credentials.json" },
|
|
{ "path": "-key.json", "comment": "/gcp-key.json, /firebase-key.json, ..." },
|
|
{ "path": "/keyfile.json" },
|
|
{ "path": "/sa.json" },
|
|
{ "path": "service-account.json" },
|
|
{ "path": "firebase-adminsdk.json" },
|
|
{ "path": "gcp-sa.json" },
|
|
{ "path": "/docker-compose.yml" },
|
|
{ "path": "/appsettings.json" },
|
|
{ "path": "application.yml" },
|
|
{ "path": "parameters.yml" },
|
|
{ "path": "/web.config" },
|
|
{ "path": "/settings.py" },
|
|
{ "path": "/wp-config.php", "comment": "never served, only probed - even on real wordpress sites" },
|
|
|
|
{ "comment": "--- generic: php probes ---" },
|
|
{ "path": "phpinfo" },
|
|
{ "path": "phpmyadmin" },
|
|
{ "path": "adminer.php" },
|
|
{ "path": ".php.bak" },
|
|
{ "path": ".php.old" },
|
|
{ "path": ".php.save" },
|
|
{ "path": ".php~" },
|
|
{ "path": "eval-stdin.php", "comment": "phpunit RCE" },
|
|
{ "path": "/vendor/phpunit" },
|
|
{ "path": "/test.php", "match": "exact" },
|
|
{ "path": "/info.php", "match": "exact" },
|
|
{ "path": "/pinfo.php", "match": "exact" },
|
|
{ "path": "/pi.php", "match": "exact" },
|
|
{ "path": "/i.php", "match": "exact" },
|
|
{ "path": "/p.php", "match": "exact" },
|
|
{ "path": "/php.php", "match": "exact" },
|
|
{ "path": "/debug.php", "match": "exact" },
|
|
{ "path": "/database.php", "match": "exact" },
|
|
{ "path": "/config.php", "match": "exact" },
|
|
{ "path": "/shell.php", "match": "exact" },
|
|
{ "path": "/upload.php", "match": "exact" },
|
|
|
|
{ "comment": "--- generic: fingerprinting & framework debug endpoints ---" },
|
|
{ "path": "wlwmanifest.xml", "comment": "wordpress fingerprinting, legit wp traffic never touches it" },
|
|
{ "path": "/actuator/env", "comment": "spring boot - keep narrow, bare /actuator would catch legit health checks" },
|
|
{ "path": "/_profiler", "comment": "symfony" },
|
|
{ "path": "/_ignition", "comment": "laravel RCE" },
|
|
{ "path": "/_environment", "comment": "cakephp" },
|
|
{ "path": "laravel.log" },
|
|
{ "path": "/env", "match": "exact" },
|
|
|
|
{ "comment": "--- generic: server status, traversal, IoT/router botnets ---" },
|
|
{ "path": "server-status" },
|
|
{ "path": "server-info" },
|
|
{ "path": "/../" },
|
|
{ "path": "%2e%2e" },
|
|
{ "path": "/etc/passwd" },
|
|
{ "path": "/cgi-bin/" },
|
|
{ "path": "/boaform" },
|
|
{ "path": "/hnap1" },
|
|
{ "path": "/gponform" },
|
|
|
|
{ "comment": "--- aggressive extras: correct for many setups, but NOT in blockfast's built-in default because they match legit traffic on some stacks. Include them only when the logs show the stack does not use them ---" },
|
|
{ "path": "/wp-login.php", "comment": "legit logins on real wordpress sites - only include if not hosting wordpress" },
|
|
{ "path": "/xmlrpc.php", "comment": "used by jetpack & wordpress mobile apps" },
|
|
{ "path": "rest_route=", "comment": "wordpress REST access with permalinks disabled" },
|
|
{ "path": "/autodiscover/", "comment": "outlook clients innocently probe this on any domain" },
|
|
{ "path": "/manager/html", "comment": "tomcat console - legit for tomcat admins" },
|
|
{ "path": "/solr/admin" },
|
|
{ "path": "/geoserver/web" },
|
|
{ "path": "/telescope/requests", "comment": "laravel telescope - legit for its admins" }
|
|
]
|
|
}
|