
Boîte à outils en ligne de commande composable pour le tri de malwares et l’analyse binaire : décoder, déchiffrer, carver et extraire des indicateurs à partir de fichiers malveillants et de shellcode.
__ __ High Octane Triage Analysis __
|| _||______ __ __________ _____ ||
|| \||___ \__| ____/ ______/___ / ____\ ||
==||=====|| | __/ |/ \ /==| / __ \ __\===]|
'======|| | \ | | \_ _| \ ___/| | ||
||____ /__|___|__/ / | \____]| | ||
=========''====\/=========/ /==|__|=====|__|======'
\ /
\/
Binary Refinery™ est une collection de scripts Python qui implémentent des transformations de données binaires telles que la compression et le chiffrement.
Nous y ferons souvent référence simplement par refinery, qui est aussi le nom du paquet correspondant.
Les scripts sont conçus pour lire exclusivement leur entrée depuis stdin et écrire leur sortie sur stdout.
La philosophie principale est que chaque script doit être une unité, dans le sens où il effectue une seule tâche,
et les unités individuelles peuvent être combinées en pipelines avec l'opérateur de pipe | en ligne de commande pour accomplir des tâches plus complexes.
Le projet est principalement axé sur le tri de malwares,
et constitue une tentative d'implémenter quelque chose comme CyberChef en ligne de commande.
Créez un environnement virtuel Python. Vous avez besoin de Python 3.10 ou plus. Installez refinery comme ceci :
python -m pip install -U pip
python -m pip install -U binary-refinery[extended]
Exécutez les unités avec -h pour apprendre leur fonctionnement, fouillez dans la documentation ou utilisez la commande binref pour les trouver.
Regardez une vidéo récente si vous voulez le voir en action.
Mais lisez aussi le reste de ce readme.
Il n'y a pas de calendrier de publication, mais les versions sortent très fréquemment et il est recommandé de mettre à jour régulièrement. Les corrections de bugs ne sont pas documentées en dehors de GIT, mais tous les autres changements (c.-à-d. les nouvelles fonctionnalités) sont documentés dans le changelog. Suivez-moi sur Mastodon pour être informé des versions particulièrement marquantes.
Le texte d'aide affiché lors de l'exécution d'une unité avec l'option -h ou --help est sa documentation principale.
La documentation générée automatiquement contient une compilation de cette sortie pour chaque unité au niveau supérieur,
mais contient aussi les spécifications des trois concepts fondamentaux de la boîte à outils :
framing, multibin arguments et meta variables.
Une recherche en texte intégral dans la description et le texte d'aide de chaque unité est également disponible en ligne de commande,
via la commande binref fournie. Conscient que la documentation de référence peut être un peu aride,
un effort continu est mené pour produire une série de tutoriels ; je recommande vivement d'aller les voir.
En plus de cela, je rassemble ci-dessous des ressources supplémentaires (dont certaines produites par des tiers).
[!NOTE]
Refinery est encore en alpha et l'interface peut parfois changer, c.-à-d. que des unités et des paramètres peuvent être supprimés ou renommés. Par conséquent, il peut arriver que des lignes de commande précises issues d'anciennes vidéos et d'articles de blog ne fonctionnent plus.
2021/08] OALabs a eu la gentillesse de me laisser présenter la boîte à outils dans une vidéo.
Dans la vidéo, je parcours essentiellement le contenu du
premier tutoriel.2021/11] Johannes Bader a écrit un article de blog incroyable sur l'analyse de malspam avec binary refinery.2024/03] Malware Analysis For Hedgehogs a réalisé une vidéo sur le dépaquetage d'un échantillon XWorm avec refinery.2024/11] the CyberYeti m'a invité en stream pour présenter refinery.2025/06] J'étais de nouveau en stream avec the CyberYeti, celui-ci est un peu plus brut.
Tous les bugs que vous pouvez voir ici ont été corrigés. 😉Les démonstrations incluent à nouveau des échantillons de la section d'exemples ci-dessous et des tutoriels.
Binary Refinery est (c) 2019 Jesko Hüttenhain, publié sous une licence BSD 3-Clause. Ce dépôt contient également une copie du texte intégral de la licence. Si vous souhaitez en faire quelque chose qui n'est pas couvert par cette licence, n'hésitez pas à contacter l'auteur.
refinery nécessite au moins Python 3.10. Il est recommandé de l'installer dans son propre environnement virtuel : Le paquet peut introduire beaucoup de dépendances, et l'installer dans votre Python global est assez sujet aux conflits de versions. De plus, comme la boîte à outils introduit un grand nombre de nouvelles commandes, il y a de fortes chances que certaines entrent en conflit sur certains systèmes ; les garder dans un environnement virtuel séparé est un moyen d'éviter cela.
Si vous voulez que toutes les commandes refinery soient disponibles dans votre shell à tout moment
(c.-à-d. sans avoir à basculer vers un environnement virtuel dédié),
vous avez aussi la possibilité de choisir un préfixe pour l'installation,
qui sera placé devant chaque script de commande installé.
Par exemple, si vous choisissez r. comme préfixe, l'unité emit sera installée comme commande r.emit.
Un avantage supplémentaire est que vous pouvez taper r. et appuyer deux fois sur Tab pour obtenir une liste de toutes les commandes refinery disponibles.
Notez cependant qu'aucun préfixe n'est supposé dans la documentation et que refinery a pour objectif de développement de ne pas entrer en conflit sur la plupart des systèmes.
L'auteur n'utilise pas de préfixe et propose cette option comme filet de sécurité.
La façon la plus simple d'installer et de mettre à jour refinery est d'utiliser pip. Assurez-vous d'avoir d'abord la dernière version de pip :
python -m pip install -U pip
Installez ensuite simplement le paquet refinery :
pip install -U binary-refinery
Si vous souhaitez choisir un préfixe pour toutes les unités, vous pouvez le spécifier via la variable d'environnement REFINERY_PREFIX.
Par exemple, la commande suivante installera refinery dans l'environnement Python courant avec le préfixe r. sous Linux :
REFINERY_PREFIX=r. pip install -U binary-refinery
Sous Windows, vous devrez exécuter les commandes suivantes :
set REFINERY_PREFIX=r.
pip install -U binary-refinery
Spécifier le préfixe spécial ! aura pour effet qu'aucune commande shell ne sera créée du tout,
et binary refinery ne sera installé que comme bibliothèque.
Si vous souhaitez installer le HEAD actuel de refinery, vous pouvez répéter toutes les étapes ci-dessus en spécifiant ce dépôt à la place du paquet pip.
Par exemple, la commande suivante installera le tout dernier commit de refinery :
pip install -U git+git://github.com/binref/refinery.git
Enfin, si vous utilisez REMnux, vous pouvez utiliser leur conteneur docker refinery.
Si vous voulez apprendre à votre claude local d'analyse de malwares à utiliser binary refinery, consultez la compétence Binary Refinery.
Voici un résumé de la qualité de prise en charge actuelle des différents environnements shell :
Si vous utilisez un autre shell et avez des retours à partager, n'hésitez pas à me le faire savoir !
Il existe des unités très spécialisées avec des dépendances externes (parfois volumineuses).
Par exemple, stego est une unité qui requiert la bibliothèque d'analyse d'images Pillow.
Certaines bibliothèques ne sont pas installées par défaut afin de maintenir un temps d'installation raisonnable pour les nouveaux utilisateurs de refinery.
Les unités correspondantes vous indiqueront quoi faire lorsque leur dépendance est manquante :
$ emit config.png | stego RG
(13:37:00) failure in stego: dependency Pillow is missing; run pip install Pillow
Vous pouvez ensuite installer manuellement ces dépendances manquantes. Si vous ne voulez pas être gêné par les dépendances manquantes et qu'une longue installation de refinery ne vous dérange pas, vous pouvez installer le paquet comme suit :
pip install -U binary-refinery[all]
ce qui installera toutes les dépendances en plus de celles requises. Plus précisément, voici les catégories supplémentaires disponibles :
| Nom | Dépendances incluses |
|---|---|
default | sélection recommandée de dépendances raisonnables, choix de l'auteur |
extended | une sélection étendue, excluant seulement les plus obscures |
all | toutes les dépendances pour toutes les unités de refinery |
Elles sont listées ici par ordre croissant, c.-à-d. que extended installera tout ce que default installerait.
Alternativement, vous pouvez cloner ce dépôt et utiliser les scripts update.sh (sous Linux) ou update.ps1 (sous Windows) pour installer le paquet refinery dans un environnement virtuel local. Pour cette méthode, l'installation et la mise à jour consistent simplement à exécuter le script :
binary-refinery,binary-refinery[all].Vous pouvez aussi générer toute la documentation localement.
Pour ce faire, exécutez le script run-pdoc3.py.
Cette opération échouera à moins de l'exécuter depuis un environnement où binary refinery a été installé comme paquet Python.
Pour l'exécuter, vous devez spécifier le chemin d'un environnement virtuel comme premier argument de ligne de commande à run-pdoc3.py,
ce qui amènera le script à se ré-exécuter avec l'interpréteur de cet environnement.
Si vous êtes certain de vouloir exécuter run-pdoc3.py,
une option de ligne de commande permet de forcer le script à s'exécuter avec l'interpréteur Python par défaut actuel.
Le script installe le paquet pdoc3 et l'utilise pour générer une documentation HTML pour le paquet refinery.
La documentation se trouve ensuite dans le sous-répertoire html, juste à côté de ce fichier readme.
Les tutoriels sont des notebooks Jupyter que vous pouvez simplement lancer et exécuter si votre environnement virtuel a Jupyter installé. Il convient de souligner que Visual Studio Code offre un support très confortable pour Jupyter.
Les unités emit et dump jouent un rôle particulier : La première sert à produire des données, tandis que la seconde sert à les écrire dans le presse-papiers ou sur le disque. À titre d'exemple, considérez le pipeline suivant :
emit M7EwMzVzBkI3IwNTczM3cyMg2wQA | b64 | zl | hex
Ici, nous émettons la chaîne M7EwMzVzBkI3IwNTczM3cyMg2wQA,
la décodons en base64 avec b64,
décompressons le résultat en zlib avec zl,
puis décodons en hexadécimal avec hex les données décompressées.
Chaque unité effectue par défaut l'opération de « décodage » d'une certaine transformation, mais certaines implémentent aussi l'opération inverse.
Si c'est le cas, cela se fait toujours en fournissant l'option de ligne de commande -R, ou --reverse.
Vous pouvez produire la chaîne base64 ci-dessus avec la commande suivante, car hex, zl et b64 fournissent tous l'opération inverse :
emit "Hello World" | hex -R | zl -R | b64 -R
Étant donné un fichier packed.bin contenant un tampon de charge utile encodé en base64, le pipeline suivant extrait cette charge utile vers payload.bin :
emit packed.bin | carve -l -t1 b64 | b64 | dump payload.bin
L'unité carve peut être utilisée pour extraire des blocs de données du tampon d'entrée ;
dans ce cas, elle recherche des données encodées en base64, les trie par longueur (-l) et renvoie le premier (-t1),
ce qui extrait de packed.bin le plus gros morceau de données ressemblant à du base64.
Les données sont ensuite décodées en base64 et écrites dans le fichier payload.bin.
L'unité pack sélectionne toutes les expressions numériques d'un tampon texte et les convertit en leur représentation binaire. Un exemple simple est le pipeline
emit "0xBA 0xAD 0xC0 0xFF 0xEE" | pack | hex -R
qui affichera la chaîne BAADC0FFEE.
Extraire le plus grand morceau de données encodées en base64 d'un BLOB et le décoder :
emit file.exe | carve -ds b64
Extraire un fichier ZIP d'un tampon, en sélectionner une DLL et afficher des informations à son sujet :
emit file.bin | carve-zip | xtzip file.dll | pemeta
Lister les sections d'un fichier PE avec leur hash SHA-256 correspondant :
emit file.exe | vsect [| sha256 -t | pf {} {path} ]]
Lister récursivement tous les fichiers du répertoire courant avec leur hash SHA-256 respectif :
ef "**" [| sha256 -t | pf {} {path} ]]
Extraire les indicateurs de tous les fichiers énumérés récursivement dans le répertoire courant :
ef "**" [| xtp -n6 ipv4 socket url email | dedup ]]
Convertir l'adresse IP codée en dur 0xC0A80C2A, en ordre d'octets réseau, vers un format lisible :
emit 0xC0A80C2A | pack -EB4 | pack -R [| sep . ]
Effectuer une attaque par force brute XOR sur un seul octet et tenter d'extraire une charge utile de fichier PE à chaque itération :
emit file.bin | rep 0x100 [| xor v:index | carve-pe -R | peek | dump {name} ]
Extraire un serveur C2 RemCos :
emit c0019718c4d4538452affb97c70d16b7af3e4816d059010c277c4e579075c944 \
| perc SETTINGS [| put keylen cut::1 | rc4 cut::keylen | xtp socket ]
Extraire une configuration AgentTesla :
emit fb47a566911905d37bdb464a08ca66b9078f18f10411ce019e9d5ab747571b40 \
| dnfields [| aes x::32 --iv x::16 -T ]] \
| rex -M "((??email))\n(.*)\n(.*)\n:Zone" addr={1} pass={2} host={3}
Extraire la charge utile PowerShell d'un dropper à macros XLS malveillant :
emit 81a1fca7a1fb97fe021a1f2cf0bf9011dd2e72a5864aad674f8fea4ef009417b [ \
| xlxtr 9.5:11.5 15.15 12.5:14.5 [ \
| scope -n 3 | chop -t 5 [| sorted -a | snip 2: | sep ] \
| pack 10 | alu --dec -sN B-S ]] \
| dump payload.cmd
Et obtenir les domaines de l'étape suivante :
emit payload.cmd | cmdarg | ps1 | xtp -f domain
Extraire la configuration d'échantillons HawkEye dépaquetés :
emit ee790d6f09c2292d457cbe92729937e06b3e21eb6b212bf2e32386ba7c2ff22c \
| put cfg perc[RCDATA]:c:: [\
| xtp guid | pbkdf2 48 rep[8]:h:00 | cca eat:cfg | aes -Q x::32 --iv x::16 ] \
| dnds
Warzone RAT :
emit 4537fab9de768a668ab4e72ae2cce3169b7af2dd36a1723ddab09c04d31d61a5 \
| vsect .bss | struct I{key:{}}{} [\
| rc4 eat:key | struct I{host:{}}{port:H} {host:u16}:{port} ]
Extraire la charge utile d'un chargeur de shellcode et en isoler son c2 :
emit 58ba30052d249805caae0107a0e2a5a3cb85f3000ba5479fafb7767e2a5a78f3 \
| rex yara:50607080.* [| struct LL{s:L}{} | xor -B2 rand[s]:msvc | xtp url ]
Récupérer les macros VBA malveillantes d'une époque révolue où c'était ainsi que l'on procédait :
emit ee103f8d64cd8fa884ff6a041db2f7aa403c502f54e26337c606044c2f205394 \
| vbamc
Puis extraire la charge utile du téléchargeur malveillant :
emit ee103f8d64cd8fa884ff6a041db2f7aa403c502f54e26337c606044c2f205394 \
| doctxt | repl drp:c: | carve -s b64 | rev | b64 | rev | ppjscript
Extraire les URL de charges utiles d'un document PDF malveillant :
emit 066aec7b106f669e587b10b3e3c6745f11f1c116f7728002f30c072bd42d6253 \
| xt JS | csd string | csd string | url | xtp url [| urlfix ]]
Extraire l'URL de la charge utile d'un document exploitant l'éditeur d'équations :
emit e850f3849ea82980cf23844ad3caadf73856b2d5b0c4179847d82ce4016e80ee \
| officecrypt | xt oleObject | xt native | rex Y:E9[] | vstack -a=x32 -w=200 | xtp
Supposons que data soit un fichier chiffré avec AES 256 bits en mode CBC.
La clé a été dérivée de la phrase de passe secrète swordfish à l'aide de la routine de dérivation de clé PBKDF2 avec le sel s4lty.
L'IV est préfixé au tampon sous la forme des 16 premiers octets.
Il peut être déchiffré avec le pipeline suivant :
emit data | aes --mode cbc --iv cut::16 pbkdf2[32,s4lty]:swordfish
Ici, cut:0:16 et pbkdf2[32,s4lty]:swordfish sont tous deux des arguments multibin qui utilisent un gestionnaire spécial.
Dans ce cas, cut:0:16 extrait la tranche 0:16 (c.-à-d. les 16 premiers octets) des données d'entrée — après application de ce gestionnaire multibin,
les 16 premiers octets sont retirés des données d'entrée et l'argument iv est défini sur ces 16 octets exactement.
Le dernier argument spécifie la clé de chiffrement de 32 octets :
Le gestionnaire pbkdf2[32,s4lty], quant à lui, demande à refinery de créer une instance de l'unité pbkdf2 comme si elle avait reçu les paramètres de ligne de commande 32 et s4lty dans cet ordre, puis de traiter la chaîne d'octets swordfish avec cette unité.
Pour un test simple, le pipeline suivant chiffrera et déchiffrera un échantillon de texte :
emit "Once upon a time, at the foot of a great mountain ..." ^
| aes pbkdf2[32,s4lty]:swordfish --iv md5:X -R | ccp md5:X ^
| aes pbkdf2[32,s4lty]:swordfish --iv cut:0:16
| Shell | Plateforme | État | Commentaire |
|---|
| Bash | Posix | 🔵 Bon | Utilisé occasionnellement par l'auteur. |
| CMD | Windows | 🔵 Bon | Utilisé intensivement par l'auteur. |
| PowerShell | Windows | 🟡 Correct | Il fonctionne tout simplement si la version de PowerShell est au moins 7.4. |
| Zsh | Posix | 🟠 Problèmes mineurs | Suite à une discussion, il existe un correctif. |
| Fish | Posix | 🟠 Problèmes mineurs | Voir le problème #55 et la discussion #22. |