Ritorno
Tech 7 min di lettura - 20 gen. 26 - Molly Allerhand

Come i colori in un terminale possono compromettere la tua sicurezza

Introduction

Immagina la scena: lanci uno strumento CLI per diagnosticare un server in prod. Visualizza informazioni su un process sospetto... poi, senza che tu capisca veramente perché, il tuo schermo salta, delle righe scompaiono, e appare un prompt: "Sessione scaduta. Reinserisci il tuo token:"
Obbedisci (per riflesso), incolli un segreto, e lo hai appena dato... a un'illusione. Non un malware, non un binario sospetto: solo testo, arricchito da sequenze di escape ANSI interpretate dal tuo emulatore di terminale.
È precisamente questo che rende le sequenze di escape ANSI così insidiose: si tende a considerare l'output del terminale come visualizzazione, mentre è anche un linguaggio di controllo. Se un attaccante riesce a iniettare queste sequenze in una stringa che verrà stampata (nome di servizio, comando di process, variabile d'ambiente, logs...), può manipolare ciò che vedi, spingerti all'errore, e a volte persino interagire con gli appunti.
Buone notizie: questo non è teorico. Apache Tomcat è un caso reale molto eloquente a causa della CVE-2025-55754.
Abbiamo individuato lo stesso problema nello strumento open-source witr, e abbiamo aperto una (PR sur GitHub), per correggerlo.

Cosa sono le sequenze ANSI?

Le sequenze ANSI (o "ANSI escape sequences") sono sequenze di caratteri che di solito iniziano con il carattere ESC (Escape, 0x1B, spesso scritto \\\\x1b) e che indicano al terminale: "non trattarlo come testo, trattalo come un'istruzione".
Servono per molte cose utili e legittime:
  • Colorare il testo (rosso, verde, grassetto, ecc.)
  • Spostare il cursore
  • Cancellare una riga, lo schermo, o parti dell'interfaccia
  • Modificare stati (ad es. modalità "alternative screen"), titoli di finestra, ecc.
