
Módulo rápido de descoberta de rede baseado em navegador
Módulo rápido de descoberta de rede baseado em navegador
Este projeto não é mais mantido.
netmap.js fornece capacidades de descoberta de hosts e varredura de portas baseadas em navegador para permitir mapear as redes dos visitantes do site.
É bastante rápido, utilizando es6-promise-pool para executar eficientemente o máximo de conexões simultâneas permitidas pelos navegadores.
Precisava de um scanner de portas baseado em navegador para uma ideia em que estava a trabalhar. Pensei que seria simples importar um módulo existente ou copiar e colar de outro projeto como BeEF.
Acontece que não existia um módulo npm decente e pronto a usar e o módulo port_scanner do BeEF é (no momento da escrita) impreciso, lento e não funciona no Chromium.
netmap.js é, portanto, um "ping" sweeper e scanner TCP um tanto otimizado que funciona em todos os navegadores modernos.
npm install --save netmap.js
Vamos descobrir o endereço IP do gateway de um visitante do site, começando por uma lista de candidatos prováveis num ambiente doméstico:
import NetMap from 'netmap.js'
const netmap = new NetMap()
const hosts = ['192.168.0.1', '192.168.0.254', '192.168.1.1', '192.168.1.254']
netmap.pingSweep(hosts).then(results => {
console.log(results)
})
{
"hosts": [
{ "host": "192.168.0.1", "delta": 1003, "live": false },
{ "host": "192.168.0.254", "delta": 1001, "live": false },
{ "host": "192.168.1.1", "delta": 18, "live": true },
{ "host": "192.168.1.254", "delta": 1002, "live": false }
],
"meta": {}
}
O host 192.168.1.1 parece estar ativo.
Vamos tentar encontrar algumas portas TCP abertas em alguns hosts:
import NetMap from 'netmap.js'
const netmap = new NetMap()
const hosts = ['192.168.1.1', '192.168.99.100', 'google.co.uk']
const ports = [80, 443, 8000, 8080, 27017]
netmap.tcpScan(hosts, ports).then(results => {
console.log(results)
})
{
"hosts": [
{
"host": "192.168.1.1",
"control": "22",
"ports": [
{ "port": 443, "delta": 15, "open": false },
{ "port": 8000, "delta": 19, "open": false },
{ "port": 8080, "delta": 21, "open": false },
{ "port": 27017, "delta": 26, "open": false },
{ "port": 80, "delta": 95, "open": true }
]
},
{
"host": "192.168.99.100",
"control": "1001",
"ports": [
{ "port": 8080, "delta": 40, "open": true },
{ "port": 80, "delta": 1001, "open": false },
{ "port": 443, "delta": 1000, "open": false },
{ "port": 8000, "delta": 1004, "open": false },
{ "port": 27017, "delta": 1000, "open": false }
]
},
{
"host": "google.co.uk",
"control": "1001",
"ports": [
{ "port": 443, "delta": 67, "open": true },
{ "port": 80, "delta": 159, "open": true },
{ "port": 8000, "delta": 1001, "open": false },
{ "port": 8080, "delta": 1002, "open": false },
{ "port": 27017, "delta": 1000, "open": false }
]
}
],
"meta": {}
}
À primeira vista, os resultados podem parecer contraditórios.
192.168.1.1 é uma máquina Linux embarcada (um router) no segmento de rede local, e a única porta aberta é a 80. Podemos ver que demorou cerca de 5 vezes mais para o navegador gerar erro na 80 comparado com as outras portas fechadas.
192.168.99.100 é uma VM apenas host com a porta 8080 aberta e google.co.uk é um host externo com as portas 443 e 80 abertas. Nestes casos, o navegador gerou um erro relativamente rápido nas portas abertas enquanto as portas fechadas simplesmente esgotaram o tempo limite. A secção Teoria mais abaixo explica quando isto acontece.
Para determinar se as portas devem ser marcadas como abertas ou fechadas, o netmap.js irá varrer uma porta de "controlo" (por defeito 45000) que se assume estar fechada. O tempo control é então usado para determinar o estado das outras portas. Se a razão delta/control for maior que um valor definido (por defeito 0.8), assume-se que a porta está fechada (resumindo: uma diferença superior a 20% do tempo de controlo significa que a porta está aberta).
Os navegadores mantêm uma lista negra de portas para as quais se recusam a conectar (como FTP, SSH ou SMTP). Se tentar varrer essas portas com netmap.js usando o protocolo predefinido (http) obterá um timeout muito curto. Um timeout curto é geralmente sinal de que a porta está fechada, mas no caso de portas na lista negra não significa nada.
Pode consultar as listas negras nestas fontes:
Antes do Firefox 61 (e possivelmente outros navegadores), é possível contornar esta limitação usando o protocolo ftp em vez de http para estabelecer conexões. Pode especificar o protocol no objeto de opções ao instanciar o NetMap. Ao usar ftp, deve esperar que portas abertas atinjam o timeout e portas fechadas gerem erro relativamente rápido. A varredura com ftp também está sujeita às limitações em torno dos pacotes TCP RST discutidas neste documento.
Pedidos de sub-recursos de protocolos "legados" como ftp estão bloqueados há algum tempo no Chromium.
A funcionalidade de "ping" sweep fornecida pelo netmap.js faz um bom trabalho em encontrar rapidamente hosts baseados em *nix ativos num segmento de rede local (outros computadores, telemóveis, routers, impressoras, etc.)
No entanto, devido à implementação, isto não funcionará quando os pacotes TCP RST não são retornados. Tipicamente:
A razão para isto é explicada na secção Teoria abaixo.
Esta limitação não afeta as capacidades de varredura TCP e ainda é possível determinar se os hosts acima estão ativos ao tentar encontrar uma porta aberta neles.
No geral, descobri que este módulo é mais preciso e rápido do que outros trechos de código que encontrei na web. Dito isto, toda a ideia de mapear redes a partir de um navegador é naturalmente instável. Os resultados podem variar.
NetMapO construtor NetMap recebe um objeto de opções que permite configurar:
protocol usado para varredura (por defeito http, veja Listas Negras de Portas para saber porque pode querer defini-lo como ftp)timeout de conexão da porta (por defeito 1000 milissegundos)import NetMap from 'netmap.js'
const netmap = new NetMap({
protocol: 'http',
timeout: 3000
})
pingSweep()O método pingSweep() determina se um determinado array de hosts está ativo. Faz isto verificando se a conexão a uma porta atinge o timeout, caso em que o host é considerado offline (veja "Ping" Sweep para limitações e Caso Padrão para a teoria).
O método recebe os seguintes parâmetros:
hosts array de hosts a varrer (endereços IP ou nomes de host)options objeto com:
maxConnections - o número máximo de conexões simultâneas (por defeito 10 no Chrome e 17 noutros navegadores - o máximo de conexões simultâneas suportadas pelos navegadores)port a varrer (por defeito 45000)Retorna uma promise.
netmap.pingSweep(['192.168.1.1'], {
maxConnections: 5,
port: 80
}).then(results => {
console.log(results)
})
tcpScan()O método tcpScan() irá realizar uma varredura de portas contra um intervalo de alvos. Leia o Caso Padrão para entender como faz isto.
O método recebe os seguintes parâmetros:
hosts array de hosts a varrer (endereços IP ou nomes de host)ports lista de portas a varrer (inteiros entre 1-65535, evite portas nas listas negras)options objeto com:
maxConnections - o número máximo de conexões simultâneas (por defeito 6 - o máximo de conexões por domínio que os navegadores permitem)portCallback - um callback a executar quando uma combinação host:porta individual terminar a varreduracontrolPort - a porta a varrer para determinar um delta de base para porta fechada (por defeito 45000)controlRatio - a similaridade, em percentagem, do delta de controlo para considerar uma porta como fechada (por defeito 0.8, veja exemplo)Retorna uma promise.
netmap.tcpScan(['192.168.1.1'], [80, 27017], {
maxConnections: 5,
portCallback: result => {
console.log(result)
},
controlPort: 45000,
controlRatio: 0.8
}).then(results => {
console.log(results)
})
Consulte o exemplo para interpretar a saída.
Esta secção cobre brevemente a teoria por trás das técnicas de descoberta do módulo.
Este módulo usa objetos Image para tentar solicitar recursos de origem cruzada (a série de URLs http://{host}:{port} em teste). O tempo que o navegador leva a gerar um erro (o delta), ou a ausência de erro após um certo valor de timeout, fornece informações sobre o estado do host e porta em análise.
Um host ativo geralmente responde relativamente rápido com um pacote TCP RST ao tentar conectar a uma porta fechada.
Se a porta estiver aberta, e mesmo que não esteja a executar um servidor HTTP, o navegador demorará um pouco mais a gerar um erro devido à sobrecarga de estabelecer uma conexão TCP completa e depois perceber que não consegue obter uma imagem a partir do URL fornecido.
Um host offline naturalmente não responderá com um RST nem permitirá que uma conexão TCP completa seja estabelecida. Os navegadores ainda tentarão estabelecer a conexão por algum tempo antes de atingir o timeout (~90 segundos). O netmap.js atinge o timeout após esperar 1000 milissegundos por defeito.
Em resumo:
delta muito curtodelta ligeiramente mais longoO caso padrão é ilustrado pelo host 192.168.1.1 no exemplo Varredura de Portas TCP.
RSTAlguns hosts (como google.co.uk ou hosts Windows) e algumas configurações de rede (como redes apenas host do VirtualBox) não retornam pacotes TCP RST ao atingir uma porta fechada.
Nestes casos, as portas fechadas geralmente atingem o timeout enquanto as portas abertas rapidamente geram um erro.
A implementação do método pingSweep() é, portanto, pouco fiável quando os pacotes RST não são retornados.
Em resumo, quando os pacotes TCP RST não são retornados por qualquer motivo:
delta curtopingSweep() não consegue distinguir entre um timeout de porta fechada e um timeout de host "morto"O caso especial é ilustrado pelos hosts 192.168.99.100 e google.co.uk no exemplo Varredura de Portas TCP.
Está bem documentado que também deve ser possível mapear redes com WebSockets e AJAX.
Tentei (e também adaptei o BeEF para tentar o seu módulo port_scanner apenas com WebSockets e AJAX); achei ambos os métodos a produzir resultados completamente pouco fiáveis.
Por favor, avise-me se estou a ignorar algo a este respeito.