
JAW: Um Framework de Análise de Segurança Baseado em Grafos para JavaScript no Lado do Cliente
Uma implementação protótipo de código aberto de grafos de propriedade para JavaScript baseada no analisador esprima e na EsTree SpiderMonkey Spec. O JAW pode ser usado para analisar o lado cliente de aplicações web e programas baseados em JavaScript.
Este projeto é licenciado sob GNU AFFERO GENERAL PUBLIC LICENSE V3.0. Consulte aqui para mais informações.
O JAW possui um site no Github Pages disponível em https://soheilkhodayari.github.io/JAW/.
Notas de Lançamento:
JAW-V2.JAW-V1.A arquitetura do JAW é mostrada abaixo.
O JAW pode ser usado de duas maneiras distintas:
Análise Arbitrária de JavaScript: Utilize o JAW para modelar e analisar qualquer programa JavaScript especificando o caminho do sistema de arquivos do programa.
Análise de Aplicação Web: Analise uma aplicação web fornecendo uma única URL inicial.
Use os recursos web coletados para criar um Grafo de Programa Híbrido (HPG), que será importado para um banco de dados Neo4j.
Opcionalmente, forneça ao módulo de construção do HPG um mapeamento de tipos semânticos para tokens de linguagem JavaScript personalizados, facilitando a categorização de funções JavaScript com base em seu propósito (por exemplo, funções de requisição HTTP).
Consulte o banco de dados de grafos Neo4j construído para várias análises. O JAW oferece travessias utilitárias para análise de fluxo de dados, análise de fluxo de controle, análise de alcançabilidade e correspondência de padrões. Essas travessias podem ser usadas para desenvolver análises de segurança personalizadas.
O JAW também inclui travessias embutidas para detectar CSRF no lado cliente, DOM Clobbering e vulnerabilidades de sequestro de requisição.
As saídas serão armazenadas na mesma pasta da entrada.
O script de instalação depende dos seguintes pré-requisitos:
npm package manager (node js)python 3.xpipEm seguida, instale as dependências necessárias via:
$ ./install.sh
Para instruções de instalação detalhadas, consulte aqui.
Você pode executar uma instância do pipeline em uma tela de fundo via:
$ python3 -m run_pipeline --conf=config.yaml
A CLI fornece as seguintes opções:
$ python3 -m run_pipeline -h
usage: run_pipeline.py [-h] [--conf FILE] [--site SITE] [--list LIST] [--from FROM] [--to TO]
This script runs the tool pipeline.
optional arguments:
-h, --help show this help message and exit
--conf FILE, -C FILE pipeline configuration file. (default: config.yaml)
--site SITE, -S SITE website to test; overrides config file (default: None)
--list LIST, -L LIST site list to test; overrides config file (default: None)
--from FROM, -F FROM the first entry to consider when a site list is provided; overrides config file (default: -1)
--to TO, -T TO the last entry to consider when a site list is provided; overrides config file (default: -1)
Config de Entrada: O JAW espera um arquivo de configuração .yaml como entrada. Veja config.yaml para um exemplo.
Dica. O arquivo de configuração especifica diferentes passos (por exemplo, rastreamento, análise estática, etc.) que podem ser ativados ou desativados para cada classe de vulnerabilidade. Isso permite executar os blocos de construção da ferramenta individualmente, ou em uma ordem diferente (por exemplo, rastrear todas as aplicações web primeiro, depois realizar a análise de segurança).
Para executar um exemplo rápido demonstrando como construir um grafo de propriedade e executar consultas Cypher sobre ele, faça:
$ python3 -m analyses.example.example_analysis --input=$(pwd)/data/test_program/test.js
Este módulo coleta os dados (ou seja, código JavaScript e valores de estado de páginas web) necessários para teste. Se você quiser testar um arquivo JavaScript específico que já possui em seu sistema de arquivos, pode pular esta etapa.
O JAW possui rastreadores baseados em Selenium (JAW-v1), Puppeteer (JAW-v2, v3) e Playwright (JAW-v3). Para recursos mais atualizados, é recomendável usar as versões baseadas em Puppeteer ou Playwright.
Este rastreador web emprega foxhound, uma versão instrumentada do Firefox, para realizar rastreamento de mancha dinâmico enquanto navega pelas páginas web. Para iniciar o rastreador, faça:
$ cd crawler
$ node crawler-taint.js --seedurl=https://google.com --maxurls=100 --headless=true --foxhoundpath=<caminho-opcional-para-executável-foxhound>
O foxhoundpath é por padrão definido para o seguinte diretório: crawler/foxhound/firefox que contém um binário chamado firefox.
Nota: você precisa de uma compilação do foxhound para usar esta versão. Uma compilação Ubuntu está incluída no lançamento JAW-v3.
Para iniciar o rastreador, faça:
$ cd crawler
$ node crawler.js --seedurl=https://google.com --maxurls=100 --browser=chrome --headless=true
Veja aqui para mais informações.
Para iniciar o rastreador, faça:
$ cd crawler/hpg_crawler
$ vim docker-compose.yaml # defina os sites que deseja rastrear aqui e salve
$ docker-compose build
$ docker-compose up -d
Consulte a documentação do hpg_crawler aqui para mais informações.
Para gerar um HPG para um determinado conjunto de arquivos JavaScript, faça:
$ node engine/cli.js --lang=js --graphid=graph1 --input=/in/file1.js --input=/in/file2.js --output=$(pwd)/data/out/ --mode=csv
argumentos opcionais:
--lang: linguagem do programa de entrada
--graphid: um identificador para o HPG gerado
--input: caminho do(s) programa(s) de entrada
--output: caminho do HPG de saída
--mode: determina o formato de saída (csv ou graphML)
Para importar um HPG dentro de um banco de dados neo4j (instância docker), faça:
$ python3 -m hpg_neo4j.hpg_import --rpath=<caminho-para-a-pasta-dos-arquivos-csv> --id=<xyz> --nodes=<nodes.csv> --edges=<rels.csv>
$ python3 -m hpg_neo4j.hpg_import -h
usage: hpg_import.py [-h] [--rpath P] [--id I] [--nodes N] [--edges E]
This script imports a CSV of a property graph into a neo4j docker database.
optional arguments:
-h, --help show this help message and exit
--rpath P relative path to the folder containing the graph CSV files inside the `data` directory
--id I an identifier for the graph or docker container
--nodes N the name of the nodes csv file (default: nodes.csv)
--edges E the name of the relations csv file (default: rels.csv)
Para criar um grafo de propriedade híbrido para a saída do hpg_crawler e importá-lo dentro de uma instância neo4j local, você também pode fazer:
$ python3 -m engine.api <caminho> --js=<programa.js> --import=<bool> --hybrid=<bool> --reqs=<requisicoes.out> --evts=<eventos.out> --cookies=<cookies.pkl> --html=<html_snapshot.html>
Especificação dos Parâmetros:
<caminho>: caminho absoluto para a pasta contendo os arquivos de programa para análise (deve estar sob a pasta engine/outputs).--js=<programa.js>: nome do programa JavaScript para análise (padrão: js_program.js).--import=<bool>: se o grafo de propriedade construído deve ser importado para um banco de dados neo4j ativo (padrão: true).--hybrid=<bool>: se o modo híbrido está ativado (padrão: false). Isso implica que o testador deseja enriquecer o grafo de propriedade inserindo arquivos para qualquer um dos instantâneos HTML, eventos disparados, requisições HTTP e cookies, conforme coletados pelo rastreador JAW.--reqs=<requisicoes.out>: apenas para modo híbrido, nome do arquivo contendo a sequência de requisições de rede observadas, passe a string false para excluir (padrão: request_logs_short.out).--evts=<eventos.out>: apenas para modo híbrido, nome do arquivo contendo a sequência de eventos disparados, passe a string false para excluir (padrão: events.out).--cookies=<cookies.pkl>: apenas para modo híbrido, nome do arquivo contendo os cookies, passe a string false para excluir (padrão: cookies.pkl).--html=<html_snapshot.html>: apenas para modo híbrido, nome do arquivo contendo o instantâneo da árvore DOM, passe a string false para excluir (padrão: html_rendered.html).Para mais informações, você pode usar a CLI de ajuda fornecida com a API de construção de grafos:
$ python3 -m engine.api -h
O HPG construído pode então ser consultado usando Cypher ou o NeoModel ORM.
Você deve colocar e executar suas consultas em analyses/<NOME_DA_ANALISE>.
Você pode usar o NeoModel ORM para consultar o HPG. Para escrever uma consulta:
example_query_orm.py na pasta analyses/example.$ python3 -m analyses.example.example_query_orm
Para mais informações, consulte aqui.
Você pode usar Cypher para escrever consultas personalizadas. Para isso:
example_query_cypher.py na pasta analyses/example.$ python3 -m analyses.example.example_query_cypher
Para mais informações, consulte aqui.
Esta seção descreve como configurar e usar o JAW para detecção de vulnerabilidades, e como interpretar a saída. O JAW contém, entre outros, consultas independentes para detectar CSRF no lado cliente e DOM Clobbering.
Passo 1. ative o componente de análise para a classe de vulnerabilidade no arquivo de entrada config.yaml:
request_hijacking:
enabled: true
# [...]
open_redirect:
enabled: false
# [...]
domclobbering:
enabled: false
# [...]
cs_csrf:
enabled: false
# [...]
Passo 2. Execute uma instância do pipeline com:
$ python3 -m run_pipeline --conf=config.yaml
Dica. Você pode executar múltiplas instâncias do pipeline sob diferentes screens:
$ screen -dmS s1 bash -c 'python3 -m run_pipeline --conf=conf1.yaml; exec sh'
$ screen -dmS s2 bash -c 'python3 -m run_pipeline --conf=conf2.yaml; exec sh'
$ # [...]
Para gerar arquivos de configuração paralelos automaticamente, você pode usar o script generate_config.py.
As saídas serão armazenadas em um arquivo chamado sink.flows.out na mesma pasta da entrada. Para CSRF no lado cliente, por exemplo, para cada requisição HTTP detectada, o JAW gera uma entrada marcando o conjunto de tipos semânticos (também conhecidos como tags semânticas ou rótulos) associados aos elementos que constroem a requisição (ou seja, as fatias do programa). Por exemplo, uma requisição HTTP marcada com o tipo semântico ['WIN.LOC'] é forjável através do ponto de injeção window.location. No entanto, uma requisição marcada com ['NON-REACH'] não é forjável.
Um exemplo de entrada de saída é mostrado abaixo:
[*] Tags: ['WIN.LOC']
[*] NodeId: {'TopExpression': '86', 'CallExpression': '87', 'Argument': '94'}
[*] Location: 29
[*] Function: ajax
[*] Template: ajaxloc + "/bearer1234/"
[*] Top Expression: $.ajax({ xhrFields: { withCredentials: "true" }, url: ajaxloc + "/bearer1234/" })
1:['WIN.LOC'] variable=ajaxloc
0 (loc:6)- var ajaxloc = window.location.href
Esta entrada mostra que na linha 29, há uma expressão de chamada $.ajax, e esta expressão de chamada dispara uma requisição ajax com o valor de template de url ajaxloc + "/bearer1234/", onde o parâmetro ajaxloc é uma fatia do programa lendo seu valor na linha 6 de window.location.href, sendo portanto forjável através de ['WIN.LOC'].
Para agilizar o processo de teste do JAW e garantir que sua configuração esteja correta, fornecemos uma aplicação web node.js simples com a qual você pode testar o JAW.
Primeiro, instale as dependências via:
$ cd tests/test-webapp
$ npm install
Em seguida, execute a aplicação em uma nova tela:
$ screen -dmS jawwebapp bash -c 'PORT=6789 npm run devstart; exec sh'
Para mais informações, visite nossa página wiki aqui. Abaixo está um índice para acesso rápido.
Pull requests são sempre bem-vindos. Este projeto pretende ser um espaço seguro e acolhedor, e espera-se que os contribuidores sigam o código de conduta do contribuidor.
Se você usar o JAW para pesquisa acadêmica, encorajamos você a citar o seguinte artigo:
@inproceedings{JAW,
title = {JAW: Studying Client-side CSRF with Hybrid Property Graphs and Declarative Traversals},
author= {Soheil Khodayari and Giancarlo Pellegrino},
booktitle = {30th {USENIX} Security Symposium ({USENIX} Security 21)},
year = {2021},
address = {Vancouver, B.C.},
publisher = {{USENIX} Association},
}
O JAW percorreu um longo caminho e queremos dar um merecido destaque aos nossos contribuidores aqui!