Brute Ratel BRC4

Analyse de malware

Publié le


Introduction

Cet article ne sera pas un rapport d'analyse de malware détaillé sur les fonctionnalités de brute ratel (BRC4) mais plutôt une étude portant sur plusieurs souches d'une même version. Dans cet article nous allons comparer quelques souches, étudier la configuration du C2 et mettre en place un script d'auto-extraction avec IDA python. Ensuite nous examinerons minutieusement la partie chiffrement et déchiffrement des données envoyées au serveur de contrôle. L'objectif ici sera de comprendre ensemble comment le C2 récupère sa configuration et comment les données sont traitées d'un point de vue chiffrement/déchiffrement.

Pour cette étude, nous nous sommes procuré 5 souches de brute ratel sur différentes plateformes comme VT et MalwareBazaar. Ces différentes souches ont été récupérées sans chercher de lien en particulier, mais il s'avère que ce sont toutes des souches d'une même version.

Voici les souches sélectionnées :

  • 1 ➔ d71dc7ba8523947e08c6eec43a726fe75aed248dfd3a7c4f6537224e9ed05f6f
  • 2 ➔ dd8652e2dcfe3f1a72631b3a9585736fbe77ffabee4098f6b3c48e1469bf27aa
  • 3 ➔ 3ad53495851bafc48caf6d2227a434ca2e0bef9ab3bd40abfe4ea8f318d37bbe
  • 4 ➔ 3ed21a4bfcf9838e06ad3058d13d5c28026c17dc996953a22a00f0609b0df3b9
  • 5 ➔ e1a9b35cf1378fda12310f0920c5c53ad461858b3cb575697ea125dfee829611

Pour rappel, Brute Ratel BRC4 est un outil de commande à distance (C2) permettant de prendre le contrôle à distance d'un ou de plusieurs agents. L'agent implanté dans la machine de la victime est capable d'effectuer diverses opérations. Pour obtenir une analyse détaillée des capacités de l'agent, vous pouvez consulter l'étude approfondie réalisée par Vlad Pasca ici.


Analyse et comparaison

Toutes les souches présentent un comportement identique lors du lancement du programme : un ensemble de données est placé dans la pile. Dans la capture ci-dessous, il est clair que la majeure partie du programme est constituée de données stockées dans la pile, en attente d'une exécution ultérieure en mémoire.
Image
On peut noter, à la fin de cette répétition, les valeurs 0x4D et 0x5A, qui correspondent respectivement à M et Z et qui représentent la séquence d'octets indiquant qu'il s'agit bel et bien d'un fichier exécutable.
Image
À partir de ce moment, nous pouvons déjà commencer à comparer les différentes souches, car les premières différences sont observées immédiatement après que le programme soit stocké dans la pile. J'ai départagé les 5 souches en deux parties car certaines d'entre-elles font appel aux fonctions de la WinAPI, tandis que d'autres utilisent des appels système (SYSCALL). Toutes les souches emploient le PEB parsing et l'API hashing pour résoudre des fonctions dynamiquement. Certaines d'entre-elles feront appel avec l'opération CALL [REGISTRE], tandis que d'autres avec l'opération SYSCALL après avoir récupéré l'identifiant correspondant à l'appel système souhaité.

Voici un tableau récapitulatif :
Groupe 1 : Souches API hashing + PEB parsing ➔ CALL [REGISTRE]

  • 1 ➔ d71dc7ba8523947e08c6eec43a726fe75aed248dfd3a7c4f6537224e9ed05f6f
  • 3 ➔ 3ad53495851bafc48caf6d2227a434ca2e0bef9ab3bd40abfe4ea8f318d37bbe
  • 5 ➔ e1a9b35cf1378fda12310f0920c5c53ad461858b3cb575697ea125dfee829611

Groupe 2 : Souches API hashing + PEB parsing ➔ SYSCALL

  • 2 ➔ dd8652e2dcfe3f1a72631b3a9585736fbe77ffabee4098f6b3c48e1469bf27aa
  • 4 ➔ 3ed21a4bfcf9838e06ad3058d13d5c28026c17dc996953a22a00f0609b0df3b9

Résolution des adresses

Les techniques du PEB parsing et de l'API hashing sont utilisées dans ces souches (pour en savoir davantage sur ces deux techniques voici mon précédent article basé sur l'API hashing et le PEB parsing)
Les souches du groupe 1 récupèrent l'adresse de base de kernel32 :
Image
Les souches du groupe 2 récupèrent l'adresse de base de ntdll :
Image
Pour les souches du groupe 1, on observe un parsing permettant de récupérer l'adresse de base du module Kernel32, tandis que pour les souches du groupe 2, nous avons le chargement du module ntdll. Cette différence est due aux différentes méthodes d'appels de fonction : le groupe 1 utilise des appels de fonction de la WinAPI, tandis que le groupe 2 effectue des appels systèmes.

Dans le processus de résolution des adresses, nous observons des hash qui sont placés dans la pile avant l'appel d'une fonction permettant le traitement du hash afin de résoudre une adresse pointant sur une fonction de la WinAPI. Concernant l'identifiant pour les souches du groupe 2, il est récupéré dans une nouvelle fonction se trouvant à la fin du traitement.
Dans la capture ci-dessous, voici un échantillon du code permettant de résoudre dynamiquement les fonctions :
Image
Pour la partie de droite, nous avons deux codes différents suivant la souche que nous analysons.

Voici l'échantillon d'une souche du groupe 1 :
Image
Voici l'échantillon d'une souche du groupe 2 :
Image
On observe une légère différence pour les souches du groupe 2, en effet une nouvelle fonction fait son apparition à la fin du traitement permettant de récupérer l'identifiant d'une fonction du module ntdll.
L'objectif de l'emploi des appels système permet de contourner la sécurité de certains anti-virus / EDR qui basent leur sécurité sur du hooking userland.
La partie de hachage est assez simple, elle consiste simplement en une opération ROR de 0x0D. Il est relativement facile de résoudre les noms des fonctions avec ce modèle de hachage peu élaboré.
Par exemple, nous pouvons extraire la liste complète des noms de fonctions des modules ntdll et kernel32 pour itérer sur chaque nom dans le processus de hachage afin de faire correspondre les hash, ou simplement nous rendre à l'instruction ADD RDI, RBP pour connaître la fonction qui sera appelée puisqu'elle figurera dans RDI.

