
Uma ferramenta para estudar malware JavaScript.
Uma utilidade para analisar JavaScript malicioso.
Basta instalar o box-js a partir do npm:
npm install box-js --global
O box-js também está disponível:
- como um módulo do Cuckoo (veja o diretório
integrationse Nwinternights/Cuckoo_Boxjs);- como um Dockerfile (veja
integrations/README.md);- como um pacote em distribuições para profissionais de segurança (REMnux, BlackArch);
- como parte de aplicações de código aberto (Intel Owl);
- como parte de serviços comerciais de terceiros (any.run).
Digamos que você tenha uma amostra chamada sample.js: para analisá-la, basta executar
box-js sample.js
É provável que você também queira baixar quaisquer payloads; use a flag --download para ativar o download. Caso contrário, o mecanismo simulará um erro 404, de modo que o script será enganado, pensando que o site de distribuição está fora do ar e contactando quaisquer sites de fallback.
O Box.js irá emular um ambiente Windows JScript, exibirá um resumo da emulação no console e criará uma pasta chamada sample.js.results (se já existir, criará sample.js.1.results e assim por diante). Esta pasta conterá:
analysis.log, um log da análise tal como foi impressa na tela;snippets.json, uma lista de trechos de código executados pela amostra (JavaScript, comandos shell, etc.);urls.json, uma lista de URLs contactados;active_urls.json, uma lista de URLs que parecem distribuir malware ativo;resources.json, os streams ADODB (ou seja, os arquivos que o script escreveu em disco) com tipos de arquivo e hashes;IOC.json, uma lista de comportamentos identificados como IOCs (Indicadores de Comprometimento). Estes incluem acessos ao registro, arquivos escritos, requisições HTTP e assim por diante.Você pode analisar estes por conta própria, ou submetê-los automaticamente ao Malwr, VirusTotal ou uma sandbox Cuckoo: para mais informações, execute box-export --help.
Para maior isolamento, é recomendado executar a análise em um contêiner Docker temporário. Consulte
integrations/README.mdpara mais informações.
Se desejar automatizar a análise, pode usar os códigos de retorno - documentados em
integrations/README.md- para distinguir entre diferentes tipos de erros.
O repositório box-js do git inclui um arquivo boilerplate.js. Este arquivo define algumas versões stubbed de objetos comuns do navegador, como document. Tente reexecutar sua análise com a opção --prepended-code=DIR/boilerplate.js, onde DIR é o diretório do repositório clonado do box-js, ou com --prepended-code=default. A opção --prepended-code diz ao box-js para anexar o JavaScript no arquivo dado antes da amostra a ser analisada.
Note que você pode copiar o boilerplate.js e adicionar suas próprias classes stubbed, objetos, etc., conforme necessário. Use a opção de linha de comando --prepended-code=show-default para imprimir o caminho completo para o arquivo boilerplate.js padrão do box-js.
Enquanto o box.js é tipicamente usado em arquivos individuais, ele também pode executar análises em lote. Você pode simplesmente passar uma lista de arquivos ou pastas para analisar:
box-js sample1.js sample2.js /var/data/mySamples ...
Por padrão, o box.js processará as amostras em paralelo, executando uma análise por núcleo. Você pode usar uma configuração diferente especificando um valor para --threads: em particular, 0 removerá o limite, fazendo com que o box-js crie o máximo de threads de análise possível, resultando em análise muito rápida, mas possivelmente sobrecarregando o sistema (note que as análises geralmente são limitadas pela CPU, não pela RAM).
Você pode usar --loglevel=warn para silenciar mensagens relacionadas à análise e exibir apenas informações de progresso.
Após a análise terminar, você pode extrair as URLs ativas assim:
cat ./*.results/active_urls.json | sort | uniq
NOME DESCRIÇÃO
-h, --help Mostra o texto de ajuda e sai
-v, --version Mostra a versão do pacote e sai
--license Mostra a licença e sai
--debug Morre quando um erro de emulação ocorre, mesmo em "modo batch", e repassa o código de saída
.
--loglevel Nível de log (debug, verbose, info, warning, error - padrão "info")
--threads Quando executando em modo batch, quantas análises rodar ao mesmo tempo (0 =
ilimitado, padrão: tantos quanto o número de núcleos de CPU)
--download Baixar os payloads de fato
--encoding Codificação da amostra de entrada (será detectada automaticamente por padrão)
--timeout O script irá expirar após este número de segundos (padrão 10)
--output-dir A localização no disco para escrever os arquivos e pastas de resultados (padrão é o
diretório atual)
--preprocess Pré-processar o código-fonte original (facilita a engenharia reversa, mas leva
alguns segundos)
--unsafe-preprocess Pré-processamento mais agressivo. Frequentemente resulta em código melhor, mas pode quebrar em
alguns casos extremos (ex.: redefinição de protótipos)
--prepended-code Arquivo ou diretório de entrada contendo código que deve ser anexado antes do(s) arquivo(s) JS
que estamos analisando. Se for um diretório, anexa o conteúdo de todos os arquivos
no diretório. Se for 'default', usa o boilerplate.js padrão que vem com o box-js.
Se for 'show-default', apenas imprime o caminho do boilerplate.js e sai (útil se quiser copiar
e modificar o código boilerplate padrão).
--fake-script-engine O mecanismo de script a reportar em WScript.FullName and WScript.Name (ex.
'cscript.exe', 'wscript.exe', ou 'node'). Padrão é wscript.exe.
--fake-cl-args Argumentos de linha de comando falsos para o script. Na string, devem ser separados por
vírgula.
--fake-sample-name Nome de arquivo falso a usar para a amostra sendo analisada. Pode ser um caminho completo ou apenas
o nome do arquivo. Se você tiver '\' no caminho, escape-os como '\\' neste
valor de argumento de linha de comando (ex. --fake-sample-name=C:\\foo\\bar.js).
--fake-language Especificar o código de idioma a retornar para Win32_OperatingSystem.OSLanguage.
Valores suportados são 'spanish', 'english' e 'portuguese'.
--fake-domain Especificar o domínio do usuário a retornar para WScript.Network.UserDomain.
--fake-download Fingir que requisições HTTP funcionam e fazer com que retornem um payload falso
--no-kill Não matar a aplicação quando erros de tempo de execução ocorrerem
--no-echo Quando o script imprime dados, não imprimi-los no console
--no-rewrite Não reescrever o código-fonte de forma alguma, exceto para suporte a `@cc_on`
--no-catch-rewrite Não reescrever cláusulas try..catch para tornar a exceção de escopo global
--no-cc_on-rewrite Não reescrever `/*@cc_on <...>@*/` para `<...>`
--no-eval-rewrite Não reescrever `eval` para que seu argumento seja reescrito
--no-file-exists Retornar `false` para Scripting.FileSystemObject.FileExists(x)
--limit-file-checks Alternar valor padrão para verificações de existência de pasta/arquivo se muitas verificações forem realizadas
(tentar quebrar loops infinitos de verificação de arquivo).
--no-folder-exists Retornar `false` para Scripting.FileSystemObject.FileExists(x)
--function-rewrite Reescrever chamadas de função para capturar chamadas eval
--no-rewrite-prototype Não reescrever expressões como `function A.prototype.B()` como `A.prototype.B =
function()`
--no-hoist-prototype Não elevar expressões como `function A.prototype.B()` (implicado por
no-rewrite-prototype)
--no-shell-error Não lançar um erro falso ao executar `WScriptShell.Run` (lança um erro falso por padrão para
fingir que os sites de distribuição estão fora do ar, para que o script tente consultar todos os sites)
--no-typeof-rewrite Não reescrever `typeof` (ex. `typeof ActiveXObject`, que deve retornar
'unknown' no padrão JScript e não 'object')
--proxy [experimental] Usar o proxy especificado para downloads. Não é relevante se
a flag --download não estiver presente.
--windows-xp Emular Windows XP (influencia o valor das variáveis de ambiente)
--dangerous-vm Usar o módulo `vm`, em vez de `vm2`. Esta sandbox pode ser quebrada, então **não
use isto** a menos que tenha 100% de certeza do que está fazendo. Ajuda na depuração
fornecendo stack traces corretos.
--rewrite-loops Reescrever alguns tipos de loops para tornar a análise mais rápida
--throttle-writes Limitar o relatório e o rastreamento de dados de escritas de arquivos que escrevem MUITOS dados
--throttle-commands Parar a análise se muitos dos mesmos comandos foram executados
--extract-conditional-code Extrair o código real a analisar de comentários condicionais JScript (/*@if(...).
--loose-script-name Reescrever verificações == para que comparações do nome do script atual com um nome de script
hard coded sempre retornem true.
--real-script-name Retornar o nome real do arquivo do script atualmente analisado em vez de um nome
falso.
--activex-as-ioc Registrar todas as chamadas ActiveX como IOC's e tentar determinar se a chamada está ofuscada
no código JS.
--ignore-wscript-quit Ignorar chamadas para WSCript.Quit() e continuar a execução.
--ignore-rewrite-errors Analisar a amostra original se alguma reescrita de amostra falhar.
A primeira fonte de informação é a saída do console. Em uma análise bem-sucedida, normalmente imprimirá algo assim:
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/
...
Neste caso, estamos vendo um dropper que baixa um arquivo de http://foo.bar/baz, definindo o cabeçalho HTTP User-Agent como Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0). Em seguida, procede a decodificá-lo e escreve o resultado no disco (um executável PE32). Finalmente, executa algum comando no shell do Windows.
sample.js.results/a0af1253-597c-4eed-9e8f-5b633ff5f66a conterá o payload conforme foi baixado de http://foo.bar/baz;sample.js.results/f8df7228-7e0a-4241-9dae-c4e1664dc5d8 conterá o payload real (executável PE);sample.js.results/d241e130-346f-4c0c-a698-f925dbd68f0c conterá o comando que foi executado no shell do Windows.Toda requisição HTTP é tanto impressa no terminal quanto registrada em urls.json. URLs duplicadas não são inseridas (ou seja, solicitar a mesma URL duas vezes resultará em apenas uma linha em urls.json).
active_urls.json contém a lista de URLs que eventualmente resultaram em um payload executável. Este arquivo é o mais interessante, se você está procurando derrubar sites de distribuição.
snippets.json contém cada trecho de código que o box-js encontrou, seja JavaScript, um comando cmd.exe ou um script PowerShell.
resources.json contém todos os arquivos escritos em disco pela amostra. Por exemplo, se a aplicação tentou salvar Hello world! em $PATH/foo.txt, o conteúdo de resources.json seria:
{
"9a24...": {
"path": "(caminho)\\foo.txt",
"type": "ASCII text, with no line terminators",
"md5": "86fb269d190d2c85f6e0468ceca42a20",
"sha1": "d3486ae9136e7856bc42212385ea797094475802",
"sha256": "c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a"
}
}
O arquivo resources.json também é importante: fique atento a qualquer recurso executável (ex. com "type": "PE32 executable (GUI) Intel 80386, for MS Windows").
Alguns scripts na natureza foram observados usando new Date().getYear() onde deveriam usar new Date().getFullYear(). Se uma amostra não estiver mostrando nenhum comportamento suspeito, fique atento a verificações de Date.
Se você encontrar arquivos .JSE, compile o decodificador e execute-o assim:
cc decoder.c -o decoder
./decoder foo.jse bar.js
node run bar.js
Ocasionalmente, você pode encontrar componentes não suportados. Nesse caso, você pode abrir uma issue no GitHub, ou emular o componente você mesmo, se souber JavaScript.
O erro normalmente se parecerá com isto (os números de linha podem 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 ...
Você pode ver que a exceção foi levantada em Proxy.ActiveXObject, que se parece com isto:
function ActiveXObject(name) {
name = name.toLowerCase();
/* ... */
switch (name) {
case "wscript.shell":
return require("./emulator/WScriptShell");
/* ... */
default:
kill(`Unknown ActiveXObject ${name}`);
break;
}
}
Adicione um novo case "winhttp.winhttprequest.5.1" (note a minúscula!), e faça com que retorne um objeto Proxy do ES6 (ex. ProxiedWinHttpRequest). Isto é usado para capturar funcionalidades não implementadas assim que são solicitadas pela amostra maliciosa:
/* emulator/WinHttpRequest.exe */
const lib = require("../lib");
module.exports = function ProxiedWinHttpRequest() {
return new Proxy(new WinHttpRequest(), {
get: function(target, name, receiver) {
switch (name) {
/* Adicione aqui "armadilhas" especiais com case statements */
default:
if (name in target) return target[name];
else lib.kill(`WinHttpRequest.${name} não implementado!`)
}
}
})
}
function WinHttpRequest() {
}
Reexecute a análise: falhará novamente, dizendo exatamente o que não foi implementado.
1 Jan 00:00:00 - WinHttpRequest.open não implementado!
Trace
at kill (/home/CapacitorSet/box-js/run.js:24:10)
at Object.ProxiedWinHttpRequest.Proxy.get (/home/CapacitorSet/box-js/run.js:89:7)
Emule WinHttpRequest.open conforme necessário:
function WinHttpRequest() {
this.open = function(method, url) {
URLLogger(method, url);
this.url = url;
}
}
e itere até que o código emule sem erros.
@CapacitorSet: Desenvolvedor original
@kirk-sayre-work: Mantenedor
--output-directory