Proof-of-concept in Go che dimostra CVE-2026-46595 in golang.org/x/crypto/ssh, utilizzando l'ispezione dei simboli di binari stripped e scansioni di immagini per verificare l'uso vulnerabile di NewServerConn.
https://www.cve.org/CVERecord?id=CVE-2026-46595 è https://pkg.go.dev/vuln/GO-2026-5023 una vulnerabilità nel simbolo NewServerConn di golang.org/x/crypto/ssh come indicato nel secondo link.
Questo repository contiene un semplice programma che dimostra un controllo positivo di vulnerabilità.
Un server golang.org/x/crypto/ssh minimale fissato a x/crypto v0.51.0 (< v0.52.0),
utilizzato per dimostrare che go tool nm su binari non strippati fornisce sia un controllo
positivo che negativo
go mod tidy
go build ./...
Dato che conosciamo il simbolo vulnerabile, lo verifichiamo. Al momento è un binario non strippato ma lo stripperemo per rimuovere le tabelle dei simboli e dimostrare che il simbolo vulnerabile rimane nell'output.
file cve-2026-46595-proof
cve-2026-46595-proof: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=kUaZmGWICI6Buw0K_I7t/L4jXRd3fy2BJy8zNlSYd/lB1_6wYHZLaOLsG0tE26/-FieQlhThdl7AkRj7rl0, BuildID[sha1]=c7ac1dd881dfcaef88a2dd180a8ccb3c5e751e32, with debug_info, not stripped
strings cve-2026-46595-proof | grep golang.org/x/crypto/ssh.NewServerConn
golang.org/x/crypto/ssh.NewServerConn
# ok, ora strippiamolo e ripetiamo
strip cve-2026-46595-proof
file cve-2026-46595-proof
cve-2026-46595-proof: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=kUaZmGWICI6Buw0K_I7t/L4jXRd3fy2BJy8zNlSYd/lB1_6wYHZLaOLsG0tE26/-FieQlhThdl7AkRj7rl0, BuildID[sha1]=c7ac1dd881dfcaef88a2dd180a8ccb3c5e751e32, stripped
strings cve-2026-46595-proof | grep -E 'ssh\.(Dial|NewServerConn|NewClientConn|NewControlClientConn)'
golang.org/x/crypto/ssh.NewServerConn
Ora controlliamo i binari distribuiti in un'immagine payload di OpenShift.
Estraiamo /usr/bin dall'immagine. --only-files è necessario, altrimenti oc image extract
si interrompe a metà del layer quando incontra un symlink come /usr/bin/ld.so.
IMG=quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:b4dde8227fb99134f71b50f0707eb7f695cfade43b017da3cbc486c2db854fe1
mkdir -p /tmp/cveimg
oc image extract "$IMG" --path /usr/bin/:/tmp/cveimg/ --only-files
ls -laS /tmp/cveimg | head
Per questo digest si tratta dell'immagine cluster-config-operator della 4.21:
-rw-r-----. 1 sdodson sdodson 117899168 Feb 24 2026 cluster-config-operator
-rw-r-----. 1 sdodson sdodson 10639741 Feb 24 2026 cluster-config-operator-tests-ext.gz
Vediamo contro quale versione di x/crypto è stato compilato:
$ go version -m /tmp/cveimg/cluster-config-operator | grep -E 'go1|x/crypto'
/tmp/cveimg/cluster-config-operator: go1.24.13 (Red Hat 1.24.13-1.el9_6) X:strictfipsruntime
dep golang.org/x/crypto v0.40.0
La v0.40.0 è al di sotto di ogni versione corretta, quindi gli scanner basati sulla versione del modulo la segnalano. Ora applichiamo il controllo sui simboli. Oltre a GO-2026-5023 ci sono altre vulnerabilità di golang.org/x/crypto corrette in v0.55.0 e v0.56.0, quindi controlliamo tutti i loro simboli vulnerabili in una volta:
GO-2026-5023 => ssh.NewServerConn GO-2026-6303 => ssh.NewServerConn di nuovo GO-2026-6354 => ssh.Dial ssh.NewClientConn ssh.NewControlClientConn ssh.NewServerConn GO-2026-6355 => ssh.Dial ssh.NewClientConn ssh.NewControlClientConn ssh.NewServerConn
Il binario tests-ext viene distribuito gzippato, quindi decomprimiamolo prima e controlliamo entrambi:
gzip -dc /tmp/cveimg/cluster-config-operator-tests-ext.gz > /tmp/cveimg/cluster-config-operator-tests-ext
strings /tmp/cveimg/cluster-config-operator{,-tests-ext} \
| grep -E 'ssh\.(Dial|NewServerConn|NewClientConn|NewControlClientConn)'
Nessun output, evviva nessuna vulnerabilità! Verifichiamo che il grep non sia silenzioso per il banale motivo che non ci sono simboli con cui confrontarsi:
$ go tool nm /tmp/cveimg/cluster-config-operator | wc -l
150240
$ go tool nm /tmp/cveimg/cluster-config-operator | grep -c golang.org/x/crypto/ssh
0
$ go tool nm /tmp/cveimg/cluster-config-operator-tests-ext | wc -l
24607
$ go tool nm /tmp/cveimg/cluster-config-operator-tests-ext | grep -c golang.org/x/crypto
39
Quindi x/crypto è effettivamente linkato, il linker ha semplicemente eliminato tutto x/crypto/ssh perché
nulla lo raggiunge.
Questo dovrebbe essere riproducibile sulle altre immagini payload; l'unica variazione per immagine è
dove si trovano i binari e se sono compressi come il -tests-ext.gz sopra.
Nota che i binari di test come *-tests-ext vengono distribuiti nelle immagini anche se non sono
su alcun percorso di esecuzione. Dove includono x/crypto/ssh (il k8s-tests-ext di kubernetes
collega ssh.NewClientConn per i suoi helper SSH e2e) è comunque fuori dal percorso di esecuzione del
componente in esecuzione. Indipendentemente da ciò lavoreremo per rimuovere i binari di test dalle immagini perché le
rendono più grandi e rallentano cose come il pull delle immagini.
Quanto segue è stato generato da Claude durante la creazione dei binari del caso di test positivo, non necessario per la proof ma l'ho lasciato.
Questo non è necessario per dimostrare che la vulnerabilità esiste, quanto sopra lo dimostra già ma sentiti libero di farlo comunque.
go run . -addr 127.0.0.1:2222 -user demo -password demo
Poi, in un altro terminale:
ssh -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null [email protected]
La password è demo. La sessione stampa un saluto e rimanda indietro l'input.
Una nuova chiave host ed25519 viene generata ad ogni avvio, quindi nulla viene scritto su disco.
Alla v0.51.0, govulncheck segnala 10 vulnerabilità raggiungibili in
golang.org/x/crypto/ssh, tutte tracciate attraverso main.go →
ssh.NewServerConn (GO-2026-5018 traccia anche attraverso ssh.NewSignerFromKey).
go get golang.org/x/[email protected]
go mod tidy
govulncheck ./...
La v0.52.0 ne elimina la maggior parte, ma tre richiedono release più recenti: GO-2026-6303 è corretta in v0.55.0, e GO-2026-6354 / GO-2026-6355 in v0.56.0. La scansione alla v0.56.0 dovrebbe risultare pulita.