Saltar al contenido principal
Version: 1.0.0

El escaneo y los dos interruptores que lo arman

El escaneo activo es la única parte de bitscanner que envía paquetes no solicitados a dispositivos que son de otra persona. Sondear una red que no está autorizado a sondear es una cuestión legal antes que técnica — y el segmento que hay delante del agente lo eligen las interfaces del host, no usted: una VPN, una red de contenedores en puente o una interfaz corporativa muy amplia pueden poner a su alcance espacio de direcciones que nadie pretendía.

Por eso el valor por defecto es la envolvente, no la capacidad. Todos los interruptores de más abajo fallan en cerrado, y los que importan solo puede ponerlos una persona editando un fichero.

Para saber qué reporta el agente sin nada de esto, consulte la visión general de bitscanner.

Los dos interruptores​

No se sondea nada salvo que AMBOS sean ciertos
  1. scanning.enabled arma la envolvente: «este agente puede sondear, este es el alcance, y estoy autorizado para ello».
  2. scanning.probes.<name> elige qué sondas se ejecutan dentro de ella — es decir, cuánto ruido se hace.

Todas las sondas valen false por defecto, así que scanning.enabled: true por sí solo sigue enviando exactamente cero paquetes.

Son dos decisiones porque los dos modos de fallo son distintos: un alcance equivocado sondea a las personas equivocadas, un conjunto de sondas equivocado sondea a las personas correctas con demasiada agresividad. Ampliar cualquiera de los dos es siempre una edición explícita.

scanning:
enabled: false # the envelope
i_accept_scanning_authorization: false # your assertion that you may probe these networks
allowed_cidrs: [] # the ONLY address space that may ever be probed
probes:
neighbor_discovery: false # …and every probe, individually

Una sonda armada mientras la envolvente está apagada es un fallo de arranque que nombra ambas claves, no un ajuste que se ignora en silencio. Ignorarlo en silencio dejaría a un operador leyendo su propio fichero creyendo que hay un escaneo en marcha — o concluyendo que el producto está roto cuando no aparecen resultados:

$ bitscanner config validate --config config.yaml
Error: configuration validation failed: failed to load config: config validation failed:
scanning.probes.neighbor_discovery, scanning.probes.port_scan are true but
scanning.enabled is false: the probe toggles choose WHICH probes run, scanning.enabled
arms the envelope that permits any of them at all, and with the envelope off every one of
those probes would be denied. Either set scanning.enabled: true (together with
scanning.i_accept_scanning_authorization and scanning.allowed_cidrs), or turn off
scanning.probes.neighbor_discovery, scanning.probes.port_scan

El mismo trato se aplica a una sonda cuyo requisito previo está apagado. Un escaneo de puertos se ejecuta sobre la lista de vecinos que produce el descubrimiento de vecinos, así que pedir uno sin el otro es una configuración que se ejecutaría sin producir nada — indistinguible, desde el lado del operador, de un agente roto:

scanning.probes.port_scan is true but scanning.probes.neighbor_discovery is false: the
port scan probes the neighbours that neighbour discovery finds; with neighbour discovery
off the target list is empty and nothing would be scanned. Set
scanning.probes.neighbor_discovery: true as well, or turn off scanning.probes.port_scan

Y habilitar la envolvente sin el reconocimiento de autorización:

scanning.enabled is true but scanning.i_accept_scanning_authorization is false: active
scanning sends unsolicited packets to other people's devices, and setting this key is you
asserting that you are authorised to probe every network listed in scanning.allowed_cidrs
(you own them, or you hold written permission from whoever does). Probing a segment you
are not authorised to probe — a shared/colocated network, or a partner network reachable
over a VPN — may be unlawful.

Tampoco hay un valor por defecto implícito de «escanearlo todo». allowed_cidrs debe estar no vacío cuando la envolvente está activa, las entradas deben ser direcciones de red canónicas, y un 0.0.0.0/0 se rechaza de plano:

