generated from Dicken/dickendock
round58: Router-IP vom vollstaendigen Portscan im Sammel-Scan ausnehmen (FritzOS-Angriffserkennung reagierte auf vollen masscan-Scan gegen sich selbst)
This commit is contained in:
@@ -50,6 +50,28 @@ function formatToolsUsed(toolsUsed: {
|
||||
return `${masscanPart}, ${httpxPart}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Löst FRITZBOX_HOST (Umgebungsvariable, siehe /api/scan/fritzbox weiter
|
||||
* unten) zur IP-Adresse des Routers selbst auf - wird genutzt, um genau
|
||||
* dieses eine Gerät vom vollständigen masscan-Portscan im Sammel-Scan
|
||||
* auszunehmen (siehe Kommentar dort). Ist FRITZBOX_HOST bereits eine
|
||||
* IP-Adresse, wird sie direkt übernommen, sonst per DNS aufgelöst. Liefert
|
||||
* null, wenn nicht konfiguriert oder nicht auflösbar - dann greift einfach
|
||||
* keine Ausnahme, wie vor round58.
|
||||
*/
|
||||
async function resolveRouterIp(): Promise<string | null> {
|
||||
const host = process.env.FRITZBOX_HOST;
|
||||
if (!host) return null;
|
||||
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(host)) return host;
|
||||
try {
|
||||
const { lookup } = await import("node:dns/promises");
|
||||
const { address } = await lookup(host);
|
||||
return address;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scannt EIN Gerät (Ports, Titel/Favicon, Softwareerkennung) und schreibt die
|
||||
* Ergebnisse in die Datenbank. Ausgelagert aus der Route, damit sowohl der
|
||||
@@ -249,7 +271,35 @@ export async function scanRoutes(app: FastifyInstance): Promise<void> {
|
||||
// keine masscan-/httpx-Aufrufe mehr, nur noch die (leichtgewichtigen,
|
||||
// reinen HTTP-)Detailabfragen pro Port (Titel, Favicon,
|
||||
// Softwareerkennung).
|
||||
const deviceIps = devices.map((d) => d.ip);
|
||||
// NEU (round58): Die FritzBox (der Router selbst, NICHT ein Repeater
|
||||
// o. ä.) hat eine eingebaute Angriffs-/Portscan-Erkennung
|
||||
// (Sicherheitseinstellungen -> Angriffserkennung). Ein voller
|
||||
// masscan-Lauf über 1-65535 Ports gegen die eigene Router-IP sieht
|
||||
// dafür genau wie ein klassischer Portscan-Angriff aus - die
|
||||
// FritzBox blockt/ignoriert die Scan-Quelle daraufhin offenbar
|
||||
// kurzzeitig, sodass selbst der direkt danach laufende normale
|
||||
// 80/443-Retry (siehe scanDeviceServices) noch nichts findet (siehe
|
||||
// Bugreport: "0 Dienst(e) gefunden (Ports: keine)" bei der FritzBox
|
||||
// selbst, während ein FRITZ!Repeater im selben Lauf ganz normal alle
|
||||
// Dienste liefert - Repeater haben typischerweise nicht dieselbe
|
||||
// Absicherung wie der Router, der ja die Sicherheits-Gateway-Rolle
|
||||
// fürs ganze Netz hat). Die Router-IP wird daher gezielt aus dem
|
||||
// masscan-Batch ausgenommen - für sie greift stattdessen weiter
|
||||
// unten ganz normal die schonende "übliche Portliste"-Prüfung
|
||||
// (kein vollständiger Portscan, kein masscan), ihre echten Dienste
|
||||
// kennen wir ohnehin schon zuverlässig über den eigenen
|
||||
// TR-064-Scanner (siehe scanner/fritzbox.ts).
|
||||
const routerIp = await resolveRouterIp();
|
||||
|
||||
const deviceIps = devices.map((d) => d.ip).filter((ip) => ip !== routerIp);
|
||||
if (routerIp && devices.some((d) => d.ip === routerIp)) {
|
||||
logRepo.logScan({
|
||||
type: "device",
|
||||
targetId: null,
|
||||
level: "info",
|
||||
message: `Sammel-Scan: Router-IP ${routerIp} vom vollständigen Portscan ausgenommen (FritzOS-Angriffserkennung reagiert empfindlich auf volle Portscans gegen sich selbst) - wird stattdessen mit der üblichen Portliste geprüft.`,
|
||||
});
|
||||
}
|
||||
|
||||
// Sichtbarer Fortschritt VOR dem eigentlichen Geräte-Scan: ohne das
|
||||
// stand die Anzeige die ganze Vorabscan-Zeit über bei "1 von 120" und
|
||||
@@ -391,20 +441,22 @@ export async function scanRoutes(app: FastifyInstance): Promise<void> {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// WICHTIG (Bugfix round56): masscanBatch.get(device.ip) liefert
|
||||
// "undefined" in ZWEI völlig unterschiedlichen Fällen: (a) der
|
||||
// gesamte masscan-Batch ist fehlgeschlagen (masscanBatch ===
|
||||
// null), ODER (b) der Batch war erfolgreich, aber DIESES
|
||||
// Gerät hat schlicht KEINE offenen Ports - ein bei Handys/
|
||||
// Smart-Geräten völlig normales, gültiges Ergebnis. Der
|
||||
// bisherige Code hat beide Fälle identisch behandelt und
|
||||
// damit für JEDES Gerät mit null gefundenen Ports fälschlich
|
||||
// einen kompletten, langsamen 65535-Port-TCP-Fallback-Scan
|
||||
// ausgelöst - genau das, was masscan eigentlich vermeiden
|
||||
// sollte. Ein leeres Array ist in JS truthy (nur undefined/
|
||||
// null/0/""/NaN/false sind falsy), daher reicht es, hier statt
|
||||
// "undefined" bewusst ein leeres Array zu liefern, wenn der
|
||||
// Batch als Ganzes erfolgreich war.
|
||||
// Router-IP: siehe Kommentar oben bei resolveRouterIp() -
|
||||
// bewusst NICHT preFetched/fullScan nutzen, sondern die
|
||||
// schonende Standard-Portliste (kein voller 1-65535-Scan
|
||||
// gegen den Router selbst, keine Angriffserkennung
|
||||
// getriggert).
|
||||
const isRouter = routerIp !== null && device.ip === routerIp;
|
||||
if (isRouter) {
|
||||
const result = await performDeviceScan(device, false);
|
||||
created += result.created;
|
||||
updated += result.updated;
|
||||
allStale.push(...result.staleServices);
|
||||
allNameChanges.push(...result.nameChanges);
|
||||
allNew.push(...result.newServices);
|
||||
return;
|
||||
}
|
||||
|
||||
const masscanPorts = masscanBatch ? masscanBatch.get(device.ip) ?? [] : undefined;
|
||||
const preFetched: PreFetchedScanData = {
|
||||
masscanAttempted: true,
|
||||
|
||||
Reference in New Issue
Block a user