La forma più nota è la sequenza CSI (Control Sequence Introducer) che assomiglia a:
  • ESC + [ + parametri + lettera di comando
  • Quando lo si rappresenta come stringa, si vedrà spesso \\\\x1b[ ... m (il m essendo tipico degli stili/colori)
Esempio semplice: mettere del testo in rosso e poi tornare allo stile normale:
  • \\\\x1b[31m → rosso
  • \\\\x1b[0m → reset

Esempio con un cambio di colore

Controllo dell'interfaccia

Alcune sequenze permettono di cancellare una riga o riposizionare il cursore (molto usato dalle barre di progresso). È pratico... ed è esattamente il tipo di meccanismo che diventa pericoloso se la stringa visualizzata è controllata da un terzo.

Il rischio di sicurezza

Una vulnerabilità del terminale legata alle sequenze ANSI non permette necessariamente un'esecuzione di codice a distanza che funzioni sempre. Molto spesso, è un attacco di manipolazione: ciò che l'utente vede non è più la realtà. E in DevSecOps, vedere il falso è spesso sufficiente per prendere una decisione sbagliata.
Ecco gli scenari più comuni.

1) Spoofing visivo: falsi prompt, falsi errori, false validazioni

Se uno strumento CLI stampa valori inaffidabili (ad es. nome del processo, argomenti, env vars, campi provenienti da un'API), un attaccante può iniettare sequenze per:
  • nascondere un avviso,
  • cancellare una riga scomoda per l'attaccante,
  • spostare il cursore e riscrivere un'uscita,
  • produrre una visualizzazione che assomiglia a un prompt di sistema.
La trappola: l'utente crede di interagire con una richiesta legittima (sudo, token, login...), mentre è solo un'illusione stampata da un programma. E gli esseri umani sono dei parser molto tolleranti.
Questo tipo di problematica è stata osservata su larga scala nell'ecosistema K8s/OpenShift: l'iniezione di sequenze ANSI in campi visualizzati nel terminale è stata documentata (in particolare attorno alla CVE-2021-25743) in ricerche sull'abuso degli emulatori di terminale.

2) Log poisoning: quando i tuoi log diventano un'arma

Quando si parla di attacchi legati al terminale, si immagina quasi sempre uno scenario di SSH compromesso, con un attaccante di fronte. Ma il vettore più subdolo è il logging:
  • log di applicazioni visualizzati in console,
  • log CI/CD,
  • registri di osservabilità,
  • tracce stampate in dashboard che finiscono... copiate/incollate in un terminale.
Un esempio recente e molto concreto: la CVE-2025-58160 in tracing-subscriber (Rust). Input inaffidabili potevano iniettare sequenze ANSI nell'output del terminale tramite i log, permettendo di manipolare la visualizzazione (titoli, cancellazione, ecc.) e di ingannare l'operatore.

3) Appunti, titoli, link cliccabili: l'attacco "a ritardo"

Alcune famiglie di sequenze (in particolare riguardo a ciò che viene chiamato "OSC" in molti terminali) permettono di interagire con funzionalità "OS-like":
  • titolo di finestra,
  • link cliccabili (phishing via URL ingannevole),
  • e in certi ambienti: interazione con gli appunti.
Un caso studio recente da conoscere: Apache Tomcat non sfuggiva alle sequenze ANSI in alcuni messaggi di log. Se Tomcat girava in una console (in particolare su Windows con supporto ANSI), un attaccante poteva iniettare sequenze tramite un URL appositamente progettato per manipolare la console e il clipboard, e tentare di spingere un admin a eseguire un comando controllato dall'attaccante (attacco di tipo social engineering).

4) Perché è così frequente nelle CLI?

Perché gli strumenti CLI mostrano informazioni fornite dal sistema operativo o da altri processi, una parte delle quali può essere controllata o influenzata da un attaccante.
  • righe di comando (argv) di processi,
  • variabili d'ambiente,
  • nomi di servizi,
  • campi provenienti da API (K8s, cloud, orchestratori),
  • nomi di file (a volte controllabili),
  • messaggi di log (spesso controllabili).
Nella PR che abbiamo aperto, riassumiamo il problema: stringhe "user-controlled / system-derived" possono contenere caratteri di controllo, inclusi gli escape ANSI, e stamparli così come sono può alterare la visualizzazione, cancellare informazioni, o impattare gli appunti.

witr, che cos'è?

witr ("Why is this running?") è uno strumento CLI orientato alla diagnostica: serve a spiegare perché un process/service esiste e quale catena di causalità (systemd, container, shell, cron, ecc.) lo mantiene in vita, con un output "narrativo" e leggibile.
In concreto, witr aggrega e visualizza informazioni provenienti dal sistema: riga di comando dei processi, relazioni genitore/figlio e contesto di esecuzione al fine di dare all'amministratore di sistema una visione comprensibile dell'origine di un servizio, senza dover incrociare manualmente più comandi (ps, systemctl, docker, ecc.).

Il problema identificato

Il fulcro dell'argomento: witr stampa molti dati provenienti dal sistema (process command lines, env vars, service names...). Ora, questi dati possono essere influenzati da un attaccante locale (o da una supply chain / un job malevolo / un container compromesso), e contenere sequenze ANSI.

La strategia di correzione

La correzione non si basa su una semplice patch, ma su una scelta di architettura lato visualizzazione, il che merita una leggera deviazione tecnica.
Piuttosto che sperare che ogni fmt.Printf(...) sia correttamente “sanitizzato” da ogni futuro maintainer, la PR adotta un approccio più duraturo: centralizzare la protezione al punto di scrittura verso il terminale.
L'idea è semplice ma strutturante: introdurre un writer "safe" che sanitizza sistematicamente tutto ciò che viene scritto verso l'output standard, e autorizzare le sequenze ANSI solo quando sono esplicitamente dichiarate come affidabili (es. i colori aggiunti volontariamente dallo strumento).
Nella codebase, questa soluzione si implementa con 3 mattoncini:
  • un SafeTerminalWriter che implementa io.Writer e neutralizza i caratteri di controllo pericolosi,
  • un wrapper Printer attorno alle funzioni di stampa al fine di evitare l'uso diretto di fmt.Print*
  • e un meccanismo "escape hatch" (tramite un tipo ansiString) per le sequenze ANSI controllate dall'applicazione (come i colori).

La soluzione tecnica

Obiettivo: impedire che una stringa non affidabile venga interpretata come un'istruzione terminale.
Esistono due approcci complementari:
  1. Neutralizzare / sfuggire i caratteri di controllo (inclusi ESC)
  2. Autorizzare gli ANSI solo tramite un canale di fiducia (es. una funzione di colorazione interna)

Prima / dopo (esempio semplificato)

Prima: stampa diretta di una stringa proveniente dal sistema
// cmdline vient de /proc ou d'une API système
fmt.Fprintf(os.Stdout, "Command: %s\\\\n", cmdline)
Dopo: sanificazione centralizzata
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))

Gli esempi precedenti illustrano il principio, ma rimangono volontariamente semplificati.
In una CLI reale, utilizzata in produzione e destinata a evolvere, la protezione contro le sequenze ANSI deve essere più fine e più sistematica
  • filtrare specificamente ESC (0x1b) e certe sequenze,
  • riconoscere ed escapare le principali sequenze ANSI (CSI, OSC, ecc.)
  • e soprattutto avvolgere la scrittura verso il terminale in un componente dedicato (tramite un safe writer), affinché un'omissione occasionale non reintroduca la vulnerabilità.
Lato ecosistemi, esistono librerie utili:
  • JS/TS: rilevamento/filtraggio tramite libs tipo ansi-regex, soppressione tramite strip-ansi, generazione controllata tramite ansi-escapes
  • Python: pulizia tramite re + allowlist, o wrapper di output
  • Go: mappatura di rune + writer (io.Writer) sicuri

Buone pratiche e conclusione

Se ci fosse un solo punto da ricordare, è che l'output di un terminale fa parte integrante della superficie d'attacco di uno strumento CLI. Qualsiasi dato visualizzato senza controllo, sia che provenga da un utente, un servizio o il sistema, deve essere trattato come inaffidabile.

Desideri essere accompagnato per lanciare il tuo progetto digitale ?

Invia il tuo progetto ora