scanning.allowed_cidrs entry "10.20.1.3/16" is not a network address: it authorises
10.20.0.0/16 (65536 addresses), which is almost certainly wider than intended. Write the
network explicitly, or use a /32 (or /128) for a single host

Ese rechazo existe porque todos los analizadores de CIDR del mundo amplían en silencio una dirección de host a la que se le pone una máscara de red. El operador escribió una dirección y autorizó 65 534.

scanning.* y security.* solo pueden venir del fichero​

Ponerlos desde una variable de entorno detiene el agente
$ BITSCANNER_SCANNING_ENABLED=true bitscanner config validate --config config.yaml
Error: configuration validation failed: failed to load config: refusing to start:
BITSCANNER_SCANNING_ENABLED set in the environment, and the scanning envelope and the
security block are settable ONLY from the config file. Those keys — scanning.enabled,
scanning.i_accept_scanning_authorization, scanning.allowed_cidrs, every
scanning.probes.*, and security.i_accept_plaintext_egress — are acknowledgements that a
person is authorised to probe somebody else's network, or that a customer's network map
may cross the wire in cleartext. An acknowledgement has to be written by hand into a file
that can be reviewed and diffed, not inherited from a shell profile, a systemd drop-in, a
compose file or a parent process. This is NOT ignored and NOT applied: the agent stops.

Este es el sentido de los reconocimientos, y vale la pena ser franco sobre el porqué.

Todo el valor de i_accept_scanning_authorization está en que una persona lo escribió en un fichero que puede revisarse, compararse y mantenerse en gestión de configuración. Una variable exportada por un perfil de shell, un drop-in de systemd, un fichero compose o un proceso padre comprometido no es nada de eso. Si el entorno pudiera ponerlo, el control sería una formalidad.

Tres propiedades lo hacen estructural en lugar de una regla que alguien deba recordar:

  • El entorno no es una fuente para esas claves. El enlace con el entorno es una lista de permitidos explícita de claves operativas corrientes (agent.id, intervalos, tamaños de lote). Nada de lo que contiene puede armar una sonda ni relajar la seguridad del transporte.
  • Los subárboles de seguridad se releen desde el fichero mediante un segundo cargador que no tiene ningún enlace con el entorno, y esos valores sobrescriben lo que produjera el cargador principal.
  • Un intento es un rechazo, no un filtrado. Todo el espacio de nombres BITSCANNER_SCANNING_* y BITSCANNER_SECURITY_* está reservado, y el error nombra la variable — porque a un operador que cree haber armado un escaneo no se le puede dejar sin respuesta.

El espacio de nombres reservado es deliberadamente más amplio que las claves de configuración: una variable propia suya llamada BITSCANNER_SECURITY_TOKEN también se rechaza. Un rechazo que nombra la variable se resuelve en segundos; una lista de patrones que tiene que adivinar qué grafías importan, no.

La escalera de sondas​

Las siete valen false por defecto. Se listan de la menos intrusiva a la más — y cada línea dice qué sale realmente al cable, porque un operador no puede consentir a algo descrito únicamente por su nombre.

SondaQué pone en el cableNecesita
neighbor_discoveryNada. Lee la propia caché ARP/NDP del kernel y enriquece cada entrada con una tabla de fabricantes MAC/OUI incorporada y sin conexión—
gateway_discoveryNada nuevo. Tabla de rutas más atribución de MAC desde la caché ARP y la tabla OUI sin conexión. ⚠️ Falso en 0.1.3-ga y versiones anteriores — también enviaba un eco ICMP a cada pasarela encontrada; véase la advertencia más abajo—
reverse_dnsUna consulta PTR por cada vecino sin nombre. Esto es un paqueteneighbor_discovery
service_discoveryUna consulta mDNS a 224.0.0.251:5353 y un M-SEARCH SSDP a 239.255.255.250:1900. Un paquete cada uno — y todos los dispositivos del dominio de difusión los venneighbor_discovery
gateway_probeConexiones TCP a los puertos de la propia pasarela (80, 443, 22, 23, 53; luego 8080/8443 para caracterizar servicios, con lectura de banners), más un eco ICMPgateway_discovery
active_arp_sweepINTRUSIVA. Recorre el rango utilizable de cada subred conectada e intenta conexiones TCP a un puñado de puertos de cada dirección — de cientos a miles de intentos de conexión por cicloneighbor_discovery
port_scanLA MÁS INTRUSIVA. Unos 19 puertos TCP habituales en cada vecino descubierto, más lectura de banners en los que presenten uno (21/22/25/110/143)neighbor_discovery

