id: exposed-zookeeper

info:
  name: Apache ZooKeeper - Unauthenticated Access
  author: pdteam,munzzyy
  severity: high
  description: |
    Apache ZooKeeper was able to be accessed without any required authentication. Since four-letter word commands are disabled by default (ZK 3.5.3+) unless explicitly whitelisted via zookeeper.4lw.commands.whitelist, a plain "envi" check alone can miss instances that are still fully readable. A second request speaks the native ZooKeeper client protocol directly (connect handshake followed by a getChildren call on "/") to confirm read access to the actual znode tree regardless of the whitelist configuration.
  impact: |
    An unauthenticated attacker can read the ZooKeeper znode tree, exposing service configuration, cluster topology, Kafka broker registrations, and other sensitive coordination metadata. Write access may also be possible depending on ACL configuration.
  remediation: |
    Enable SASL/Kerberos or mutual TLS authentication on ZooKeeper. Restrict network access to port 2181 via firewall rules, and set zookeeper.requireClientAuthScheme to enforce client authentication.
  reference:
    - https://zookeeper.apache.org/security.html
    - https://zookeeper.apache.org/doc/r3.5.5/zookeeperAdmin.html#sc_zkCommands
    - https://github.com/h1thub/Unauthorized-zookeeper
  classification:
    cwe-id: CWE-306
  metadata:
    verified: true
    max-request: 2
    shodan-query: 'X-Powered-By: ZooKeeper'
    fofa-query: protocol="zookeeper"
  tags: network,zookeeper,unauth,exposure,tcp,discovery

flow: tcp(1) || tcp(2)

tcp:
  - inputs:
      - data: "envi\r\nquit\r\n"

    host:
      - "{{Hostname}}"
    port: 2181
    read-size: 2048

    matchers:
      - type: word
        words:
          - "zookeeper.version"

    extractors:
      - type: regex
        regex:
          - "zookeeper\\.version=([^\r\n]+)"

  - inputs:
      - data: "0000002d000000000000000000000000000075300000000000000000000000100000000000000000000000000000000000"
        type: hex
        read: 1024

      - data: "0000000e0000000100000008000000012f00"
        type: hex
        read: 4096

    host:
      - "{{Hostname}}"
    port: 2181

    matchers-condition: and
    matchers:
      - type: word
        part: raw
        words:
          - "zookeeper"

      - type: dsl
        dsl:
          - "len(raw) > 70"

    extractors:
      - type: regex
        part: raw
        regex:
          - "zookeeper"
# digest: 490a00463044022067a8ae96b6af41ed2c2281ce8824b87b062940f238002c2aecdd80273a11cd6002207da35a41411385ca92e243240ea4cf9e8d9f7546a62e6cbeaf14152370aa6877:922c64590222798bb761d5b6d8e72950