Voici les fonctions appelées par les souches du groupe 1 :
Image
Voici les fonctions appelées par les souches du groupe 2 :
Image
Pour récupérer l'identifiant qui fait référence à la fonction souhaitée, il faut d'abord résoudre l'adresse pointant vers celle-ci. Ce processus vise à résoudre le bon identifiant, car celui-ci peut varier en fonction des versions de Windows, et une erreur dans cet identifiant peut provoquer un crash du programme, car la mauvaise fonction serait alors appelée avec des arguments incorrects. Ces identifiants sont recensés à travers certains blogs comme celui de j00ru ici.

Pour mieux comprendre comment l'identifiant est récupéré à l'emplacement [pointeur+4], il suffit d'observer la manière dont sont forgées les fonctions.
Voici un échantillon des fonctions NtReadFile et NtAllocateVirtualMemory :
Image


Observation des premiers résultats

Les premières fonctions résolues pour les souches des deux groupes sont VirtualAllocEx et NtAllocateVirtualMemory. Ces fonctions nous indiquent que deux nouvelles zones mémoire vont faire leur apparition puisqu'elles sont appelées à deux reprises. Ces deux zones mémoire contiendront des données. L'une d'entre-elles contiendra le programme et sera exécutée tandis que l'autre zone mémoire contiendra la configuration du C2.
Voici un échantillon du groupe 1 permettant de constater la création des deux zones mémoires :
Image
Voici un échantillon du groupe 1 permettant de constater l'injection des données dans ces deux zones mémoires :
Image


Configuration et script

Dans cette section, nous allons examiner comment automatiser la récupération de la configuration à l'aide d'IDA et de Python. Il est essentiel de noter que la configuration est injectée dans une zone mémoire à l'adresse 0x20000, quel que soit le variant utilisé.
Voici les étapes de notre script :
1. Nous énumérons toutes les instructions des fonctions reconnues par IDA et ajoutons 1 à la valeur de l'adresse de la dernière instruction trouvée. Tout ceci est fait dans le but d'atteindre la première instruction de la dernière fonction visée, qui ne figure pas dans la liste des fonctions reconnues par IDA :

import idc
import idaapi
import time

print(
    """###############
#    START    #
###############
"""
)

for func in idautils.Functions():
    for instruction in idautils.Heads(func, idc.get_func_attr(func, idc.FUNCATTR_END)):
        True

instruction += 1
start_address = instruction

 
2. Nous ajoutons toutes les adresses et leurs instructions à la liste "instructions" afin de parcourir cette liste ultérieurement pour rechercher toutes les instructions contenant les termes call ou syscall :

instructions = []
for i, addr in enumerate(idautils.Heads(start_address, ida_ida.cvar.inf.max_ea)):
    mnem = idaapi.ua_mnem(addr)
    operand = idc.print_operand(addr, 0)
    operand2 = idc.print_operand(addr, 1)
    if operand2 != "":
        instructions.append(hex(addr) + ": " + mnem + " " + operand + ", " + operand2)
    elif mnem == "retn":
        break
    else:
        instructions.append(hex(addr) + ": " + mnem + " " + operand)

call_func = []
for i in instructions:
    if "call" in i:
        if "syscall" in i.split(": ")[1]:
            call_func.append(i.split(":")[0])
        elif len(i.split(": ")[1]) == 8:
            call_func.append(i.split(":")[0])

 
3. Nous récupérons l'adresse de la dernière instruction CALL ou SYSCALL afin de l'ajouter en tant que point d'arrêt avant la configuration et le lancement du débogueur :

addr_to_break = call_func[-1]
time.sleep(2)

idaapi.add_bpt(int(addr_to_break, base=16), 0, idaapi.BPT_DEFAULT)
idaapi.load_debugger("windbg", 1)
idaapi.start_process(ida_nalt.get_input_file_path(), "", "")
idaapi.continue_process()

 
4. Nous récupérons la valeur du registre RSP pour énumérer les adresses qu'il contient, dans le but de rechercher l'adresse 0x20000 qui contient la configuration du C2 :

ida_dbg.wait_for_next_event(ida_dbg.WFNE_SUSP, -1)
rsp_value = idaapi.get_reg_val("RSP")

addr = 0
while hex(addr) != "0x20000":
    rsp_value += 8
    addr = idaapi.get_bytes(rsp_value, 8)
    addr = struct.unpack("<Q", addr)[0]
    print(hex(addr))

print(idaapi.get_bytes(addr, 256))
idaapi.exit_process()
print(
    """#############
#    END    #
#############
"""
)

 
Voici le code complet :

import idautils
import idc
import idaapi
import time

print(
    """###############
#    START    #
###############
"""
)

for func in idautils.Functions():
    for instruction in idautils.Heads(func, idc.get_func_attr(func, idc.FUNCATTR_END)):
        True

instruction += 1
start_address = instruction
instructions = []

for i, addr in enumerate(idautils.Heads(start_address, ida_ida.cvar.inf.max_ea)):
    mnem = idaapi.ua_mnem(addr)
    operand = idc.print_operand(addr, 0)
    operand2 = idc.print_operand(addr, 1)
    if operand2 != "":
        instructions.append(hex(addr) + ": " + mnem + " " + operand + ", " + operand2)
    elif mnem == "retn":
        break
    else:
        instructions.append(hex(addr) + ": " + mnem + " " + operand)

call_func = []
for i in instructions:
    if "call" in i:
        if "syscall" in i.split(": ")[1]:
            call_func.append(i.split(":")[0])
        elif len(i.split(": ")[1]) == 8:
            call_func.append(i.split(":")[0])

addr_to_break = call_func[-1]
time.sleep(2)

idaapi.add_bpt(int(addr_to_break, base=16), 0, idaapi.BPT_DEFAULT)
idaapi.load_debugger("windbg", 1)
idaapi.start_process(ida_nalt.get_input_file_path(), "", "")
idaapi.continue_process()
ida_dbg.wait_for_next_event(ida_dbg.WFNE_SUSP, -1)
rsp_value = idaapi.get_reg_val("RSP")

addr = 0
while hex(addr) != "0x20000":
    rsp_value += 8
    addr = idaapi.get_bytes(rsp_value, 8)
    addr = struct.unpack("<Q", addr)[0]
    print(hex(addr))

print(idaapi.get_bytes(addr, 256))
idaapi.exit_process()
print(
    """#############
#    END    #
#############
"""
)

 
Voici le résultat obtenu sur un échantillon du groupe 1 :
Image
Voici le résultat obtenu sur un échantillon du groupe 2 :
Image
Sur certaines souches, nous observons une configuration différente où le contenu est chiffré en RC4, puis encodé en base64. Le mot de passe nécessaire pour déchiffrer le contenu RC4 se trouve dans le programme injecté en mémoire. Ce mot de passe peut être récupéré en examinant les chaînes de caractères ou en poursuivant l'analyse dynamique jusqu'au moment du déchiffrement de la configuration.