Tres de ellas merecen más que una fila de tabla.

reverse_dns es un interruptor porque no lo era​

El descubrimiento de vecinos se presenta como la primera ejecución segura: leer las tablas del kernel, no enviar nada. Esa afirmación era falsa. Todos los vecinos sin nombre se resolvían de forma inversa sin condiciones, y no había configuración que lo desactivara — una observación de la envolvente de escaneo con solo neighbor_discovery armado registró dos sondas concedidas por ciclo: eran estas resoluciones.

Una resolución inversa es un paquete, y revela más de lo que parece. Va al resolutor al que apunte este host — en un segmento corporativo, a menudo uno que opera y registra el propio equipo de seguridad del cliente — y la consulta revela con precisión cuáles de sus hosts ha enumerado este agente, un PTR cada vez. En un segmento donde el agente se está probando sin que lo sepa el equipo de redes, esa es la divulgación, no el escaneo de puertos.

Está desactivada por defecto para que el modo de cero paquetes sea alcanzable por configuración.

gateway_probe necesita un privilegio que puede no tener​

La comprobación de alcanzabilidad abre un socket ICMP en bruto (ip4:icmp), lo que exige root o CAP_NET_RAW. Sin él, la apertura falla y el agente recurre a un datagrama UDP — y ese recurso alternativo pasa por el guardián por separado, porque un objetivo no autorizado no debe volverse autorizado solo porque el primer método falló.

La pasarela es el router de alguien, y a menudo el único dispositivo cuyo fallo tumba todo el segmento. Estar en su tabla de rutas no es autorización para sondearla, y por eso gateway_discovery (pasiva, responde a «¿detrás de qué router estoy y cambió su MAC?») y gateway_probe (activa) son claves distintas.

gateway_discovery también abría este socket en bruto, en 0.1.3-ga y versiones anteriores

La fila de arriba dice que gateway_discovery no pone nada nuevo en la red, y el párrafo de arriba dice que el socket ICMP en bruto pertenece a gateway_probe. En todas las versiones hasta 0.1.3-ga incluida, ninguna de las dos cosas es cierta.

La llamada de alcanzabilidad estaba en la rama que se toma cuando gateway_probe está desactivada. Así que armar gateway_discovery y declinar deliberadamente gateway_probe — que es exactamente como un operador dice «no abran un socket en bruto contra mi router y no me obliguen a conceder CAP_NET_RAW» — abría uno igualmente, una vez por pasarela y por ciclo, con un datagrama UDP al puerto 33434 como recurso alternativo cada vez que el socket en bruto era rechazado.

Lo que nunca se vio afectado: esa sonda seguía autorizada por el guardián de escaneo, de modo que solo podía alcanzar una dirección dentro de sus allowed_cidrs, y se rechazaba por completo con scanning.enabled: false. El defecto es que un interruptor documentado como apagado actuaba igualmente — no que algo escapara de la envolvente.

Si ejecuta 0.1.3-ga o una versión anterior con gateway_discovery armado, acepte que su pasarela recibe un ping en cada ciclo o desactive gateway_discovery hasta que pueda actualizar. En el código fuente, la vía pasiva ya no envía nada en absoluto: informa de alcanzabilidad únicamente a partir de la entrada ARP resuelta por el propio núcleo, no informa de ningún tiempo de ida y vuelta que no haya medido, y la compilación falla si alguna función distinta de la vía de gateway_probe puede alcanzar ese socket — fijar el socket a una función en lugar de a sus llamadores es lo que permitió que esto sobreviviera a dos revisiones.

