
Una herramienta para estudiar malware en JavaScript.
Una utilidad para analizar JavaScript malicioso.
Simplemente instala box-js desde npm:
npm install box-js --global
box-js también está disponible:
- como módulo de Cuckoo (consulta el directorio
integrationsy Nwinternights/Cuckoo_Boxjs);- como Dockerfile (consulta
integrations/README.md);- como paquete en distribuciones para profesionales de seguridad (REMnux, BlackArch);
- como parte de aplicaciones de código abierto (Intel Owl);
- como parte de servicios comerciales de terceros (any.run).
Supongamos que tienes una muestra llamada sample.js: para analizarla, simplemente ejecuta
box-js sample.js
Probablemente también querrás descargar cualquier payload; usa la bandera --download para habilitar la descarga. De lo contrario, el motor simulará un error 404, de modo que el script será engañado para pensar que el sitio de distribución está caído y contactar con cualquier sitio de respaldo.
Box.js emulará un entorno Windows JScript, imprimirá un resumen de la emulación en la consola y creará una carpeta llamada sample.js.results (si ya existe, creará sample.js.1.results y así sucesivamente). Esta carpeta contendrá:
analysis.log, un registro del análisis tal como se imprimió en pantalla;snippets.json, una lista de fragmentos de código ejecutados por la muestra (JavaScript, comandos de shell, etc.);urls.json, una lista de URLs contactadas;active_urls.json, una lista de URLs que parecen soltar malware activo;resources.json, los flujos ADODB (es decir, los archivos que el script escribió en el disco) con tipos de archivo y hashes;IOC.json, una lista de comportamientos identificados como IOCs (Indicadores de Compromiso). Estos incluyen accesos al registro, archivos escritos, solicitudes HTTP, etc.Puedes analizarlos por tu cuenta, o puedes enviarlos automáticamente a Malwr, VirusTotal o una sandbox de Cuckoo: para más información, ejecuta box-export --help.
Para un mayor aislamiento, se recomienda ejecutar el análisis en un contenedor Docker temporal. Consulta
integrations/README.mdpara más información.
Si deseas automatizar el análisis, puedes usar los códigos de retorno - documentados en
integrations/README.md- para distinguir entre diferentes tipos de errores.
El repositorio de box-js desde git incluye un archivo boilerplate.js. Este archivo define algunas versiones simuladas de objetos comunes del navegador como document. Intenta volver a ejecutar tu análisis con la opción --prepended-code=DIR/boilerplate.js, donde DIR es el directorio del repositorio clonado de box-js o con --prepended-code=default. La opción --prepended-code le indica a box-js que anteponga el JavaScript del archivo dado a la muestra que se está analizando.
Ten en cuenta que puedes copiar boilerplate.js y agregar tus propias clases, objetos, etc. simulados según sea necesario. Usa la opción de línea de comandos --prepended-code=show-default para imprimir la ruta completa al archivo boilerplate.js predeterminado de box-js.
Aunque box.js se usa típicamente en archivos individuales, también puede ejecutar análisis por lotes. Simplemente puedes pasar una lista de archivos o carpetas a analizar:
box-js sample1.js sample2.js /var/data/mySamples ...
Por defecto, box.js procesará las muestras en paralelo, ejecutando un análisis por núcleo. Puedes usar una configuración diferente especificando un valor para --threads: en particular, 0 eliminará el límite, haciendo que box-js genere tantos hilos de análisis como sea posible y resultando en un análisis muy rápido pero posiblemente sobrecargando el sistema (nota que los análisis suelen estar limitados por la CPU, no por la RAM).
Puedes usar --loglevel=warn para silenciar los mensajes relacionados con el análisis y solo mostrar información de progreso.
Después de que el análisis termine, puedes extraer las URLs activas de esta manera:
cat ./*.results/active_urls.json | sort | uniq
NOMBRE DESCRIPCIÓN
-h, --help Muestra el texto de ayuda y sale
-v, --version Muestra la versión del paquete y sale
--license Muestra la licencia y sale
--debug Termina cuando ocurre un error de emulación, incluso en "modo por lotes", y pasa el código de salida
.
--loglevel Nivel de registro (debug, verbose, info, warning, error - por defecto "info")
--threads Cuando se ejecuta en modo por lotes, cuántos análisis ejecutar al mismo tiempo (0 = ilimitado, por defecto: tantos como núcleos de CPU)
--download Descargar realmente los payloads
--encoding Codificación de la muestra de entrada (se detectará automáticamente por defecto)
--timeout El script expirará después de tantos segundos (por defecto 10)
--output-dir La ubicación en disco para escribir los archivos y carpetas de resultados (por defecto el
directorio actual)
--preprocess Preprocesa el código fuente original (facilita la ingeniería inversa, pero lleva
unos segundos)
--unsafe-preprocess Preprocesamiento más agresivo. A menudo produce mejor código, pero puede romperse en
algunos casos extremos (por ejemplo, redefiniendo prototipos)
--prepended-code Archivo o directorio de entrada que contiene código que debe anteponerse a los archivos JS
que estamos analizando. Si se da un directorio, antepone el contenido de todos los archivos
en el directorio. Si se da 'default', usa el boilerplate.js predeterminado que
viene con box-js. Si se da 'show-default', solo imprime la ruta de boilerplate.js
y sale (útil si quieres copiar y modificar el código boilerplate predeterminado).
--fake-script-engine El motor de script a reportar en WScript.FullName y WScript.Name (ej.
'cscript.exe', 'wscript.exe', o 'node'). El predeterminado es wscript.exe.
--fake-cl-args Argumentos falsos de línea de comandos del script. En la cadena deben estar separados por comas
separados.
--fake-sample-name Nombre de archivo falso para usar para la muestra que se analiza. Puede ser una ruta completa o solo
el nombre de archivo a usar. Si tienes '\' en la ruta, escápalos como '\\' en este
valor del argumento de línea de comandos (ej. --fake-sample-name=C:\\foo\\bar.js).
--fake-language Especifica el código de idioma a devolver para Win32_OperatingSystem.OSLanguage.
Los valores admitidos son 'spanish', 'english' y 'portuguese'.
--fake-domain Especifica el dominio del usuario a devolver para WScript.Network.UserDomain.
--fake-download Finge que las solicitudes HTTP funcionan y haz que devuelvan un payload falso
--no-kill No terminar la aplicación cuando ocurran errores en tiempo de ejecución
--no-echo Cuando el script imprime datos, no imprimirlos en la consola
--no-rewrite No reescribir el código fuente en absoluto, excepto para el soporte de `@cc_on`
--no-catch-rewrite No reescribir las cláusulas try..catch para hacer la excepción de ámbito global
--no-cc_on-rewrite No reescribir `/*@cc_on <...>@*/` a `<...>`
--no-eval-rewrite No reescribir `eval` para que su argumento sea reescrito
--no-file-exists Devolver `false` para Scripting.FileSystemObject.FileExists(x)
--limit-file-checks Cambiar el valor predeterminado para las comprobaciones de existencia de carpetas/archivos si se realizan muchas comprobaciones
(intenta romper bucles infinitos de comprobación de archivos).
--no-folder-exists Devolver `false` para Scripting.FileSystemObject.FileExists(x)
--function-rewrite Reescribir llamadas a funciones para capturar llamadas a eval
--no-rewrite-prototype No reescribir expresiones como `function A.prototype.B()` como `A.prototype.B =
function()`
--no-hoist-prototype No elevar expresiones como `function A.prototype.B()` (implicado por
no-rewrite-prototype)
--no-shell-error No lanzar un error falso al ejecutar `WScriptShell.Run` (lanza un error falso
por defecto para simular que los sitios de distribución están caídos, para que el
script intente consultar cada sitio)
--no-typeof-rewrite No reescribir `typeof` (por ejemplo, `typeof ActiveXObject`, que debe devolver
'unknown' en el estándar JScript y no 'object')
--proxy [experimental] Usar el proxy especificado para descargas. Esto no es relevante si
la bandera --download no está presente.
--windows-xp Emular Windows XP (influye en el valor de las variables de entorno)
--dangerous-vm Usar el módulo `vm`, en lugar de `vm2`. Esta sandbox puede romperse, así que **no
uses esto** a menos que estés 100% seguro de lo que haces. Ayuda con la depuración al
dar trazas de pila correctas.
--rewrite-loops Reescribir algunos tipos de bucles para hacer el análisis más rápido
--throttle-writes Limitar el informe y el seguimiento de datos de escrituras de archivos que escriben MUCHOS datos
--throttle-commands Detener el análisis si se han ejecutado MUCHOS comandos iguales
--extract-conditional-code Extraer el código real a analizar de los comentarios condicionales de JScript (/*@if(...).
--loose-script-name Reescribir comprobaciones == para que las comparaciones del nombre del script actual con un nombre
de script codificado siempre devuelvan true.
--real-script-name Devolver el nombre de archivo real del script analizado actualmente en lugar de uno
falso.
--activex-as-ioc Registra todas las llamadas ActiveX como IOCs e intenta determinar si la llamada está ofuscada
en el código fuente JS.
--ignore-wscript-quit Ignorar llamadas a WSCript.Quit() y continuar la ejecución.
--ignore-rewrite-errors Analizar la muestra original si falla alguna reescritura de muestra.
La primera fuente de información es la salida de la consola. En un análisis exitoso, generalmente imprimirá algo como esto:
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/
...
En este caso, estamos viendo un dropper que descarga un archivo de http://foo.bar/baz, estableciendo la cabecera HTTP User-Agent a Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0). Luego, procede a decodificarlo y escribe el resultado en el disco (un ejecutable PE32). Finalmente, ejecuta algún comando en el shell de Windows.
sample.js.results/a0af1253-597c-4eed-9e8f-5b633ff5f66a contendrá el payload tal como se descargó de http://foo.bar/baz;sample.js.results/f8df7228-7e0a-4241-9dae-c4e1664dc5d8 contendrá el payload real (ejecutable PE);sample.js.results/d241e130-346f-4c0c-a698-f925dbd68f0c contendrá el comando que se ejecutó en el shell de Windows.Cada solicitud HTTP se imprime tanto en la terminal como se registra en urls.json. Las URLs duplicadas no se insertan (es decir, solicitar la misma URL dos veces resultará en solo una línea en urls.json).
active_urls.json contiene la lista de URLs que finalmente resultaron en un payload ejecutable. Este archivo es el más interesante, si buscas derribar sitios de distribución.
snippets.json contiene cada pieza de código que box-js encontró, ya sea JavaScript, un comando de cmd.exe o un script de PowerShell.
resources.json contiene cada archivo escrito en el disco por la muestra. Por ejemplo, si la aplicación intentó guardar Hello world! en $PATH/foo.txt, el contenido de resources.json sería:
{
"9a24...": {
"path": "(path)\\foo.txt",
"type": "ASCII text, with no line terminators",
"md5": "86fb269d190d2c85f6e0468ceca42a20",
"sha1": "d3486ae9136e7856bc42212385ea797094475802",
"sha256": "c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a"
}
}
El archivo resources.json también es importante: presta atención a cualquier recurso ejecutable (ej. con "type": "PE32 executable (GUI) Intel 80386, for MS Windows").
Se ha observado que algunos scripts en la naturaleza usan new Date().getYear() donde new Date().getFullYear(). Si una muestra no muestra ningún comportamiento sospechoso, presta atención a las comprobaciones de Date.
Si te encuentras con archivos .JSE, compila el decodificador y ejecútalo así:
cc decoder.c -o decoder
./decoder foo.jse bar.js
node run bar.js
Ocasionalmente puedes encontrarte con componentes no compatibles. En este caso, puedes abrir un issue en GitHub, o emular el componente tú mismo si sabes JavaScript.
El error típicamente se verá así (los números de línea pueden ser diferentes):
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 ...
Puedes ver que la excepción se lanzó en Proxy.ActiveXObject, que se ve así:
function ActiveXObject(name) {
name = name.toLowerCase();
/* ... */
switch (name) {
case "wscript.shell":
return require("./emulator/WScriptShell");
/* ... */
default:
kill(`Unknown ActiveXObject ${name}`);
break;
}
}
Añade un nuevo case "winhttp.winhttprequest.5.1" (¡nota las minúsculas!), y haz que devuelva un objeto Proxy de ES6 (ej. ProxiedWinHttpRequest). Esto se usa para detectar características no implementadas tan pronto como las solicite la muestra maliciosa:
/* emulator/WinHttpRequest.exe */
const lib = require("../lib");
module.exports = function ProxiedWinHttpRequest() {
return new Proxy(new WinHttpRequest(), {
get: function(target, name, receiver) {
switch (name) {
/* Add here "special" traps with case statements */
default:
if (name in target) return target[name];
else lib.kill(`WinHttpRequest.${name} not implemented!`)
}
}
})
}
function WinHttpRequest() {
}
Vuelve a ejecutar el análisis: fallará de nuevo, diciéndote exactamente lo que no estaba implementado.
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)
Emula WinHttpRequest.open según sea necesario:
function WinHttpRequest() {
this.open = function(method, url) {
URLLogger(method, url);
this.url = url;
}
}
e itera hasta que el código se emule sin errores.
@CapacitorSet: Desarrollador original
@kirk-sayre-work: Mantenedor
--output-directory