
Un outil pour étudier les malwares JavaScript.
Un utilitaire pour analyser du JavaScript malveillant.
Installez simplement box-js depuis npm :
npm install box-js --global
box-js est également disponible :
- en tant que module Cuckoo (voir le répertoire
integrationset Nwinternights/Cuckoo_Boxjs);- en tant que Dockerfile (voir
integrations/README.md);- en tant que paquet dans des distributions pour professionnels de la sécurité (REMnux, BlackArch);
- dans le cadre d'applications open source (Intel Owl);
- dans le cadre de services commerciaux tiers (any.run).
Supposons que vous ayez un échantillon nommé sample.js : pour l'analyser, exécutez simplement
box-js sample.js
Il y a de fortes chances que vous souhaitiez également télécharger les éventuelles charges utiles ; utilisez le drapeau --download pour activer le téléchargement. Sinon, le moteur simulera une erreur 404, de sorte que le script sera amené à croire que le site de distribution est indisponible et contactera d'éventuels sites de repli.
Box.js émule un environnement Windows JScript, affiche un résumé de l'émulation dans la console et crée un dossier nommé sample.js.results (s'il existe déjà, il créera sample.js.1.results et ainsi de suite). Ce dossier contiendra :
analysis.log, un journal de l'analyse tel qu'affiché à l'écran ;snippets.json, une liste d'extraits de code exécutés par l'échantillon (JavaScript, commandes shell, etc.) ;urls.json, une liste des URL contactées ;active_urls.json, une liste des URL qui semblent distribuer des logiciels malveillants actifs ;resources.json, les flux ADODB (c'est-à-dire les fichiers que le script a écrits sur le disque) avec les types de fichiers et les hachages ;IOC.json, une liste des comportements identifiés comme IOC (Indicateurs de Compromission). Ceux-ci incluent les accès au registre, les fichiers écrits, les requêtes HTTP, etc.Vous pouvez les analyser vous-même, ou les soumettre automatiquement à Malwr, VirusTotal ou un bac à sable Cuckoo : pour plus d'informations, exécutez box-export --help.
Pour un isolement supplémentaire, il est recommandé d'exécuter l'analyse dans un conteneur Docker temporaire. Consultez
integrations/README.mdpour plus d'informations.
Si vous souhaitez automatiser l'analyse, vous pouvez utiliser les codes de retour – documentés dans
integrations/README.md– pour distinguer les différents types d'erreurs.
Le dépôt git box-js inclut un fichier boilerplate.js. Ce fichier définit des versions simulées d'objets navigateur courants tels que document. Essayez de relancer votre analyse avec l'option --prepended-code=DIR/boilerplate.js, où DIR est le répertoire du dépôt box-js cloné, ou avec --prepended-code=default. L'option --prepended-code indique à box-js de préfixer le JavaScript du fichier donné à l'échantillon analysé.
Notez que vous pouvez copier boilerplate.js et ajouter vos propres classes simulées, objets, etc. selon vos besoins. Utilisez l'option en ligne de commande --prepended-code=show-default pour afficher le chemin complet du fichier boilerplate.js par défaut de box-js.
Bien que box.js soit généralement utilisé sur des fichiers uniques, il peut également exécuter des analyses par lots. Vous pouvez simplement passer une liste de fichiers ou de dossiers à analyser :
box-js sample1.js sample2.js /var/data/mySamples ...
Par défaut, box.js traite les échantillons en parallèle, en exécutant une analyse par cœur. Vous pouvez utiliser un réglage différent en spécifiant une valeur pour --threads : en particulier, 0 supprimera la limite, ce qui fera que box-js générera autant de threads d'analyse que possible, résultant en une analyse très rapide mais pouvant surcharger le système (notez que les analyses sont généralement liées au CPU, pas à la RAM).
Vous pouvez utiliser --loglevel=warn pour masquer les messages liés à l'analyse et n'afficher que les informations de progression.
Une fois l'analyse terminée, vous pouvez extraire les URL actives comme ceci :
cat ./*.results/active_urls.json | sort | uniq
NOM DESCRIPTION
-h, --help Affiche le texte d'aide et quitte
-v, --version Affiche la version du paquet et quitte
--license Affiche la licence et quitte
--debug Arrête le programme en cas d'erreur d'émulation, même en "mode batch", et transmet le code de sortie.
--loglevel Niveau de journalisation (debug, verbose, info, warning, error - défaut "info")
--threads En mode batch, nombre d'analyses à exécuter simultanément (0 = illimité, défaut : autant que le nombre de cœurs CPU)
--download Télécharge effectivement les charges utiles
--encoding Encodage de l'échantillon d'entrée (sera détecté automatiquement par défaut)
--timeout Le script expirera après ce nombre de secondes (défaut 10)
--output-dir Emplacement sur le disque pour écrire les fichiers et dossiers de résultats (par défaut, le répertoire courant)
--preprocess Prétraite le code source original (facilite la rétro-ingénierie, mais prend quelques secondes)
--unsafe-preprocess Prétraitement plus agressif. Donne souvent un meilleur code, mais peut échouer dans certains cas particuliers (ex. redéfinition de prototypes)
--prepended-code Fichier ou répertoire d'entrée contenant du code à préfixer au(x) fichier(s) JS que nous analysons. Si un répertoire est donné, préfixe le contenu de tous les fichiers du répertoire. Si 'default' est donné, utilise le boilerplate.js par défaut fourni avec box-js. Si 'show-default' est donné, affiche simplement le chemin de boilerplate.js et quitte (utile si vous voulez copier et modifier le code boilerplate par défaut).
--fake-script-engine Le moteur de script à signaler dans WScript.FullName et WScript.Name (ex. 'cscript.exe', 'wscript.exe', ou 'node'). Par défaut, wscript.exe.
--fake-cl-args Arguments de ligne de commande factices pour le script. Dans la chaîne, ils doivent être séparés par des virgules.
--fake-sample-name Nom de fichier factice à utiliser pour l'échantillon analysé. Peut être un chemin complet ou simplement le nom du fichier. Si vous avez des '\' dans le chemin, échappez-les en '\\' dans la valeur de cet argument (ex. --fake-sample-name=C:\\foo\\bar.js).
--fake-language Spécifie le code de langue à retourner pour Win32_OperatingSystem.OSLanguage. Les valeurs supportées sont 'spanish', 'english', et 'portuguese'.
--fake-domain Spécifie le domaine utilisateur à retourner pour WScript.Network.UserDomain.
--fake-download Simule que les requêtes HTTP fonctionnent et qu'elles retournent une charge utile factice
--no-kill Ne tue pas l'application lorsque des erreurs d'exécution se produisent
--no-echo Lorsque le script affiche des données, ne les affiche pas dans la console
--no-rewrite Ne réécrit pas du tout le code source, sauf pour le support de `@cc_on`
--no-catch-rewrite Ne réécrit pas les clauses try..catch pour rendre l'exception de portée globale
--no-cc_on-rewrite Ne réécrit pas `/*@cc_on <...>@*/` en `<...>`
--no-eval-rewrite Ne réécrit pas `eval` pour que son argument soit réécrit
--no-file-exists Retourne `false` pour Scripting.FileSystemObject.FileExists(x)
--limit-file-checks Bascule la valeur par défaut pour les vérifications d'existence de fichiers/dossiers si de nombreuses vérifications sont effectuées (essaye de briser les boucles infinies de vérification de fichiers).
--no-folder-exists Retourne `false` pour Scripting.FileSystemObject.FileExists(x)
--function-rewrite Réécrit les appels de fonction afin d'intercepter les appels eval
--no-rewrite-prototype Ne réécrit pas les expressions comme `function A.prototype.B()` en `A.prototype.B = function()`
--no-hoist-prototype Ne remonte pas les expressions comme `function A.prototype.B()` (impliqué par no-rewrite-prototype)
--no-shell-error Ne génère pas d'erreur factice lors de l'exécution de `WScriptShell.Run` (elle génère une erreur factice par défaut pour faire croire que les sites de distribution sont indisponibles, de sorte que le script tentera d'interroger chaque site)
--no-typeof-rewrite Ne réécrit pas `typeof` (ex. `typeof ActiveXObject`, qui doit retourner 'unknown' selon la norme JScript et non 'object')
--proxy [expérimental] Utilise le proxy spécifié pour les téléchargements. Non pertinent si le drapeau --download n'est pas présent.
--windows-xp Émule Windows XP (influence la valeur des variables d'environnement)
--dangerous-vm Utilise le module `vm`, plutôt que `vm2`. Ce bac à sable peut être cassé, donc **ne l'utilisez pas** sauf si vous êtes sûr à 100 % de ce que vous faites. Aide au débogage en donnant des traces de pile correctes.
--rewrite-loops Réécrit certains types de boucles pour accélérer l'analyse
--throttle-writes Limite le rapport et le suivi des données des écritures de fichiers qui écrivent BEAUCOUP de données
--throttle-commands Arrête l'analyse si BEAUCOUP des mêmes commandes ont été exécutées
--extract-conditional-code Extrait le code réel à analyser des commentaires conditionnels JScript (/*@if(...).
--loose-script-name Réécrit les vérifications == pour que les comparaisons du nom de script actuel avec un nom de script codé en dur retournent toujours vrai.
--real-script-name Retourne le vrai nom de fichier du script actuellement analysé plutôt qu'un nom factice.
--activex-as-ioc Journalise tous les appels ActiveX en tant qu'IOC et essaie de déterminer si l'appel est obscurci dans la source JS.
--ignore-wscript-quit Ignore les appels à WSCript.Quit() et continue l'exécution.
--ignore-rewrite-errors Analyse l'échantillon original si une réécriture échoue.
La première source d'information est la sortie console. Sur une analyse réussie, elle affiche généralement quelque chose comme ceci :
Using a 10 seconds timeout, pass --timeout to specify another timeout in seconds
Analyzing sample.js
Header set for http://foo.bar/baz: User-Agent Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
Emulating a GET request to http://foo.bar/baz
Downloaded 301054 bytes.
Saved sample.js.results/a0af1253-597c-4eed-9e8f-5b633ff5f66a (301054 bytes)
sample.js.results/a0af1253-597c-4eed-9e8f-5b633ff5f66a has been detected as data.
Saved sample.js.results/f8df7228-7e0a-4241-9dae-c4e1664dc5d8 (303128 bytes)
sample.js.results/f8df7228-7e0a-4241-9dae-c4e1664dc5d8 has been detected as PE32 executable (GUI) Intel 80386, for MS Windows.
http://foo.bar/baz is an active URL.
Executing sample.js.results/d241e130-346f-4c0c-a698-f925dbd68f0c in the WScript shell
Header set for http://somethingelse.com/: User-Agent Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
Emulating a GET request to http://somethingelse.com/
...
Dans ce cas, nous voyons un dropper qui télécharge un fichier depuis http://foo.bar/baz, en définissant l'en-tête HTTP User-Agent à Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0). Ensuite, il le décode et écrit le résultat sur le disque (un exécutable PE32). Enfin, il exécute une commande dans le shell Windows.
sample.js.results/a0af1253-597c-4eed-9e8f-5b633ff5f66a contiendra la charge utile telle qu'elle a été téléchargée depuis http://foo.bar/baz ;sample.js.results/f8df7228-7e0a-4241-9dae-c4e1664dc5d8 contiendra la charge utile réelle (exécutable PE) ;sample.js.results/d241e130-346f-4c0c-a698-f925dbd68f0c contiendra la commande qui a été exécutée dans le shell Windows.Chaque requête HTTP est à la fois affichée dans le terminal et enregistrée dans urls.json. Les URL en double ne sont pas insérées (c'est-à-dire que demander la même URL deux fois ne produira qu'une seule ligne dans urls.json).
active_urls.json contient la liste des URL qui ont finalement abouti à une charge utile exécutable. Ce fichier est le plus intéressant si vous cherchez à démanteler les sites de distribution.
snippets.json contient chaque morceau de code que box-js a rencontré, que ce soit du JavaScript, une commande cmd.exe ou un script PowerShell.
resources.json contient chaque fichier écrit sur le disque par l'échantillon. Par exemple, si l'application a essayé d'enregistrer Hello world! dans $PATH/foo.txt, le contenu de resources.json serait :
{
"9a24...": {
"path": "(path)\\foo.txt",
"type": "ASCII text, with no line terminators",
"md5": "86fb269d190d2c85f6e0468ceca42a20",
"sha1": "d3486ae9136e7856bc42212385ea797094475802",
"sha256": "c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a"
}
}
Le fichier resources.json est également important : surveillez toute ressource exécutable (par exemple avec "type": "PE32 executable (GUI) Intel 80386, for MS Windows").
Certains scripts dans la nature ont été observés utilisant new Date().getYear() là où new Date().getFullYear() serait approprié. Si un échantillon ne montre aucun comportement suspect, surveillez les vérifications de Date.
Si vous rencontrez des fichiers .JSE, compilez le décodeur et exécutez-le comme ceci :
cc decoder.c -o decoder
./decoder foo.jse bar.js
node run bar.js
Vous pouvez parfois rencontrer des composants non pris en charge. Dans ce cas, vous pouvez ouvrir un problème sur GitHub, ou émuler le composant vous-même si vous connaissez JavaScript.
L'erreur ressemblera généralement à ceci (les numéros de ligne peuvent différer) :
1 Jan 00:00:00 - Unknown ActiveXObject WinHttp.WinHttpRequest.5.1
Trace
at kill (/home/CapacitorSet/box-js/run.js:24:10)
at Proxy.ActiveXObject (/home/CapacitorSet/box-js/run.js:75:4)
at evalmachine.<anonymous>:1:6471
at ContextifyScript.Script.runInNewContext (vm.js:18:15)
at ...
Vous pouvez voir que l'exception a été levée dans Proxy.ActiveXObject, qui ressemble à ceci :
function ActiveXObject(name) {
name = name.toLowerCase();
/* ... */
switch (name) {
case "wscript.shell":
return require("./emulator/WScriptShell");
/* ... */
default:
kill(`Unknown ActiveXObject ${name}`);
break;
}
}
Ajoutez un nouveau case "winhttp.winhttprequest.5.1" (notez les minuscules !), et faites en sorte qu'il retourne un objet Proxy ES6 (par exemple ProxiedWinHttpRequest). Ceci est utilisé pour intercepter les fonctionnalités non implémentées dès qu'elles sont demandées par l'échantillon malveillant :
/* emulator/WinHttpRequest.exe */
const lib = require("../lib");
module.exports = function ProxiedWinHttpRequest() {
return new Proxy(new WinHttpRequest(), {
get: function(target, name, receiver) {
switch (name) {
/* Ajoutez ici des pièges "spéciaux" avec des case */
default:
if (name in target) return target[name];
else lib.kill(`WinHttpRequest.${name} not implemented!`)
}
}
})
}
function WinHttpRequest() {
}
Relancez l'analyse : elle échouera à nouveau, en vous indiquant exactement ce qui n'a pas été implémenté.
1 Jan 00:00:00 - WinHttpRequest.open not implemented!
Trace
at kill (/home/CapacitorSet/box-js/run.js:24:10)
at Object.ProxiedWinHttpRequest.Proxy.get (/home/CapacitorSet/box-js/run.js:89:7)
Émulez WinHttpRequest.open selon vos besoins :
function WinHttpRequest() {
this.open = function(method, url) {
URLLogger(method, url);
this.url = url;
}
}
et itérez jusqu'à ce que le code s'émule sans erreur.
@CapacitorSet : Développeur original
@kirk-sayre-work : Mainteneur
--output-directory@ALange :
@psrok :