active_arp_sweep está acotada por el fichero, no por el host​

Las subredes vienen de las propias interfaces de este host, así que es una VPN o una interfaz corporativa muy amplia — y no su configuración — la que decide qué hay delante del barrido. allowed_cidrs y min_prefix_length son lo que lo contiene. min_prefix_length vale 22 por defecto (unos 1022 hosts) y se rechaza por debajo de /16 sin excepción: no por los paquetes, que acota el presupuesto de hosts, sino porque el bucle del barrido en un /8 recorrería 16 millones de direcciones pidiendo permiso para cada una.

El resultado de cero paquetes, medido​

La afirmación «el descubrimiento de vecinos por sí solo no envía nada» es de las que hay que medir en lugar de asegurar. Ejecutado en un host Linux corriente con scanning.enabled: true, neighbor_discovery armado y todas las demás sondas apagadas — con el rango permitido de abajo sustituido por un rango de documentación y sin alterar nada más:

WARN scanguard active scanning is ENABLED: this agent will send unsolicited probes
{"allowed_cidrs": ["10.20.0.0/24"], "allow_public_targets": false,
"min_prefix_length": 22, "max_hosts_per_cycle": 1024, "max_concurrency": 16,
"per_probe_delay": "50ms", "max_cycle_duration": "5m0s"}
WARN agent ACTIVE SCANNING ARMED: this agent will send unsolicited probes
{"armed_probes": ["neighbor_discovery"], "confined_to_allowed_cidrs": ["10.20.0.0/24"], …}

INFO neighbor_scanner neighbor discovery complete
{"neighbors_found": 116, "subnets": 27, "duration": "71.116454ms"}
INFO network_collector scan envelope
{"probes_allowed_since_start": 0, "probes_denied_since_start": 0,
"denied_by_reason_since_start": {}, "hosts_probed_this_cycle": 0,
"host_budget_remaining_this_cycle": 1024}
INFO network_collector collection cycle complete
{"queued_for_delivery": ["network_state", "neighbor_table", "neighbor_discovery"]}

Cuatro ciclos consecutivos, de 116 a 119 vecinos reales en 27 subredes conectadas, y probes_allowed_since_start se mantuvo en 0 en todos ellos. Al guardián de escaneo nunca se le consultó, porque no había nada que preguntarle: los vecinos salieron de las tablas del kernel y los nombres de fabricante de una tabla incorporada. Aun así se encolaron tres registros de telemetría por ciclo.

Esa es la forma de un primer despliegue: encienda la envolvente, arme solo neighbor_discovery y averigüe qué hay en el segmento antes de decidir si se justifica algo más ruidoso.

Los contadores dicen en qué reloj van

probes_allowed_since_start y probes_denied_since_start son totales del tiempo de vida del proceso; hosts_probed_this_cycle y host_budget_remaining_this_cycle se reinician en cada ciclo. Antes se imprimían juntos bajo un encabezado que afirmaba que los cuatro eran por ciclo, y en un agente en marcha el recuento de concedidas subió de 686 a 1142 entre ciclos mientras el recuento de hosts se mantenía en 16 — lo que se lee como un escaneo que se dispara y no era nada de eso. El guardián no lleva contadores de decisiones por ciclo, así que los totales se etiquetan como totales en lugar de inventar una cifra por ciclo para la línea de registro.

scanguard: la decisión se toma donde sale el paquete​

Cada sonda se autoriza inmediatamente antes de intentar la conexión, no cuando se construye la lista de objetivos.

Por qué esa distinción es todo el diseño

Una lista de objetivos filtrada es una convención, y las convenciones son lo que las futuras rutas de código sortean sin hacer ruido. Alguien añade una ruta que construye su propia lista, o reutiliza una dirección de una respuesta, y el filtro que se aplicó tres funciones antes no se le aplica. Poner la comprobación donde sale el paquete significa que una ruta nueva o le pregunta al guardián o no compila contra la fontanería del recopilador.

