Merge pull request #17 from leonardobambini/feat/randomized-server-header

Feat/randomized server header
This commit is contained in:
Patrick Di Fazio
2025-12-30 00:14:28 +01:00
committed by GitHub
9 changed files with 54 additions and 11 deletions

View File

@@ -185,7 +185,7 @@ To customize the deception server installation several **environment variables**
| `CANARY_TOKEN_URL` | External canary token URL | None | | `CANARY_TOKEN_URL` | External canary token URL | None |
| `DASHBOARD_SECRET_PATH` | Custom dashboard path | Auto-generated | | `DASHBOARD_SECRET_PATH` | Custom dashboard path | Auto-generated |
| `PROBABILITY_ERROR_CODES` | Error response probability (0-100%) | `0` | | `PROBABILITY_ERROR_CODES` | Error response probability (0-100%) | `0` |
| `SERVER_HEADER` | HTTP Server header for deception | `Apache/2.2.22 (Ubuntu)` | | `SERVER_HEADER` | HTTP Server header for deception, if not set use random server header | |
| `TIMEZONE` | IANA timezone for logs and dashboard (e.g., `America/New_York`, `Europe/Rome`) | System timezone | | `TIMEZONE` | IANA timezone for logs and dashboard (e.g., `America/New_York`, `Europe/Rome`) | System timezone |
## robots.txt ## robots.txt

View File

@@ -20,7 +20,7 @@ services:
- MAX_COUNTER=10 - MAX_COUNTER=10
- CANARY_TOKEN_TRIES=10 - CANARY_TOKEN_TRIES=10
- PROBABILITY_ERROR_CODES=0 - PROBABILITY_ERROR_CODES=0
- SERVER_HEADER=Apache/2.2.22 (Ubuntu) # - SERVER_HEADER=Apache/2.2.22 (Ubuntu)
# Optional: Set your canary token URL # Optional: Set your canary token URL
# - CANARY_TOKEN_URL=http://canarytokens.com/api/users/YOUR_TOKEN/passwords.txt # - CANARY_TOKEN_URL=http://canarytokens.com/api/users/YOUR_TOKEN/passwords.txt
# Optional: Set custom dashboard path (auto-generated if not set) # Optional: Set custom dashboard path (auto-generated if not set)

View File

@@ -14,8 +14,13 @@ data:
MAX_COUNTER: {{ .Values.config.maxCounter | quote }} MAX_COUNTER: {{ .Values.config.maxCounter | quote }}
CANARY_TOKEN_TRIES: {{ .Values.config.canaryTokenTries | quote }} CANARY_TOKEN_TRIES: {{ .Values.config.canaryTokenTries | quote }}
PROBABILITY_ERROR_CODES: {{ .Values.config.probabilityErrorCodes | quote }} PROBABILITY_ERROR_CODES: {{ .Values.config.probabilityErrorCodes | quote }}
SERVER_HEADER: {{ .Values.config.serverHeader | quote }}
CANARY_TOKEN_URL: {{ .Values.config.canaryTokenUrl | quote }} CANARY_TOKEN_URL: {{ .Values.config.canaryTokenUrl | quote }}
{{- if .Values.config.dashboardSecretPath }}
DASHBOARD_SECRET_PATH: {{ .Values.config.dashboardSecretPath | quote }}
{{- end }}
{{- if .Values.config.serverHeader }}
SERVER_HEADER: {{ .Values.config.serverHeader | quote }}
{{- end }}
{{- if .Values.config.timezone }} {{- if .Values.config.timezone }}
TIMEZONE: {{ .Values.config.timezone | quote }} TIMEZONE: {{ .Values.config.timezone | quote }}
{{- end }} {{- end }}

View File

@@ -73,7 +73,9 @@ config:
maxCounter: 10 maxCounter: 10
canaryTokenTries: 10 canaryTokenTries: 10
probabilityErrorCodes: 0 probabilityErrorCodes: 0
serverHeader: "Apache/2.2.22 (Ubuntu)" # timezone: "UTC"
# serverHeader: "Apache/2.2.22 (Ubuntu)"
# dashboardSecretPath: "/my-secret-dashboard"
# canaryTokenUrl: set-your-canary-token-url-here # canaryTokenUrl: set-your-canary-token-url-here
# timezone: "UTC" # IANA timezone (e.g., "America/New_York", "Europe/Rome"). If not set, system timezone is used. # timezone: "UTC" # IANA timezone (e.g., "America/New_York", "Europe/Rome"). If not set, system timezone is used.
@@ -269,6 +271,17 @@ wordlists:
- .git/ - .git/
- keys/ - keys/
- credentials/ - credentials/
server_headers:
- Apache/2.2.22 (Ubuntu)
- nginx/1.18.0
- Microsoft-IIS/10.0
- LiteSpeed
- Caddy
- Gunicorn/20.0.4
- uvicorn/0.13.4
- Express
- Flask/1.1.2
- Django/3.1
error_codes: error_codes:
- 400 - 400
- 401 - 401

