Zurück
Tech 7 Min. Lesezeit - 20 Jan. 26 - Molly Allerhand

Wie Farben in einem Terminal Sie kompromittieren können

Introduction

Stellen Sie sich die Szene vor : Sie starten ein CLI-Tool, um einen Produktionsserver zu diagnostizieren. Es zeigt Informationen über einen verdächtigen Prozess an... dann, ohne dass Sie wirklich verstehen, warum, springt Ihr Bildschirm, Zeilen verschwinden, und ein Prompt erscheint : "Sitzung abgelaufen. Bitte geben Sie Ihr Token erneut ein :"
Sie gehorchen (reflexartig), Sie fügen ein Geheimnis ein, und Sie haben es gerade... einer Illusion gegeben. Kein Malware, kein verdächtiges Binärprogramm : nur Text, angereichert mit ANSI-Escape-Sequenzen, die von Ihrem Terminal-Emulator interpretiert werden.
Genau das macht ANSI-Escape-Sequenzen so verräterisch : man neigt dazu, die Terminalausgabe als Anzeige zu betrachten, während es auch eine Kontrollsprache ist. Falls ein Angreifer es schafft, diese Sequenzen in eine Kette einzuschleusen, die gedruckt wird (Dienstname, Prozessbefehl, Umgebungsvariable, Logs...), kann er manipulieren, was Sie sehen, Sie in die Irre führen und manchmal sogar mit der Zwischenablage interagieren.
Gute Nachricht : das ist nicht theoretisch. Apache Tomcat ist ein sehr anschaulicher realer Fall wegen der CVE-2025-55754.
Wir haben das gleiche Problem im Open-Source-Tool entdeckt witr, und wir haben eine (PR auf GitHub), um es zu beheben.

Was sind ANSI-Sequenzen?

ANSI-Sequenzen (oder "ANSI escape sequences") sind Zeichenfolgen, die normalerweise mit dem Zeichen ESC (Escape, 0x1B, oft geschrieben \\\\x1b) und dem Terminal signalisieren: "behandle das nicht als Text, behandle es als Anweisung".
Sie dienen vielen nützlichen und legitimen Zwecken:
  • Text einfärben (rot, grün, fett, etc.)
  • Den Cursor verschieben
  • Eine Zeile, den Bildschirm oder Teile der Anzeige löschen
  • Zustände ändern (z.B. Modus "alternative screen"), Fenstertitel, etc.
