Was den Host verlässt
bitscanner versendet eine Beschreibung des Netzes von jemandem von der Maschine weg, auf der er läuft. Damit werden zwei Fragen tragend: was darin steht und wie es reist. Diese Seite beantwortet beide und benennt anschließend die Grenzen dieses Releases unumwunden.
Was das Sondieren überhaupt erst scharfstellt, steht unter Scannen, und die beiden Schalter, die es scharfstellen.
Was in der Telemetrie steht
Alles, was bitscanner versendet, betrifft das Netz rund um den Host, nie die Inhalte des Hosts selbst.
| Datensatz | Felder |
|---|---|
network_state | Nur Routen: destination, gateway, interface, metric, flags |
neighbor_table | Je Nachbar: ip_address, hardware_addr, interface, state, type |
neighbor_discovery | Je Nachbar: Adresse, MAC, vendor (Offline-OUI-Nachschlag), device_type, is_reachable, first_seen/last_seen — dazu hostname, wenn reverse_dns scharfgestellt ist, services (Ports, und Banner, wenn port_scan scharfgestellt ist), mdns_services/ssdp_info, wenn service_discovery scharfgestellt ist. Dazu die gescannten Subnetze |
gateway_discovery | Je Gateway: Adresse, MAC, Hersteller, is_default, Erreichbarkeit und — ausschließlich mit gateway_probe — die Ports, die geantwortet haben, sowie jedes Banner, das sie vorgezeigt haben |
service_discovery | Antwortende Geräte: name, type, host, port, protocol, discovered_via, mDNS-TXT-Einträge |
Zwei Folgen, die ausdrücklich genannt gehören:
- Die lauteren Sonden erfassen die Softwareversionen anderer Leute. Ein Banner von Port 22
oder 25 ist eine Versionsangabe, die zu einem Gerät gehört, das Ihnen womöglich nicht
gehört. Das ist kein Nebeneffekt, den man später entdeckt; genau dafür sind
port_scanundgateway_probeda, und genau deshalb sind sie standardmäßig aus und hinter einer Berechtigungsbestätigung verriegelt. hostnameexistiert nur, wenn Siereverse_dnsscharfgestellt haben. Ohne das ist ein Nachbar eine Adresse, eine MAC und eine Vermutung zum Hersteller.
Was er bewusst nicht erfasst
network_state trug früher interfaces (Name, MTU, MAC, Adressen), listeners (das lokale
Socket-Inventar), eine immer leere connections-Liste und ein Feld dns_servers, das nie
irgendetwas befüllt hat — ein Nullfeld, das ein Resolver-Inventar behauptete, das nie erhoben
wurde. Alle sind verschwunden, samt dem Code und den Typen dahinter.
Die ersten drei waren nichts anderes als die Netzwerk- und Port-Collectors von bitcollector, noch einmal aufgesagt. bitscanner erfasst keine Host-Fakten — keine Prozesse, keine installierten Pakete, keine lokalen Konten, keine Kommandozeilen. Wenn Sie einen Datensatz über die Maschine selbst brauchen, kommt er vom Collector, unter dessen Datenschutz-Voreinstellungen.
Jeder Datensatz ist in einen Umschlag verpackt, der eine SHA-256-checksum über seinen Header
und seine Nutzlast trägt. Das ist eine Integritätsprüfung, keine Signatur — bitscanner hat
kein Gegenstück zu bitcollectors signierter, per Hash verketteter
Nachweiskette.
Ausgehender Verkehr läuft über https, sonst startet der Agent nicht
Die Regel betrifft die Daten, nicht die Zugangsdaten: Eine Karte des Kundennetzes, die im Klartext über die Leitung geht, ist für jeden auf dem Weg lesbar und veränderbar — ob nun ein Token mitreist oder nicht.
telemetry.destinations[0]: endpoint http://es.example.com:9200 is not https — refusing to
start. Everything this agent collects about the customer's network would cross that link
in cleartext, readable and alterable by anyone on the path, credential or no credential.
Use an https endpoint; if this is a lab and you accept that the data is exposed, set
security.i_accept_plaintext_egress: true
security.i_accept_plaintext_egress ist das einzige Opt-out, es steht ausschließlich in der
Datei, es wird bei jedem Start im Log angekündigt, und es ist für Labore gedacht. Sind
Zugangsdaten konfiguriert, gibt es überhaupt kein Opt-out — ein Klartext-Endpunkt oder
tls.skip_verify: true wird rundheraus abgelehnt, denn alles, was für den Endpunkt antworten
kann, sammelt die Zugangsdaten ein.
Drei weitere Ablehnungen derselben Familie:
- In die URL eingebettete Zugangsdaten werden abgelehnt, mit der Anweisung, sie in den
auth-Block zu verschieben. Go macht aus dem Userinfo-Teil einen echtenAuthorization: Basic-Header, er landet in jeder Log-Zeile, die den Endpunkt ausgibt, und die Schw ärzung des Agenten kommt nicht an ihn heran. - Zertifikatsmaterial, das stillschweigend verworfen würde, wird abgelehnt.
ca_cert/client_cert/client_keyzu setzen, währendtls.enabledfalse ist, ist ein Fehler, denn der Abschnitt läse sich wie mutual TLS, während nichts vorgezeigt wird. headers:gibt es nicht. Die alte Beispielkonfiguration bewarbheaders: {Authorization: "Bearer YOUR-TOKEN"}, und es gab nie eine Zeile Go, die das gelesen hätte — Operatoren glaubten, ihre Telemetrie sei authentifiziert, während sie anonym hinausging. Eine Konfiguration, die diesen Schlüssel enthält, wird namentlich zurückgewiesen.
Es wird niemals einem Redirect gefolgt
refusing to follow the redirect from <from> to <to>: this agent never follows redirects on
egress, because net/http would re-attach the credential when the hostname matches (even
downgrading https to http) and would replay the batch body on a 307/308 to a host the
server chose. Point the endpoint at its final URL instead
Beide Hälften davon sind Eigenschaften der Go-Standardbibliothek, keine Spekulation. Ihre
Regel für das erneute Anhängen von Authorization vergleicht ausschließlich den
Hostnamen — nicht das Schema, nicht den Port —, sodass ein Server, der mit
301 Location: http://same-host:80/ antwortet, das Bearer-Token im Klartext zurückbekommt.
Ein eigener API-Key-Header steht auf der Liste sensibler Header der Standardbibliothek
überhaupt nicht, wird also an jeden Host über jedes Schema kopiert. Und bei einem
307/308 wird der Anfrage-Body — das Netzinventar des Kunden — an genau den Host wiederholt
gesendet, den der Redirect nennt.
Der ausgehende Client ist kein *http.Client. Jedes Feld dieses Structs ist exportiert,
sodass c.CheckRedirect = nil oder ein ausgetauschter Transport beide Kontrollen mit einer
einzigen Zuweisung entfernt hätte — und für beide Änderungen wurde nachgewiesen, dass die
Testsuite dabei vollständig grün bleibt. Der Client steckt in einem Typ, der nach außen nur
Do und CloseIdleConnections zeigt, und der Transport wird aus einer TLS-Konfiguration
gebaut, statt vom Aufrufer entgegengenommen zu werden — es bleibt also kein vom Aufrufer
gelieferter RoundTripper übrig, mit dem sich eine Anfrage umschreiben ließe.
Die Zugangsdaten werden zum Sendezeitpunkt angewendet, nicht beim Zusammenbauen der Anfrage, und im Zweifelsfall wird verweigert: Wenn der Kanal oder die Zugangsdaten nicht in Ordnung sind — nicht aufgelöst, nicht https, Zertifikatsprüfung aus —, wird der Batch nicht gesendet. „Setze den Header, falls wir zufällig einen haben“ ist genau die Form, die schon einmal unauthentifizierte Telemetrie ausgeliefert hat.
Endpunkte werden in Logs geschwärzt
Bei jedem Exportfehler wird ein Endpunkt protokolliert; deshalb schwärzt der Agent im Zweifel
alles, statt sich auf eine Liste von Namen zu verlassen, die nach Zugangsdaten aussehen:
jeder Query-Wert und das gesamte Fragment werden geschwärzt, wobei die Parameternamen
erhalten bleiben, damit die Zeile weiterhin diagnostizierbar ist (api_key=[redacted] sagt,
welcher Parameter gesetzt war). Auch Pfadsegmente, die nach Zugangsdaten aussehen, werden
geschwärzt. Lässt sich die URL überhaupt nicht parsen, gibt der Schwärzer eine Konstante
zurück — niemals den Text des Aufrufers, denn eine URL, die sich nicht parsen lässt, ist meist
eine, deren Passwort genau die Zeichen enthält, die den Parser brechen.
Dieser letzte Punkt hat eine ausgesprochene Grenze: Die Pfad-Heuristik kann ein kurzes oder
wortähnliches Geheimnis in einem Pfadsegment übersehen. Die tragende Kontrolle ist, dass
Zugangsdaten in den auth-Block gehören, wo sie typisiert sind, geprüft werden und niemals in
eine Zeichenkette formatiert werden.
Geheimnisdateien
Ein Token ist erst dann wirklich aus der Konfigurationsdatei heraus, wenn niemand sonst es
lesen kann. bitscanner config validate verweigert bei jedem der folgenden Fälle den Start,
und die Meldung benennt den beanstandeten Pfad und die Abhilfe. Durch Ausführung verifiziert —
die Pfade unten sind Dokumentationspfade, eingesetzt anstelle derer aus dem echten Mitschnitt:
# mode
control_plane: auth.token_file "/etc/bitscanner/control-plane.token" is mode 0644 —
readable by group or other; restrict it with `chmod 600 /etc/bitscanner/control-plane.token`
# any directory on the path, not just the one holding the file
control_plane: the directory "/opt/agent" on the path to auth.token_file
"/opt/agent/secrets/control-plane.token" is mode 0775 — group- or world-writable, so
another account can replace the file this agent reads. It is not the directory holding the
file — it is 1 level(s) above it — but another account can rename the whole subtree and
put its own in place. Restrict it with `chmod 755 /opt/agent` (or tighter), or move the
secret somewhere only root and this agent can write
Die vollständige Menge der Ablehnungen:
| Abgelehnt | Weil |
|---|---|
| Ein Modus, der für Gruppe oder andere lesbar ist | Jedes lokale Konto auf dem Host kann das Token lesen |
| Einem dritten Konto gehörend | „Nur der Eigentümer kann es lesen“ ist nichts wert, wenn der Eigentümer jemand anderes ist |
| Keine reguläre Datei | Ein FIFO oder ein Gerät ist keine Geheimnisdatei — und ein einfaches Öffnen eines FIFO ohne Schreiber würde den Agenten beim Start ohne jede Diagnose hängen lassen |
| Über einen Symlink erreicht | Das Ziel des Links liegt in einem Verzeichnis, in das die Prüfung nicht geschaut hätte |
Jedes durch die Gruppe oder alle beschreibbare Verzeichnis auf dem Pfad — einschließlich /tmp | Dieses Konto kann den Teilbaum umbenennen und seine eigene Datei an dessen Stelle setzen |
Abgelaufen werden sowohl die Vorgängerverzeichnisse des aufgelösten Pfades als auch der
Pfad so, wie er geschrieben steht, und für den aufgelösten Pfad wird über Gerät und Inode
belegt, dass es genau die Datei ist, deren Deskriptor gelesen wird. Jedes davon war ein echtes
Loch, geschlossen in jeweils einer anderen Welle: nur das unmittelbare Elternverzeichnis zu
pr üfen; ein für alle beschreibbares Großelternverzeichnis zu übersehen; und dann das
Spiegelbild davon — nur die aufgelöste Kette abzulaufen, was eine 0600-Datei in einem
0700-Verzeichnis akzeptierte, das über ein 0777-Verzeichnis erreicht wurde, weil die
aufgelöste Kette makellos ist und der Angreifer in der geschriebenen Kette sitzt.
Schließlich stammt jedes Geheimnis aus genau einer Quelle: dem Inline-Wert, einer
<field>_file oder einer <field>_env, die eine Umgebungsvariable benennt. Zwei Quellen
gleichzeitig sind ein Fehler und keine Vorrangregel — mit einer Vorrangregel kann ein
Operator, der token_file hinzufügt und dabei ein veraltetes Inline-Token stehen lässt, nicht
erkennen, welches davon über die Leitung geht.
Enrolment: Der Schlüssel verlässt den Host nie
Sie erzeugen im Cert-IX-Dashboard ein einmalig verwendbares Enrolment-Token und legen es in eine Datei, die nur der Agent lesen kann:
enrollment:
endpoints:
- "https://<your-cert-ix-agent-endpoint>"
enrollment_token_file: "/etc/bitscanner/enrollment.token" # chmod 600, owner-only
Beim ersten Start tut der Agent Folgendes:
- Er erzeugt auf dem Host ein ECDSA-P-256-Schlüsselpaar. Die private Hälfte wird mit
0600inagent.data_dirgeschrieben, das auf0700gezwungen wird — beim Anlegen und durch ein erneuteschmod, dennMkdirAlllässt den Modus eines bestehenden Verzeichnisses unangetastet, und ein Upgrade in ein0755-Datenverzeichnis würde ihn sonst beibehalten. Der private Schlüssel verlässt die Maschine nie. - Er registriert sich genau einmal und legt dabei das Enrolment-Token im Header
X-Agent-Enrollment-Tokenund seinen öffentlichen Schlüssel im Body vor. - Er speichert das Agent-JWT, das das Gateway zurückgibt, mit
0600, zusammen mit dessen Ablaufzeitpunkt.
Danach ist das Token verbraucht: Die Datei kann gelöscht werden, und ein Neustart lädt die
gespeicherte Identität und registriert sich nicht erneut. Eine zweite Registrierung wäre ein
409 gegen ein bereits verbrauchtes Token, und der Agent bliebe tot. Das JWT wird eine Stunde
vor seinem Ablauf am Erneuerungs-Endpunkt erneuert, unter Verwendung des Tokens selbst — ein
Enrolment-Token ist daran nicht beteiligt.
Es wird ausschließlich in X-Agent-Enrollment-Token vorgelegt, bei genau einer Anfrage.
Es in auth.token_file zu legen, ist die Fehlkonfiguration, die das Onboarding in einem
früheren Build unmöglich machte: Das Ingest-Gateway will ein Agent-JWT, also endet jede
Telemetrie-Anfrage mit 401. Verwenden Sie stattdessen auth: {type: enrollment} am Ziel —
damit wird die gespeicherte Identität vorgelegt, und es gibt nichts einzufügen.
Der Enrolment-Endpunkt muss unter jeder Konfiguration https sein.
security.i_accept_plaintext_egress gilt dafür nicht — über ihn kommt die Identität zurück.
Weder der private Schlüssel noch das Token werden je protokolliert, in eine Fehlermeldung
formatiert oder in ein Log-Feld gelegt: Die eigenen Methoden String() und GoString() des
Identitätstyps geben nur die Agent-ID und den Ablaufzeitpunkt aus, sodass ein versehentliches
%v das JWT dieses Hosts nicht für immer in eine Logdatei schreiben kann.
Ehrliche Grenzen
Hier benannt, statt später entdeckt.
Die Advisories der Go-Standardbibliothek, die 0.1.2 betrafen, sind in 0.1.3 behoben
0.1.3-ga ist mit go1.25.12 gebaut, und das bringt die Korrektur für beide Advisories
mit, denen 0.1.2-ga (gebaut mit go1.26.4) ausgesetzt war:
| Advisory | CVSS | Aktiv ausgenutzt? | Worum es geht | Behoben in 0.1.3-ga |
|---|---|---|---|---|
CVE-2026-39822 (GO-2026-4970) | 7.8 Hoch | Nein — nicht auf der CISA-KEV-Liste, EPSS unterhalb der Schwelle | Ausbruch aus dem Wurzelverzeichnis über einen Symlink plus abschließenden Schrägstrich in os | ✅ |
CVE-2026-42505 (GO-2026-5856) | 5.3 Mittel | Nein — nicht auf der CISA-KEV-Liste, EPSS unterhalb der Schwelle | Preisgabe privater Informationen bei Encrypted Client Hello in crypto/tls | ✅ |
🪤 Wissenswert, wenn Sie Toolchains selbst festpinnen: Eine höhere Go-Version ist nicht
immer die korrigierte. CVE-2026-39822 ist in go1.25.12 auf der 1.25-Linie behoben, auf der
1.26-Linie aber erst in go1.26.5 — go1.26.4, eine numerisch neuere Toolchain, trug sie also
weiterhin. Eine einzelne „Mindestversions“-Untergrenze kann einen zweiglinienspezifischen
Backport nicht ausdrücken; deshalb pinnt dieses Release ein exaktes Image fest und überlässt
die Autorität dem Scanner statt der Versionsnummer. Unser eigener Build wurde genau daran
einmal verweigert, bevor das Pinning korrigiert war.
Wenn Sie noch 0.1.2-ga betreiben, trägt es das High-Advisory — aktualisieren Sie. Die
Toolchain-Version gibt bitscanner version aus, und sie ist in der CycloneDX-SBOM des Releases
festgehalten — prüfen Sie also den Build, der vor Ihnen liegt, statt dieser Tabelle für immer
zu vertrauen.
„Zugestellt“ heißt „es kam ein 2xx zurück“
Der Agent meldet, was er eingereiht hat und was angenommen wurde:
INFO network_collector collection cycle complete
{"queued_for_delivery": ["network_state", "neighbor_table", "neighbor_discovery"]}
INFO telemetry telemetry batch accepted by destination {"status": 200, …}
Ein 2xx ist das Wort des Ziels und kein Beleg dafür, dass der Datensatz gespeichert wurde;
deshalb steht dort mit Absicht accepted und nicht delivered. Mehr kann der Agent nicht
wissen, und eine Zeile, die etwas anderes behauptete, würde eine Garantie erfinden. Die
queued_for_delivery-Zeile wird in jedem Zyklus ausgegeben — auch bei ausgeschaltetem
Scannen —, damit ein Zustellfehler niemals mit einem Collector verwechselt werden kann, der
gar nicht läuft.
config validate kann ein Enrolment-Token nicht von einem JWT unterscheiden
Beide sind undurchsichtige Zeichenketten in einer Datei; ein bitscanner config validate auf
einer Konfiguration, deren auth.token_file ein Enrolment-Token enthält, meldet daher:
Configuration is valid.
… und jeder Export scheitert anschließend zur Laufzeit. Die Validierung prüft die
Berechtigungen der Datei, ihren Eigentümer, jedes Verzeichnis auf dem Weg dorthin und dass
genau eine Quelle konfiguriert ist — sie prüft nicht und kann nicht prüfen, ob die Bytes darin
die richtige Art von Zugangsdaten sind. Wenn Ihre Telemetrie bei einer frischen Installation
mit 401 scheitert, ist das die erste Sache, die Sie nachsehen sollten.
Nächste Schritte
- Scannen, und die beiden Schalter, die es scharfstellen — was jede Sonde ins Netz sendet, und der Guard, der es freigibt.
- bitscanner — Überblick — was ein Zyklus hervorbringt, und wie Sie den Download verifizieren.
- Datenschutz bei bitcollector — die entsprechenden Voreinstellungen für Host-Daten: Kommandozeilen aus, keine Umgebungsvariablen, keine Passwort-Hashes.
- Ihre Datenschutzrechte (DSGVO) — wie Cert-IX Anfragen zu personenbezogenen Daten bearbeitet.
War diese Seite hilfreich?