Voici le résultat obtenu avec un échantillon présentant une configuration différente :
Image
Afin d'identifier la clé, il est nécessaire de se rendre dans la fonction traitant la configuration. Pour une souche avec une configuration non chiffrée, cette fonction sert simplement à récupérer chaque élément de la configuration en vue d'une utilisation ultérieure. En revanche, pour une souche avec une configuration chiffrée, nous observons au début de cette fonction deux appels de fonction distincts.
La première fonction est utilisée pour décoder la configuration en base64, tandis que la deuxième fonction permet de déchiffrer le résultat à l'aide de la clé placée sur la pile lors de l'appel. Cette fonction représente une implémentation de RC4 pour le déchiffrement.

Considérons cette capture pour étayer notre analyse :
Image
Voici la configuration de cette souche déchiffrée :
Image


Chiffrement / Déchiffrement

Cette dernière section de l'article se concentre sur le traitement des données envoyées et reçues par l'agent BRC4, dans le but de développer un programme Python qui nous permettra de chiffrer et déchiffrer les requêtes BRC4. Cette partie peut être divisée en trois étapes distinctes :

  • - L'étape de génération de la clé de chiffrement/déchiffrement
  • - L'étape du chiffrement
  • - L'étape du déchiffrement


Dans la configuration, nous trouvons un mot de passe, qui sert de clé pour générer une clé plus longue. La clé et la clé longue seront utilisées pour le chiffrement et le déchiffrement des données.

Voici un visuel de la partie chiffrement :
Image

Voici un visuel de la partie déchiffrement :
Image
Un élément commun à ces deux parties est la fonction responsable de la génération de la clé longue, qui est essentielle pour les deux cas d'utilisation. Cette fonction effectue plusieurs opérations, commençant par définir la taille de la clé à 16 caractères. Cela signifie que si la clé d'origine fait moins de 16 caractères, elle sera remplie avec la valeur 0x00 jusqu'à atteindre une taille de 16 caractères. Ensuite, cette clé subit une opération XOR avec une clé courte générée à partir de la clé d'origine et de valeurs constantes.

Voici le processus illustré dans la capture ci-dessous :
Image
Voici la partie du code python reproduisant cette section du programme analysée :

if __name__ == "__main__":

    parser = argparse.ArgumentParser(description="Encrypt or decrypt data of BRC4.")
    parser.add_argument("choice", help="d for decrypt or e for encrypt")
    parser.add_argument("offset", help="offset for encryption/decryption")
    parser.add_argument("mykey", help="key for encryption/decryption")
    args = parser.parse_args()
    offset = int(args.offset)
    mykey = [ord(i) for i in args.mykey]
    if len(mykey) < 16:
        for i in range(16 - len(mykey)):
            mykey.append(0x00)

    values1 = [ 
        0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 
        0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 
        0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 
        0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 
        0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 
        0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 
        0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 
        0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 
        0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 
        0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 
        0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 
        0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 
        0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 
        0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 
        0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 
        0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 
    ]

    values2 = [ 
        0x8D, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A, 
        0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A, 0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39, 
        0x72, 0xE4, 0xD3, 0xBD, 0x61, 0xC2, 0x9F, 0x25, 0x4A, 0x94, 0x33, 0x66, 0xCC, 0x83, 0x1D, 0x3A, 
        0x74, 0xE8, 0xCB, 0x8D, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C, 0xD8, 
        0xAB, 0x4D, 0x9A, 0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A, 0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 
        0xC5, 0x91, 0x39, 0x72, 0xE4, 0xD3, 0xBD, 0x61, 0xC2, 0x9F, 0x25, 0x4A, 0x94, 0x33, 0x66, 0xCC, 
        0x83, 0x1D, 0x3A, 0x74, 0xE8, 0xCB, 0x8D, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 
        0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A, 0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A, 0xD4, 0xB3, 
        0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39, 0x72, 0xE4, 0xD3, 0xBD, 0x61, 0xC2, 0x9F, 0x25, 0x4A, 0x94, 
        0x33, 0x66, 0xCC, 0x83, 0x1D, 0x3A, 0x74, 0xE8, 0xCB, 0x8D, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 
        0x40, 0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A, 0x2F, 0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 
        0x6A, 0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39, 0x72, 0xE4, 0xD3, 0xBD, 0x61, 0xC2, 0x9F, 
        0x25, 0x4A, 0x94, 0x33, 0x66, 0xCC, 0x83, 0x1D, 0x3A, 0x74, 0xE8, 0xCB, 0x8D, 0x01, 0x02, 0x04, 
        0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A, 0x2F, 0x5E, 0xBC, 0x63, 
        0xC6, 0x97, 0x35, 0x6A, 0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91, 0x39, 0x72, 0xE4, 0xD3, 0xBD, 
        0x61, 0xC2, 0x9F, 0x25, 0x4A, 0x94, 0x33, 0x66, 0xCC, 0x83, 0x1D, 0x3A, 0x74, 0xE8, 0xCB, 0x8D, 
        0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 
        0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 
        0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 
        0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 
        0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 
        0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 
        0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 
        0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 
        0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 
        0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 
        0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 
        0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 
        0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 
        0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 
        0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 
        0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 
    ]

    values1 = [(i + offset & 0xFF) for i in values1]
    values2 = [(i + offset & 0xFF) for i in values2]

    mypass = []
    for i in mykey:
        mypass.append(i)

    edx = 1

    keytmp = [i for i in mypass[-4:]]
    key = [0, 0, 0, 0]
    key[0] = keytmp[1]
    key[1] = keytmp[2]
    key[2] = keytmp[3]
    key[3] = keytmp[0]
    newkey = []
    newkey.append(values1[key[0]])
    newkey.append(values1[key[1]])
    newkey.append(values1[key[2]])
    newkey.append(values1[key[3]])
    newkey[0] = values1[key[0]] ^ values2[edx]
    key = newkey

    long_pass = []

    i = 0
    z = 0
    zz = 0

    while z <= 0xB0:
        if z != 0:
            if z == 160:
                save_mypass = []
                for x in range(len(mypass)):
                    save_mypass.append(mypass[x])

            if z == 156:
                save_key = []
                for x in range(len(key)):
                    save_key.append(key[x])

        for j in range(0x04):
            tmp = mypass[i] ^ key[j]
            mypass[i] = tmp
            key[j] = tmp
            if z < 160:
                long_pass.append(tmp)
            i += 1
            z += 1
            zz += 1
            if i == 0x10:
                i = 0

        if zz == 16:
            zz = 0
            edx += 1

            keytmp = []
            for l in range(4):
                keytmp.append(key[l])
            key[0] = keytmp[1]
            key[1] = keytmp[2]
            key[2] = keytmp[3]
            key[3] = keytmp[0]
            newkey = []
            newkey.append(values1[key[0]])
            newkey.append(values1[key[1]])
            newkey.append(values1[key[2]])
            newkey.append(values1[key[3]])
            newkey[0] = values1[key[0]] ^ values2[edx]
            key = newkey

    key = []
    for i in mykey:
        key.append(i)

    long_data_with_pass = key + long_pass

    if args.choice in ["e", "encrypt"]:
        # insert json in data
        data = ""
        encryptSring(data, key, values1, long_data_with_pass, long_pass, offset)

    elif args.choice in ["d", "decrypt"]:
        # insert base64 value in data
        data = ""
        decryptSring(data, key, long_data_with_pass, long_pass, offset)

 