Die bekannteste Form ist die CSI-Sequenz (Control Sequence Introducer) die so aussieht:
  • ESC + [ + Parameter + Befehlsbuchstabe
  • Wenn man das als String darstellt, sieht man oft \\\\x1b[ ... m (das m typisch für Stile/Farben ist)
Einfaches Beispiel: Text rot färben und dann zum normalen Stil zurückkehren:
  • \\\\x1b[31m → rot
  • \\\\x1b[0m → reset

Beispiel mit einem Farbwechsel

Anzeigesteuerung

Bestimmte Sequenzen ermöglichen es, eine Zeile zu löschen oder den Cursor neu zu positionieren (wird häufig von Fortschrittsbalken verwendet). Das ist praktisch... und genau der Mechanismus, der gefährlich wird, wenn die angezeigte Zeichenfolge von einem Dritten kontrolliert wird.

Das Risiko für die Sicherheit

Eine terminalbezogene Schwachstelle im Zusammenhang mit ANSI-Sequenzen ermöglicht nicht zwangsläufig eine Remote-Code-Ausführung, die immer funktioniert. Meistens handelt es sich um einen Manipulationsangriff: was der Benutzer sieht, entspricht nicht mehr der Realität. Und in DevSecOps reicht es oft aus, etwas Falsches zu sehen, um eine falsche Entscheidung zu treffen.
Hier sind die häufigsten Szenarien.

1) Visuelles Spoofing: gefälschte Prompts, gefälschte Fehler, gefälschte Validierungen

Wenn ein CLI-Tool unzuverlässige Werte ausgibt (z.B. Process-Name, Arguments, Env Vars, Felder von einer API), kann ein Angreifer Sequenzen injizieren, um:
  • eine Warnung zu maskieren,
  • eine für den Angreifer störende Zeile zu löschen,
  • den Cursor zu verschieben und eine Ausgabe neu zu schreiben,
  • eine Anzeige zu erzeugen, die einem System-Prompt ähnelt.
Die Falle: der Benutzer glaubt, mit einer legitimen Anfrage zu interagieren (sudo, token, login...), während es nur eine von einem Programm gedruckte Illusion ist. Und Menschen sind sehr tolerante Parser.
Diese Art von Problematik wurde in grossem Massstab im K8s/OpenShift-Ökosystem beobachtet: die Injektion von ANSI-Sequenzen in im Terminal angezeigte Felder wurde dokumentiert (insbesondere im Zusammenhang mit der CVE-2021-25743) in Forschungen über den Missbrauch von Terminal-Emulatoren.

2) Log poisoning: wenn Ihre Logs zu einer Waffe werden

Wenn man über terminalbezogene Angriffe spricht, stellt man sich fast immer ein kompromittiertes SSH-Szenario mit einem Angreifer gegenüber vor. Aber der heimtückischste Vektor ist das Logging:
  • in der Konsole angezeigte Anwendungs-Logs,
  • CI/CD-Logs,
  • Observability-Journale,
  • in Dashboards gedruckte Traces, die schliesslich... in ein Terminal kopiert/eingefügt werden.
Ein aktuelles und sehr konkretes Beispiel: die CVE-2025-58160 in tracing-subscriber (Rust). Unzuverlässige Eingaben konnten ANSI-Sequenzen in die Terminal-Ausgabe über die Logs injizieren, was die Manipulation der Anzeige (Titel, Löschen, etc.) und das Täuschen des Operators ermöglichte.

3) Zwischenablage, Titel, klickbare Links: der "Zeitverzögerte"-Angriff

Bestimmte Sequenzfamilien (insbesondere im Zusammenhang mit dem, was in vielen Terminals als "OSC" bezeichnet wird) ermöglichen die Interaktion mit “OS-like"-Funktionen:
  • Fenstertitel,
  • klickbare Links (Phishing über irreführende URL),
  • und in gewissen Umgebungen: Interaktion mit der Zwischenablage.
Ein aktueller Fall, den man kennen sollte: Apache Tomcat entging die ANSI-Sequenzen in gewissen Log-Meldungen nicht. Wenn Tomcat in einer Konsole lief (insbesondere unter Windows mit ANSI-Unterstützung), konnte ein Angreifer Sequenzen über eine speziell gestaltete URL injizieren, um die Konsole und das Clipboard zu manipulieren und versuchen, einen Admin dazu zu bringen, einen vom Angreifer kontrollierten Befehl auszuführen (Social-Engineering-Angriff).

4) Warum ist das in CLIs so häufig?

Weil CLI-Tools Informationen anzeigen, die vom Betriebssystem oder von anderen Prozessen bereitgestellt werden, wovon ein Teil von einem Angreifer kontrolliert oder beeinflusst werden kann.
  • Befehlszeilen (argv) von Prozessen,
  • Umgebungsvariablen,
  • Dienstnamen,
  • Felder aus APIs (K8s, Cloud, Orchestratoren),
  • Dateinamen (manchmal kontrollierbar),
  • Log-Meldungen (oft kontrollierbar).
Im von uns geöffneten PR fassen wir das Problem zusammen: «user-controlled / system-derived»-Zeichenketten können Steuerzeichen, einschliesslich ANSI-Escapes, enthalten, und diese so wie sie sind auszudrucken kann die Anzeige beeinträchtigen, Informationen löschen oder die Zwischenablage beeinflussen.

witr, was ist das?

witr ("Why is this running?") ist ein diagnostisch orientiertes CLI-Tool: Es dient dazu zu erklären, warum ein Prozess/Service existiert und welche Kausalkette (systemd, Container, Shell, Cron usw.) ihn am Leben erhält, mit einer «narrativen» und lesbaren Ausgabe.
Konkret, witr aggregiert und zeigt Informationen aus dem System an: Befehlszeilen von Prozessen, Eltern-/Kind-Beziehungen und Ausführungskontext mit dem Ziel, dem Systemadministrator eine verständliche Übersicht über den Ursprung eines Dienstes zu geben, ohne manuell mehrere Befehle abgleichen zu müssen (ps, systemctl, docker, usw.).

Das identifizierte Problem

Der Kern des Themas: witr druckt viele Daten aus dem System (process command lines, env vars, service names...). Diese Daten können jedoch von einem lokalen Angreifer beeinflusst werden (oder durch eine Supply Chain / einen bösartigen Job / einen kompromittierten Container) und ANSI-Sequenzen enthalten.