El guardián falla en cerrado en todos los niveles: el guardián sin configurar lo deniega todo; un guardián nulo entregado a un recopilador se sustituye por un «denegar todo» en lugar de saltárselo; una dirección que no se analiza — o se analiza de forma ambigua — se deniega; el agotamiento del presupuesto, del ritmo o del plazo deniega en lugar de degradar.

Cada decisión, permitir o denegar, se registra con un motivo legible por máquina:

RechazadoMotivo registrado
El escaneo activo está desactivadoscanning_disabled
El objetivo no se analizaunparseable_target
Bucle local, enlace local, multidifusión, dirección no especificada, dirección de red o de difusión del rango permitidoreserved_address
Cualquier cosa fuera de allowed_cidrsoutside_allowed_cidrs
Espacio de direcciones público sin allow_public_targetspublic_target_not_allowed
Se alcanzó max_hosts_per_cyclehost_budget_exhausted
Se alcanzó max_cycle_durationcycle_duration_exceeded
Una subred más amplia que min_prefix_length, o fuera de alcancesubnet_wider_than_min_prefix, subnet_outside_allowed_cidrs
Un grupo de multidifusión que no es un grupo de descubrimiento conocidonot_a_discovery_multicast_group
El agente se está deteniendo, o el contexto del ciclo se canceló durante el sondeocontext_cancelled

Las concesiones también llevan motivo — in_scope_private, in_scope_public_explicitly_allowed, link_local_discovery_group, subnet_within_sweep_limits — de modo que el rastro de auditoría diga por qué se permitió una sonda, y no solo que se permitió.

Los rechazos se sostienen incluso cuando la lista de permitidos coincidiría. Eso lo ejercita directamente la propia batería de pruebas del guardián, que pasa en el commit distribuido: el bucle local (v4 y v6), el enlace local (v4 y v6), la multidifusión incluido el grupo SSDP, la dirección no especificada, la dirección de difusión limitada y las direcciones de red y de difusión del rango permitido se deniegan todas estando dentro de allowed_cidrs; un host privado dentro del alcance se concede sin la activación explícita de objetivos públicos; y una dirección IPv6 mapeada a IPv4 no puede esquivar la lista de permitidos.

0 probes sent, 4094 denied outside_allowed_cidrs no es un mal funcionamiento. Es la prueba visible para el operador de que es la lista de permitidos la que decide.

Los techos forman parte de la envolvente​

ClavePor defectoLímitesQué contiene
min_prefix_length2216–32La subred más amplia que puede recorrer un barrido
max_hosts_per_cycle10241–65536Hosts distintos sondeados por ciclo, en todas las subredes
max_concurrency161–256Sondas en vuelo a la vez, impuesto por el propio guardián
per_probe_delay50ms0–10 sSeparación mínima entre paquetes de sonda — por sonda, así que un escaneo de 19 puertos a un host se ritma 19 veces
max_cycle_duration5m≤ 1 hTecho de tiempo real para el sondeo dentro de un ciclo
allow_public_targetsfalse—Sondear fuera de RFC1918 / RFC6598 / RFC4193

No son sugerencias de ajuste. Un max_concurrency de 10 000 con retardo cero sobre un /22 es una denegación de servicio contra el propio segmento del cliente, entregada por un binario que instaló por recomendación nuestra — así que los valores se comprueban por rango en la carga y se rechazan fuera de él.

Próximos pasos​

  • Qué sale del host — qué contienen realmente los registros descubiertos, cómo viajan y los límites con los que se distribuye esta versión.
  • Visión general de bitscanner — qué produce un ciclo, cuánto cuesta y cómo verificar la descarga.
  • Las protecciones de bitenforcer — el mismo instinto de diseño aplicado a otro radio de impacto: medirlo, nombrarlo y rechazar en lugar de adivinar.
  • Gestión de activos — donde los dispositivos descubiertos se convierten en activos con propietario.

¿Te resultó útil esta página?