Voici la partie du code python reproduisant le chiffrement des données :

import base64
import argparse
import sys

def encryptSring(data, key, values1, long_data_with_pass, long_pass, offset):
    data = [i for i in data]
    encryptString = []

    for i in range(16):
        if ((len(data) + i) % 16) == 0:
            len_max = len(data) + i
            break

    for j in range(i):
        if j == 0:
            data.append(0x0D)
        elif j == 1:
            data.append(0x0A)
        else:
            data.append(0)

    T = 0
    while T < (len_max / 16):
        newpass = []
        for i in range(16):
            try:
                tmp = ord(data[(16 * T) + i]) ^ key[i]
            except TypeError:
                tmp = data[(16 * T) + i] ^ key[i]
            newpass.append(tmp)

        tempo_pass = []
        for i in range(0xA0, len(long_data_with_pass)):
            tempo_pass.append(long_data_with_pass[i])

            ##############
            # START LOOP #
            ##############

        count = 0
        while count < 9:
            for i in range(16):
                tmp = newpass[i]
                tmp = values1[tmp]
                newpass[i] = tmp

            tmppass = []
            for i in [0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11]:
                tmppass.append(newpass[i])

            valueEngine1 = [ 
                0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x1E, 
                0x20, 0x22, 0x24, 0x26, 0x28, 0x2A, 0x2C, 0x2E, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3A, 0x3C, 0x3E, 
                0x40, 0x42, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5A, 0x5C, 0x5E, 
                0x60, 0x62, 0x64, 0x66, 0x68, 0x6A, 0x6C, 0x6E, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E, 
                0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8E, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E, 
                0xA0, 0xA2, 0xA4, 0xA6, 0xA8, 0xAA, 0xAC, 0xAE, 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBC, 0xBE, 
                0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, 
                0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, 0xF0, 0xF2, 0xF4, 0xF6, 0xF8, 0xFA, 0xFC, 0xFE, 
                0x1B, 0x19, 0x1F, 0x1D, 0x13, 0x11, 0x17, 0x15, 0x0B, 0x09, 0x0F, 0x0D, 0x03, 0x01, 0x07, 0x05, 
                0x3B, 0x39, 0x3F, 0x3D, 0x33, 0x31, 0x37, 0x35, 0x2B, 0x29, 0x2F, 0x2D, 0x23, 0x21, 0x27, 0x25, 
                0x5B, 0x59, 0x5F, 0x5D, 0x53, 0x51, 0x57, 0x55, 0x4B, 0x49, 0x4F, 0x4D, 0x43, 0x41, 0x47, 0x45, 
                0x7B, 0x79, 0x7F, 0x7D, 0x73, 0x71, 0x77, 0x75, 0x6B, 0x69, 0x6F, 0x6D, 0x63, 0x61, 0x67, 0x65, 
                0x9B, 0x99, 0x9F, 0x9D, 0x93, 0x91, 0x97, 0x95, 0x8B, 0x89, 0x8F, 0x8D, 0x83, 0x81, 0x87, 0x85, 
                0xBB, 0xB9, 0xBF, 0xBD, 0xB3, 0xB1, 0xB7, 0xB5, 0xAB, 0xA9, 0xAF, 0xAD, 0xA3, 0xA1, 0xA7, 0xA5, 
                0xDB, 0xD9, 0xDF, 0xDD, 0xD3, 0xD1, 0xD7, 0xD5, 0xCB, 0xC9, 0xCF, 0xCD, 0xC3, 0xC1, 0xC7, 0xC5, 
                0xFB, 0xF9, 0xFF, 0xFD, 0xF3, 0xF1, 0xF7, 0xF5, 0xEB, 0xE9, 0xEF, 0xED, 0xE3, 0xE1, 0xE7, 0xE5 
            ]
                
            valueEngine2 = [ 
                0x00, 0x03, 0x06, 0x05, 0x0C, 0x0F, 0x0A, 0x09, 0x18, 0x1B, 0x1E, 0x1D, 0x14, 0x17, 0x12, 0x11, 
                0x30, 0x33, 0x36, 0x35, 0x3C, 0x3F, 0x3A, 0x39, 0x28, 0x2B, 0x2E, 0x2D, 0x24, 0x27, 0x22, 0x21, 
                0x60, 0x63, 0x66, 0x65, 0x6C, 0x6F, 0x6A, 0x69, 0x78, 0x7B, 0x7E, 0x7D, 0x74, 0x77, 0x72, 0x71, 
                0x50, 0x53, 0x56, 0x55, 0x5C, 0x5F, 0x5A, 0x59, 0x48, 0x4B, 0x4E, 0x4D, 0x44, 0x47, 0x42, 0x41, 
                0xC0, 0xC3, 0xC6, 0xC5, 0xCC, 0xCF, 0xCA, 0xC9, 0xD8, 0xDB, 0xDE, 0xDD, 0xD4, 0xD7, 0xD2, 0xD1, 
                0xF0, 0xF3, 0xF6, 0xF5, 0xFC, 0xFF, 0xFA, 0xF9, 0xE8, 0xEB, 0xEE, 0xED, 0xE4, 0xE7, 0xE2, 0xE1, 
                0xA0, 0xA3, 0xA6, 0xA5, 0xAC, 0xAF, 0xAA, 0xA9, 0xB8, 0xBB, 0xBE, 0xBD, 0xB4, 0xB7, 0xB2, 0xB1, 
                0x90, 0x93, 0x96, 0x95, 0x9C, 0x9F, 0x9A, 0x99, 0x88, 0x8B, 0x8E, 0x8D, 0x84, 0x87, 0x82, 0x81, 
                0x9B, 0x98, 0x9D, 0x9E, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8F, 0x8C, 0x89, 0x8A, 
                0xAB, 0xA8, 0xAD, 0xAE, 0xA7, 0xA4, 0xA1, 0xA2, 0xB3, 0xB0, 0xB5, 0xB6, 0xBF, 0xBC, 0xB9, 0xBA, 
                0xFB, 0xF8, 0xFD, 0xFE, 0xF7, 0xF4, 0xF1, 0xF2, 0xE3, 0xE0, 0xE5, 0xE6, 0xEF, 0xEC, 0xE9, 0xEA, 
                0xCB, 0xC8, 0xCD, 0xCE, 0xC7, 0xC4, 0xC1, 0xC2, 0xD3, 0xD0, 0xD5, 0xD6, 0xDF, 0xDC, 0xD9, 0xDA, 
                0x5B, 0x58, 0x5D, 0x5E, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4F, 0x4C, 0x49, 0x4A, 
                0x6B, 0x68, 0x6D, 0x6E, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7F, 0x7C, 0x79, 0x7A, 
                0x3B, 0x38, 0x3D, 0x3E, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2F, 0x2C, 0x29, 0x2A, 
                0x0B, 0x08, 0x0D, 0x0E, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1F, 0x1C, 0x19, 0x1A 
            ]

            valueEngine1 = [(i + offset & 0xFF) for i in valueEngine1]
            valueEngine2 = [(i + offset & 0xFF) for i in valueEngine2]

            v42 = [0] * 16

            v42[0] = (
                valueEngine2[tmppass[1]]
                ^ valueEngine1[tmppass[0]]
                ^ tmppass[3]
                ^ tmppass[2]
            )
            v42[1] = valueEngine2[tmppass[2]] ^ (
                valueEngine1[tmppass[1]] ^ tmppass[3] ^ tmppass[0]
            )
            v42[2] = valueEngine2[tmppass[3]] ^ (
                valueEngine1[tmppass[2]] ^ (tmppass[1] ^ tmppass[0])
            )
            v42[3] = valueEngine1[tmppass[3]] ^ (
                valueEngine2[tmppass[0]] ^ (tmppass[2] ^ tmppass[1])
            )
            v42[4] = (
                valueEngine2[tmppass[5]]
                ^ valueEngine1[tmppass[4]]
                ^ tmppass[7]
                ^ tmppass[6]
            )
            v42[5] = (
                valueEngine2[tmppass[6]]
                ^ valueEngine1[tmppass[5]]
                ^ tmppass[7]
                ^ tmppass[4]
            )
            v42[6] = valueEngine2[tmppass[7]] ^ (
                valueEngine1[tmppass[6]] ^ tmppass[5] ^ tmppass[4]
            )
            v42[7] = (
                valueEngine1[tmppass[7]]
                ^ valueEngine2[tmppass[4]]
                ^ tmppass[6]
                ^ tmppass[5]
            )
            v42[8] = (
                valueEngine2[tmppass[9]]
                ^ valueEngine1[tmppass[8]]
                ^ tmppass[11]
                ^ tmppass[10]
            )
            v42[9] = (
                valueEngine2[tmppass[10]]
                ^ valueEngine1[tmppass[9]]
                ^ tmppass[11]
                ^ tmppass[8]
            )
            v42[10] = (
                valueEngine2[tmppass[11]]
                ^ valueEngine1[tmppass[10]]
                ^ tmppass[9]
                ^ tmppass[8]
            )
            v42[11] = valueEngine1[tmppass[11]] ^ (
                valueEngine2[tmppass[8]] ^ (tmppass[10] ^ tmppass[9])
            )
            v42[12] = (
                valueEngine2[tmppass[13]]
                ^ valueEngine1[tmppass[12]]
                ^ tmppass[15]
                ^ tmppass[14]
            )
            v42[13] = (
                valueEngine2[tmppass[14]]
                ^ valueEngine1[tmppass[13]]
                ^ tmppass[15]
                ^ tmppass[12]
            )
            v42[14] = valueEngine2[tmppass[15]] ^ (
                valueEngine1[tmppass[14]] ^ tmppass[13] ^ tmppass[12]
            )
            v42[15] = (
                valueEngine1[tmppass[15]]
                ^ valueEngine2[tmppass[12]]
                ^ tmppass[14]
                ^ tmppass[13]
            )

            for i in range(16):
                tmppass[i] = v42[i]

            tmp_long_pass = []

            for i in range(count * 16, (count + 1) * 16):
                tmp_long_pass.append(long_pass[i])

            for i in range(16):
                tmp = tmppass[i] ^ tmp_long_pass[i]
                tmppass[i] = tmp

            for i in range(16):
                newpass[i] = tmppass[i]
            count += 1
            ##############
            #  END LOOP  #
            ##############

        for i in range(16):
            tmp = tmppass[i]
            tmp = values1[tmp]
            newpass[i] = tmp

        tmppass = []
        for i in [0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11]:
            tmppass.append(newpass[i])

        for i in range(16):
            tmp = tmppass[i] ^ tempo_pass[i]
            tmppass[i] = tmp

        for i in range(16):
            encryptString.append(tmppass[i])

        T += 1

    hex_bytes = []
    for hex_str in encryptString:
        hex_byte = hex_str.to_bytes(1, "big")
        hex_bytes.append(hex_byte)

    b64encryptredString = base64.b64encode(b"".join(hex_bytes)).decode()
    print("\n\nEncrypted data : \n", b64encryptredString)

 