Die Korrekturstrategie

Die Korrektur basiert nicht auf einem einfachen Patch, sondern auf einer architektonischen Entscheidung auf der Anzeigeseite, was einen kleinen technischen Umweg verdient.
Anstatt zu hoffen, dass jedes fmt.Printf(...) von jedem zukünftigen Maintainer korrekt «bereinigt» wird, verfolgt der PR einen nachhaltigeren Ansatz: den Schutz am Schreibpunkt zum Terminal hin zu zentralisieren.
Die Idee ist einfach, aber strukturierend: einen «sicheren» Writer einzuführen, der systematisch alles bereinigt, was auf die Standardausgabe geschrieben wird, und ANSI-Sequenzen nur zuzulassen, wenn sie explizit als vertrauenswürdig deklariert sind (z. B. die vom Tool absichtlich hinzugefügten Farben).
In der Codebasis wird diese Lösung mit 3 Bausteinen implementiert:
  • ein SafeTerminalWriter der implementiert io.Writer und gefährliche Steuerzeichen neutralisiert,
  • ein Wrapper Printer um die Druckfunktionen herum, um die direkte Verwendung von fmt.Print*
  • und einen «escape hatch»-Mechanismus (über einen Typen ansiString) für von der Anwendung kontrollierte ANSI-Sequenzen (wie Farben).

Die technische Lösung

Ziel: Verhindern, dass eine unzuverlässige Zeichenkette als Terminalbefehl interpretiert wird.
Es gibt zwei ergänzende Ansätze:
  1. Steuerzeichen neutralisieren / escapen (einschliesslich ESC)
  2. ANSI nur über einen vertrauenswürdigen Kanal zulassen (z. B. eine interne Farbgebungsfunktion)

Vorher / Nachher (vereinfachtes Beispiel)

Vorher: Direkter Ausdruck einer Zeichenkette aus dem System
// cmdline vient de /proc ou d'une API système
fmt.Fprintf(os.Stdout, "Command: %s\\\\n", cmdline)
Nachher: Zentralisierte Bereinigung
func SanitizeForTerminal(s string) string {
    // Approche simple : remplacer les caractères de contrôle (dont ESC)
    // en représentation visible. Conserver \\\\n et \\\\t si besoin.
    out := make([]rune, 0, len(s))
    for _, r := range s {
        if r == '\\\\n' || r == '\\\\t' {
            out = append(out, r)
            continue
        }
        if r < 0x20 || r == 0x7f {
            out = append(out, '\\\\x1b')
            continue
        }
        out = append(out, r)
    }
    return string(out)
}

fmt.Fprintf(os.Stdout, "Command: %s\\\\n", SanitizeForTerminal(cmdline))

Die vorhergehenden Beispiele illustrieren das Prinzip, bleiben aber absichtlich vereinfacht.
In einem realen CLI, das in Produktion eingesetzt wird und sich weiterentwickeln soll, muss der Schutz vor ANSI-Sequenzen feiner und systematischer sein
  • spezifisch filtern ESC (0x1b) und gewisse Sequenzen,
  • die wichtigsten ANSI-Sequenzen erkennen und escapen (CSI, OSC usw.)
  • und vor allem das Schreiben zum Terminal hin in eine dedizierte Komponente einbetten (über einen Safe Writer), damit ein einmaliges Vergessen die Schwachstelle nicht wieder einführt.
Im Ökosystem gibt es nützliche Bibliotheken:
  • JS/TS: Erkennung/Filterung über Libs wie ansi-regex, Unterdrückung via strip-ansi, kontrollierte Generierung via ansi-escapes
  • Python: Bereinigung via re + Allowlist, oder Output-Wrapper
  • Go: Rune-Mapping + Writer (io.Writer) gesichert

Gute Praktiken und Fazit

Wenn nur ein Punkt festzuhalten ist, dann der, dass die Ausgabe eines Terminals ein integraler Bestandteil der Angriffsfläche eines CLI-Tools ist. Alle unkontrolliert angezeigten Daten, ob sie von einem Benutzer, einem Dienst oder dem System stammen, müssen als unzuverlässig behandelt werden.

Möchten Sie bei der Lancierung Ihres digitalen Projekts begleitet werden?

Reichen Sie Ihr Projekt jetzt ein