View File

@@ -23,7 +23,7 @@ class Config:
api_server_port: int = 8080 api_server_port: int = 8080
api_server_path: str = "/api/v2/users" api_server_path: str = "/api/v2/users"
probability_error_codes: int = 0 # Percentage (0-100) probability_error_codes: int = 0 # Percentage (0-100)
server_header: str = "Apache/2.2.22 (Ubuntu)" server_header: Optional[str] = None
# Database settings # Database settings
database_path: str = "data/krawl.db" database_path: str = "data/krawl.db"
database_retention_days: int = 30 database_retention_days: int = 30
@@ -84,9 +84,10 @@ class Config:
api_server_url=os.getenv('API_SERVER_URL'), api_server_url=os.getenv('API_SERVER_URL'),
api_server_port=int(os.getenv('API_SERVER_PORT', 8080)), api_server_port=int(os.getenv('API_SERVER_PORT', 8080)),
api_server_path=os.getenv('API_SERVER_PATH', '/api/v2/users'), api_server_path=os.getenv('API_SERVER_PATH', '/api/v2/users'),
server_header=os.getenv('SERVER_HEADER', 'Apache/2.2.22 (Ubuntu)'), probability_error_codes=int(os.getenv('PROBABILITY_ERROR_CODES', 0)),
server_header=os.getenv('SERVER_HEADER')
database_path=os.getenv('DATABASE_PATH', 'data/krawl.db'), database_path=os.getenv('DATABASE_PATH', 'data/krawl.db'),
database_retention_days=int(os.getenv('DATABASE_RETENTION_DAYS', 30)), database_retention_days=int(os.getenv('DATABASE_RETENTION_DAYS', 30)),
probability_error_codes=int(os.getenv('PROBABILITY_ERROR_CODES', 0)),
timezone=os.getenv('TIMEZONE') # If not set, will use system timezone timezone=os.getenv('TIMEZONE') # If not set, will use system timezone
) )

View File

@@ -9,7 +9,8 @@ import string
import json import json
from templates import html_templates from templates import html_templates
from wordlists import get_wordlists from wordlists import get_wordlists
from config import Config
from logger import get_app_logger
def random_username() -> str: def random_username() -> str:
"""Generate random username""" """Generate random username"""
@@ -36,6 +37,16 @@ def random_email(username: str = None) -> str:
username = random_username() username = random_username()
return f"{username}@{random.choice(wl.email_domains)}" return f"{username}@{random.choice(wl.email_domains)}"
def random_server_header() -> str:
"""Generate random server header"""
if Config.from_env().server_header:
server_header = Config.from_env().server_header
else:
wl = get_wordlists()
server_header = random.choice(wl.server_headers)
return server_header
def random_api_key() -> str: def random_api_key() -> str:
"""Generate random API key""" """Generate random API key"""

View File

@@ -13,7 +13,7 @@ from templates import html_templates
from templates.dashboard_template import generate_dashboard from templates.dashboard_template import generate_dashboard
from generators import ( from generators import (
credentials_txt, passwords_txt, users_json, api_keys_json, credentials_txt, passwords_txt, users_json, api_keys_json,
api_response, directory_listing api_response, directory_listing, random_server_header
) )
from wordlists import get_wordlists from wordlists import get_wordlists
@@ -52,7 +52,7 @@ class Handler(BaseHTTPRequestHandler):
def version_string(self) -> str: def version_string(self) -> str:
"""Return custom server version for deception.""" """Return custom server version for deception."""
return self.config.server_header return random_server_header()
def _should_return_error(self) -> bool: def _should_return_error(self) -> bool:
"""Check if we should return an error based on probability""" """Check if we should return an error based on probability"""

View File

@@ -57,7 +57,8 @@ class Wordlists:
}, },
"users": { "users": {
"roles": ["Administrator", "User"] "roles": ["Administrator", "User"]
} },
"server_headers": ["Apache/2.4.41 (Ubuntu)", "nginx/1.18.0"]
} }
@property @property
@@ -111,6 +112,10 @@ class Wordlists:
@property @property
def error_codes(self): def error_codes(self):
return self._data.get("error_codes", []) return self._data.get("error_codes", [])
@property
def server_headers(self):
return self._data.get("server_headers", [])
_wordlists_instance = None _wordlists_instance = None

View File

@@ -193,5 +193,13 @@
500, 500,
502, 502,
503 503
],
"server_headers": [
"Apache/2.4.41 (Ubuntu)",
"nginx/1.18.0",
"Microsoft-IIS/10.0",
"cloudflare",
"AmazonS3",
"gunicorn/20.1.0"
] ]
} }