Voici la partie du code python reproduisant le déchiffrement des données :

def decryptSring(data, key, long_data_with_pass, long_pass, offset):
    decryptSrings = ""
    data = base64.b64decode(data)

    len_max = len(data)

    for i in range(16):
        if (len_max + i) % 16 == 0:
            break
        else:
            data += b"\x00"

    tempo_pass = []
    for i in range(0xA0, len(long_data_with_pass)):
        tempo_pass.append(long_data_with_pass[i])

    T = 0
    while T < (len_max / 16):
        newpass = []
        for i in range(16):
            tmp = data[(16 * T) + i] ^ tempo_pass[i]
            newpass.append(tmp)

        tmppass = []
        for i in [0, 0x0D, 0xA, 7, 4, 1, 0x0E, 0x0B, 8, 5, 2, 0x0F, 0x0C, 9, 6, 3]:
            tmppass.append(newpass[i])

        values3 = [
            0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
            0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
            0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
            0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
            0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
            0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
            0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
            0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
            0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
            0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
            0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
            0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
            0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
            0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
            0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
            0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
        ]
        values3 = [(i + offset & 0xFF) for i in values3]

        for i in range(16):
            tmp = tmppass[i]
            tmp = values3[tmp]
            newpass[i] = tmp

            ##############
            # START LOOP #
            ##############

        count = 9
        while count > 0:
            tmp_long_pass = []
            for i in range((count - 1) * 16, count * 16):
                tmp_long_pass.append(long_pass[i])

            for i in range(16):
                tmp = newpass[i] ^ tmp_long_pass[i]
                newpass[i] = tmp

            valueEngine1 = [
                0x00, 0x0E, 0x1C, 0x12, 0x38, 0x36, 0x24, 0x2A, 0x70, 0x7E, 0x6C, 0x62, 0x48, 0x46, 0x54, 0x5A,
                0xE0, 0xEE, 0xFC, 0xF2, 0xD8, 0xD6, 0xC4, 0xCA, 0x90, 0x9E, 0x8C, 0x82, 0xA8, 0xA6, 0xB4, 0xBA,
                0xDB, 0xD5, 0xC7, 0xC9, 0xE3, 0xED, 0xFF, 0xF1, 0xAB, 0xA5, 0xB7, 0xB9, 0x93, 0x9D, 0x8F, 0x81,
                0x3B, 0x35, 0x27, 0x29, 0x03, 0x0D, 0x1F, 0x11, 0x4B, 0x45, 0x57, 0x59, 0x73, 0x7D, 0x6F, 0x61,
                0xAD, 0xA3, 0xB1, 0xBF, 0x95, 0x9B, 0x89, 0x87, 0xDD, 0xD3, 0xC1, 0xCF, 0xE5, 0xEB, 0xF9, 0xF7,
                0x4D, 0x43, 0x51, 0x5F, 0x75, 0x7B, 0x69, 0x67, 0x3D, 0x33, 0x21, 0x2F, 0x05, 0x0B, 0x19, 0x17,
                0x76, 0x78, 0x6A, 0x64, 0x4E, 0x40, 0x52, 0x5C, 0x06, 0x08, 0x1A, 0x14, 0x3E, 0x30, 0x22, 0x2C,
                0x96, 0x98, 0x8A, 0x84, 0xAE, 0xA0, 0xB2, 0xBC, 0xE6, 0xE8, 0xFA, 0xF4, 0xDE, 0xD0, 0xC2, 0xCC,
                0x41, 0x4F, 0x5D, 0x53, 0x79, 0x77, 0x65, 0x6B, 0x31, 0x3F, 0x2D, 0x23, 0x09, 0x07, 0x15, 0x1B,
                0xA1, 0xAF, 0xBD, 0xB3, 0x99, 0x97, 0x85, 0x8B, 0xD1, 0xDF, 0xCD, 0xC3, 0xE9, 0xE7, 0xF5, 0xFB,
                0x9A, 0x94, 0x86, 0x88, 0xA2, 0xAC, 0xBE, 0xB0, 0xEA, 0xE4, 0xF6, 0xF8, 0xD2, 0xDC, 0xCE, 0xC0,
                0x7A, 0x74, 0x66, 0x68, 0x42, 0x4C, 0x5E, 0x50, 0x0A, 0x04, 0x16, 0x18, 0x32, 0x3C, 0x2E, 0x20,
                0xEC, 0xE2, 0xF0, 0xFE, 0xD4, 0xDA, 0xC8, 0xC6, 0x9C, 0x92, 0x80, 0x8E, 0xA4, 0xAA, 0xB8, 0xB6,
                0x0C, 0x02, 0x10, 0x1E, 0x34, 0x3A, 0x28, 0x26, 0x7C, 0x72, 0x60, 0x6E, 0x44, 0x4A, 0x58, 0x56,
                0x37, 0x39, 0x2B, 0x25, 0x0F, 0x01, 0x13, 0x1D, 0x47, 0x49, 0x5B, 0x55, 0x7F, 0x71, 0x63, 0x6D,
                0xD7, 0xD9, 0xCB, 0xC5, 0xEF, 0xE1, 0xF3, 0xFD, 0xA7, 0xA9, 0xBB, 0xB5, 0x9F, 0x91, 0x83, 0x8D
            ]
               
            valueEngine2 = [ 
                0x00, 0x0D, 0x1A, 0x17, 0x34, 0x39, 0x2E, 0x23, 0x68, 0x65, 0x72, 0x7F, 0x5C, 0x51, 0x46, 0x4B,
                0xD0, 0xDD, 0xCA, 0xC7, 0xE4, 0xE9, 0xFE, 0xF3, 0xB8, 0xB5, 0xA2, 0xAF, 0x8C, 0x81, 0x96, 0x9B,
                0xBB, 0xB6, 0xA1, 0xAC, 0x8F, 0x82, 0x95, 0x98, 0xD3, 0xDE, 0xC9, 0xC4, 0xE7, 0xEA, 0xFD, 0xF0,
                0x6B, 0x66, 0x71, 0x7C, 0x5F, 0x52, 0x45, 0x48, 0x03, 0x0E, 0x19, 0x14, 0x37, 0x3A, 0x2D, 0x20,
                0x6D, 0x60, 0x77, 0x7A, 0x59, 0x54, 0x43, 0x4E, 0x05, 0x08, 0x1F, 0x12, 0x31, 0x3C, 0x2B, 0x26,
                0xBD, 0xB0, 0xA7, 0xAA, 0x89, 0x84, 0x93, 0x9E, 0xD5, 0xD8, 0xCF, 0xC2, 0xE1, 0xEC, 0xFB, 0xF6,
                0xD6, 0xDB, 0xCC, 0xC1, 0xE2, 0xEF, 0xF8, 0xF5, 0xBE, 0xB3, 0xA4, 0xA9, 0x8A, 0x87, 0x90, 0x9D,
                0x06, 0x0B, 0x1C, 0x11, 0x32, 0x3F, 0x28, 0x25, 0x6E, 0x63, 0x74, 0x79, 0x5A, 0x57, 0x40, 0x4D,
                0xDA, 0xD7, 0xC0, 0xCD, 0xEE, 0xE3, 0xF4, 0xF9, 0xB2, 0xBF, 0xA8, 0xA5, 0x86, 0x8B, 0x9C, 0x91,
                0x0A, 0x07, 0x10, 0x1D, 0x3E, 0x33, 0x24, 0x29, 0x62, 0x6F, 0x78, 0x75, 0x56, 0x5B, 0x4C, 0x41,
                0x61, 0x6C, 0x7B, 0x76, 0x55, 0x58, 0x4F, 0x42, 0x09, 0x04, 0x13, 0x1E, 0x3D, 0x30, 0x27, 0x2A,
                0xB1, 0xBC, 0xAB, 0xA6, 0x85, 0x88, 0x9F, 0x92, 0xD9, 0xD4, 0xC3, 0xCE, 0xED, 0xE0, 0xF7, 0xFA,
                0xB7, 0xBA, 0xAD, 0xA0, 0x83, 0x8E, 0x99, 0x94, 0xDF, 0xD2, 0xC5, 0xC8, 0xEB, 0xE6, 0xF1, 0xFC,
                0x67, 0x6A, 0x7D, 0x70, 0x53, 0x5E, 0x49, 0x44, 0x0F, 0x02, 0x15, 0x18, 0x3B, 0x36, 0x21, 0x2C,
                0x0C, 0x01, 0x16, 0x1B, 0x38, 0x35, 0x22, 0x2F, 0x64, 0x69, 0x7E, 0x73, 0x50, 0x5D, 0x4A, 0x47,
                0xDC, 0xD1, 0xC6, 0xCB, 0xE8, 0xE5, 0xF2, 0xFF, 0xB4, 0xB9, 0xAE, 0xA3, 0x80, 0x8D, 0x9A, 0x97 
            ]
                
            valueEngine3 = [ 
                0x00, 0x09, 0x12, 0x1B, 0x24, 0x2D, 0x36, 0x3F, 0x48, 0x41, 0x5A, 0x53, 0x6C, 0x65, 0x7E, 0x77,
                0x90, 0x99, 0x82, 0x8B, 0xB4, 0xBD, 0xA6, 0xAF, 0xD8, 0xD1, 0xCA, 0xC3, 0xFC, 0xF5, 0xEE, 0xE7,
                0x3B, 0x32, 0x29, 0x20, 0x1F, 0x16, 0x0D, 0x04, 0x73, 0x7A, 0x61, 0x68, 0x57, 0x5E, 0x45, 0x4C,
                0xAB, 0xA2, 0xB9, 0xB0, 0x8F, 0x86, 0x9D, 0x94, 0xE3, 0xEA, 0xF1, 0xF8, 0xC7, 0xCE, 0xD5, 0xDC,
                0x76, 0x7F, 0x64, 0x6D, 0x52, 0x5B, 0x40, 0x49, 0x3E, 0x37, 0x2C, 0x25, 0x1A, 0x13, 0x08, 0x01,
                0xE6, 0xEF, 0xF4, 0xFD, 0xC2, 0xCB, 0xD0, 0xD9, 0xAE, 0xA7, 0xBC, 0xB5, 0x8A, 0x83, 0x98, 0x91,
                0x4D, 0x44, 0x5F, 0x56, 0x69, 0x60, 0x7B, 0x72, 0x05, 0x0C, 0x17, 0x1E, 0x21, 0x28, 0x33, 0x3A,
                0xDD, 0xD4, 0xCF, 0xC6, 0xF9, 0xF0, 0xEB, 0xE2, 0x95, 0x9C, 0x87, 0x8E, 0xB1, 0xB8, 0xA3, 0xAA,
                0xEC, 0xE5, 0xFE, 0xF7, 0xC8, 0xC1, 0xDA, 0xD3, 0xA4, 0xAD, 0xB6, 0xBF, 0x80, 0x89, 0x92, 0x9B,
                0x7C, 0x75, 0x6E, 0x67, 0x58, 0x51, 0x4A, 0x43, 0x34, 0x3D, 0x26, 0x2F, 0x10, 0x19, 0x02, 0x0B,
                0xD7, 0xDE, 0xC5, 0xCC, 0xF3, 0xFA, 0xE1, 0xE8, 0x9F, 0x96, 0x8D, 0x84, 0xBB, 0xB2, 0xA9, 0xA0,
                0x47, 0x4E, 0x55, 0x5C, 0x63, 0x6A, 0x71, 0x78, 0x0F, 0x06, 0x1D, 0x14, 0x2B, 0x22, 0x39, 0x30,
                0x9A, 0x93, 0x88, 0x81, 0xBE, 0xB7, 0xAC, 0xA5, 0xD2, 0xDB, 0xC0, 0xC9, 0xF6, 0xFF, 0xE4, 0xED,
                0x0A, 0x03, 0x18, 0x11, 0x2E, 0x27, 0x3C, 0x35, 0x42, 0x4B, 0x50, 0x59, 0x66, 0x6F, 0x74, 0x7D,
                0xA1, 0xA8, 0xB3, 0xBA, 0x85, 0x8C, 0x97, 0x9E, 0xE9, 0xE0, 0xFB, 0xF2, 0xCD, 0xC4, 0xDF, 0xD6,
                0x31, 0x38, 0x23, 0x2A, 0x15, 0x1C, 0x07, 0x0E, 0x79, 0x70, 0x6B, 0x62, 0x5D, 0x54, 0x4F, 0x46
            ]   
                
            valueEngine4 = [ 
                0x00, 0x0B, 0x16, 0x1D, 0x2C, 0x27, 0x3A, 0x31, 0x58, 0x53, 0x4E, 0x45, 0x74, 0x7F, 0x62, 0x69,
                0xB0, 0xBB, 0xA6, 0xAD, 0x9C, 0x97, 0x8A, 0x81, 0xE8, 0xE3, 0xFE, 0xF5, 0xC4, 0xCF, 0xD2, 0xD9,
                0x7B, 0x70, 0x6D, 0x66, 0x57, 0x5C, 0x41, 0x4A, 0x23, 0x28, 0x35, 0x3E, 0x0F, 0x04, 0x19, 0x12,
                0xCB, 0xC0, 0xDD, 0xD6, 0xE7, 0xEC, 0xF1, 0xFA, 0x93, 0x98, 0x85, 0x8E, 0xBF, 0xB4, 0xA9, 0xA2,
                0xF6, 0xFD, 0xE0, 0xEB, 0xDA, 0xD1, 0xCC, 0xC7, 0xAE, 0xA5, 0xB8, 0xB3, 0x82, 0x89, 0x94, 0x9F,
                0x46, 0x4D, 0x50, 0x5B, 0x6A, 0x61, 0x7C, 0x77, 0x1E, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2F,
                0x8D, 0x86, 0x9B, 0x90, 0xA1, 0xAA, 0xB7, 0xBC, 0xD5, 0xDE, 0xC3, 0xC8, 0xF9, 0xF2, 0xEF, 0xE4,
                0x3D, 0x36, 0x2B, 0x20, 0x11, 0x1A, 0x07, 0x0C, 0x65, 0x6E, 0x73, 0x78, 0x49, 0x42, 0x5F, 0x54,
                0xF7, 0xFC, 0xE1, 0xEA, 0xDB, 0xD0, 0xCD, 0xC6, 0xAF, 0xA4, 0xB9, 0xB2, 0x83, 0x88, 0x95, 0x9E,
                0x47, 0x4C, 0x51, 0x5A, 0x6B, 0x60, 0x7D, 0x76, 0x1F, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2E,
                0x8C, 0x87, 0x9A, 0x91, 0xA0, 0xAB, 0xB6, 0xBD, 0xD4, 0xDF, 0xC2, 0xC9, 0xF8, 0xF3, 0xEE, 0xE5,
                0x3C, 0x37, 0x2A, 0x21, 0x10, 0x1B, 0x06, 0x0D, 0x64, 0x6F, 0x72, 0x79, 0x48, 0x43, 0x5E, 0x55,
                0x01, 0x0A, 0x17, 0x1C, 0x2D, 0x26, 0x3B, 0x30, 0x59, 0x52, 0x4F, 0x44, 0x75, 0x7E, 0x63, 0x68,
                0xB1, 0xBA, 0xA7, 0xAC, 0x9D, 0x96, 0x8B, 0x80, 0xE9, 0xE2, 0xFF, 0xF4, 0xC5, 0xCE, 0xD3, 0xD8,
                0x7A, 0x71, 0x6C, 0x67, 0x56, 0x5D, 0x40, 0x4B, 0x22, 0x29, 0x34, 0x3F, 0x0E, 0x05, 0x18, 0x13,
                0xCA, 0xC1, 0xDC, 0xD7, 0xE6, 0xED, 0xF0, 0xFB, 0x92, 0x99, 0x84, 0x8F, 0xBE, 0xB5, 0xA8, 0xA3 
            ]

            valueEngine1 = [(i + offset & 0xFF) for i in valueEngine1]
            valueEngine2 = [(i + offset & 0xFF) for i in valueEngine2]
            valueEngine3 = [(i + offset & 0xFF) for i in valueEngine3]
            valueEngine4 = [(i + offset & 0xFF) for i in valueEngine4]

            result = [0] * 16

            result[0] = (
                valueEngine3[newpass[3]]
                ^ valueEngine2[newpass[2]]
                ^ valueEngine4[newpass[1]]
                ^ valueEngine1[newpass[0]]
            )
            result[1] = (
                valueEngine2[newpass[3]]
                ^ valueEngine4[newpass[2]]
                ^ valueEngine1[newpass[1]]
                ^ valueEngine3[newpass[0]]
            )
            result[2] = (
                valueEngine4[newpass[3]]
                ^ valueEngine1[newpass[2]]
                ^ valueEngine3[newpass[1]]
                ^ valueEngine2[newpass[0]]
            )
            result[3] = valueEngine1[newpass[3]] ^ (
                valueEngine3[newpass[2]]
                ^ (valueEngine2[newpass[1]] ^ valueEngine4[newpass[0]])
            )
            result[4] = (
                valueEngine3[newpass[7]]
                ^ valueEngine2[newpass[6]]
                ^ valueEngine4[newpass[5]]
                ^ valueEngine1[newpass[4]]
            )
            result[5] = (
                valueEngine2[newpass[7]]
                ^ valueEngine4[newpass[6]]
                ^ valueEngine1[newpass[5]]
                ^ valueEngine3[newpass[4]]
            )
            result[6] = (
                valueEngine4[newpass[7]]
                ^ valueEngine1[newpass[6]]
                ^ valueEngine3[newpass[5]]
                ^ valueEngine2[newpass[4]]
            )
            result[7] = valueEngine1[newpass[7]] ^ (
                valueEngine3[newpass[6]]
                ^ (valueEngine2[newpass[5]] ^ valueEngine4[newpass[4]])
            )
            result[8] = (
                valueEngine3[newpass[11]]
                ^ valueEngine2[newpass[10]]
                ^ valueEngine4[newpass[9]]
                ^ valueEngine1[newpass[8]]
            )
            result[9] = (
                valueEngine2[newpass[11]]
                ^ valueEngine4[newpass[10]]
                ^ valueEngine1[newpass[9]]
                ^ valueEngine3[newpass[8]]
            )
            result[10] = (
                valueEngine4[newpass[11]]
                ^ valueEngine1[newpass[10]]
                ^ valueEngine3[newpass[9]]
                ^ valueEngine2[newpass[8]]
            )
            result[11] = valueEngine1[newpass[11]] ^ (
                valueEngine3[newpass[10]]
                ^ (valueEngine2[newpass[9]] ^ valueEngine4[newpass[8]])
            )
            result[12] = (
                valueEngine3[newpass[15]]
                ^ valueEngine2[newpass[14]]
                ^ valueEngine4[newpass[13]]
                ^ valueEngine1[newpass[12]]
            )
            result[13] = (
                valueEngine2[newpass[15]]
                ^ valueEngine4[newpass[14]]
                ^ valueEngine1[newpass[13]]
                ^ valueEngine3[newpass[12]]
            )
            result[14] = (
                valueEngine4[newpass[15]]
                ^ valueEngine1[newpass[14]]
                ^ valueEngine3[newpass[13]]
                ^ valueEngine2[newpass[12]]
            )
            result[15] = (
                (valueEngine2[newpass[13]] ^ valueEngine4[newpass[12]])
                ^ (valueEngine3[newpass[14]])
                ^ valueEngine1[newpass[15]]
            )

            for i in range(16):
                tmppass[i] = result[i]

            tmppass = []
            for i in [0, 0x0D, 0xA, 7, 4, 1, 0x0E, 0x0B, 8, 5, 2, 0x0F, 0x0C, 9, 6, 3]:
                tmppass.append(result[i])

            for i in range(16):
                tmp = tmppass[i]
                tmp = values3[tmp]
                newpass[i] = tmp

            count -= 1
            ##############
            #  END LOOP  #
            ##############

        for i in range(16):
            tmp = newpass[i] ^ key[i]
            newpass[i] = tmp

        decryptSrings += "".join([chr(k) for k in newpass])
        T += 1

    print("\n\nDecrypted data : \n", decryptSrings)

 
Les scripts sont disponible ici.


Conclusion

Cette analyse a permis d'examiner le fonctionnement de la configuration, ainsi que les processus de chiffrement et de déchiffrement des données. Dans l'ensemble, cette étude met en lumière l'importance du reverse engineering dans le cadre d'analyse de malware (en l'occurrence ici, un agent C2).
Il permet d'étudier les requêtes vers le serveur de contrôle et d'acquérir une meilleure compréhension des tactiques employées par les attaquants. Toutefois, il est impératif que les souches soient récupérées sans être endommagées et que le trafic réseau soit correctement monitoré afin de reproduire efficacement les scénarios d'attaque.
Bien que cette version de Brute Ratel ne soit pas la plus récente, il est important de noter que certaines souches de cette version continuent de circuler, c'est pourquoi j'ai considéré qu'il était pertinent d'écrire sur ce sujet.